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

Last change on this file since 3036 was 3036, checked in by jrm21, 22 years ago

text strings weren't being properly converted for file output via the
convertclass. Input is now properly set so that we don't get garbage output.

  • 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, 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_APPEND);
699
700 if (fd == -1) {
701 logout << "Error: Couldn't open file " << lfile << "\n";
702 delete lfile;
703 return false;
704 }
705
706 //int fd = GSDL_GET_FILEDESC(log);
707
708 // lock_val is set to 0 if file is locked successfully
709 int lock_val = 1;
710 GSDL_LOCK_FILE (fd);
711 if (lock_val == 0) {
712 text_t tmp_log_str(logstr); // so we don't pass a const to setinput...
713 text_t2utf8.setinput(&tmp_log_str);
714 char *buffer=new char[logstr.size()];
715 size_t num_chars;
716 convertclass::status_t status;
717 text_t2utf8.convert(buffer, logstr.size(), num_chars, status);
718 // ignore status - assume it is "finished" as buffer is big enough
719 write(fd, buffer, num_chars);
720 GSDL_UNLOCK_FILE (fd);
721 delete buffer;
722 } else {
723 logout << "Error: Couldn't lock file " << lfile << "\n";
724 close(fd);
725 delete lfile;
726 return false;
727 }
728
729 close(fd);
730
731 delete lfile;
732 return true;
733}
734
735text_t receptionist::expandmacros (const text_t &astring, cgiargsclass &args,
736 ostream &logout) {
737 text_t outstring;
738 outconvertclass text_t2ascii;
739
740 action *a = actions.getaction (args["a"]);
741 prepare_page (a, args, text_t2ascii, logout);
742 disp.expandstring ("Global", astring, outstring);
743 return outstring;
744}
745
746// produce_cgi_page will call get_cgihead_info and
747// produce_content in the appropriate way to output a cgi header and
748// the page content (if needed). If a page could not be created it
749// will return false
750bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout,
751 ostream &logout, text_tmap &fcgienv) {
752 outconvertclass text_t2ascii;
753
754 response_t response;
755 text_t response_data;
756
757 // produce cgi header
758 get_cgihead_info (args, response, response_data, logout, fcgienv);
759 if (response == location) {
760 // location response (url may contain macros!!)
761 response_data = expandmacros (response_data, args, logout);
762 contentout << text_t2ascii << "Location: " << response_data << "\n\n";
763 contentout << flush;
764 return true;
765 } else if (response == content) {
766 // content response
767
768 // use the later of build.cfg and collect.cfg modification times
769 // as the Last-Modified: header, for caching values
770 struct stat file_info;
771 time_t latest=0;
772
773 text_t collectname="";
774 collectname=args["c"];
775 if (collectname != "") {
776 text_t collectdir=filename_cat(configinfo.gsdlhome,"collect");
777 collectdir=filename_cat(collectdir,collectname);
778 text_t buildcfg=filename_cat(collectdir,"index");
779 buildcfg=filename_cat(buildcfg,"build.cfg");
780 char *buildcfg_ptr=buildcfg.getcstr();
781 text_t collectcfg=filename_cat(collectdir,"etc");
782 collectcfg=filename_cat(collectcfg,"collect.cfg");
783 char *collectcfg_ptr=collectcfg.getcstr();
784
785 if (stat(buildcfg_ptr, &file_info)) {
786 // we got an error. Currently don't handle error :(
787 // logout <<
788 } else {
789 latest=file_info.st_mtime;
790 }
791
792 if (stat(collectcfg_ptr, &file_info)) {
793 // error - unhandled for now
794 } else {
795 if (latest<file_info.st_mtime) latest=file_info.st_mtime;
796 }
797 delete buildcfg_ptr;
798 delete collectcfg_ptr;
799
800 if (latest>0) {
801 // print out modified time, "DDD, dd MMM YYYY hh:mm:ss" format
802 // c library takes care of mem for this string... (has \n at end!!!!)
803 // latest is currently local time, convert to UTC.
804 struct tm* utc_latest;
805 utc_latest=gmtime(&latest);
806 contentout << "Last-Modified: " << asctime(utc_latest);
807 }
808 } // end of collection != ""
809
810 /*
811 // This block of code should prevent the users browser from caching
812 // the page. It may be useful to uncomment this code and comment out
813 // that directly above (which sets a "Last-Modified" header) when
814 // doing interface development work where a given page may be
815 // changing often.
816
817 contentout << "Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"; // date in the past
818 tm *tm_ptr = NULL;
819 time_t t = time(NULL);
820 tm_ptr = gmtime (&t);
821 if (tm_ptr != NULL) {
822 char *timestr = new char[128];
823 strftime (timestr, 128, "%a, %d %b %Y %H:%M:%S", tm_ptr);
824 contentout << "Last-Modified: " << timestr << " GMT\n"; // always modified
825 delete timestr;
826 }
827 contentout << "Cache-Control: no-cache, must-revalidate\n"; // HTTP/1.1
828 contentout << "Pragma: no-cache\n"; // HTTP/1.0
829 */
830
831 contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
832 } else {
833 // unknown response
834 logout << "Error: get_cgihead_info returned an unknown response type.\n";
835 return false;
836 }
837
838 // produce cgi page
839 if (!produce_content (args, contentout, logout)) return false;
840
841 // flush contentout
842 contentout << flush;
843 return true;
844}
845
846
847// get_cgihead_info determines the cgi header information for
848// a set of cgi arguments. If response contains location then
849// response_data contains the redirect address. If reponse
850// contains content then reponse_data contains the content-type.
851// Note that images can now be produced by the receptionist.
852void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
853 text_t &response_data, ostream &logout,
854 text_tmap &fcgienv) {
855 outconvertclass text_t2ascii;
856
857 // get the action
858 action *a = actions.getaction (args["a"]);
859 if (a != NULL) {
860 a->get_cgihead_info (args, &protocols, response, response_data, logout);
861
862 } else {
863 // the action was not found!!
864 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
865 << args["a"] << "\" could not be found.\n";
866 response = content;
867 response_data = "text/html";
868 }
869
870 // add the encoding information
871 if (response == content) {
872 if (converters.find(args["w"]) != converters.end()) {
873 response_data += "; charset=" + args["w"];
874 } else {
875 // default to latin 1
876 response_data += "; charset=ISO-8859-1";
877 }
878
879 // add cookie if required
880 if (configinfo.usecookies && !get_cookie(fcgienv))
881 response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
882 + "; expires=25-Dec-37 00:00:00 GMT";
883 }
884}
885
886
887// produce the page content
888bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
889 ostream &logout) {
890
891 // decide on the output conversion class
892 text_t &arg_w = args["w"];
893 rzwsoutconvertclass defaultoutconverter;
894 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
895 if (outconverter == NULL) outconverter = &defaultoutconverter;
896 outconverter->reset();
897
898
899 recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
900 if (collectproto != NULL) {
901 // get browsers to process OID
902 text_t OID = args["d"];
903 if (OID.empty()) OID = args["cl"];
904 if (!OID.empty()) {
905 text_tset metadata;
906 text_tarray OIDs;
907 OIDs.push_back (OID);
908 if (!is_top(OID)) OIDs.push_back (OID + ".pr");
909 FilterResponse_t response;
910 metadata.insert ("childtype");
911 if (get_info (OIDs, args["c"], metadata, false, collectproto, response, logout)) {
912 text_t classifytype;
913 if (!response.docInfo[0].metadata["childtype"].values[0].empty())
914 classifytype = response.docInfo[0].metadata["childtype"].values[0];
915 else if (!is_top (OID)) {
916 if (!response.docInfo[1].metadata["childtype"].values[0].empty())
917 classifytype = response.docInfo[1].metadata["childtype"].values[0];
918 }
919 browserclass *b = browsers.getbrowser (classifytype);
920 b->processOID (args, collectproto, logout);
921 }
922 }
923
924 // translate "d" and "cl" arguments if required
925 translate_OIDs (args, collectproto, logout);
926 }
927
928 // produce the page using the desired action
929 action *a = actions.getaction (args["a"]);
930 if (a != NULL) {
931 if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
932 if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
933 return false;
934 } else {
935 // the action was not found!!
936 outconvertclass text_t2ascii;
937
938 logout << text_t2ascii << "Error receptionist::produce_content: the action \""
939 << args["a"] << "\" could not be found.\n";
940
941 contentout << (*outconverter)
942 << "<html>\n"
943 << "<head>\n"
944 << "<title>Error</title>\n"
945 << "</head>\n"
946 << "<body>\n"
947 << "<h2>Oops!</h2>\n"
948 << "Undefined Page. The action \""
949 << args["a"] << "\" could not be found.\n"
950 << "</body>\n"
951 << "</html>\n";
952 }
953 return true;
954}
955
956
957// returns the compressed argument ("e") corresponding to the argument
958// list. This can be used to save preferences between sessions.
959text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
960 // decide on the output conversion class
961 text_t &arg_w = args["w"];
962 rzwsoutconvertclass defaultoutconverter;
963 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
964 if (outconverter == NULL) outconverter = &defaultoutconverter;
965 outconverter->reset();
966
967 text_t compressed_args;
968 if (compress_save_args (argsinfo, configinfo.saveconf, args,
969 compressed_args, *outconverter, logout))
970 return compressed_args;
971
972 return "";
973}
974
975
976// will read in all the macro files. If one is not found an
977// error message will be written to logout and the method will
978// return false.
979bool receptionist::read_macrofiles (ostream &logout) {
980 outconvertclass text_t2ascii;
981
982 // redirect the error output to logout
983 ostream *savedlogout = disp.setlogout (&logout);
984
985 // load up the default macro files, the collection directory
986 // is searched first for the file (if this is being used in
987 // collection specific mode) and then the main directory(s)
988 text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
989
990 text_tset maindirs;
991 text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
992 maindirs.insert (gsdlmacrodir);
993 colinfo_tmap::iterator colhere = configinfo.collectinfo.begin();
994 colinfo_tmap::iterator colend = configinfo.collectinfo.end();
995 while (colhere != colend) {
996 if (!((*colhere).second.gsdl_gsdlhome).empty()) {
997 gsdlmacrodir = filename_cat ((*colhere).second.gsdl_gsdlhome, "macros");
998 maindirs.insert (gsdlmacrodir);
999 }
1000 colhere ++;
1001 }
1002
1003 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1004 text_tset::iterator arrend = configinfo.macrofiles.end();
1005 text_t filename;
1006 while (arrhere != arrend) {
1007 bool foundfile = false;
1008
1009 // try in the collection directory if this is being
1010 // run in collection specific mode
1011 if (!configinfo.collection.empty()) {
1012 filename = filename_cat (colmacrodir, *arrhere);
1013 if (file_exists (filename)) {
1014 disp.loaddefaultmacros(filename);
1015 foundfile = true;
1016 }
1017 }
1018
1019 // if we haven't found the macro file yet try in
1020 // the main macro directory(s)
1021 // if file is found in more than one main directory
1022 // we'll load all copies
1023 if (!foundfile) {
1024 text_tset::const_iterator dirhere = maindirs.begin();
1025 text_tset::const_iterator dirend = maindirs.end();
1026 while (dirhere != dirend) {
1027 filename = filename_cat (*dirhere, *arrhere);
1028 if (file_exists (filename)) {
1029 disp.loaddefaultmacros(filename);
1030 foundfile = true;
1031 }
1032 dirhere ++;
1033 }
1034 }
1035
1036 // see if we found the file or not
1037 if (!foundfile) {
1038 logout << text_t2ascii
1039 << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
1040 if (configinfo.collection.empty()) {
1041 text_t dirs;
1042 joinchar (maindirs, ", ", dirs);
1043 logout << text_t2ascii
1044 << "It should be in either of the following directories ("
1045 << dirs << ").\n\n";
1046
1047 } else {
1048 logout << text_t2ascii
1049 << "It should be in either " << colmacrodir << " or in "
1050 << gsdlmacrodir << ".\n\n";
1051 }
1052 // reset logout to what it was
1053 disp.setlogout (savedlogout);
1054 return false;
1055 }
1056 arrhere++;
1057 }
1058
1059 // success
1060
1061 // reset logout to what it was
1062 disp.setlogout (savedlogout);
1063 return true;
1064}
1065
1066
1067// check_mainargs will check all the main arguments. If a major
1068// error is found it will return false and no cgi page should
1069// be created using the arguments.
1070bool receptionist::check_mainargs (cgiargsclass &args, ostream &logout) {
1071 // if this receptionist is running in collection dependant mode
1072 // then it should always set the collection argument to the
1073 // collection
1074 if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
1075
1076 // if current collection uses ccscols make sure
1077 // "ccs" argument is set and make "cc" default to
1078 // all collections in "ccs"
1079 if (!args["c"].empty()) {
1080
1081 text_t &arg_c = args["c"];
1082 recptproto *collectproto = protocols.getrecptproto (arg_c, logout);
1083 if (collectproto == NULL) {
1084 // oops, this collection isn't valid
1085 outconvertclass text_t2ascii;
1086 logout << text_t2ascii << "ERROR: Invalid collection: " << arg_c << "\n";
1087 args["c"].clear();
1088
1089 } else {
1090
1091 ColInfoResponse_t *cinfo = get_collectinfo_ptr (collectproto, arg_c, logout);
1092
1093 if (cinfo != NULL) {
1094 if (!cinfo->ccsCols.empty()) {
1095 args["ccs"] = 1;
1096 if (args["cc"].empty()) {
1097 text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
1098 text_tarray::const_iterator col_end = cinfo->ccsCols.end();
1099 bool first = true;
1100 while (col_here != col_end) {
1101 // make sure it's a valid collection
1102 if (protocols.getrecptproto (*col_here, logout) != NULL) {
1103 if (!first) args["cc"].push_back (',');
1104 args["cc"] += *col_here;
1105 first = false;
1106 }
1107 col_here ++;
1108 }
1109 }
1110 }
1111 } else {
1112 logout << "ERROR (receptionist::check_mainargs): get_collectinfo_ptr returned NULL\n";
1113 }
1114 }
1115 }
1116
1117 // argument "v" can only be 0 or 1. Use the default value
1118 // if it is out of range
1119 int arg_v = args.getintarg ("v");
1120 if (arg_v != 0 && arg_v != 1) {
1121 cgiarginfo *vinfo = argsinfo.getarginfo ("v");
1122 if (vinfo != NULL) args["v"] = vinfo->argdefault;
1123 }
1124
1125 // argument "f" can only be 0 or 1. Use the default value
1126 // if it is out of range
1127 int arg_f = args.getintarg ("f");
1128 if (arg_f != 0 && arg_f != 1) {
1129 cgiarginfo *finfo = argsinfo.getarginfo ("f");
1130 if (finfo != NULL) args["f"] = finfo->argdefault;
1131 }
1132
1133 return true;
1134}
1135
1136// translate_OIDs translates the "d" and "cl" arguments to their correct values
1137// if they use the tricky ".fc", ".lc" type syntax.
1138void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto,
1139 ostream &logout) {
1140
1141 FilterResponse_t response;
1142 FilterRequest_t request;
1143 comerror_t err;
1144 text_t &arg_d = args["d"];
1145 text_t &arg_cl = args["cl"];
1146 text_t &collection = args["c"];
1147
1148 // do a call to translate OIDs if required
1149 request.filterName = "NullFilter";
1150 request.filterResultOptions = FROID;
1151 if (!arg_d.empty() && needs_translating (arg_d)) {
1152 request.docSet.push_back (arg_d);
1153 collectproto->filter (collection, request, response, err, logout);
1154 arg_d = response.docInfo[0].OID;
1155 request.clear();
1156 }
1157 // we'll also check here that the "cl" argument has a "classify" doctype
1158 // (in case ".fc" or ".lc" have screwed up)
1159 if (needs_translating (arg_cl)) {
1160 request.fields.insert ("doctype");
1161 request.docSet.push_back (arg_cl);
1162 request.filterResultOptions = FRmetadata;
1163 collectproto->filter (collection, request, response, err, logout);
1164 // set to original value (without .xx stuff) if doctype isn't "classify"
1165 if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
1166 strip_suffix (arg_cl);
1167 else
1168 arg_cl = response.docInfo[0].OID;
1169 }
1170}
1171
1172// prepare_page sets up page parameters, sets display macros
1173// and opens the page ready for output
1174void receptionist::prepare_page (action *a, cgiargsclass &args,
1175 outconvertclass &outconvert,
1176 ostream &logout) {
1177 // set up page parameters
1178 text_t pageparams;
1179 bool first = true;
1180
1181 text_tmap::iterator params_here = configinfo.pageparams.begin();
1182 text_tmap::iterator params_end = configinfo.pageparams.end();
1183 while (params_here != params_end) {
1184 if (args[(*params_here).first] != (*params_here).second) {
1185 if (!first) pageparams += ",";
1186 first = false;
1187 pageparams += (*params_here).first;
1188 pageparams += "=";
1189 pageparams += args[(*params_here).first];
1190 }
1191
1192 params_here++;
1193 }
1194
1195
1196 // open the page
1197 disp.openpage(pageparams, configinfo.macroprecedence);
1198
1199 // define external macros for each action
1200 actionptrmap::iterator actionhere = actions.begin ();
1201 actionptrmap::iterator actionend = actions.end ();
1202
1203 while (actionhere != actionend) {
1204 assert ((*actionhere).second.a != NULL);
1205 if ((*actionhere).second.a != NULL) {
1206 (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
1207 }
1208 actionhere++;
1209 }
1210
1211 // define internal macros for the current action
1212 a->define_internal_macros (disp, args, &protocols, logout);
1213
1214 // define general macros. the defining of general macros is done here so that
1215 // the last possible version of the cgi arguments are used
1216 define_general_macros (args, outconvert, logout);
1217}
1218
1219void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/,
1220 ostream &logout) {
1221
1222 text_t &collection = args["c"];
1223
1224 disp.setmacro ("gsdlhome", "Global", dm_safe(configinfo.gsdlhome));
1225 disp.setmacro ("gwcgi", "Global", configinfo.gwcgi);
1226 disp.setmacro ("httpimg", "Global", configinfo.httpimg);
1227 disp.setmacro ("httpprefix", "Global", configinfo.httpprefix);
1228
1229 if (!collection.empty()) {
1230 // DB // ****
1231 ColInfoResponse_t cinfo;
1232 comerror_t err;
1233 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1234 if (collectproto != NULL) {
1235 collectproto->get_collectinfo (collection, cinfo, err, logout);
1236 text_t httpcollection;
1237 if (!cinfo.httpdomain.empty()) httpcollection = "http://";
1238 httpcollection += cinfo.httpdomain + cinfo.httpprefix + "/collect/" + collection;
1239 disp.setmacro ("httpcollection", "Global", httpcollection);
1240 }
1241 }
1242
1243 text_t compressedoptions = get_compressed_arg(args, logout);
1244 disp.setmacro ("compressedoptions", "Global", dm_safe(compressedoptions));
1245 // need a decoded version of compressedoptions for use within forms
1246 // as browsers encode values from forms before sending to server
1247 // (e.g. %25 becomes %2525)
1248 decode_cgi_arg (compressedoptions);
1249 disp.setmacro ("decodedcompressedoptions", "Global", dm_safe(compressedoptions));
1250
1251#if defined (__WIN32__)
1252 disp.setmacro ("win32", "Global", "1");
1253#endif
1254
1255 // set _cgiargX_ macros for each cgi argument
1256 cgiargsclass::const_iterator argshere = args.begin();
1257 cgiargsclass::const_iterator argsend = args.end();
1258 while (argshere != argsend) {
1259 if (((*argshere).first == "q") ||
1260 ((*argshere).first == "qa") ||
1261 ((*argshere).first == "qtt") ||
1262 ((*argshere).first == "qty") ||
1263 ((*argshere).first == "qp") ||
1264 ((*argshere).first == "qpl") ||
1265 ((*argshere).first == "qr") ||
1266 ((*argshere).first == "q2"))
1267 // need to escape special characters from query string
1268 disp.setmacro ("cgiarg" + (*argshere).first,
1269 "Global", html_safe((*argshere).second.value));
1270 else
1271 disp.setmacro ("cgiarg" + (*argshere).first, "Global", dm_safe((*argshere).second.value));
1272 argshere ++;
1273 }
1274
1275 // display text right to left if language is arabic (and if browser can support it)
1276 if (args["l"] == "ar")
1277 disp.setmacro ("htmlextra", "Global", " dir=rtl");
1278
1279 // set collection specific macros
1280 if (!collection.empty()) {
1281 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1282 if (collectproto != NULL) {
1283 FilterResponse_t response;
1284 text_tset metadata;
1285 get_info ("collection", collection, metadata, false,
1286 collectproto, response, logout);
1287
1288 if (!response.docInfo[0].metadata.empty()) {
1289 MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
1290 MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
1291 while (here != end) {
1292 if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
1293 ((*here).first != "hasprevious")) {
1294 // check for args in form name:lang
1295 text_t name = "";
1296 text_t lang = "";
1297 bool colonfound=false;
1298 text_t::const_iterator a = (*here).first.begin();
1299 text_t::const_iterator b = (*here).first.end();
1300 while (a !=b) {
1301 if (*a==':') {
1302 colonfound=true;
1303 }
1304 else {
1305 if (colonfound)
1306 lang.push_back(*a);
1307 else name.push_back(*a);
1308 }
1309 a++;
1310 }
1311 if (!lang.empty()) {
1312 if (args["l"]==lang) {
1313 disp.setmacro (name, "Global", (*here).second.values[0]);
1314 }
1315 }
1316 else { // the default one
1317 disp.setmacro ((*here).first, "Global", (*here).second.values[0]);
1318 }
1319 }
1320 here ++;
1321 }
1322 }
1323
1324 text_t iconcollection;
1325 disp.expandstring ("Global", "_iconcollection_", iconcollection);
1326 if (!iconcollection.empty())
1327 {
1328 ColInfoResponse_t cinfo;
1329 comerror_t err;
1330 collectproto->get_collectinfo (collection, cinfo, err, logout);
1331 if (iconcollection[0]=='/' && !cinfo.httpdomain.empty())
1332 {
1333 // local but with full path
1334 iconcollection = "http://" + cinfo.httpdomain + iconcollection;
1335 disp.setmacro("iconcollection", "Global", iconcollection);
1336 }
1337 }
1338 }
1339 }
1340}
1341
1342// gets collection info from cache if found or
1343// calls collection server (and updates cache)
1344// returns NULL if there's an error
1345ColInfoResponse_t *receptionist::get_collectinfo_ptr (recptproto *collectproto,
1346 const text_t &collection,
1347 ostream &logout) {
1348
1349 // check the cache
1350 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1351 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1352 // found it
1353 return &((*it).second.info);
1354 }
1355
1356 // not cached, get info from collection server
1357 if (collectproto == NULL) {
1358 logout << "ERROR: receptionist::get_collectinfo_ptr passed null collectproto\n";
1359 return NULL;
1360 }
1361
1362 comerror_t err;
1363 if (it == configinfo.collectinfo.end()) {
1364 collectioninfo_t cinfo;
1365 collectproto->get_collectinfo (collection, cinfo.info, err, logout);
1366 if (err != noError) {
1367 outconvertclass text_t2ascii;
1368 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1369 << get_comerror_string (err) << "\"while getting collectinfo\n";
1370 return NULL;
1371 }
1372 cinfo.info_loaded = true;
1373 configinfo.collectinfo[collection] = cinfo;
1374 return &(configinfo.collectinfo[collection].info);
1375 } else {
1376 collectproto->get_collectinfo (collection, (*it).second.info, err, logout);
1377 if (err != noError) {
1378 outconvertclass text_t2ascii;
1379 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1380 << get_comerror_string (err) << "\"while getting collectinfo\n";
1381 return NULL;
1382 }
1383 (*it).second.info_loaded = true;
1384 return &((*it).second.info);
1385 }
1386}
1387
1388// removes a collection from the cache so that the next
1389// call to get_collectinfo_ptr() for that collection will
1390// retrieve the collection info from the collection server
1391void receptionist::uncache_collection (const text_t &collection) {
1392
1393 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1394 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1395
1396 (*it).second.info_loaded = false;
1397
1398 }
1399}
1400
1401// Handles an "Encoding" line from a configuration file - note that the
1402// configinfo.encodings map is a bit of a hack (to be fixed when the
1403// configuration files are tidied up).
1404void receptionist::configure_encoding (const text_tarray &cfgline) {
1405
1406 text_t subkey, subvalue, shortname, longname, mapfile;
1407 int multibyte = 0;
1408 text_t::const_iterator cfglinesub_here;
1409 text_tarray::const_iterator cfgline_here = cfgline.begin();
1410 text_tarray::const_iterator cfgline_end = cfgline.end();
1411 while (cfgline_here != cfgline_end) {
1412 if (*cfgline_here == "multibyte") {
1413 multibyte = 1;
1414 } else {
1415 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
1416 (*cfgline_here).end(), '=', subkey);
1417 if (subkey == "shortname") {
1418 shortname = substr (cfglinesub_here, (*cfgline_here).end());
1419 } else if (subkey == "longname") {
1420 longname = substr (cfglinesub_here, (*cfgline_here).end());
1421 } else if (subkey == "map") {
1422 mapfile = substr (cfglinesub_here, (*cfgline_here).end());
1423 }
1424 }
1425 cfgline_here++;
1426 }
1427 if (!shortname.empty()) {
1428 if (longname.empty()) longname = shortname;
1429
1430 // add the converter
1431 if (shortname == "utf-8") {
1432 utf8inconvertclass *utf8inconvert = new utf8inconvertclass();
1433 utf8outconvertclass *utf8outconvert = new utf8outconvertclass();
1434 utf8outconvert->set_rzws(1);
1435 add_converter (shortname, utf8inconvert, utf8outconvert);
1436 configinfo.encodings[longname] = shortname;
1437
1438 } else if (!mapfile.empty()) {
1439
1440 if (mapfile == "8859_1.ump") {
1441 // iso-8859-1 is a special case as it'll always be supported by the
1442 // standard converter class and therefore doesn't need to use its
1443 // mapping file
1444 inconvertclass *inconvert = new inconvertclass();
1445 rzwsoutconvertclass *outconvert = new rzwsoutconvertclass();
1446 outconvert->set_rzws(1);
1447 add_converter (shortname, inconvert, outconvert);
1448 configinfo.encodings[longname] = shortname;
1449
1450 } else {
1451 text_t to_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "to_uc", mapfile);
1452 text_t from_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "from_uc", mapfile);
1453 if (file_exists(to_uc_map) && file_exists(from_uc_map)) {
1454
1455 mapinconvertclass *mapinconvert = new mapinconvertclass();
1456 mapinconvert->setmapfile (to_uc_map, 0x003F);
1457 mapinconvert->set_multibyte (multibyte);
1458 mapoutconvertclass *mapoutconvert = new mapoutconvertclass();
1459 mapoutconvert->setmapfile (from_uc_map, 0x3F);
1460 mapoutconvert->set_multibyte (multibyte);
1461 mapoutconvert->set_rzws(1);
1462 add_converter (shortname, mapinconvert, mapoutconvert);
1463 configinfo.encodings[longname] = shortname;
1464 }
1465 }
1466 }
1467 }
1468}
Note: See TracBrowser for help on using the repository browser.