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

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

Changed the authentication directives to more easier to use ones.
authenticate option replaces auth_collection and takes the same options. The
options public_documents and private_documents replace the allowallexcept
and denyallexcept ACLs. The allow_acls has been removed. The auth_group remains
the same.

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