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

Last change on this file since 15418 was 15418, checked in by mdewsnip, 13 years ago

(Untangling colservr/recpt) Split recpt/OIDtools into two: lib/OIDtools.cpp/h contains the purely string-based functions (may be used by the colservr), and recpt/recptprototools.cpp/h contains the functions requiring a call to the colservr (get_info() etc.).

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