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

Last change on this file since 11561 was 10931, checked in by jrm21, 19 years ago

instead of making all 'collectionmacros' read from the collect.cfg file
in the Global package, support Packagename:macroname arguments.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 54.6 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 = g_EmptyText;
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 = g_EmptyText;
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 = g_EmptyText;
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 = g_EmptyText;
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 = g_EmptyText;
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 = g_EmptyText;
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()) {
534 // we don't support the encoding specified as default for this language
535 if (configinfo.encodings.size()==1) {
536 // only 1 encoding specified in main.cfg, so use it
537 return configinfo.encodings.begin()->second;
538 }
539 return "";
540 }
541
542 return default_encoding;
543}
544
545// parse_cgi_args parses cgi arguments into an argument class.
546// This function should be called for each page request. It returns false
547// if there was a major problem with the cgi arguments.
548bool receptionist::parse_cgi_args (const text_t &argstr, cgiargsclass &args,
549 ostream &logout, text_tmap &fcgienv) {
550
551 // get an initial list of cgi arguments
552 args.clear();
553 split_cgi_args (argsinfo, argstr, args);
554
555 // expand the compressed argument (if there was one)
556 if (!expand_save_args (argsinfo, configinfo.saveconf, args, logout)) return false;
557
558 // add the defaults
559 add_default_args (argsinfo, args, logout);
560
561 // get the cookie
562 if (configinfo.usecookies) get_cookie(args["z"], fcgienv);
563
564 // if we're changing languages, set the encoding to the default for the new language
565 if (args["nl"] == "1") {
566 args["nw"] = get_default_encoding(args["l"]);
567 }
568
569 // get the input encoding
570 // if encoding isn't set, set it to the default for the current language
571 if ((args.getarg("w") == NULL) || args["w"].empty()) {
572 args["w"] = get_default_encoding(args["l"]);
573 }
574
575 text_t &arg_w = args["w"];
576
577 inconvertclass defaultinconvert;
578 inconvertclass *inconvert = converters.get_inconverter (arg_w);
579 if (inconvert == NULL) inconvert = &defaultinconvert;
580
581 // see if the next page will have a different encoding
582 if (args.getarg("nw") != NULL) arg_w = args["nw"];
583
584 // convert arguments which aren't in unicode to unicode
585 args_tounicode (args, *inconvert);
586
587
588 // decide on the output conversion class (needed for checking the external
589 // cgi arguments)
590 rzwsoutconvertclass defaultoutconverter;
591 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
592 if (outconverter == NULL) outconverter = &defaultoutconverter;
593 outconverter->reset();
594
595 // check the main cgi arguments
596 if (!check_mainargs (args, logout)) return false;
597
598 // check the arguments for the action
599 action *a = actions.getaction (args["a"]);
600 if (a != NULL) {
601 if (!a->check_cgiargs (argsinfo, args, &protocols, logout)) return false;
602 } else {
603 // the action was not found!!
604 outconvertclass text_t2ascii;
605 logout << text_t2ascii << "Error: the action \"" << args["a"]
606 << "\" could not be found.\n";
607 return false;
608 }
609
610 // check external cgi arguments for each action
611 actionptrmap::iterator actionhere = actions.begin ();
612 actionptrmap::iterator actionend = actions.end ();
613 while (actionhere != actionend) {
614 assert ((*actionhere).second.a != NULL);
615 if ((*actionhere).second.a != NULL) {
616 if (!(*actionhere).second.a->check_external_cgiargs (argsinfo, args, *outconverter,
617 configinfo.saveconf, logout))
618 return false;
619 }
620 ++actionhere;
621 }
622
623 // the action might have changed but we will assume that
624 // the cgiargs were checked properly when the change was made
625
626 return true;
627}
628
629// returns true if cookie already existed, false
630// if it was generated
631bool receptionist::get_cookie (text_t &cookie, text_tmap &fcgienv) {
632
633 text_t cookiestring = gsdl_getenv ("HTTP_COOKIE", fcgienv);
634
635 text_t::const_iterator end = cookiestring.end();
636 text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
637
638 while (here+9 < end) {
639
640 if (substr(here, here+8) == "GSDL_UID") {
641 cookie = substr (here+9, findchar (here+9, end, ';'));
642 return true;
643 }
644 ++here;
645 here = findchar (here, end, 'G');
646 }
647
648 cookie.clear();
649 text_t host = gsdl_getenv("REMOTE_ADDR", fcgienv);
650 time_t ttime = time(NULL);
651 if (!host.empty()) {
652 cookie += host;
653 cookie.push_back ('-');
654 }
655 cookie += text_t(ttime);
656
657 return false;
658}
659
660// as above but just tests if cookie exists
661bool receptionist::get_cookie (text_tmap &fcgienv) {
662
663 text_t c = gsdl_getenv("HTTP_COOKIE", fcgienv);
664 if (!c.empty()) {
665 text_t cookiestring = c;
666
667 text_t::const_iterator end = cookiestring.end();
668 text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
669
670 while (here+9 < end) {
671 if (substr(here, here+8) == "GSDL_UID") return true;
672 ++here;
673 here = findchar (here, end, 'G');
674 }
675 }
676 return false;
677}
678
679bool receptionist::log_cgi_args (cgiargsclass &args, ostream &logout, text_tmap &fcgienv) {
680
681 // see if we want to log the cgi arguments
682 if (!configinfo.logcgiargs) return true;
683
684 text_t host = gsdl_getenv ("REMOTE_HOST", fcgienv);
685 text_t script_name = gsdl_getenv ("SCRIPT_NAME", fcgienv);
686 if (host.empty()) host = gsdl_getenv ("REMOTE_ADDR", fcgienv);
687 text_t browser = gsdl_getenv ("HTTP_USER_AGENT", fcgienv);
688
689 cgiargsclass::const_iterator args_here = args.begin();
690 cgiargsclass::const_iterator args_end = args.end();
691
692 text_t argstr;
693 bool first = true;
694 while (args_here != args_end) {
695 if (!first) argstr += ", ";
696 argstr += (*args_here).first + "=" + (*args_here).second.value;
697 first = false;
698 ++args_here;
699 }
700
701 text_t logfile = filename_cat (configinfo.gdbmhome, "etc", "usage.txt");
702
703 text_t logstr = script_name;
704 logstr += " " + host;
705 logstr += " [";
706 if (configinfo.LogDateFormat == UTCTime) {
707 logstr += get_date (false);
708 } else if (configinfo.LogDateFormat == Absolute) {
709 time_t ttime = time(NULL);
710 logstr += ttime;
711 } else {
712 // LocalTime
713 logstr += get_date (true);
714 }
715 logstr += "] (" + argstr + ") \"";
716 logstr += browser;
717 logstr += "\"\n";
718
719 return append_logstr (logfile, logstr, logout);
720}
721
722bool receptionist::append_logstr (const text_t &filename, const text_t &logstr,
723 ostream &logout) {
724
725 utf8outconvertclass text_t2utf8;
726 char *lfile = filename.getcstr();
727
728 int fd = open(lfile, O_WRONLY | O_APPEND);
729
730 if (fd == -1) {
731 logout << "Error: Couldn't open file " << lfile << "\n";
732 delete []lfile;
733 return false;
734 }
735
736 // lock_val is set to 0 if file is locked successfully
737 int lock_val = 1;
738 GSDL_LOCK_FILE (fd);
739 if (lock_val == 0) {
740 text_t tmp_log_str(logstr); // so we don't pass a const to setinput...
741 text_t2utf8.setinput(&tmp_log_str);
742 char *buffer=new char[logstr.size()];
743 size_t num_chars;
744 convertclass::status_t status;
745 text_t2utf8.convert(buffer, logstr.size(), num_chars, status);
746 // ignore status - assume it is "finished" as buffer is big enough
747 write(fd, buffer, num_chars);
748 GSDL_UNLOCK_FILE (fd);
749 delete []buffer;
750 } else {
751 logout << "Error: Couldn't lock file " << lfile << "\n";
752 close(fd);
753 delete []lfile;
754 return false;
755 }
756
757 close(fd);
758
759 delete []lfile;
760 return true;
761}
762
763text_t receptionist::expandmacros (const text_t &astring, cgiargsclass &args,
764 ostream &logout) {
765 text_t outstring;
766 outconvertclass text_t2ascii;
767
768 action *a = actions.getaction (args["a"]);
769 prepare_page (a, args, text_t2ascii, logout);
770 disp.expandstring (displayclass::defaultpackage, astring, outstring);
771 return outstring;
772}
773
774// produce_cgi_page will call get_cgihead_info and
775// produce_content in the appropriate way to output a cgi header and
776// the page content (if needed). If a page could not be created it
777// will return false
778bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout,
779 ostream &logout, text_tmap &fcgienv) {
780 outconvertclass text_t2ascii;
781
782 response_t response;
783 text_t response_data;
784
785 // produce cgi header
786 get_cgihead_info (args, response, response_data, logout, fcgienv);
787 if (response == location) {
788 // location response (url may contain macros!!)
789 response_data = expandmacros (response_data, args, logout);
790 contentout << text_t2ascii << "Location: " << response_data << "\n\n";
791 contentout << flush;
792 return true;
793 } else if (response == content) {
794 // content response
795
796#ifdef GSDL_NOCACHE
797 contentout << "Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"; // date in the past
798 tm *tm_ptr = NULL;
799 time_t t = time(NULL);
800 tm_ptr = gmtime (&t);
801 if (tm_ptr != NULL) {
802 char *timestr = new char[128];
803 strftime (timestr, 128, "%a, %d %b %Y %H:%M:%S", tm_ptr);
804 contentout << "Last-Modified: " << timestr << " GMT\n"; // always modified
805 delete []timestr;
806 }
807 contentout << "Cache-Control: no-cache, must-revalidate\n"; // HTTP/1.1
808 contentout << "Pragma: no-cache\n"; // HTTP/1.0
809
810#else
811
812 // use the later of build.cfg and collect.cfg modification times
813 // as the Last-Modified: header, for caching values
814 struct stat file_info;
815 time_t latest=0;
816
817 text_t collectname="";
818 collectname=args["c"];
819 if (collectname != "") {
820 text_t collectdir=filename_cat(configinfo.gsdlhome,"collect");
821 collectdir=filename_cat(collectdir,collectname);
822 text_t buildcfg=filename_cat(collectdir,"index");
823 buildcfg=filename_cat(buildcfg,"build.cfg");
824 char *buildcfg_ptr=buildcfg.getcstr();
825 text_t collectcfg=filename_cat(collectdir,"etc");
826 collectcfg=filename_cat(collectcfg,"collect.cfg");
827 char *collectcfg_ptr=collectcfg.getcstr();
828
829 if (stat(buildcfg_ptr, &file_info)) {
830 // we got an error. Currently don't handle error :(
831 // logout <<
832 } else {
833 latest=file_info.st_mtime;
834 }
835
836 if (stat(collectcfg_ptr, &file_info)) {
837 // error - unhandled for now
838 } else {
839 if (latest<file_info.st_mtime) latest=file_info.st_mtime;
840 }
841 delete []buildcfg_ptr;
842 delete []collectcfg_ptr;
843
844 if (latest>0) {
845 // print out modified time, "DDD, dd MMM YYYY hh:mm:ss" format
846 // c library takes care of mem for this string... (has \n at end!!!!)
847 // latest is currently local time, convert to UTC.
848 struct tm* utc_latest;
849 utc_latest=gmtime(&latest);
850 contentout << "Last-Modified: " << asctime(utc_latest);
851 }
852 } // end of collection != ""
853
854#endif
855
856 contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
857 }
858 else if (response == undecided_location) {
859 // Wait until later to output the target location
860 // Used for the "I'm feeling lucky" functionality
861 }
862 else {
863 // unknown response
864 logout << "Error: get_cgihead_info returned an unknown response type.\n";
865 return false;
866 }
867
868 // produce cgi page
869 if (!produce_content (args, contentout, logout)) return false;
870
871 // flush contentout
872 contentout << flush;
873 return true;
874}
875
876
877// get_cgihead_info determines the cgi header information for
878// a set of cgi arguments. If response contains location then
879// response_data contains the redirect address. If reponse
880// contains content then reponse_data contains the content-type.
881// Note that images can now be produced by the receptionist.
882// Note also, alternative for get_cgihead_info below which
883// stores the information in a text_tmap so it is more easily digested
884
885void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
886 text_t &response_data, ostream &logout,
887 text_tmap &fcgienv) {
888 outconvertclass text_t2ascii;
889
890 // get the action
891 action *a = actions.getaction (args["a"]);
892 if (a != NULL) {
893 a->get_cgihead_info (args, &protocols, response, response_data, logout);
894
895 } else {
896 // the action was not found!!
897 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
898 << args["a"] << "\" could not be found.\n";
899 response = content;
900 response_data = "text/html";
901 }
902
903 // add the encoding information
904 if (response == content) {
905 if (converters.find(args["w"]) != converters.end()) {
906 response_data += "; charset=" + args["w"];
907 } else {
908 // default to latin 1
909 response_data += "; charset=ISO-8859-1";
910 }
911
912 // add cookie if required
913 if (configinfo.usecookies && !get_cookie(fcgienv))
914 response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
915 + "; expires=25-Dec-37 00:00:00 GMT";
916 }
917}
918
919
920// Alternative version of get_cgihead_info, stores fielded infomation
921// in text_tmap rather than concatenated string
922void receptionist::get_cgihead_info (cgiargsclass &args, text_tmap &headers,
923 ostream &logout, text_tmap &fcgienv) {
924
925 response_t response;
926 text_t response_data;
927
928 // get the action
929 action *a = actions.getaction (args["a"]);
930 if (a != NULL) {
931 a->get_cgihead_info (args, &protocols, response, response_data, logout);
932
933 } else {
934 // the action was not found!!
935 outconvertclass text_t2ascii;
936 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
937 << args["a"] << "\" could not be found.\n";
938 response = content;
939 response_data = "text/html";
940 }
941
942 if (response == location) {
943 response_data = expandmacros(response_data, args, logout);
944 headers["Location"] = response_data;
945 return;
946 }
947
948 // add the encoding information
949 if (response == content) {
950
951 if (converters.find(args["w"]) != converters.end()) {
952 headers["content-encoding"] = args["w"];
953 response_data += "; charset=" + args["w"];
954 } else {
955 // default to utf-8
956 headers["content-encoding"] = "utf-8";
957 response_data += "; charset=utf-8";
958 }
959
960 headers["content-type"] = response_data;
961
962 }
963
964}
965
966
967
968// produce the page content
969bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
970 ostream &logout) {
971
972 // decide on the output conversion class
973 text_t &arg_w = args["w"];
974 rzwsoutconvertclass defaultoutconverter;
975 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
976 if (outconverter == NULL) outconverter = &defaultoutconverter;
977 outconverter->reset();
978
979 // needed for 16-bit unicode only - big endian marker 0xfeff (RFC 2781)
980 if (arg_w=="utf-16be") {
981 contentout << '\xfe' << '\xff' ;
982 }
983
984 recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
985 if (collectproto != NULL) {
986 // get browsers to process OID
987 text_t OID = args["d"];
988 if (OID.empty()) OID = args["cl"];
989 if (!OID.empty()) {
990 text_tset metadata;
991 text_tarray OIDs;
992 OIDs.push_back (OID);
993 if (!is_top(OID)) OIDs.push_back (OID + ".pr");
994 FilterResponse_t response;
995 metadata.insert ("childtype");
996 if (get_info (OIDs, args["c"], args["l"], metadata, false, collectproto, response, logout)) {
997 text_t classifytype;
998 if (!response.docInfo[0].metadata["childtype"].values[0].empty())
999 classifytype = response.docInfo[0].metadata["childtype"].values[0];
1000 else if (!is_top (OID)) {
1001 // not sure why this is occasionally not set, but it will
1002 // cause a segfault... possibly if built with no_text? jrm21
1003 if (response.docInfo[1].metadata.find("childtype")
1004 == response.docInfo[1].metadata.end()) {
1005 cerr << "receptionist: no childtype element in metadata map!"
1006 << endl;
1007 } else {
1008 if (!response.docInfo[1].metadata["childtype"].values[0].empty())
1009 classifytype = response.docInfo[1].metadata["childtype"].values[0];
1010 }
1011 }
1012 browserclass *b = browsers.getbrowser (classifytype);
1013 b->processOID (args, collectproto, logout);
1014 }
1015 }
1016
1017 // translate "d" and "cl" arguments if required
1018 translate_OIDs (args, collectproto, logout);
1019 }
1020
1021 // produce the page using the desired action
1022 action *a = actions.getaction (args["a"]);
1023 if (a != NULL) {
1024 if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
1025 if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
1026 return false;
1027 } else {
1028 // the action was not found!!
1029 outconvertclass text_t2ascii;
1030
1031 logout << text_t2ascii << "Error receptionist::produce_content: the action \""
1032 << args["a"] << "\" could not be found.\n";
1033
1034 contentout << (*outconverter)
1035 << "<html>\n"
1036 << "<head>\n"
1037 << "<title>Error</title>\n"
1038 << "</head>\n"
1039 << "<body>\n"
1040 << "<h2>Oops!</h2>\n"
1041 << "Undefined Page. The action \""
1042 << args["a"] << "\" could not be found.\n"
1043 << "</body>\n"
1044 << "</html>\n";
1045 }
1046 return true;
1047}
1048
1049
1050// returns the compressed argument ("e") corresponding to the argument
1051// list. This can be used to save preferences between sessions.
1052text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
1053 // decide on the output conversion class
1054 text_t &arg_w = args["w"];
1055 rzwsoutconvertclass defaultoutconverter;
1056 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
1057 if (outconverter == NULL) outconverter = &defaultoutconverter;
1058 outconverter->reset();
1059
1060 text_t compressed_args;
1061 if (compress_save_args (argsinfo, configinfo.saveconf, args,
1062 compressed_args, *outconverter, logout))
1063 return compressed_args;
1064
1065 return g_EmptyText;
1066}
1067
1068
1069// will read in all the macro files. If one is not found an
1070// error message will be written to logout and the method will
1071// return false.
1072bool receptionist::read_macrofiles (ostream &logout) {
1073 outconvertclass text_t2ascii;
1074
1075 // redirect the error output to logout
1076 ostream *savedlogout = disp.setlogout (&logout);
1077
1078 // unload any macros that were previously loaded - this allows us to call
1079 // this function a second time to reload all the macro files (useful for
1080 // reading in changed macro files in server versions of greenstone)
1081 disp.unloaddefaultmacros();
1082
1083 // load up the default macro files, the collection directory
1084 // is searched first for the file (if this is being used in
1085 // collection specific mode) and then the main directory(s)
1086 text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
1087
1088 text_tset maindirs;
1089 text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
1090 maindirs.insert (gsdlmacrodir);
1091 colinfo_tmap::iterator colhere = configinfo.collectinfo.begin();
1092 colinfo_tmap::iterator colend = configinfo.collectinfo.end();
1093 while (colhere != colend) {
1094 if (!((*colhere).second.gsdl_gsdlhome).empty()) {
1095 gsdlmacrodir = filename_cat ((*colhere).second.gsdl_gsdlhome, "macros");
1096 maindirs.insert (gsdlmacrodir);
1097 }
1098 ++colhere;
1099 }
1100
1101 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1102 text_tset::iterator arrend = configinfo.macrofiles.end();
1103 text_t filename;
1104 while (arrhere != arrend) {
1105 bool foundfile = false;
1106
1107 // try in the collection directory if this is being
1108 // run in collection specific mode
1109 if (!configinfo.collection.empty()) {
1110 filename = filename_cat (colmacrodir, *arrhere);
1111 if (file_exists (filename)) {
1112 disp.loaddefaultmacros(filename);
1113 foundfile = true;
1114 }
1115 }
1116
1117 // if we haven't found the macro file yet try in
1118 // the main macro directory(s)
1119 // if file is found in more than one main directory
1120 // we'll load all copies
1121 if (!foundfile) {
1122 text_tset::const_iterator dirhere = maindirs.begin();
1123 text_tset::const_iterator dirend = maindirs.end();
1124 while (dirhere != dirend) {
1125 filename = filename_cat (*dirhere, *arrhere);
1126 if (file_exists (filename)) {
1127 disp.loaddefaultmacros(filename);
1128 foundfile = true;
1129 }
1130 ++dirhere;
1131 }
1132 }
1133
1134 // see if we found the file or not
1135 if (!foundfile) {
1136 logout << text_t2ascii
1137 << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
1138 if (configinfo.collection.empty()) {
1139 text_t dirs;
1140 joinchar (maindirs, ", ", dirs);
1141 logout << text_t2ascii
1142 << "It should be in either of the following directories ("
1143 << dirs << ").\n\n";
1144
1145 } else {
1146 logout << text_t2ascii
1147 << "It should be in either " << colmacrodir << " or in "
1148 << gsdlmacrodir << ".\n\n";
1149 }
1150 // don't crap out if a macro file is missing
1151 //disp.setlogout (savedlogout);
1152 //return false;
1153 }
1154 ++arrhere;
1155 }
1156
1157 // success
1158
1159 // reset logout to what it was
1160 disp.setlogout (savedlogout);
1161 return true;
1162}
1163
1164
1165
1166
1167// Go through the list of macro files looking to see
1168// if any exist in the collectoin specific area. If they
1169// do then read them in and add them to the set of existing
1170// current macros
1171
1172void receptionist::read_collection_macrofiles (const text_t& collection, ostream &logout)
1173{
1174 outconvertclass text_t2ascii;
1175
1176 // disp.unloadcollectionmacros();
1177
1178 // redirect the error output to logout
1179 ostream *savedlogout = disp.setlogout (&logout);
1180
1181 text_t colmacrodir
1182 = filename_cat (configinfo.gsdlhome,"collect",collection, "macros");
1183
1184 if (directory_exists (colmacrodir)) {
1185
1186 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1187 text_tset::iterator arrend = configinfo.macrofiles.end();
1188 text_t filename;
1189 while (arrhere != arrend) {
1190
1191 filename = filename_cat (colmacrodir, *arrhere);
1192 if (file_exists (filename)) {
1193 disp.loadcollectionmacros(filename);
1194 }
1195
1196 ++arrhere;
1197 }
1198 }
1199
1200 // reset logout to what it was
1201 disp.setlogout (savedlogout);
1202}
1203
1204
1205
1206
1207// check_mainargs will check all the main arguments. If a major
1208// error is found it will return false and no cgi page should
1209// be created using the arguments.
1210
1211bool receptionist::check_mainargs (cgiargsclass &args, ostream &logout) {
1212
1213 if(configinfo.site_auth)
1214 {
1215 args["uan"] = "1";
1216 args["ug"] = configinfo.site_group;
1217 }
1218
1219
1220 // if this receptionist is running in collection dependant mode
1221 // then it should always set the collection argument to the
1222 // collection
1223 if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
1224
1225 // if current collection uses ccscols make sure
1226 // "ccs" argument is set and make "cc" default to
1227 // all collections in "ccs"
1228 if (args["a"] != "config" && !args["c"].empty()) {
1229
1230 text_t &arg_c = args["c"];
1231 recptproto *collectproto = protocols.getrecptproto (arg_c, logout);
1232 if (collectproto == NULL) {
1233 // oops, this collection isn't valid
1234 outconvertclass text_t2ascii;
1235 logout << text_t2ascii << "ERROR: Invalid collection: " << arg_c << "\n";
1236 // args["c"].clear();
1237
1238 } else {
1239
1240 ColInfoResponse_t *cinfo = get_collectinfo_ptr (collectproto, arg_c, logout);
1241
1242 if(cinfo->authenticate == "collection")
1243 {
1244 args["uan"] = "1";
1245 args["ug"] = cinfo->auth_group;
1246 }
1247
1248
1249 if (cinfo != NULL) {
1250 if (!cinfo->ccsCols.empty()) {
1251 args["ccs"] = 1;
1252 if (args["cc"].empty()) {
1253 text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
1254 text_tarray::const_iterator col_end = cinfo->ccsCols.end();
1255 bool first = true;
1256 while (col_here != col_end) {
1257 // make sure it's a valid collection
1258 if (protocols.getrecptproto (*col_here, logout) != NULL) {
1259 if (!first) args["cc"].push_back (',');
1260 args["cc"] += *col_here;
1261 first = false;
1262 }
1263 ++col_here;
1264 }
1265 }
1266 }
1267 } else {
1268 logout << "ERROR (receptionist::check_mainargs): get_collectinfo_ptr returned NULL\n";
1269 }
1270 }
1271 }
1272
1273 // argument "v" can only be 0 or 1. Use the default value
1274 // if it is out of range
1275 int arg_v = args.getintarg ("v");
1276 if (arg_v != 0 && arg_v != 1) {
1277 cgiarginfo *vinfo = argsinfo.getarginfo ("v");
1278 if (vinfo != NULL) args["v"] = vinfo->argdefault;
1279 }
1280
1281 // argument "f" can only be 0 or 1. Use the default value
1282 // if it is out of range
1283 int arg_f = args.getintarg ("f");
1284 if (arg_f != 0 && arg_f != 1) {
1285 cgiarginfo *finfo = argsinfo.getarginfo ("f");
1286 if (finfo != NULL) args["f"] = finfo->argdefault;
1287 }
1288
1289 return true;
1290}
1291
1292// translate_OIDs translates the "d" and "cl" arguments to their correct values
1293// if they use the tricky ".fc", ".lc" type syntax.
1294void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto,
1295 ostream &logout) {
1296
1297 FilterResponse_t response;
1298 FilterRequest_t request;
1299 comerror_t err;
1300 text_t &arg_d = args["d"];
1301 text_t &arg_cl = args["cl"];
1302 text_t &collection = args["c"];
1303
1304 // do a call to translate OIDs if required
1305 request.filterName = "NullFilter";
1306 request.filterResultOptions = FROID;
1307 if (!arg_d.empty() && needs_translating (arg_d)) {
1308 request.docSet.push_back (arg_d);
1309 collectproto->filter (collection, request, response, err, logout);
1310 arg_d = response.docInfo[0].OID;
1311 request.clear();
1312 }
1313 // we'll also check here that the "cl" argument has a "classify" doctype
1314 // (in case ".fc" or ".lc" have screwed up)
1315 if (needs_translating (arg_cl)) {
1316 request.fields.insert ("doctype");
1317 request.docSet.push_back (arg_cl);
1318 request.filterResultOptions = FRmetadata;
1319 collectproto->filter (collection, request, response, err, logout);
1320 // set to original value (without .xx stuff) if doctype isn't "classify"
1321 if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
1322 strip_suffix (arg_cl);
1323 else
1324 arg_cl = response.docInfo[0].OID;
1325 }
1326}
1327
1328// prepare_page sets up page parameters, sets display macros
1329// and opens the page ready for output
1330void receptionist::prepare_page (action *a, cgiargsclass &args,
1331 outconvertclass &outconvert,
1332 ostream &logout) {
1333 // set up page parameters
1334 text_t pageparams;
1335 bool first = true;
1336
1337 text_tmap::iterator params_here = configinfo.pageparams.begin();
1338 text_tmap::iterator params_end = configinfo.pageparams.end();
1339 while (params_here != params_end) {
1340 // page params are those from main.cfg (eg pageparam v 0) plus
1341 // two defaults set in recptconf.clear() (c="" and l=en)
1342 // This used to check if the current value of the page param
1343 // == the default value, then don't add in it the list
1344 // but if l=en, and there is a macro with [l=en], then it doesn't
1345 // find it.
1346 // so now all page params will go into the list. I assume this will
1347 // mean more attempts to find each macro, but nothing worsee than
1348 // that. --kjdon
1349 //if (args[(*params_here).first] != (*params_here).second) {
1350 if (first)
1351 first = false;
1352 else
1353 pageparams += ",";
1354
1355 pageparams += (*params_here).first;
1356 pageparams += "=";
1357 pageparams += args[(*params_here).first];
1358 // }
1359
1360 ++params_here;
1361 }
1362
1363
1364 // open the page
1365 disp.openpage(pageparams, configinfo.macroprecedence);
1366
1367 disp.unloadcollectionmacros();
1368
1369 text_t collection = args["c"];
1370 if (!collection.empty()) {
1371 read_collection_macrofiles(collection,logout);
1372 }
1373
1374 // define external macros for each action
1375 actionptrmap::iterator actionhere = actions.begin ();
1376 actionptrmap::iterator actionend = actions.end ();
1377
1378 while (actionhere != actionend) {
1379 assert ((*actionhere).second.a != NULL);
1380 if ((*actionhere).second.a != NULL) {
1381 (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
1382 }
1383 ++actionhere;
1384 }
1385
1386
1387 // define internal macros for the current action
1388 a->define_internal_macros (disp, args, &protocols, logout);
1389
1390 // define general macros. the defining of general macros is done here so that
1391 // the last possible version of the cgi arguments are used
1392 define_general_macros (args, outconvert, logout);
1393}
1394
1395void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/,
1396 ostream &logout) {
1397
1398 text_t &collection = args["c"];
1399
1400 disp.setmacro ("gsdlhome", displayclass::defaultpackage, dm_safe(configinfo.gsdlhome));
1401 disp.setmacro ("gwcgi", displayclass::defaultpackage, configinfo.gwcgi);
1402 disp.setmacro ("httpimg", displayclass::defaultpackage, configinfo.httpimg);
1403 disp.setmacro ("httpprefix", displayclass::defaultpackage, configinfo.httpprefix);
1404
1405
1406 text_t compressedoptions = get_compressed_arg(args, logout);
1407 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1408 // need a decoded version of compressedoptions for use within forms
1409 // as browsers encode values from forms before sending to server
1410 // (e.g. %25 becomes %2525)
1411 decode_cgi_arg (compressedoptions);
1412 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1413
1414#if defined (__WIN32__)
1415 disp.setmacro ("win32", displayclass::defaultpackage, "1");
1416#endif
1417
1418 // set _cgiargX_ macros for each cgi argument
1419 cgiargsclass::const_iterator argshere = args.begin();
1420 cgiargsclass::const_iterator argsend = args.end();
1421 while (argshere != argsend) {
1422 if (((*argshere).first == "q") ||
1423 ((*argshere).first == "qa") ||
1424 ((*argshere).first == "qtt") ||
1425 ((*argshere).first == "qty") ||
1426 ((*argshere).first == "qp") ||
1427 ((*argshere).first == "qpl") ||
1428 ((*argshere).first == "qr") ||
1429 ((*argshere).first == "q2"))
1430 // need to escape special characters from query string
1431 disp.setmacro ("cgiarg" + (*argshere).first,
1432 displayclass::defaultpackage, html_safe((*argshere).second.value));
1433 else if ((*argshere).first == "hp") {
1434 disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, (*argshere).second.value);
1435 } else {
1436 disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, dm_safe((*argshere).second.value));
1437 }
1438 ++argshere;
1439 }
1440
1441 // display text right to left if language is arabic/farsi (and if browser can support it)
1442 if (args["l"] == "ar" || args["l"] == "fa")
1443 disp.setmacro ("htmlextra", displayclass::defaultpackage, " dir=rtl");
1444
1445 // set collection specific macros
1446 if (!collection.empty()) {
1447 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1448 if (collectproto != NULL) {
1449 FilterResponse_t response;
1450 text_tset metadata;
1451 get_info ("collection", collection, args["l"], metadata, false,
1452 collectproto, response, logout);
1453
1454 if (!response.docInfo[0].metadata.empty()) {
1455 MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
1456 MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
1457 while (here != end) {
1458 if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
1459 ((*here).first != "hasprevious")) {
1460 // check for args in form name:lang
1461 text_t name = g_EmptyText;
1462 text_t lang = g_EmptyText;
1463 bool colonfound=false;
1464 text_t::const_iterator a = (*here).first.begin();
1465 text_t::const_iterator b = (*here).first.end();
1466 while (a !=b) {
1467 if (*a==':') {
1468 colonfound=true;
1469 }
1470 else {
1471 if (colonfound)
1472 lang.push_back(*a);
1473 else name.push_back(*a);
1474 }
1475 ++a;
1476 }
1477 if (!lang.empty()) {
1478 if (args["l"]==lang) {
1479 disp.setcollectionmacro(displayclass::defaultpackage, name, "", (*here).second.values[0]);
1480 }
1481 }
1482 else { // the default one
1483 disp.setcollectionmacro(displayclass::defaultpackage, (*here).first, "", (*here).second.values[0]);
1484 }
1485 }
1486 ++here;
1487 }
1488 }
1489
1490 text_t iconcollection;
1491 disp.expandstring (displayclass::defaultpackage, "_iconcollection_", iconcollection);
1492 if (!iconcollection.empty())
1493 {
1494 ColInfoResponse_t cinfo;
1495 comerror_t err;
1496 collectproto->get_collectinfo (collection, cinfo, err, logout);
1497 if (iconcollection[0]=='/' && !cinfo.httpdomain.empty())
1498 {
1499 // local but with full path
1500 iconcollection = "http://" + cinfo.httpdomain + iconcollection;
1501 disp.setmacro("iconcollection", displayclass::defaultpackage, iconcollection);
1502 }
1503 }
1504 }
1505 }
1506
1507 if (!collection.empty()) {
1508 ColInfoResponse_t cinfo;
1509 comerror_t err;
1510 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1511 if (collectproto != NULL) {
1512 collectproto->get_collectinfo (collection, cinfo, err, logout);
1513 text_t httpcollection;
1514 if (!cinfo.httpdomain.empty()) httpcollection = "http://";
1515 httpcollection += cinfo.httpdomain + cinfo.httpprefix + "/collect/"
1516 + collection;
1517 disp.setmacro ("httpcollection", displayclass::defaultpackage,
1518 httpcollection);
1519 // as of gsdl 2.53, collect.cfg can specify macros
1520 if (cinfo.collection_macros.size() > 0) {
1521 macros_map::const_iterator this_macro=cinfo.collection_macros.begin();
1522 macros_map::const_iterator done_macro=cinfo.collection_macros.end();
1523 while (this_macro != done_macro) {
1524 text_t package = "Global";
1525 text_t macroname = this_macro->first;
1526 // if this macro name is AAA:bbb then extract the package name
1527 text_t::const_iterator thischar, donechar;
1528 thischar = macroname.begin();
1529 donechar = macroname.end();
1530 while (thischar < donechar) {
1531 if (*thischar == ':') {
1532 package = substr(macroname.begin(),thischar);
1533 macroname = substr(thischar+1,donechar);
1534 break;
1535 }
1536 ++thischar;
1537 }
1538
1539 disp.setcollectionmacro(package,
1540 macroname,
1541 this_macro->second.first, // params
1542 this_macro->second.second); // value
1543 ++this_macro;
1544 }
1545 } // col macros
1546 } // collectproto != NULL
1547 }
1548
1549}
1550
1551// gets collection info from cache if found or
1552// calls collection server (and updates cache)
1553// returns NULL if there's an error
1554ColInfoResponse_t *receptionist::get_collectinfo_ptr (recptproto *collectproto,
1555 const text_t &collection,
1556 ostream &logout) {
1557
1558 // check the cache
1559 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1560 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1561 // found it
1562 return &((*it).second.info);
1563 }
1564
1565 // not cached, get info from collection server
1566 if (collectproto == NULL) {
1567 logout << "ERROR: receptionist::get_collectinfo_ptr passed null collectproto\n";
1568 return NULL;
1569 }
1570
1571 comerror_t err;
1572 if (it == configinfo.collectinfo.end()) {
1573 collectioninfo_t cinfo;
1574 collectproto->get_collectinfo (collection, cinfo.info, err, logout);
1575 if (err != noError) {
1576 outconvertclass text_t2ascii;
1577 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1578 << get_comerror_string (err) << "\"while getting collectinfo\n";
1579 return NULL;
1580 }
1581 cinfo.info_loaded = true;
1582 configinfo.collectinfo[collection] = cinfo;
1583 return &(configinfo.collectinfo[collection].info);
1584 } else {
1585 collectproto->get_collectinfo (collection, (*it).second.info, err, logout);
1586 if (err != noError) {
1587 outconvertclass text_t2ascii;
1588 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1589 << get_comerror_string (err) << "\"while getting collectinfo\n";
1590 return NULL;
1591 }
1592 (*it).second.info_loaded = true;
1593 return &((*it).second.info);
1594 }
1595}
1596
1597// removes a collection from the cache so that the next
1598// call to get_collectinfo_ptr() for that collection will
1599// retrieve the collection info from the collection server
1600void receptionist::uncache_collection (const text_t &collection) {
1601
1602 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1603 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1604
1605 (*it).second.info_loaded = false;
1606
1607 }
1608}
1609
1610// Handles an "Encoding" line from a configuration file - note that the
1611// configinfo.encodings map is a bit of a hack (to be fixed when the
1612// configuration files are tidied up).
1613void receptionist::configure_encoding (const text_tarray &cfgline) {
1614
1615 text_t subkey, subvalue, shortname, longname, mapfile;
1616 int multibyte = 0;
1617 text_t::const_iterator cfglinesub_here;
1618 text_tarray::const_iterator cfgline_here = cfgline.begin();
1619 text_tarray::const_iterator cfgline_end = cfgline.end();
1620 while (cfgline_here != cfgline_end) {
1621 if (*cfgline_here == "multibyte") {
1622 multibyte = 1;
1623 } else {
1624 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
1625 (*cfgline_here).end(), '=', subkey);
1626 if (subkey == "shortname") {
1627 shortname = substr (cfglinesub_here, (*cfgline_here).end());
1628 } else if (subkey == "longname") {
1629 longname = substr (cfglinesub_here, (*cfgline_here).end());
1630 } else if (subkey == "map") {
1631 mapfile = substr (cfglinesub_here, (*cfgline_here).end());
1632 }
1633 }
1634 ++cfgline_here;
1635 }
1636 if (!shortname.empty()) {
1637 if (longname.empty()) longname = shortname;
1638
1639 // add the converter
1640 if (shortname == "utf-8") {
1641 utf8inconvertclass *utf8inconvert = new utf8inconvertclass();
1642 utf8outconvertclass *utf8outconvert = new utf8outconvertclass();
1643 utf8outconvert->set_rzws(1);
1644 add_converter (shortname, utf8inconvert, utf8outconvert);
1645 configinfo.encodings[longname] = shortname;
1646
1647 } else if (shortname == "utf-16be") {
1648 // we use the default input converter as this shouldn't ever be used
1649 // for converting from unicode...
1650 inconvertclass *inconverter = new inconvertclass();
1651 utf16outconvertclass *outconverter = new utf16outconvertclass();
1652 add_converter (shortname, inconverter, outconverter);
1653 configinfo.encodings[longname] = shortname;
1654
1655 } else if (!mapfile.empty()) {
1656
1657 if (mapfile == "8859_1.ump") {
1658 // iso-8859-1 is a special case as it'll always be supported by the
1659 // standard converter class and therefore doesn't need to use its
1660 // mapping file
1661 inconvertclass *inconvert = new inconvertclass();
1662 rzwsoutconvertclass *outconvert = new rzwsoutconvertclass();
1663 outconvert->set_rzws(1);
1664 add_converter (shortname, inconvert, outconvert);
1665 configinfo.encodings[longname] = shortname;
1666
1667 } else {
1668 text_t to_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "to_uc", mapfile);
1669 text_t from_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "from_uc", mapfile);
1670 if (file_exists(to_uc_map) && file_exists(from_uc_map)) {
1671
1672 mapinconvertclass *mapinconvert = new mapinconvertclass();
1673 mapinconvert->setmapfile (to_uc_map, 0x003F);
1674 mapinconvert->set_multibyte (multibyte);
1675 mapoutconvertclass *mapoutconvert = new mapoutconvertclass();
1676 mapoutconvert->setmapfile (from_uc_map, 0x3F);
1677 mapoutconvert->set_multibyte (multibyte);
1678 mapoutconvert->set_rzws(1);
1679 add_converter (shortname, mapinconvert, mapoutconvert);
1680 configinfo.encodings[longname] = shortname;
1681 }
1682 }
1683 }
1684 }
1685}
Note: See TracBrowser for help on using the repository browser.