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

Last change on this file since 16571 was 16310, checked in by davidb, 16 years ago

Introduction of 'collecthome' which parallels 'gsdlhome' to allow the toplevel collect folder to be outside of the gsdlhome area

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