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

Last change on this file since 3668 was 3668, checked in by jrm21, 21 years ago

use the utf16outconverter class if we are told to use an encoding called
"utf-16".

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