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

Last change on this file since 3546 was 3546, checked in by sjboddie, 21 years ago

Added protos to argument list of action::check_cgiargs() as I always seem to want it to
be there.

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