source: trunk/gsdl/src/recpt/receptionist.cpp@ 7392

Last change on this file since 7392 was 7390, checked in by davidb, 20 years ago

Functionality extended to support collection specific macro files. Most
of the changes are in 'display.cpp'. Here the main change is to -- at
the point a page is away to be produced -- look in the collection's
macro folder for any macro files. If any are found then they are loaded
in on to of existing macros definitions.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 52.2 KB
Line 
1/**********************************************************************
2 *
3 * receptionist.cpp -- a web interface for the gsdl
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26// following line required to get fstream.filedesc() on darwin (Mac OS X)
27// gcc 2.91 automatically defines this in stream.h
28#define _STREAM_COMPAT 1
29
30#include "receptionist.h"
31#include "fileutil.h"
32#include "cgiutils.h"
33#include "htmlutils.h"
34#include "gsdltools.h"
35#include "gsdltimes.h"
36#include "OIDtools.h"
37#include <assert.h>
38#include <time.h>
39#include <stdio.h> // for open()
40#include <fcntl.h> // for open() flags
41// following 2 are for printing Last-Modified http header.
42#include <sys/stat.h>
43#include <time.h>
44
45#if defined (GSDL_USE_IOS_H)
46#include <fstream.h>
47#else
48#include <fstream>
49#endif
50
51void recptconf::clear () {
52 gsdlhome.clear();
53 gdbmhome.clear();
54 collectinfo.erase(collectinfo.begin(), collectinfo.end());
55 collection.clear();
56 collectdir.clear();
57 httpprefix.clear();
58 httpimg = "/images";
59 gwcgi.clear();
60 macrofiles.erase(macrofiles.begin(), macrofiles.end());
61 saveconf.clear();
62 usecookies = false;
63 logcgiargs = false;
64 LogDateFormat = LocalTime;
65
66 maintainer.clear();
67 MailServer.clear();
68 LogEvents = Disabled;
69 EmailEvents = Disabled;
70 EmailUserEvents = false;
71
72 languages.erase(languages.begin(), languages.end());
73 encodings.erase(encodings.begin(), encodings.end());
74
75 site_auth = false;
76 HomePageType = "images";
77 HomePageCols = 3;
78
79 // these default page parameters can always be overriden
80 // in the configuration file
81 pageparams.erase(pageparams.begin(), pageparams.end());
82 pageparams["c"] = "";
83 pageparams["l"] = "en";
84
85#ifdef MACROPRECEDENCE
86 macroprecedence = MACROPRECEDENCE;
87#else
88 macroprecedence.clear();
89#endif
90}
91
92void collectioninfo_t::clear () {
93 gsdl_gsdlhome.clear();
94 gsdl_gdbmhome.clear();
95
96 info_loaded = false;
97 info.clear();
98}
99
100void languageinfo_t::clear () {
101 longname.clear();
102 defaultencoding.clear();
103}
104
105receptionist::receptionist () {
106 // create a list of cgi arguments
107 // this must be done before the configuration
108
109 cgiarginfo ainfo;
110
111 ainfo.shortname = "e";
112 ainfo.longname = "compressed arguments";
113 ainfo.multiplechar = true;
114 ainfo.defaultstatus = cgiarginfo::good;
115 ainfo.argdefault = "";
116 ainfo.savedarginfo = cgiarginfo::mustnot;
117 argsinfo.addarginfo (NULL, ainfo);
118
119 ainfo.shortname = "a";
120 ainfo.longname = "action";
121 ainfo.multiplechar = true;
122 ainfo.defaultstatus = cgiarginfo::none;
123 ainfo.argdefault = "";
124 ainfo.savedarginfo = cgiarginfo::must;
125 argsinfo.addarginfo (NULL, ainfo);
126
127 // w=western
128 ainfo.shortname = "w";
129 ainfo.longname = "encoding";
130 ainfo.multiplechar = true;
131 ainfo.defaultstatus = cgiarginfo::none;
132 ainfo.argdefault = "";
133 ainfo.savedarginfo = cgiarginfo::must;
134 argsinfo.addarginfo (NULL, ainfo);
135
136 ainfo.shortname = "nw";
137 ainfo.longname = "new encoding";
138 ainfo.multiplechar = true;
139 ainfo.defaultstatus = cgiarginfo::none;
140 ainfo.argdefault = "";
141 ainfo.savedarginfo = cgiarginfo::mustnot;
142 argsinfo.addarginfo (NULL, ainfo);
143
144 ainfo.shortname = "c";
145 ainfo.longname = "collection";
146 ainfo.multiplechar = true;
147 ainfo.defaultstatus = cgiarginfo::none;
148 ainfo.argdefault = "";
149 ainfo.savedarginfo = cgiarginfo::must;
150 argsinfo.addarginfo (NULL, ainfo);
151
152 // the interface language name should use the ISO 639
153 // standard
154 ainfo.shortname = "l";
155 ainfo.longname = "interface language";
156 ainfo.multiplechar = true;
157 ainfo.defaultstatus = cgiarginfo::weak;
158 ainfo.argdefault = "en";
159 ainfo.savedarginfo = cgiarginfo::must;
160 argsinfo.addarginfo (NULL, ainfo);
161
162 ainfo.shortname = "nl";
163 ainfo.longname = "new language";
164 ainfo.multiplechar = false;
165 ainfo.defaultstatus = cgiarginfo::none;
166 ainfo.argdefault = "0";
167 ainfo.savedarginfo = cgiarginfo::mustnot;
168 argsinfo.addarginfo (NULL, ainfo);
169
170 // the GSDL_UID (cookie)
171 ainfo.shortname = "z";
172 ainfo.longname = "gsdl uid";
173 ainfo.multiplechar = true;
174 ainfo.defaultstatus = cgiarginfo::none;
175 ainfo.argdefault = "";
176 ainfo.savedarginfo = cgiarginfo::mustnot;
177 argsinfo.addarginfo (NULL, ainfo);
178}
179
180
181void receptionist::add_action (action *theaction) {
182 // make sure we have an action to add
183 if (theaction == NULL) return;
184
185 // add this action to the list of actions
186 actions.addaction(theaction);
187
188 // add the cgi arguments from this action
189 argsinfo.addarginfo (NULL, theaction->getargsinfo());
190}
191
192
193void receptionist::add_browser (browserclass *thebrowser) {
194 // make sure we have a browser to add
195 if (thebrowser == NULL) return;
196
197 // add this browser to the list of browsers
198 browsers.addbrowser(thebrowser);
199}
200
201
202void receptionist::setdefaultbrowser (const text_t &browsername) {
203 browsers.setdefaultbrowser (browsername);
204}
205
206
207// configure should be called for each line in the
208// configuration files to configure the receptionist and everything
209// it contains. The configuration should take place after everything
210// has been added but before the initialisation.
211
212void receptionist::configure (const text_t &key, const text_tarray &cfgline) {
213 // configure the receptionist
214
215
216
217 if (cfgline.size() >= 1) {
218 cgiarginfo *info = NULL;
219 if (key == "gsdlhome") {
220 configinfo.gsdlhome = cfgline[0];
221 if (configinfo.gdbmhome.empty()) configinfo.gdbmhome = cfgline[0];
222 }
223 else if (key == "gdbmhome") configinfo.gdbmhome = cfgline[0];
224 else if (key == "collection") {
225 configinfo.collection = cfgline[0];
226 // also need to set the default arg to this collection
227 if ((info = argsinfo.getarginfo("c")) != NULL) {
228 info->defaultstatus = cgiarginfo::good;
229 info->argdefault = cfgline[0];
230 }
231
232 } else if (key == "collectdir") configinfo.collectdir = cfgline[0];
233 else if (key == "httpprefix") configinfo.httpprefix = cfgline[0];
234 else if (key == "httpimg") configinfo.httpimg = cfgline[0];
235 else if (key == "gwcgi") configinfo.gwcgi = cfgline[0];
236 else if (key == "macrofiles") {
237 // want to append to macrofiles (i.e. may be several config files
238 // contributing, maybe from several collections).
239 text_tarray::const_iterator here = cfgline.begin();
240 text_tarray::const_iterator end = cfgline.end();
241 while (here != end) {
242 configinfo.macrofiles.insert (*here);
243 here ++;
244 }
245 }
246 else if (key == "saveconf") configinfo.saveconf = cfgline[0];
247 else if (key == "usecookies") configinfo.usecookies = (cfgline[0] == "true");
248 else if (key == "logcgiargs") configinfo.logcgiargs = (cfgline[0] == "true");
249 else if (key == "maintainer") configinfo.maintainer = cfgline[0];
250 else if (key == "MailServer") configinfo.MailServer = cfgline[0];
251 else if (key == "LogDateFormat") {
252 if (cfgline[0] == "UTCTime") configinfo.LogDateFormat = UTCTime;
253 else if (cfgline[0] == "Absolute") configinfo.LogDateFormat = Absolute;
254 }
255 else if (key == "LogEvents") {
256 if (cfgline[0] == "CollectorEvents") configinfo.LogEvents = CollectorEvents;
257 else if (cfgline[0] == "AllEvents") configinfo.LogEvents = AllEvents;
258 }
259 else if (key == "EmailEvents") {
260 if (cfgline[0] == "CollectorEvents") configinfo.EmailEvents = CollectorEvents;
261 else if (cfgline[0] == "AllEvents") configinfo.EmailEvents = AllEvents;
262 }
263 else if (key == "EmailUserEvents") configinfo.EmailUserEvents = (cfgline[0] == "true");
264 else if (key == "pageparam") {
265 if (cfgline.size() >= 2) configinfo.pageparams[cfgline[0]] = cfgline[1];
266 else configinfo.pageparams[cfgline[0]] = "";
267 }
268 else if (key == "macroprecedence") configinfo.macroprecedence = cfgline[0];
269 else if (key == "collectinfo") {
270 if (cfgline.size() >= 3) {
271 collectioninfo_t cinfo;
272 cinfo.gsdl_gsdlhome = cfgline[1];
273 cinfo.gsdl_gdbmhome = cfgline[2];
274 configinfo.collectinfo[cfgline[0]] = cinfo;
275 }
276 }
277
278 // Read in the value for the site_auth directive either true or false
279 else if (key == "site_auth") configinfo.site_auth = (cfgline[0] == "true");
280
281 else if (key == "site_group")
282 joinchar(cfgline,',',configinfo.site_group);
283
284 else if (key == "SiteFormat") {
285 if (cfgline[0] == "HomePageType") {
286 configinfo.HomePageType = cfgline[1];
287 } else if (cfgline[0] == "HomePageCols") {
288 configinfo.HomePageCols = cfgline[1].getint();
289 }
290 }
291
292 else if (key == "cgiarg") {
293 // get shortname
294 bool seen_defaultstatus = false;
295 text_t subkey, subvalue;
296 text_t shortname;
297 text_t::const_iterator cfglinesub_here;
298 text_tarray::const_iterator cfgline_here = cfgline.begin();
299 text_tarray::const_iterator cfgline_end = cfgline.end();
300 while (cfgline_here != cfgline_end) {
301 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
302 (*cfgline_here).end(), '=', subkey);
303 if (subkey == "shortname") {
304 shortname = substr (cfglinesub_here, (*cfgline_here).end());
305 }
306 cfgline_here++;
307 }
308
309 // if we found the shortname process the line again filling in values
310 if (!shortname.empty()) {
311 cgiarginfo &chinfo = argsinfo[shortname];
312 chinfo.shortname = shortname; // in case this is a new argument
313
314 cfgline_here = cfgline.begin();
315 while (cfgline_here != cfgline_end) {
316 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
317 (*cfgline_here).end(), '=', subkey);
318 subvalue = substr (cfglinesub_here, (*cfgline_here).end());
319
320 if (subkey == "longname") chinfo.longname = subvalue;
321 else if (subkey == "multiplechar") chinfo.multiplechar = (subvalue == "true");
322 else if (subkey == "defaultstatus") {
323 seen_defaultstatus = true;
324 if (subvalue == "none") chinfo.defaultstatus = cgiarginfo::none;
325 else if (subvalue == "weak") chinfo.defaultstatus = cgiarginfo::weak;
326 else if (subvalue == "good") chinfo.defaultstatus = cgiarginfo::good;
327 else if (subvalue == "config") chinfo.defaultstatus = cgiarginfo::config;
328 else if (subvalue == "imperative") chinfo.defaultstatus = cgiarginfo::imperative;
329 }
330 else if (subkey == "argdefault") {
331 chinfo.argdefault = subvalue;
332 if (!seen_defaultstatus) chinfo.defaultstatus = cgiarginfo::config;
333 }
334 else if (subkey == "savedarginfo") {
335 if (subvalue == "mustnot") chinfo.savedarginfo = cgiarginfo::mustnot;
336 else if (subvalue == "can") chinfo.savedarginfo = cgiarginfo::can;
337 else if (subvalue == "must") chinfo.savedarginfo = cgiarginfo::must;
338 }
339
340 cfgline_here++;
341 }
342 }
343
344 } else if (key == "Encoding") {
345
346 configure_encoding (cfgline);
347
348 } else if (key == "Language") {
349 text_t subkey, subvalue, shortname;
350 languageinfo_t lang;
351 text_t::const_iterator cfglinesub_here;
352 text_tarray::const_iterator cfgline_here = cfgline.begin();
353 text_tarray::const_iterator cfgline_end = cfgline.end();
354 while (cfgline_here != cfgline_end) {
355 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
356 (*cfgline_here).end(), '=', subkey);
357 if (subkey == "shortname") {
358 shortname = substr (cfglinesub_here, (*cfgline_here).end());
359 } else if (subkey == "longname") {
360 lang.longname = substr (cfglinesub_here, (*cfgline_here).end());
361 } else if (subkey == "default_encoding") {
362 lang.defaultencoding = substr (cfglinesub_here, (*cfgline_here).end());
363 }
364 cfgline_here++;
365 }
366 if (!shortname.empty()) {
367 if (lang.longname.empty()) lang.longname = shortname;
368 configinfo.languages[shortname] = lang;
369 }
370 }
371 }
372
373 // configure the actions
374 actionptrmap::iterator actionhere = actions.begin ();
375 actionptrmap::iterator actionend = actions.end ();
376
377 while (actionhere != actionend) {
378 assert ((*actionhere).second.a != NULL);
379 if ((*actionhere).second.a != NULL)
380 (*actionhere).second.a->configure(key, cfgline);
381
382 actionhere++;
383 }
384
385 // configure the protocols
386 recptprotolistclass::iterator protohere = protocols.begin ();
387 recptprotolistclass::iterator protoend = protocols.end ();
388
389 while (protohere != protoend) {
390 assert ((*protohere).p != NULL);
391 comerror_t err;
392 if ((*protohere).p != NULL)
393 (*protohere).p->configure(key, cfgline, err);
394
395 protohere++;
396 }
397
398 // configure the browsers
399 browserptrmap::iterator browserhere = browsers.begin ();
400 browserptrmap::iterator browserend = browsers.end ();
401
402 while (browserhere != browserend) {
403 assert ((*browserhere).second.b != NULL);
404 if ((*browserhere).second.b != NULL)
405 (*browserhere).second.b->configure(key, cfgline);
406
407 browserhere++;
408 }
409}
410
411
412void receptionist::configure (const text_t &key, const text_t &value) {
413 text_tarray cfgline;
414 cfgline.push_back (value);
415 configure(key, cfgline);
416}
417
418
419// init should be called after all the actions and protocols have been
420// added to the receptionist and after everything has been configured but
421// before any pages are created. It returns true on success and false on
422// failure. If false is returned getpage should not be called (without
423// producing meaningless output), instead an error page should be produced
424// by the calling code.
425bool receptionist::init (ostream &logout) {
426
427 // first configure collectdir
428 text_t thecollectdir = configinfo.gsdlhome;
429 if (!configinfo.collection.empty()) {
430 // collection specific mode
431 if (!configinfo.collectdir.empty()) {
432 // has already been configured
433 thecollectdir = configinfo.collectdir;
434 } else {
435 // decide where collectdir is by searching for collect.cfg
436 // look in $GSDLHOME/collect/collection-name/etc/collect.cfg and
437 // then $GSDLHOME/etc/collect.cfg
438 thecollectdir = filename_cat (configinfo.gsdlhome, "collect");
439 thecollectdir = filename_cat (thecollectdir, configinfo.collection);
440 text_t filename = filename_cat (thecollectdir, "etc");
441 filename = filename_cat (filename, "collect.cfg");
442
443 if (!file_exists(filename)) thecollectdir = configinfo.gsdlhome;
444 }
445 }
446 configure("collectdir", thecollectdir);
447
448 // read in the macro files
449 if (!read_macrofiles (logout)) return false;
450
451 // there must be at least one action defined
452 if (actions.empty()) {
453 logout << "Error: no actions have been added to the receptionist\n";
454 return false;
455 }
456
457 // there must be at least one browser defined
458 if (browsers.empty()) {
459 logout << "Error: no browsers have been added to the receptionist\n";
460 return false;
461 }
462
463 // create a saveconf string if there isn't one already
464 if (configinfo.saveconf.empty())
465 configinfo.saveconf = create_save_conf_str (argsinfo, logout);
466
467 // check the saveconf string
468 if (!check_save_conf_str (configinfo.saveconf, argsinfo, logout))
469 return false;
470
471 // set a random seed
472 srand (time(NULL));
473
474 // if maintainer email address is something dodgy (for now I'll define
475 // dodgy as being anything that doesn't contain '@') disable EmailEvents
476 // and EmailUserEvents (we don't strictly need to disable EmailUserEvents
477 // in this case but we will as it seems likely that MailServer will also
478 // be screwed up if maintainer is).
479 text_t::const_iterator maintainer_end = configinfo.maintainer.end ();
480 text_t::const_iterator maintainer_here = findchar ((text_t::const_iterator)configinfo.maintainer.begin(),
481 maintainer_end, '@');
482 if (maintainer_here == maintainer_end) {
483 configinfo.EmailEvents = Disabled;
484 configinfo.EmailUserEvents = Disabled;
485 } else {
486 // if MailServer isn't set it should default to mail.maintainer-domain
487 if (configinfo.MailServer.empty()) {
488 configinfo.MailServer = "mail." + substr (maintainer_here+1, maintainer_end);
489 }
490 }
491
492 // init the actions
493 actionptrmap::iterator actionhere = actions.begin ();
494 actionptrmap::iterator actionend = actions.end ();
495 while (actionhere != actionend) {
496 if (((*actionhere).second.a == NULL) ||
497 !(*actionhere).second.a->init(logout)) return false;
498 actionhere++;
499 }
500
501 // init the protocols
502 recptprotolistclass::iterator protohere = protocols.begin ();
503 recptprotolistclass::iterator protoend = protocols.end ();
504 while (protohere != protoend) {
505 comerror_t err;
506 if (((*protohere).p == NULL) ||
507 !(*protohere).p->init(err, logout)) return false;
508 protohere++;
509 }
510
511 // init the browsers
512 browserptrmap::iterator browserhere = browsers.begin ();
513 browserptrmap::iterator browserend = browsers.end ();
514 while (browserhere != browserend) {
515 if (((*browserhere).second.b == NULL) ||
516 !(*browserhere).second.b->init(logout)) return false;
517 browserhere++;
518 }
519
520 return true;
521}
522
523// get the default encoding for the given language - if it fails for any
524// reason return ""
525text_t receptionist::get_default_encoding (const text_t &language) {
526
527 // make sure language is valid
528 if (configinfo.languages.find(language) == configinfo.languages.end()) return "";
529
530 text_t default_encoding = configinfo.languages[language].defaultencoding;
531
532 // make sure the encoding is valid
533 if (converters.find(default_encoding) == converters.end()) return "";
534
535 return default_encoding;
536}
537
538// parse_cgi_args parses cgi arguments into an argument class.
539// This function should be called for each page request. It returns false
540// if there was a major problem with the cgi arguments.
541bool receptionist::parse_cgi_args (const text_t &argstr, cgiargsclass &args,
542 ostream &logout, text_tmap &fcgienv) {
543
544 // get an initial list of cgi arguments
545 args.clear();
546 split_cgi_args (argsinfo, argstr, args);
547
548 // expand the compressed argument (if there was one)
549 if (!expand_save_args (argsinfo, configinfo.saveconf, args, logout)) return false;
550
551 // add the defaults
552 add_default_args (argsinfo, args, logout);
553
554 // get the cookie
555 if (configinfo.usecookies) get_cookie(args["z"], fcgienv);
556
557 // if we're changing languages, set the encoding to the default for the new language
558 if (args["nl"] == "1") {
559 args["nw"] = get_default_encoding(args["l"]);
560 }
561
562 // get the input encoding
563 // if encoding isn't set, set it to the default for the current language
564 if ((args.getarg("w") == NULL) || args["w"].empty()) {
565 args["w"] = get_default_encoding(args["l"]);
566 }
567
568 text_t &arg_w = args["w"];
569
570 inconvertclass defaultinconvert;
571 inconvertclass *inconvert = converters.get_inconverter (arg_w);
572 if (inconvert == NULL) inconvert = &defaultinconvert;
573
574 // see if the next page will have a different encoding
575 if (args.getarg("nw") != NULL) arg_w = args["nw"];
576
577 // convert arguments which aren't in unicode to unicode
578 args_tounicode (args, *inconvert);
579
580
581 // decide on the output conversion class (needed for checking the external
582 // cgi arguments)
583 rzwsoutconvertclass defaultoutconverter;
584 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
585 if (outconverter == NULL) outconverter = &defaultoutconverter;
586 outconverter->reset();
587
588 // check the main cgi arguments
589 if (!check_mainargs (args, logout)) return false;
590
591 // check the arguments for the action
592 action *a = actions.getaction (args["a"]);
593 if (a != NULL) {
594 if (!a->check_cgiargs (argsinfo, args, &protocols, logout)) return false;
595 } else {
596 // the action was not found!!
597 outconvertclass text_t2ascii;
598 logout << text_t2ascii << "Error: the action \"" << args["a"]
599 << "\" could not be found.\n";
600 return false;
601 }
602
603 // check external cgi arguments for each action
604 actionptrmap::iterator actionhere = actions.begin ();
605 actionptrmap::iterator actionend = actions.end ();
606 while (actionhere != actionend) {
607 assert ((*actionhere).second.a != NULL);
608 if ((*actionhere).second.a != NULL) {
609 if (!(*actionhere).second.a->check_external_cgiargs (argsinfo, args, *outconverter,
610 configinfo.saveconf, logout))
611 return false;
612 }
613 actionhere++;
614 }
615
616 // the action might have changed but we will assume that
617 // the cgiargs were checked properly when the change was made
618
619 return true;
620}
621
622// returns true if cookie already existed, false
623// if it was generated
624bool receptionist::get_cookie (text_t &cookie, text_tmap &fcgienv) {
625
626 text_t cookiestring = gsdl_getenv ("HTTP_COOKIE", fcgienv);
627
628 text_t::const_iterator end = cookiestring.end();
629 text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
630
631 while (here+9 < end) {
632
633 if (substr(here, here+8) == "GSDL_UID") {
634 cookie = substr (here+9, findchar (here+9, end, ';'));
635 return true;
636 }
637 here++;
638 here = findchar (here, end, 'G');
639 }
640
641 cookie.clear();
642 text_t host = gsdl_getenv("REMOTE_ADDR", fcgienv);
643 time_t ttime = time(NULL);
644 if (!host.empty()) {
645 cookie += host;
646 cookie.push_back ('-');
647 }
648 cookie += text_t(ttime);
649
650 return false;
651}
652
653// as above but just tests if cookie exists
654bool receptionist::get_cookie (text_tmap &fcgienv) {
655
656 text_t c = gsdl_getenv("HTTP_COOKIE", fcgienv);
657 if (!c.empty()) {
658 text_t cookiestring = c;
659
660 text_t::const_iterator end = cookiestring.end();
661 text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
662
663 while (here+9 < end) {
664 if (substr(here, here+8) == "GSDL_UID") return true;
665 here++;
666 here = findchar (here, end, 'G');
667 }
668 }
669 return false;
670}
671
672bool receptionist::log_cgi_args (cgiargsclass &args, ostream &logout, text_tmap &fcgienv) {
673
674 // see if we want to log the cgi arguments
675 if (!configinfo.logcgiargs) return true;
676
677 text_t host = gsdl_getenv ("REMOTE_HOST", fcgienv);
678 text_t script_name = gsdl_getenv ("SCRIPT_NAME", fcgienv);
679 if (host.empty()) host = gsdl_getenv ("REMOTE_ADDR", fcgienv);
680 text_t browser = gsdl_getenv ("HTTP_USER_AGENT", fcgienv);
681
682 cgiargsclass::const_iterator args_here = args.begin();
683 cgiargsclass::const_iterator args_end = args.end();
684
685 text_t argstr;
686 bool first = true;
687 while (args_here != args_end) {
688 if (!first) argstr += ", ";
689 argstr += (*args_here).first + "=" + (*args_here).second.value;
690 first = false;
691 args_here ++;
692 }
693
694 text_t logfile = filename_cat (configinfo.gdbmhome, "etc", "usage.txt");
695
696 text_t logstr = script_name;
697 logstr += " " + host;
698 logstr += " [";
699 if (configinfo.LogDateFormat == UTCTime) {
700 logstr += get_date (false);
701 } else if (configinfo.LogDateFormat == Absolute) {
702 time_t ttime = time(NULL);
703 logstr += ttime;
704 } else {
705 // LocalTime
706 logstr += get_date (true);
707 }
708 logstr += "] (" + argstr + ") \"";
709 logstr += browser;
710 logstr += "\"\n";
711
712 return append_logstr (logfile, logstr, logout);
713}
714
715bool receptionist::append_logstr (const text_t &filename, const text_t &logstr,
716 ostream &logout) {
717
718 utf8outconvertclass text_t2utf8;
719 char *lfile = filename.getcstr();
720
721 int fd = open(lfile, O_WRONLY | O_APPEND);
722
723 if (fd == -1) {
724 logout << "Error: Couldn't open file " << lfile << "\n";
725 delete lfile;
726 return false;
727 }
728
729 // lock_val is set to 0 if file is locked successfully
730 int lock_val = 1;
731 GSDL_LOCK_FILE (fd);
732 if (lock_val == 0) {
733 text_t tmp_log_str(logstr); // so we don't pass a const to setinput...
734 text_t2utf8.setinput(&tmp_log_str);
735 char *buffer=new char[logstr.size()];
736 size_t num_chars;
737 convertclass::status_t status;
738 text_t2utf8.convert(buffer, logstr.size(), num_chars, status);
739 // ignore status - assume it is "finished" as buffer is big enough
740 write(fd, buffer, num_chars);
741 GSDL_UNLOCK_FILE (fd);
742 delete buffer;
743 } else {
744 logout << "Error: Couldn't lock file " << lfile << "\n";
745 close(fd);
746 delete lfile;
747 return false;
748 }
749
750 close(fd);
751
752 delete lfile;
753 return true;
754}
755
756text_t receptionist::expandmacros (const text_t &astring, cgiargsclass &args,
757 ostream &logout) {
758 text_t outstring;
759 outconvertclass text_t2ascii;
760
761 action *a = actions.getaction (args["a"]);
762 prepare_page (a, args, text_t2ascii, logout);
763 disp.expandstring ("Global", astring, outstring);
764 return outstring;
765}
766
767// produce_cgi_page will call get_cgihead_info and
768// produce_content in the appropriate way to output a cgi header and
769// the page content (if needed). If a page could not be created it
770// will return false
771bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout,
772 ostream &logout, text_tmap &fcgienv) {
773 outconvertclass text_t2ascii;
774
775 response_t response;
776 text_t response_data;
777
778 // produce cgi header
779 get_cgihead_info (args, response, response_data, logout, fcgienv);
780 if (response == location) {
781 // location response (url may contain macros!!)
782 response_data = expandmacros (response_data, args, logout);
783 contentout << text_t2ascii << "Location: " << response_data << "\n\n";
784 contentout << flush;
785 return true;
786 } else if (response == content) {
787 // content response
788
789#ifdef GSDL_NOCACHE
790 contentout << "Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"; // date in the past
791 tm *tm_ptr = NULL;
792 time_t t = time(NULL);
793 tm_ptr = gmtime (&t);
794 if (tm_ptr != NULL) {
795 char *timestr = new char[128];
796 strftime (timestr, 128, "%a, %d %b %Y %H:%M:%S", tm_ptr);
797 contentout << "Last-Modified: " << timestr << " GMT\n"; // always modified
798 delete timestr;
799 }
800 contentout << "Cache-Control: no-cache, must-revalidate\n"; // HTTP/1.1
801 contentout << "Pragma: no-cache\n"; // HTTP/1.0
802
803#else
804
805 // use the later of build.cfg and collect.cfg modification times
806 // as the Last-Modified: header, for caching values
807 struct stat file_info;
808 time_t latest=0;
809
810 text_t collectname="";
811 collectname=args["c"];
812 if (collectname != "") {
813 text_t collectdir=filename_cat(configinfo.gsdlhome,"collect");
814 collectdir=filename_cat(collectdir,collectname);
815 text_t buildcfg=filename_cat(collectdir,"index");
816 buildcfg=filename_cat(buildcfg,"build.cfg");
817 char *buildcfg_ptr=buildcfg.getcstr();
818 text_t collectcfg=filename_cat(collectdir,"etc");
819 collectcfg=filename_cat(collectcfg,"collect.cfg");
820 char *collectcfg_ptr=collectcfg.getcstr();
821
822 if (stat(buildcfg_ptr, &file_info)) {
823 // we got an error. Currently don't handle error :(
824 // logout <<
825 } else {
826 latest=file_info.st_mtime;
827 }
828
829 if (stat(collectcfg_ptr, &file_info)) {
830 // error - unhandled for now
831 } else {
832 if (latest<file_info.st_mtime) latest=file_info.st_mtime;
833 }
834 delete buildcfg_ptr;
835 delete collectcfg_ptr;
836
837 if (latest>0) {
838 // print out modified time, "DDD, dd MMM YYYY hh:mm:ss" format
839 // c library takes care of mem for this string... (has \n at end!!!!)
840 // latest is currently local time, convert to UTC.
841 struct tm* utc_latest;
842 utc_latest=gmtime(&latest);
843 contentout << "Last-Modified: " << asctime(utc_latest);
844 }
845 } // end of collection != ""
846
847#endif
848
849 contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
850 } else {
851 // unknown response
852 logout << "Error: get_cgihead_info returned an unknown response type.\n";
853 return false;
854 }
855
856 // produce cgi page
857 if (!produce_content (args, contentout, logout)) return false;
858
859 // flush contentout
860 contentout << flush;
861 return true;
862}
863
864
865// get_cgihead_info determines the cgi header information for
866// a set of cgi arguments. If response contains location then
867// response_data contains the redirect address. If reponse
868// contains content then reponse_data contains the content-type.
869// Note that images can now be produced by the receptionist.
870// Note also, alternative for get_cgihead_info below which
871// stores the information in a text_tmap so it is more easily digested
872
873void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
874 text_t &response_data, ostream &logout,
875 text_tmap &fcgienv) {
876 outconvertclass text_t2ascii;
877
878 // get the action
879 action *a = actions.getaction (args["a"]);
880 if (a != NULL) {
881 a->get_cgihead_info (args, &protocols, response, response_data, logout);
882
883 } else {
884 // the action was not found!!
885 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
886 << args["a"] << "\" could not be found.\n";
887 response = content;
888 response_data = "text/html";
889 }
890
891 // add the encoding information
892 if (response == content) {
893 if (converters.find(args["w"]) != converters.end()) {
894 response_data += "; charset=" + args["w"];
895 } else {
896 // default to latin 1
897 response_data += "; charset=ISO-8859-1";
898 }
899
900 // add cookie if required
901 if (configinfo.usecookies && !get_cookie(fcgienv))
902 response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
903 + "; expires=25-Dec-37 00:00:00 GMT";
904 }
905}
906
907
908// Alternative version of get_cgihead_info, stores fielded infomation
909// in text_tmap rather than concatenated string
910void receptionist::get_cgihead_info (cgiargsclass &args, text_tmap &headers,
911 ostream &logout, text_tmap &fcgienv) {
912
913 response_t response;
914 text_t response_data;
915
916 // get the action
917 action *a = actions.getaction (args["a"]);
918 if (a != NULL) {
919 a->get_cgihead_info (args, &protocols, response, response_data, logout);
920
921 } else {
922 // the action was not found!!
923 outconvertclass text_t2ascii;
924 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
925 << args["a"] << "\" could not be found.\n";
926 response = content;
927 response_data = "text/html";
928 }
929
930 if (response == location) {
931 response_data = expandmacros(response_data, args, logout);
932 headers["Location"] = response_data;
933 return;
934 }
935
936 // add the encoding information
937 if (response == content) {
938
939 if (converters.find(args["w"]) != converters.end()) {
940 headers["content-encoding"] = args["w"];
941 response_data += "; charset=" + args["w"];
942 } else {
943 // default to utf-8
944 headers["content-encoding"] = "utf-8";
945 response_data += "; charset=utf-8";
946 }
947
948 headers["content-type"] = response_data;
949
950 }
951
952}
953
954
955
956// produce the page content
957bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
958 ostream &logout) {
959
960 // decide on the output conversion class
961 text_t &arg_w = args["w"];
962 rzwsoutconvertclass defaultoutconverter;
963 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
964 if (outconverter == NULL) outconverter = &defaultoutconverter;
965 outconverter->reset();
966
967 // needed for 16-bit unicode only - big endian marker 0xfeff (RFC 2781)
968 if (arg_w=="utf-16") {
969 contentout << '\xfe' << '\xff' ;
970 }
971
972 recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
973 if (collectproto != NULL) {
974 // get browsers to process OID
975 text_t OID = args["d"];
976 if (OID.empty()) OID = args["cl"];
977 if (!OID.empty()) {
978 text_tset metadata;
979 text_tarray OIDs;
980 OIDs.push_back (OID);
981 if (!is_top(OID)) OIDs.push_back (OID + ".pr");
982 FilterResponse_t response;
983 metadata.insert ("childtype");
984 if (get_info (OIDs, args["c"], metadata, false, collectproto, response, logout)) {
985 text_t classifytype;
986 if (!response.docInfo[0].metadata["childtype"].values[0].empty())
987 classifytype = response.docInfo[0].metadata["childtype"].values[0];
988 else if (!is_top (OID)) {
989 // not sure why this is occasionally not set, but it will
990 // cause a segfault... possibly if built with no_text? jrm21
991 if (response.docInfo[1].metadata.find("childtype")
992 == response.docInfo[1].metadata.end()) {
993 cerr << "receptionist: no childtype element in metadata map!"
994 << endl;
995 } else {
996 if (!response.docInfo[1].metadata["childtype"].values[0].empty())
997 classifytype = response.docInfo[1].metadata["childtype"].values[0];
998 }
999 }
1000 browserclass *b = browsers.getbrowser (classifytype);
1001 b->processOID (args, collectproto, logout);
1002 }
1003 }
1004
1005 // translate "d" and "cl" arguments if required
1006 translate_OIDs (args, collectproto, logout);
1007 }
1008
1009 // produce the page using the desired action
1010 action *a = actions.getaction (args["a"]);
1011 if (a != NULL) {
1012 if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
1013 if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
1014 return false;
1015 } else {
1016 // the action was not found!!
1017 outconvertclass text_t2ascii;
1018
1019 logout << text_t2ascii << "Error receptionist::produce_content: the action \""
1020 << args["a"] << "\" could not be found.\n";
1021
1022 contentout << (*outconverter)
1023 << "<html>\n"
1024 << "<head>\n"
1025 << "<title>Error</title>\n"
1026 << "</head>\n"
1027 << "<body>\n"
1028 << "<h2>Oops!</h2>\n"
1029 << "Undefined Page. The action \""
1030 << args["a"] << "\" could not be found.\n"
1031 << "</body>\n"
1032 << "</html>\n";
1033 }
1034 return true;
1035}
1036
1037
1038// returns the compressed argument ("e") corresponding to the argument
1039// list. This can be used to save preferences between sessions.
1040text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
1041 // decide on the output conversion class
1042 text_t &arg_w = args["w"];
1043 rzwsoutconvertclass defaultoutconverter;
1044 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
1045 if (outconverter == NULL) outconverter = &defaultoutconverter;
1046 outconverter->reset();
1047
1048 text_t compressed_args;
1049 if (compress_save_args (argsinfo, configinfo.saveconf, args,
1050 compressed_args, *outconverter, logout))
1051 return compressed_args;
1052
1053 return "";
1054}
1055
1056
1057// will read in all the macro files. If one is not found an
1058// error message will be written to logout and the method will
1059// return false.
1060bool receptionist::read_macrofiles (ostream &logout) {
1061 outconvertclass text_t2ascii;
1062
1063 // redirect the error output to logout
1064 ostream *savedlogout = disp.setlogout (&logout);
1065
1066 // unload any macros that were previously loaded - this allows us to call
1067 // this function a second time to reload all the macro files (useful for
1068 // reading in changed macro files in server versions of greenstone)
1069 disp.unloaddefaultmacros();
1070
1071 // load up the default macro files, the collection directory
1072 // is searched first for the file (if this is being used in
1073 // collection specific mode) and then the main directory(s)
1074 text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
1075
1076 text_tset maindirs;
1077 text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
1078 maindirs.insert (gsdlmacrodir);
1079 colinfo_tmap::iterator colhere = configinfo.collectinfo.begin();
1080 colinfo_tmap::iterator colend = configinfo.collectinfo.end();
1081 while (colhere != colend) {
1082 if (!((*colhere).second.gsdl_gsdlhome).empty()) {
1083 gsdlmacrodir = filename_cat ((*colhere).second.gsdl_gsdlhome, "macros");
1084 maindirs.insert (gsdlmacrodir);
1085 }
1086 colhere ++;
1087 }
1088
1089 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1090 text_tset::iterator arrend = configinfo.macrofiles.end();
1091 text_t filename;
1092 while (arrhere != arrend) {
1093 bool foundfile = false;
1094
1095 // try in the collection directory if this is being
1096 // run in collection specific mode
1097 if (!configinfo.collection.empty()) {
1098 filename = filename_cat (colmacrodir, *arrhere);
1099 if (file_exists (filename)) {
1100 disp.loaddefaultmacros(filename);
1101 foundfile = true;
1102 }
1103 }
1104
1105 // if we haven't found the macro file yet try in
1106 // the main macro directory(s)
1107 // if file is found in more than one main directory
1108 // we'll load all copies
1109 if (!foundfile) {
1110 text_tset::const_iterator dirhere = maindirs.begin();
1111 text_tset::const_iterator dirend = maindirs.end();
1112 while (dirhere != dirend) {
1113 filename = filename_cat (*dirhere, *arrhere);
1114 if (file_exists (filename)) {
1115 disp.loaddefaultmacros(filename);
1116 foundfile = true;
1117 }
1118 dirhere ++;
1119 }
1120 }
1121
1122 // see if we found the file or not
1123 if (!foundfile) {
1124 logout << text_t2ascii
1125 << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
1126 if (configinfo.collection.empty()) {
1127 text_t dirs;
1128 joinchar (maindirs, ", ", dirs);
1129 logout << text_t2ascii
1130 << "It should be in either of the following directories ("
1131 << dirs << ").\n\n";
1132
1133 } else {
1134 logout << text_t2ascii
1135 << "It should be in either " << colmacrodir << " or in "
1136 << gsdlmacrodir << ".\n\n";
1137 }
1138 // reset logout to what it was
1139 disp.setlogout (savedlogout);
1140 return false;
1141 }
1142 arrhere++;
1143 }
1144
1145 // success
1146
1147 // reset logout to what it was
1148 disp.setlogout (savedlogout);
1149 return true;
1150}
1151
1152
1153
1154
1155// Go through the list of macro files looking to see
1156// if any exist in the collectoin specific area. If they
1157// do then read them in and add them to the set of existing
1158// current macros
1159
1160void receptionist::read_collection_macrofiles (const text_t& collection, ostream &logout)
1161{
1162 outconvertclass text_t2ascii;
1163
1164 disp.unloadcollectionmacros();
1165
1166 // redirect the error output to logout
1167 ostream *savedlogout = disp.setlogout (&logout);
1168
1169 text_t colmacrodir
1170 = filename_cat (configinfo.gsdlhome,"collect",collection, "macros");
1171
1172 if (file_exists (colmacrodir)) {
1173
1174 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1175 text_tset::iterator arrend = configinfo.macrofiles.end();
1176 text_t filename;
1177 while (arrhere != arrend) {
1178
1179 filename = filename_cat (colmacrodir, *arrhere);
1180 if (file_exists (filename)) {
1181 disp.loadcollectionmacros(filename);
1182 }
1183
1184 arrhere++;
1185 }
1186 }
1187
1188 // reset logout to what it was
1189 disp.setlogout (savedlogout);
1190}
1191
1192
1193
1194
1195// check_mainargs will check all the main arguments. If a major
1196// error is found it will return false and no cgi page should
1197// be created using the arguments.
1198
1199bool receptionist::check_mainargs (cgiargsclass &args, ostream &logout) {
1200
1201 if(configinfo.site_auth)
1202 {
1203 args["uan"] = "1";
1204 args["ug"] = configinfo.site_group;
1205 }
1206
1207
1208 // if this receptionist is running in collection dependant mode
1209 // then it should always set the collection argument to the
1210 // collection
1211 if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
1212
1213 // if current collection uses ccscols make sure
1214 // "ccs" argument is set and make "cc" default to
1215 // all collections in "ccs"
1216 if (args["a"] != "config" && !args["c"].empty()) {
1217
1218 text_t &arg_c = args["c"];
1219 recptproto *collectproto = protocols.getrecptproto (arg_c, logout);
1220 if (collectproto == NULL) {
1221 // oops, this collection isn't valid
1222 outconvertclass text_t2ascii;
1223 logout << text_t2ascii << "ERROR: Invalid collection: " << arg_c << "\n";
1224 // args["c"].clear();
1225
1226 } else {
1227
1228 ColInfoResponse_t *cinfo = get_collectinfo_ptr (collectproto, arg_c, logout);
1229
1230 if(cinfo->authenticate == "collection")
1231 {
1232 args["uan"] = "1";
1233 args["ug"] = cinfo->auth_group;
1234 }
1235
1236
1237 if (cinfo != NULL) {
1238 if (!cinfo->ccsCols.empty()) {
1239 args["ccs"] = 1;
1240 if (args["cc"].empty()) {
1241 text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
1242 text_tarray::const_iterator col_end = cinfo->ccsCols.end();
1243 bool first = true;
1244 while (col_here != col_end) {
1245 // make sure it's a valid collection
1246 if (protocols.getrecptproto (*col_here, logout) != NULL) {
1247 if (!first) args["cc"].push_back (',');
1248 args["cc"] += *col_here;
1249 first = false;
1250 }
1251 col_here ++;
1252 }
1253 }
1254 }
1255 } else {
1256 logout << "ERROR (receptionist::check_mainargs): get_collectinfo_ptr returned NULL\n";
1257 }
1258 }
1259 }
1260
1261 // argument "v" can only be 0 or 1. Use the default value
1262 // if it is out of range
1263 int arg_v = args.getintarg ("v");
1264 if (arg_v != 0 && arg_v != 1) {
1265 cgiarginfo *vinfo = argsinfo.getarginfo ("v");
1266 if (vinfo != NULL) args["v"] = vinfo->argdefault;
1267 }
1268
1269 // argument "f" can only be 0 or 1. Use the default value
1270 // if it is out of range
1271 int arg_f = args.getintarg ("f");
1272 if (arg_f != 0 && arg_f != 1) {
1273 cgiarginfo *finfo = argsinfo.getarginfo ("f");
1274 if (finfo != NULL) args["f"] = finfo->argdefault;
1275 }
1276
1277 return true;
1278}
1279
1280// translate_OIDs translates the "d" and "cl" arguments to their correct values
1281// if they use the tricky ".fc", ".lc" type syntax.
1282void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto,
1283 ostream &logout) {
1284
1285 FilterResponse_t response;
1286 FilterRequest_t request;
1287 comerror_t err;
1288 text_t &arg_d = args["d"];
1289 text_t &arg_cl = args["cl"];
1290 text_t &collection = args["c"];
1291
1292 // do a call to translate OIDs if required
1293 request.filterName = "NullFilter";
1294 request.filterResultOptions = FROID;
1295 if (!arg_d.empty() && needs_translating (arg_d)) {
1296 request.docSet.push_back (arg_d);
1297 collectproto->filter (collection, request, response, err, logout);
1298 arg_d = response.docInfo[0].OID;
1299 request.clear();
1300 }
1301 // we'll also check here that the "cl" argument has a "classify" doctype
1302 // (in case ".fc" or ".lc" have screwed up)
1303 if (needs_translating (arg_cl)) {
1304 request.fields.insert ("doctype");
1305 request.docSet.push_back (arg_cl);
1306 request.filterResultOptions = FRmetadata;
1307 collectproto->filter (collection, request, response, err, logout);
1308 // set to original value (without .xx stuff) if doctype isn't "classify"
1309 if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
1310 strip_suffix (arg_cl);
1311 else
1312 arg_cl = response.docInfo[0].OID;
1313 }
1314}
1315
1316// prepare_page sets up page parameters, sets display macros
1317// and opens the page ready for output
1318void receptionist::prepare_page (action *a, cgiargsclass &args,
1319 outconvertclass &outconvert,
1320 ostream &logout) {
1321 // set up page parameters
1322 text_t pageparams;
1323 bool first = true;
1324
1325 text_tmap::iterator params_here = configinfo.pageparams.begin();
1326 text_tmap::iterator params_end = configinfo.pageparams.end();
1327 while (params_here != params_end) {
1328 if (args[(*params_here).first] != (*params_here).second) {
1329 if (!first) pageparams += ",";
1330 first = false;
1331 pageparams += (*params_here).first;
1332 pageparams += "=";
1333 pageparams += args[(*params_here).first];
1334 }
1335
1336 params_here++;
1337 }
1338
1339
1340 // open the page
1341 disp.openpage(pageparams, configinfo.macroprecedence);
1342
1343 // define external macros for each action
1344 actionptrmap::iterator actionhere = actions.begin ();
1345 actionptrmap::iterator actionend = actions.end ();
1346
1347 while (actionhere != actionend) {
1348 assert ((*actionhere).second.a != NULL);
1349 if ((*actionhere).second.a != NULL) {
1350 (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
1351 }
1352 actionhere++;
1353 }
1354
1355 text_t collection = args["c"];
1356 if (!collection.empty()) {
1357 read_collection_macrofiles(collection,logout);
1358 }
1359
1360 // define internal macros for the current action
1361 a->define_internal_macros (disp, args, &protocols, logout);
1362
1363 // define general macros. the defining of general macros is done here so that
1364 // the last possible version of the cgi arguments are used
1365 define_general_macros (args, outconvert, logout);
1366}
1367
1368void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/,
1369 ostream &logout) {
1370
1371 text_t &collection = args["c"];
1372
1373 disp.setmacro ("gsdlhome", "Global", dm_safe(configinfo.gsdlhome));
1374 disp.setmacro ("gwcgi", "Global", configinfo.gwcgi);
1375 disp.setmacro ("httpimg", "Global", configinfo.httpimg);
1376 disp.setmacro ("httpprefix", "Global", configinfo.httpprefix);
1377
1378 if (!collection.empty()) {
1379 ColInfoResponse_t cinfo;
1380 comerror_t err;
1381 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1382 if (collectproto != NULL) {
1383 collectproto->get_collectinfo (collection, cinfo, err, logout);
1384 text_t httpcollection;
1385 if (!cinfo.httpdomain.empty()) httpcollection = "http://";
1386 httpcollection += cinfo.httpdomain + cinfo.httpprefix + "/collect/" + collection;
1387 disp.setmacro ("httpcollection", "Global", httpcollection);
1388 }
1389 }
1390
1391 text_t compressedoptions = get_compressed_arg(args, logout);
1392 disp.setmacro ("compressedoptions", "Global", dm_safe(compressedoptions));
1393 // need a decoded version of compressedoptions for use within forms
1394 // as browsers encode values from forms before sending to server
1395 // (e.g. %25 becomes %2525)
1396 decode_cgi_arg (compressedoptions);
1397 disp.setmacro ("decodedcompressedoptions", "Global", dm_safe(compressedoptions));
1398
1399#if defined (__WIN32__)
1400 disp.setmacro ("win32", "Global", "1");
1401#endif
1402
1403 // set _cgiargX_ macros for each cgi argument
1404 cgiargsclass::const_iterator argshere = args.begin();
1405 cgiargsclass::const_iterator argsend = args.end();
1406 while (argshere != argsend) {
1407 if (((*argshere).first == "q") ||
1408 ((*argshere).first == "qa") ||
1409 ((*argshere).first == "qtt") ||
1410 ((*argshere).first == "qty") ||
1411 ((*argshere).first == "qp") ||
1412 ((*argshere).first == "qpl") ||
1413 ((*argshere).first == "qr") ||
1414 ((*argshere).first == "q2"))
1415 // need to escape special characters from query string
1416 disp.setmacro ("cgiarg" + (*argshere).first,
1417 "Global", html_safe((*argshere).second.value));
1418 else if ((*argshere).first == "hp") {
1419 disp.setmacro ("cgiarg" + (*argshere).first, "Global", (*argshere).second.value);
1420 } else {
1421 disp.setmacro ("cgiarg" + (*argshere).first, "Global", dm_safe((*argshere).second.value));
1422 }
1423 argshere ++;
1424 }
1425
1426 // display text right to left if language is arabic/farsi (and if browser can support it)
1427 if (args["l"] == "ar" || args["l"] == "fa")
1428 disp.setmacro ("htmlextra", "Global", " dir=rtl");
1429
1430 // set collection specific macros
1431 if (!collection.empty()) {
1432 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1433 if (collectproto != NULL) {
1434 FilterResponse_t response;
1435 text_tset metadata;
1436 get_info ("collection", collection, metadata, false,
1437 collectproto, response, logout);
1438
1439 if (!response.docInfo[0].metadata.empty()) {
1440 MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
1441 MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
1442 while (here != end) {
1443 if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
1444 ((*here).first != "hasprevious")) {
1445 // check for args in form name:lang
1446 text_t name = "";
1447 text_t lang = "";
1448 bool colonfound=false;
1449 text_t::const_iterator a = (*here).first.begin();
1450 text_t::const_iterator b = (*here).first.end();
1451 while (a !=b) {
1452 if (*a==':') {
1453 colonfound=true;
1454 }
1455 else {
1456 if (colonfound)
1457 lang.push_back(*a);
1458 else name.push_back(*a);
1459 }
1460 a++;
1461 }
1462 if (!lang.empty()) {
1463 if (args["l"]==lang) {
1464 disp.setmacro (name, "Global", (*here).second.values[0]);
1465 }
1466 }
1467 else { // the default one
1468 disp.setmacro ((*here).first, "Global", (*here).second.values[0]);
1469 }
1470 }
1471 here ++;
1472 }
1473 }
1474
1475 text_t iconcollection;
1476 disp.expandstring ("Global", "_iconcollection_", iconcollection);
1477 if (!iconcollection.empty())
1478 {
1479 ColInfoResponse_t cinfo;
1480 comerror_t err;
1481 collectproto->get_collectinfo (collection, cinfo, err, logout);
1482 if (iconcollection[0]=='/' && !cinfo.httpdomain.empty())
1483 {
1484 // local but with full path
1485 iconcollection = "http://" + cinfo.httpdomain + iconcollection;
1486 disp.setmacro("iconcollection", "Global", iconcollection);
1487 }
1488 }
1489 }
1490 }
1491}
1492
1493// gets collection info from cache if found or
1494// calls collection server (and updates cache)
1495// returns NULL if there's an error
1496ColInfoResponse_t *receptionist::get_collectinfo_ptr (recptproto *collectproto,
1497 const text_t &collection,
1498 ostream &logout) {
1499
1500 // check the cache
1501 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1502 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1503 // found it
1504 return &((*it).second.info);
1505 }
1506
1507 // not cached, get info from collection server
1508 if (collectproto == NULL) {
1509 logout << "ERROR: receptionist::get_collectinfo_ptr passed null collectproto\n";
1510 return NULL;
1511 }
1512
1513 comerror_t err;
1514 if (it == configinfo.collectinfo.end()) {
1515 collectioninfo_t cinfo;
1516 collectproto->get_collectinfo (collection, cinfo.info, err, logout);
1517 if (err != noError) {
1518 outconvertclass text_t2ascii;
1519 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1520 << get_comerror_string (err) << "\"while getting collectinfo\n";
1521 return NULL;
1522 }
1523 cinfo.info_loaded = true;
1524 configinfo.collectinfo[collection] = cinfo;
1525 return &(configinfo.collectinfo[collection].info);
1526 } else {
1527 collectproto->get_collectinfo (collection, (*it).second.info, err, logout);
1528 if (err != noError) {
1529 outconvertclass text_t2ascii;
1530 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1531 << get_comerror_string (err) << "\"while getting collectinfo\n";
1532 return NULL;
1533 }
1534 (*it).second.info_loaded = true;
1535 return &((*it).second.info);
1536 }
1537}
1538
1539// removes a collection from the cache so that the next
1540// call to get_collectinfo_ptr() for that collection will
1541// retrieve the collection info from the collection server
1542void receptionist::uncache_collection (const text_t &collection) {
1543
1544 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1545 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1546
1547 (*it).second.info_loaded = false;
1548
1549 }
1550}
1551
1552// Handles an "Encoding" line from a configuration file - note that the
1553// configinfo.encodings map is a bit of a hack (to be fixed when the
1554// configuration files are tidied up).
1555void receptionist::configure_encoding (const text_tarray &cfgline) {
1556
1557 text_t subkey, subvalue, shortname, longname, mapfile;
1558 int multibyte = 0;
1559 text_t::const_iterator cfglinesub_here;
1560 text_tarray::const_iterator cfgline_here = cfgline.begin();
1561 text_tarray::const_iterator cfgline_end = cfgline.end();
1562 while (cfgline_here != cfgline_end) {
1563 if (*cfgline_here == "multibyte") {
1564 multibyte = 1;
1565 } else {
1566 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
1567 (*cfgline_here).end(), '=', subkey);
1568 if (subkey == "shortname") {
1569 shortname = substr (cfglinesub_here, (*cfgline_here).end());
1570 } else if (subkey == "longname") {
1571 longname = substr (cfglinesub_here, (*cfgline_here).end());
1572 } else if (subkey == "map") {
1573 mapfile = substr (cfglinesub_here, (*cfgline_here).end());
1574 }
1575 }
1576 cfgline_here++;
1577 }
1578 if (!shortname.empty()) {
1579 if (longname.empty()) longname = shortname;
1580
1581 // add the converter
1582 if (shortname == "utf-8") {
1583 utf8inconvertclass *utf8inconvert = new utf8inconvertclass();
1584 utf8outconvertclass *utf8outconvert = new utf8outconvertclass();
1585 utf8outconvert->set_rzws(1);
1586 add_converter (shortname, utf8inconvert, utf8outconvert);
1587 configinfo.encodings[longname] = shortname;
1588
1589 } else if (shortname == "utf-16") {
1590 // we use the default input converter as this shouldn't ever be used
1591 // for converting from unicode...
1592 inconvertclass *inconverter = new inconvertclass();
1593 utf16outconvertclass *outconverter = new utf16outconvertclass();
1594 add_converter (shortname, inconverter, outconverter);
1595 configinfo.encodings[longname] = shortname;
1596
1597 } else if (!mapfile.empty()) {
1598
1599 if (mapfile == "8859_1.ump") {
1600 // iso-8859-1 is a special case as it'll always be supported by the
1601 // standard converter class and therefore doesn't need to use its
1602 // mapping file
1603 inconvertclass *inconvert = new inconvertclass();
1604 rzwsoutconvertclass *outconvert = new rzwsoutconvertclass();
1605 outconvert->set_rzws(1);
1606 add_converter (shortname, inconvert, outconvert);
1607 configinfo.encodings[longname] = shortname;
1608
1609 } else {
1610 text_t to_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "to_uc", mapfile);
1611 text_t from_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "from_uc", mapfile);
1612 if (file_exists(to_uc_map) && file_exists(from_uc_map)) {
1613
1614 mapinconvertclass *mapinconvert = new mapinconvertclass();
1615 mapinconvert->setmapfile (to_uc_map, 0x003F);
1616 mapinconvert->set_multibyte (multibyte);
1617 mapoutconvertclass *mapoutconvert = new mapoutconvertclass();
1618 mapoutconvert->setmapfile (from_uc_map, 0x3F);
1619 mapoutconvert->set_multibyte (multibyte);
1620 mapoutconvert->set_rzws(1);
1621 add_converter (shortname, mapinconvert, mapoutconvert);
1622 configinfo.encodings[longname] = shortname;
1623 }
1624 }
1625 }
1626 }
1627}
Note: See TracBrowser for help on using the repository browser.