source: main/tags/2.80/gsdl/src/recpt/receptionist.cpp@ 24528

Last change on this file since 24528 was 13463, checked in by kjdon, 17 years ago

decode_cgi_arg returns utf-8 if the original encoding was utf-8. need to use to_uni on the output in this case. If the encoding isn't utf-8, then 8 bit encodings may be fine but others will be screwed up. Need to fix this properly...

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