source: gsdl/trunk/runtime-src/src/recpt/receptionist.cpp@ 19109

Last change on this file since 19109 was 19109, checked in by kjdon, 15 years ago

httpimg changed to httpweb, and default is now httpprefix/web. not /images or /gsdl/images

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