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

Last change on this file since 2490 was 2397, checked in by sjboddie, 23 years ago

* empty log message *

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