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

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

Added site configuration directives site_auth and site_groups for site wide
authentication using the existing groups and password authentication structure

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