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

Last change on this file since 2664 was 2660, checked in by jrm21, 23 years ago

minor tweak - we were printing Last-Modified header with UTC set to the
localtime.

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