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

Last change on this file since 9615 was 9615, checked in by davidb, 17 years ago

relocation of disp.unloadcollectionmacros() so it always unloads collection
macros before doing the next "exececution" of the library executable.
(Relevant in local library server and apache module version of Greenstone)

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 53.9 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()) 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 (displayclass::defaultpackage, 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 }
851 else if (response == undecided_location) {
852 // Wait until later to output the target location
853 // Used for the "I'm feeling lucky" functionality
854 }
855 else {
856 // unknown response
857 logout << "Error: get_cgihead_info returned an unknown response type.\n";
858 return false;
859 }
860
861 // produce cgi page
862 if (!produce_content (args, contentout, logout)) return false;
863
864 // flush contentout
865 contentout << flush;
866 return true;
867}
868
869
870// get_cgihead_info determines the cgi header information for
871// a set of cgi arguments. If response contains location then
872// response_data contains the redirect address. If reponse
873// contains content then reponse_data contains the content-type.
874// Note that images can now be produced by the receptionist.
875// Note also, alternative for get_cgihead_info below which
876// stores the information in a text_tmap so it is more easily digested
877
878void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
879 text_t &response_data, ostream &logout,
880 text_tmap &fcgienv) {
881 outconvertclass text_t2ascii;
882
883 // get the action
884 action *a = actions.getaction (args["a"]);
885 if (a != NULL) {
886 a->get_cgihead_info (args, &protocols, response, response_data, logout);
887
888 } else {
889 // the action was not found!!
890 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
891 << args["a"] << "\" could not be found.\n";
892 response = content;
893 response_data = "text/html";
894 }
895
896 // add the encoding information
897 if (response == content) {
898 if (converters.find(args["w"]) != converters.end()) {
899 response_data += "; charset=" + args["w"];
900 } else {
901 // default to latin 1
902 response_data += "; charset=ISO-8859-1";
903 }
904
905 // add cookie if required
906 if (configinfo.usecookies && !get_cookie(fcgienv))
907 response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
908 + "; expires=25-Dec-37 00:00:00 GMT";
909 }
910}
911
912
913// Alternative version of get_cgihead_info, stores fielded infomation
914// in text_tmap rather than concatenated string
915void receptionist::get_cgihead_info (cgiargsclass &args, text_tmap &headers,
916 ostream &logout, text_tmap &fcgienv) {
917
918 response_t response;
919 text_t response_data;
920
921 // get the action
922 action *a = actions.getaction (args["a"]);
923 if (a != NULL) {
924 a->get_cgihead_info (args, &protocols, response, response_data, logout);
925
926 } else {
927 // the action was not found!!
928 outconvertclass text_t2ascii;
929 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
930 << args["a"] << "\" could not be found.\n";
931 response = content;
932 response_data = "text/html";
933 }
934
935 if (response == location) {
936 response_data = expandmacros(response_data, args, logout);
937 headers["Location"] = response_data;
938 return;
939 }
940
941 // add the encoding information
942 if (response == content) {
943
944 if (converters.find(args["w"]) != converters.end()) {
945 headers["content-encoding"] = args["w"];
946 response_data += "; charset=" + args["w"];
947 } else {
948 // default to utf-8
949 headers["content-encoding"] = "utf-8";
950 response_data += "; charset=utf-8";
951 }
952
953 headers["content-type"] = response_data;
954
955 }
956
957}
958
959
960
961// produce the page content
962bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
963 ostream &logout) {
964
965 // decide on the output conversion class
966 text_t &arg_w = args["w"];
967 rzwsoutconvertclass defaultoutconverter;
968 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
969 if (outconverter == NULL) outconverter = &defaultoutconverter;
970 outconverter->reset();
971
972 // needed for 16-bit unicode only - big endian marker 0xfeff (RFC 2781)
973 if (arg_w=="utf-16") {
974 contentout << '\xfe' << '\xff' ;
975 }
976
977 recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
978 if (collectproto != NULL) {
979 // get browsers to process OID
980 text_t OID = args["d"];
981 if (OID.empty()) OID = args["cl"];
982 if (!OID.empty()) {
983 text_tset metadata;
984 text_tarray OIDs;
985 OIDs.push_back (OID);
986 if (!is_top(OID)) OIDs.push_back (OID + ".pr");
987 FilterResponse_t response;
988 metadata.insert ("childtype");
989 if (get_info (OIDs, args["c"], args["l"], metadata, false, collectproto, response, logout)) {
990 text_t classifytype;
991 if (!response.docInfo[0].metadata["childtype"].values[0].empty())
992 classifytype = response.docInfo[0].metadata["childtype"].values[0];
993 else if (!is_top (OID)) {
994 // not sure why this is occasionally not set, but it will
995 // cause a segfault... possibly if built with no_text? jrm21
996 if (response.docInfo[1].metadata.find("childtype")
997 == response.docInfo[1].metadata.end()) {
998 cerr << "receptionist: no childtype element in metadata map!"
999 << endl;
1000 } else {
1001 if (!response.docInfo[1].metadata["childtype"].values[0].empty())
1002 classifytype = response.docInfo[1].metadata["childtype"].values[0];
1003 }
1004 }
1005 browserclass *b = browsers.getbrowser (classifytype);
1006 b->processOID (args, collectproto, logout);
1007 }
1008 }
1009
1010 // translate "d" and "cl" arguments if required
1011 translate_OIDs (args, collectproto, logout);
1012 }
1013
1014 // produce the page using the desired action
1015 action *a = actions.getaction (args["a"]);
1016 if (a != NULL) {
1017 if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
1018 if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
1019 return false;
1020 } else {
1021 // the action was not found!!
1022 outconvertclass text_t2ascii;
1023
1024 logout << text_t2ascii << "Error receptionist::produce_content: the action \""
1025 << args["a"] << "\" could not be found.\n";
1026
1027 contentout << (*outconverter)
1028 << "<html>\n"
1029 << "<head>\n"
1030 << "<title>Error</title>\n"
1031 << "</head>\n"
1032 << "<body>\n"
1033 << "<h2>Oops!</h2>\n"
1034 << "Undefined Page. The action \""
1035 << args["a"] << "\" could not be found.\n"
1036 << "</body>\n"
1037 << "</html>\n";
1038 }
1039 return true;
1040}
1041
1042
1043// returns the compressed argument ("e") corresponding to the argument
1044// list. This can be used to save preferences between sessions.
1045text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
1046 // decide on the output conversion class
1047 text_t &arg_w = args["w"];
1048 rzwsoutconvertclass defaultoutconverter;
1049 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
1050 if (outconverter == NULL) outconverter = &defaultoutconverter;
1051 outconverter->reset();
1052
1053 text_t compressed_args;
1054 if (compress_save_args (argsinfo, configinfo.saveconf, args,
1055 compressed_args, *outconverter, logout))
1056 return compressed_args;
1057
1058 return g_EmptyText;
1059}
1060
1061
1062// will read in all the macro files. If one is not found an
1063// error message will be written to logout and the method will
1064// return false.
1065bool receptionist::read_macrofiles (ostream &logout) {
1066 outconvertclass text_t2ascii;
1067
1068 // redirect the error output to logout
1069 ostream *savedlogout = disp.setlogout (&logout);
1070
1071 // unload any macros that were previously loaded - this allows us to call
1072 // this function a second time to reload all the macro files (useful for
1073 // reading in changed macro files in server versions of greenstone)
1074 disp.unloaddefaultmacros();
1075
1076 // load up the default macro files, the collection directory
1077 // is searched first for the file (if this is being used in
1078 // collection specific mode) and then the main directory(s)
1079 text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
1080
1081 text_tset maindirs;
1082 text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
1083 maindirs.insert (gsdlmacrodir);
1084 colinfo_tmap::iterator colhere = configinfo.collectinfo.begin();
1085 colinfo_tmap::iterator colend = configinfo.collectinfo.end();
1086 while (colhere != colend) {
1087 if (!((*colhere).second.gsdl_gsdlhome).empty()) {
1088 gsdlmacrodir = filename_cat ((*colhere).second.gsdl_gsdlhome, "macros");
1089 maindirs.insert (gsdlmacrodir);
1090 }
1091 ++colhere;
1092 }
1093
1094 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1095 text_tset::iterator arrend = configinfo.macrofiles.end();
1096 text_t filename;
1097 while (arrhere != arrend) {
1098 bool foundfile = false;
1099
1100 // try in the collection directory if this is being
1101 // run in collection specific mode
1102 if (!configinfo.collection.empty()) {
1103 filename = filename_cat (colmacrodir, *arrhere);
1104 if (file_exists (filename)) {
1105 disp.loaddefaultmacros(filename);
1106 foundfile = true;
1107 }
1108 }
1109
1110 // if we haven't found the macro file yet try in
1111 // the main macro directory(s)
1112 // if file is found in more than one main directory
1113 // we'll load all copies
1114 if (!foundfile) {
1115 text_tset::const_iterator dirhere = maindirs.begin();
1116 text_tset::const_iterator dirend = maindirs.end();
1117 while (dirhere != dirend) {
1118 filename = filename_cat (*dirhere, *arrhere);
1119 if (file_exists (filename)) {
1120 disp.loaddefaultmacros(filename);
1121 foundfile = true;
1122 }
1123 ++dirhere;
1124 }
1125 }
1126
1127 // see if we found the file or not
1128 if (!foundfile) {
1129 logout << text_t2ascii
1130 << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
1131 if (configinfo.collection.empty()) {
1132 text_t dirs;
1133 joinchar (maindirs, ", ", dirs);
1134 logout << text_t2ascii
1135 << "It should be in either of the following directories ("
1136 << dirs << ").\n\n";
1137
1138 } else {
1139 logout << text_t2ascii
1140 << "It should be in either " << colmacrodir << " or in "
1141 << gsdlmacrodir << ".\n\n";
1142 }
1143 // reset logout to what it was
1144 disp.setlogout (savedlogout);
1145 return false;
1146 }
1147 ++arrhere;
1148 }
1149
1150 // success
1151
1152 // reset logout to what it was
1153 disp.setlogout (savedlogout);
1154 return true;
1155}
1156
1157
1158
1159
1160// Go through the list of macro files looking to see
1161// if any exist in the collectoin specific area. If they
1162// do then read them in and add them to the set of existing
1163// current macros
1164
1165void receptionist::read_collection_macrofiles (const text_t& collection, ostream &logout)
1166{
1167 outconvertclass text_t2ascii;
1168
1169 // disp.unloadcollectionmacros();
1170
1171 // redirect the error output to logout
1172 ostream *savedlogout = disp.setlogout (&logout);
1173
1174 text_t colmacrodir
1175 = filename_cat (configinfo.gsdlhome,"collect",collection, "macros");
1176
1177 if (directory_exists (colmacrodir)) {
1178
1179 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1180 text_tset::iterator arrend = configinfo.macrofiles.end();
1181 text_t filename;
1182 while (arrhere != arrend) {
1183
1184 filename = filename_cat (colmacrodir, *arrhere);
1185 if (file_exists (filename)) {
1186 disp.loadcollectionmacros(filename);
1187 }
1188
1189 ++arrhere;
1190 }
1191 }
1192
1193 // reset logout to what it was
1194 disp.setlogout (savedlogout);
1195}
1196
1197
1198
1199
1200// check_mainargs will check all the main arguments. If a major
1201// error is found it will return false and no cgi page should
1202// be created using the arguments.
1203
1204bool receptionist::check_mainargs (cgiargsclass &args, ostream &logout) {
1205
1206 if(configinfo.site_auth)
1207 {
1208 args["uan"] = "1";
1209 args["ug"] = configinfo.site_group;
1210 }
1211
1212
1213 // if this receptionist is running in collection dependant mode
1214 // then it should always set the collection argument to the
1215 // collection
1216 if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
1217
1218 // if current collection uses ccscols make sure
1219 // "ccs" argument is set and make "cc" default to
1220 // all collections in "ccs"
1221 if (args["a"] != "config" && !args["c"].empty()) {
1222
1223 text_t &arg_c = args["c"];
1224 recptproto *collectproto = protocols.getrecptproto (arg_c, logout);
1225 if (collectproto == NULL) {
1226 // oops, this collection isn't valid
1227 outconvertclass text_t2ascii;
1228 logout << text_t2ascii << "ERROR: Invalid collection: " << arg_c << "\n";
1229 // args["c"].clear();
1230
1231 } else {
1232
1233 ColInfoResponse_t *cinfo = get_collectinfo_ptr (collectproto, arg_c, logout);
1234
1235 if(cinfo->authenticate == "collection")
1236 {
1237 args["uan"] = "1";
1238 args["ug"] = cinfo->auth_group;
1239 }
1240
1241
1242 if (cinfo != NULL) {
1243 if (!cinfo->ccsCols.empty()) {
1244 args["ccs"] = 1;
1245 if (args["cc"].empty()) {
1246 text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
1247 text_tarray::const_iterator col_end = cinfo->ccsCols.end();
1248 bool first = true;
1249 while (col_here != col_end) {
1250 // make sure it's a valid collection
1251 if (protocols.getrecptproto (*col_here, logout) != NULL) {
1252 if (!first) args["cc"].push_back (',');
1253 args["cc"] += *col_here;
1254 first = false;
1255 }
1256 ++col_here;
1257 }
1258 }
1259 }
1260 } else {
1261 logout << "ERROR (receptionist::check_mainargs): get_collectinfo_ptr returned NULL\n";
1262 }
1263 }
1264 }
1265
1266 // argument "v" can only be 0 or 1. Use the default value
1267 // if it is out of range
1268 int arg_v = args.getintarg ("v");
1269 if (arg_v != 0 && arg_v != 1) {
1270 cgiarginfo *vinfo = argsinfo.getarginfo ("v");
1271 if (vinfo != NULL) args["v"] = vinfo->argdefault;
1272 }
1273
1274 // argument "f" can only be 0 or 1. Use the default value
1275 // if it is out of range
1276 int arg_f = args.getintarg ("f");
1277 if (arg_f != 0 && arg_f != 1) {
1278 cgiarginfo *finfo = argsinfo.getarginfo ("f");
1279 if (finfo != NULL) args["f"] = finfo->argdefault;
1280 }
1281
1282 return true;
1283}
1284
1285// translate_OIDs translates the "d" and "cl" arguments to their correct values
1286// if they use the tricky ".fc", ".lc" type syntax.
1287void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto,
1288 ostream &logout) {
1289
1290 FilterResponse_t response;
1291 FilterRequest_t request;
1292 comerror_t err;
1293 text_t &arg_d = args["d"];
1294 text_t &arg_cl = args["cl"];
1295 text_t &collection = args["c"];
1296
1297 // do a call to translate OIDs if required
1298 request.filterName = "NullFilter";
1299 request.filterResultOptions = FROID;
1300 if (!arg_d.empty() && needs_translating (arg_d)) {
1301 request.docSet.push_back (arg_d);
1302 collectproto->filter (collection, request, response, err, logout);
1303 arg_d = response.docInfo[0].OID;
1304 request.clear();
1305 }
1306 // we'll also check here that the "cl" argument has a "classify" doctype
1307 // (in case ".fc" or ".lc" have screwed up)
1308 if (needs_translating (arg_cl)) {
1309 request.fields.insert ("doctype");
1310 request.docSet.push_back (arg_cl);
1311 request.filterResultOptions = FRmetadata;
1312 collectproto->filter (collection, request, response, err, logout);
1313 // set to original value (without .xx stuff) if doctype isn't "classify"
1314 if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
1315 strip_suffix (arg_cl);
1316 else
1317 arg_cl = response.docInfo[0].OID;
1318 }
1319}
1320
1321// prepare_page sets up page parameters, sets display macros
1322// and opens the page ready for output
1323void receptionist::prepare_page (action *a, cgiargsclass &args,
1324 outconvertclass &outconvert,
1325 ostream &logout) {
1326 // set up page parameters
1327 text_t pageparams;
1328 bool first = true;
1329
1330 text_tmap::iterator params_here = configinfo.pageparams.begin();
1331 text_tmap::iterator params_end = configinfo.pageparams.end();
1332 while (params_here != params_end) {
1333 // page params are those from main.cfg (eg pageparam v 0) plus
1334 // two defaults set in recptconf.clear() (c="" and l=en)
1335 // This used to check if the current value of the page param
1336 // == the default value, then don't add in it the list
1337 // but if l=en, and there is a macro with [l=en], then it doesn't
1338 // find it.
1339 // so now all page params will go into the list. I assume this will
1340 // mean more attempts to find each macro, but nothing worsee than
1341 // that. --kjdon
1342 //if (args[(*params_here).first] != (*params_here).second) {
1343 if (first)
1344 first = false;
1345 else
1346 pageparams += ",";
1347
1348 pageparams += (*params_here).first;
1349 pageparams += "=";
1350 pageparams += args[(*params_here).first];
1351 // }
1352
1353 ++params_here;
1354 }
1355
1356
1357 // open the page
1358 disp.openpage(pageparams, configinfo.macroprecedence);
1359
1360 disp.unloadcollectionmacros();
1361
1362 text_t collection = args["c"];
1363 if (!collection.empty()) {
1364 read_collection_macrofiles(collection,logout);
1365 }
1366
1367 // define external macros for each action
1368 actionptrmap::iterator actionhere = actions.begin ();
1369 actionptrmap::iterator actionend = actions.end ();
1370
1371 while (actionhere != actionend) {
1372 assert ((*actionhere).second.a != NULL);
1373 if ((*actionhere).second.a != NULL) {
1374 (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
1375 }
1376 ++actionhere;
1377 }
1378
1379
1380 // define internal macros for the current action
1381 a->define_internal_macros (disp, args, &protocols, logout);
1382
1383 // define general macros. the defining of general macros is done here so that
1384 // the last possible version of the cgi arguments are used
1385 define_general_macros (args, outconvert, logout);
1386}
1387
1388void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/,
1389 ostream &logout) {
1390
1391 text_t &collection = args["c"];
1392
1393 disp.setmacro ("gsdlhome", displayclass::defaultpackage, dm_safe(configinfo.gsdlhome));
1394 disp.setmacro ("gwcgi", displayclass::defaultpackage, configinfo.gwcgi);
1395 disp.setmacro ("httpimg", displayclass::defaultpackage, configinfo.httpimg);
1396 disp.setmacro ("httpprefix", displayclass::defaultpackage, configinfo.httpprefix);
1397
1398 // get relevant info from the collection
1399 if (!collection.empty()) {
1400 ColInfoResponse_t cinfo;
1401 comerror_t err;
1402 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1403 if (collectproto != NULL) {
1404 collectproto->get_collectinfo (collection, cinfo, err, logout);
1405 text_t httpcollection;
1406 if (!cinfo.httpdomain.empty()) httpcollection = "http://";
1407 httpcollection += cinfo.httpdomain + cinfo.httpprefix + "/collect/"
1408 + collection;
1409 disp.setmacro ("httpcollection", displayclass::defaultpackage,
1410 httpcollection);
1411 // as of gsdl 2.53, collect.cfg can specify macros
1412 if (cinfo.collection_macros.size() > 0) {
1413 macros_map::const_iterator this_macro=cinfo.collection_macros.begin();
1414 macros_map::const_iterator done_macro=cinfo.collection_macros.end();
1415 while (this_macro != done_macro) {
1416
1417 disp.setcollectionmacro("Global", // package
1418 this_macro->first, // macro name
1419 this_macro->second.first, // params
1420 this_macro->second.second); // value
1421 ++this_macro;
1422 }
1423 } // col macros
1424 } // collectproto != NULL
1425 }
1426
1427 text_t compressedoptions = get_compressed_arg(args, logout);
1428 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1429 // need a decoded version of compressedoptions for use within forms
1430 // as browsers encode values from forms before sending to server
1431 // (e.g. %25 becomes %2525)
1432 decode_cgi_arg (compressedoptions);
1433 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1434
1435#if defined (__WIN32__)
1436 disp.setmacro ("win32", displayclass::defaultpackage, "1");
1437#endif
1438
1439 // set _cgiargX_ macros for each cgi argument
1440 cgiargsclass::const_iterator argshere = args.begin();
1441 cgiargsclass::const_iterator argsend = args.end();
1442 while (argshere != argsend) {
1443 if (((*argshere).first == "q") ||
1444 ((*argshere).first == "qa") ||
1445 ((*argshere).first == "qtt") ||
1446 ((*argshere).first == "qty") ||
1447 ((*argshere).first == "qp") ||
1448 ((*argshere).first == "qpl") ||
1449 ((*argshere).first == "qr") ||
1450 ((*argshere).first == "q2"))
1451 // need to escape special characters from query string
1452 disp.setmacro ("cgiarg" + (*argshere).first,
1453 displayclass::defaultpackage, html_safe((*argshere).second.value));
1454 else if ((*argshere).first == "hp") {
1455 disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, (*argshere).second.value);
1456 } else {
1457 disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, dm_safe((*argshere).second.value));
1458 }
1459 ++argshere;
1460 }
1461
1462 // display text right to left if language is arabic/farsi (and if browser can support it)
1463 if (args["l"] == "ar" || args["l"] == "fa")
1464 disp.setmacro ("htmlextra", displayclass::defaultpackage, " dir=rtl");
1465
1466 // set collection specific macros
1467 if (!collection.empty()) {
1468 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1469 if (collectproto != NULL) {
1470 FilterResponse_t response;
1471 text_tset metadata;
1472 get_info ("collection", collection, args["l"], metadata, false,
1473 collectproto, response, logout);
1474
1475 if (!response.docInfo[0].metadata.empty()) {
1476 MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
1477 MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
1478 while (here != end) {
1479 if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
1480 ((*here).first != "hasprevious")) {
1481 // check for args in form name:lang
1482 text_t name = g_EmptyText;
1483 text_t lang = g_EmptyText;
1484 bool colonfound=false;
1485 text_t::const_iterator a = (*here).first.begin();
1486 text_t::const_iterator b = (*here).first.end();
1487 while (a !=b) {
1488 if (*a==':') {
1489 colonfound=true;
1490 }
1491 else {
1492 if (colonfound)
1493 lang.push_back(*a);
1494 else name.push_back(*a);
1495 }
1496 ++a;
1497 }
1498 if (!lang.empty()) {
1499 if (args["l"]==lang) {
1500 disp.setmacro (name, displayclass::defaultpackage, (*here).second.values[0]);
1501 }
1502 }
1503 else { // the default one
1504 disp.setmacro ((*here).first, displayclass::defaultpackage, (*here).second.values[0]);
1505 }
1506 }
1507 ++here;
1508 }
1509 }
1510
1511 text_t iconcollection;
1512 disp.expandstring (displayclass::defaultpackage, "_iconcollection_", iconcollection);
1513 if (!iconcollection.empty())
1514 {
1515 ColInfoResponse_t cinfo;
1516 comerror_t err;
1517 collectproto->get_collectinfo (collection, cinfo, err, logout);
1518 if (iconcollection[0]=='/' && !cinfo.httpdomain.empty())
1519 {
1520 // local but with full path
1521 iconcollection = "http://" + cinfo.httpdomain + iconcollection;
1522 disp.setmacro("iconcollection", displayclass::defaultpackage, iconcollection);
1523 }
1524 }
1525 }
1526 }
1527}
1528
1529// gets collection info from cache if found or
1530// calls collection server (and updates cache)
1531// returns NULL if there's an error
1532ColInfoResponse_t *receptionist::get_collectinfo_ptr (recptproto *collectproto,
1533 const text_t &collection,
1534 ostream &logout) {
1535
1536 // check the cache
1537 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1538 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1539 // found it
1540 return &((*it).second.info);
1541 }
1542
1543 // not cached, get info from collection server
1544 if (collectproto == NULL) {
1545 logout << "ERROR: receptionist::get_collectinfo_ptr passed null collectproto\n";
1546 return NULL;
1547 }
1548
1549 comerror_t err;
1550 if (it == configinfo.collectinfo.end()) {
1551 collectioninfo_t cinfo;
1552 collectproto->get_collectinfo (collection, cinfo.info, err, logout);
1553 if (err != noError) {
1554 outconvertclass text_t2ascii;
1555 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1556 << get_comerror_string (err) << "\"while getting collectinfo\n";
1557 return NULL;
1558 }
1559 cinfo.info_loaded = true;
1560 configinfo.collectinfo[collection] = cinfo;
1561 return &(configinfo.collectinfo[collection].info);
1562 } else {
1563 collectproto->get_collectinfo (collection, (*it).second.info, err, logout);
1564 if (err != noError) {
1565 outconvertclass text_t2ascii;
1566 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1567 << get_comerror_string (err) << "\"while getting collectinfo\n";
1568 return NULL;
1569 }
1570 (*it).second.info_loaded = true;
1571 return &((*it).second.info);
1572 }
1573}
1574
1575// removes a collection from the cache so that the next
1576// call to get_collectinfo_ptr() for that collection will
1577// retrieve the collection info from the collection server
1578void receptionist::uncache_collection (const text_t &collection) {
1579
1580 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1581 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1582
1583 (*it).second.info_loaded = false;
1584
1585 }
1586}
1587
1588// Handles an "Encoding" line from a configuration file - note that the
1589// configinfo.encodings map is a bit of a hack (to be fixed when the
1590// configuration files are tidied up).
1591void receptionist::configure_encoding (const text_tarray &cfgline) {
1592
1593 text_t subkey, subvalue, shortname, longname, mapfile;
1594 int multibyte = 0;
1595 text_t::const_iterator cfglinesub_here;
1596 text_tarray::const_iterator cfgline_here = cfgline.begin();
1597 text_tarray::const_iterator cfgline_end = cfgline.end();
1598 while (cfgline_here != cfgline_end) {
1599 if (*cfgline_here == "multibyte") {
1600 multibyte = 1;
1601 } else {
1602 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
1603 (*cfgline_here).end(), '=', subkey);
1604 if (subkey == "shortname") {
1605 shortname = substr (cfglinesub_here, (*cfgline_here).end());
1606 } else if (subkey == "longname") {
1607 longname = substr (cfglinesub_here, (*cfgline_here).end());
1608 } else if (subkey == "map") {
1609 mapfile = substr (cfglinesub_here, (*cfgline_here).end());
1610 }
1611 }
1612 ++cfgline_here;
1613 }
1614 if (!shortname.empty()) {
1615 if (longname.empty()) longname = shortname;
1616
1617 // add the converter
1618 if (shortname == "utf-8") {
1619 utf8inconvertclass *utf8inconvert = new utf8inconvertclass();
1620 utf8outconvertclass *utf8outconvert = new utf8outconvertclass();
1621 utf8outconvert->set_rzws(1);
1622 add_converter (shortname, utf8inconvert, utf8outconvert);
1623 configinfo.encodings[longname] = shortname;
1624
1625 } else if (shortname == "utf-16") {
1626 // we use the default input converter as this shouldn't ever be used
1627 // for converting from unicode...
1628 inconvertclass *inconverter = new inconvertclass();
1629 utf16outconvertclass *outconverter = new utf16outconvertclass();
1630 add_converter (shortname, inconverter, outconverter);
1631 configinfo.encodings[longname] = shortname;
1632
1633 } else if (!mapfile.empty()) {
1634
1635 if (mapfile == "8859_1.ump") {
1636 // iso-8859-1 is a special case as it'll always be supported by the
1637 // standard converter class and therefore doesn't need to use its
1638 // mapping file
1639 inconvertclass *inconvert = new inconvertclass();
1640 rzwsoutconvertclass *outconvert = new rzwsoutconvertclass();
1641 outconvert->set_rzws(1);
1642 add_converter (shortname, inconvert, outconvert);
1643 configinfo.encodings[longname] = shortname;
1644
1645 } else {
1646 text_t to_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "to_uc", mapfile);
1647 text_t from_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "from_uc", mapfile);
1648 if (file_exists(to_uc_map) && file_exists(from_uc_map)) {
1649
1650 mapinconvertclass *mapinconvert = new mapinconvertclass();
1651 mapinconvert->setmapfile (to_uc_map, 0x003F);
1652 mapinconvert->set_multibyte (multibyte);
1653 mapoutconvertclass *mapoutconvert = new mapoutconvertclass();
1654 mapoutconvert->setmapfile (from_uc_map, 0x3F);
1655 mapoutconvert->set_multibyte (multibyte);
1656 mapoutconvert->set_rzws(1);
1657 add_converter (shortname, mapinconvert, mapoutconvert);
1658 configinfo.encodings[longname] = shortname;
1659 }
1660 }
1661 }
1662 }
1663}
Note: See TracBrowser for help on using the repository browser.