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

Last change on this file since 13017 was 12509, checked in by kjdon, 18 years ago

Added Stefan's fileupload code to replace use of cgicc by depositor. action.getargsinfo changed, parse_cgi_args now has a fileuploads argument

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