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

Last change on this file since 770 was 769, checked in by sjboddie, 25 years ago

A location url may now contain macros

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 36.6 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 * $Id: receptionist.cpp 769 1999-11-03 22:49:10Z sjboddie $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.39 1999/11/03 22:49:10 sjboddie
31 A location url may now contain macros
32
33 Revision 1.38 1999/11/01 21:49:34 sjboddie
34 changes to arguments of many functions
35
36 Revision 1.37 1999/10/20 03:55:03 sjboddie
37 yet another problem with calling browserclass
38 processOID functions correctly
39
40 Revision 1.36 1999/10/19 03:23:44 davidb
41 Collection building support through web pages
42 and internal and external link handling for collection documents
43
44 Revision 1.35 1999/10/18 20:07:05 sjboddie
45 tidied up a few things - moved processing of "hp" argument
46 to htmlbrowserclass
47
48 Revision 1.34 1999/10/14 23:00:52 sjboddie
49 finished changes to browsing support
50
51 Revision 1.33 1999/10/10 08:14:10 sjboddie
52 - metadata now returns mp rather than array
53 - redesigned browsing support (although it's not finished so
54 won't currently work ;-)
55
56 Revision 1.32 1999/09/21 11:28:45 sjboddie
57 tidied up file locking
58
59 Revision 1.31 1999/09/16 21:38:17 sjboddie
60 added some file locking stuff for logging. Windows still needs to
61 be done.
62
63 Revision 1.30 1999/09/07 04:56:58 sjboddie
64 added GPL notice
65
66 Revision 1.29 1999/09/03 10:02:30 rjmcnab
67 Made the page parameters configurable. Now the page parameters must
68 correspond to cgi arguments in name and value (ie language=zh should now
69 be l=zh) which makes things more consistent anyway. Removed a couple of
70 specialised NZDL page parameters.
71
72 Moved the combining of the cgi arguments so that the receptionist does
73 all the configuration now.
74
75 Made the macro precedence configurable.
76
77 Made cgi arguments totally configurable. Now any piece of information about
78 a cgi argument can be configured meaning that cgi arguments can be declared
79 from the configuration file.
80
81 Removed the argdefault configuration argument. This should now be done
82 using cgiarg.
83
84 Revision 1.28 1999/09/03 04:39:46 rjmcnab
85 Made cookies and logs optional (they are turned off by default). To
86 turn them on put
87
88 usecookies true
89 logcgiargs true
90
91 in your configuration file.
92
93 Revision 1.27 1999/09/02 00:27:21 rjmcnab
94 A few small things.
95
96 Revision 1.26 1999/08/25 04:43:06 sjboddie
97 made FilterRequest_t::docSet an array rather than a set
98
99 Revision 1.25 1999/08/20 00:59:01 sjboddie
100 -fixed up location redirection
101 -added some usage logging, also now set a GSDL_UID cookie. Logging
102 does NOT presently lock the log file while it's in use. That has yet
103 to be done.
104
105 Revision 1.24 1999/08/13 04:16:42 sjboddie
106 added some collection-level metadata stuff
107
108 Revision 1.23 1999/08/11 23:28:59 sjboddie
109 added support for html classifier (i.e. the hp argumant now must be
110 translated too).
111
112 Revision 1.22 1999/08/10 22:45:21 sjboddie
113 format option ShowTopPages is now called DocumentTopPages
114
115 Revision 1.21 1999/08/09 04:25:17 sjboddie
116 moved OID translation stuff from documentaction::define_external_macros
117 to receptionist
118
119 Revision 1.20 1999/07/30 02:13:09 sjboddie
120 -added collectinfo argument to some functions
121 -made some function prototypes virtual
122
123 Revision 1.19 1999/07/15 06:02:05 rjmcnab
124 Moved the setting of argsinfo into the constructor. Added the configuration
125 command argdefault (as used by the actions). Added code to output the
126 correct charset based on the page encoding so that the user does not need
127 to specify the encoding used for a particular page.
128
129 Revision 1.18 1999/07/11 01:05:20 rjmcnab
130 Stored origin of cgiarg with argument.
131
132 Revision 1.17 1999/07/10 22:18:26 rjmcnab
133 Added calls to define_external_cgiargs.
134
135 Revision 1.16 1999/06/27 21:49:03 sjboddie
136 fixed a couple of version conflicts - tidied up some small things
137
138 Revision 1.15 1999/06/26 01:14:32 rjmcnab
139 Made a couple of changes to handle different encodings.
140
141 Revision 1.14 1999/06/09 00:08:36 sjboddie
142 query string macro (_cgiargq_) is now made html safe before being set
143
144 Revision 1.13 1999/06/08 04:29:31 sjboddie
145 added argsinfo to the call to check_cgiargs to make it easy to set
146 args to their default if they're found to be screwed up
147
148 Revision 1.12 1999/04/30 01:59:42 sjboddie
149 lots of stuff - getting documentaction working (documentaction replaces
150 old browseaction)
151
152 Revision 1.11 1999/03/25 03:06:43 sjboddie
153
154 altered receptionist slightly so it now passes *collectproto to
155 define_internal_macros and define_external_macros - need it
156 for browseaction
157
158 Revision 1.10 1999/03/05 03:53:54 sjboddie
159
160 fixed some bugs
161
162 Revision 1.9 1999/02/28 20:00:16 rjmcnab
163
164
165 Fixed a few things.
166
167 Revision 1.8 1999/02/25 21:58:59 rjmcnab
168
169 Merged sources.
170
171 Revision 1.7 1999/02/21 22:33:55 rjmcnab
172
173 Lots of stuff :-)
174
175 Revision 1.6 1999/02/11 01:24:05 rjmcnab
176
177 Fixed a few compiler warnings.
178
179 Revision 1.5 1999/02/08 01:28:02 rjmcnab
180
181 Got the receptionist producing something using the statusaction.
182
183 Revision 1.4 1999/02/05 10:42:46 rjmcnab
184
185 Continued working on receptionist
186
187 Revision 1.3 1999/02/04 10:00:56 rjmcnab
188
189 Developed the idea of an "action" and having them define the cgi arguments
190 which they need and how those cgi arguments function.
191
192 Revision 1.2 1999/02/04 01:17:27 rjmcnab
193
194 Got it outputing something.
195
196
197 */
198
199
200#include "receptionist.h"
201#include "fileutil.h"
202#include "cgiutils.h"
203#include "htmlutils.h"
204#include "OIDtools.h"
205#include <assert.h>
206#include <time.h>
207#include <stdio.h>
208#include <fstream.h>
209
210#if defined (__WIN32_)
211#include "wincgiutils.h"
212#endif
213
214void recptconf::clear () {
215 gsdlhome.clear();
216 collection.clear();
217 collectdir.clear();
218 httpprefix.clear();
219 httpimg.clear();
220 gwcgi.clear();
221 macrofiles.erase(macrofiles.begin(), macrofiles.end());
222 saveconf.clear();
223 usecookies = false;
224 logcgiargs = false;
225
226 // these default page parameters can always be overriden
227 // in the configuration file
228 pageparams.erase(pageparams.begin(), pageparams.end());
229 pageparams["c"] = "";
230 pageparams["l"] = "en";
231
232#ifdef MACROPRECEDENCE
233 macroprecedence = MACROPRECEDENCE;
234#else
235 macroprecedence.clear();
236#endif
237}
238
239
240
241receptionist::receptionist () {
242 // create a list of cgi arguments
243 // this must be done before the configuration
244
245 cgiarginfo ainfo;
246
247 ainfo.shortname = "e";
248 ainfo.longname = "compressed arguments";
249 ainfo.multiplechar = true;
250 ainfo.defaultstatus = cgiarginfo::good;
251 ainfo.argdefault = "";
252 ainfo.savedarginfo = cgiarginfo::mustnot;
253 argsinfo.addarginfo (NULL, ainfo);
254
255 ainfo.shortname = "a";
256 ainfo.longname = "action";
257 ainfo.multiplechar = true;
258 ainfo.defaultstatus = cgiarginfo::none;
259 ainfo.argdefault = "";
260 ainfo.savedarginfo = cgiarginfo::must;
261 argsinfo.addarginfo (NULL, ainfo);
262
263 // w=western
264 ainfo.shortname = "w";
265 ainfo.longname = "encoding";
266 ainfo.multiplechar = true;
267 ainfo.defaultstatus = cgiarginfo::weak;
268 ainfo.argdefault = "w";
269 ainfo.savedarginfo = cgiarginfo::must;
270 argsinfo.addarginfo (NULL, ainfo);
271
272 ainfo.shortname = "nw";
273 ainfo.longname = "new encoding";
274 ainfo.multiplechar = true;
275 ainfo.defaultstatus = cgiarginfo::none;
276 ainfo.argdefault = "";
277 ainfo.savedarginfo = cgiarginfo::mustnot;
278 argsinfo.addarginfo (NULL, ainfo);
279
280 ainfo.shortname = "c";
281 ainfo.longname = "collection";
282 ainfo.multiplechar = true;
283 ainfo.defaultstatus = cgiarginfo::none;
284 ainfo.argdefault = "";
285 ainfo.savedarginfo = cgiarginfo::must;
286 argsinfo.addarginfo (NULL, ainfo);
287
288 // the interface language name should use the ISO 639
289 // standard
290 ainfo.shortname = "l";
291 ainfo.longname = "interface language";
292 ainfo.multiplechar = true;
293 ainfo.defaultstatus = cgiarginfo::weak;
294 ainfo.argdefault = "en";
295 ainfo.savedarginfo = cgiarginfo::must;
296 argsinfo.addarginfo (NULL, ainfo);
297
298 // the GSDL_UID (cookie)
299 ainfo.shortname = "z";
300 ainfo.longname = "gsdl uid";
301 ainfo.multiplechar = true;
302 ainfo.defaultstatus = cgiarginfo::none;
303 ainfo.argdefault = "";
304 ainfo.savedarginfo = cgiarginfo::mustnot;
305 argsinfo.addarginfo (NULL, ainfo);
306}
307
308
309void receptionist::add_action (action *theaction) {
310 // make sure we have an action to add
311 if (theaction == NULL) return;
312
313 // add this action to the list of actions
314 actions.addaction(theaction);
315
316 // add the cgi arguments from this action
317 argsinfo.addarginfo (NULL, theaction->getargsinfo());
318}
319
320
321void receptionist::add_browser (browserclass *thebrowser) {
322 // make sure we have a browser to add
323 if (thebrowser == NULL) return;
324
325 // add this browser to the list of browsers
326 browsers.addbrowser(thebrowser);
327}
328
329
330void receptionist::setdefaultbrowser (const text_t &browsername) {
331 browsers.setdefaultbrowser (browsername);
332}
333
334
335// configure should be called for each line in the
336// configuration files to configure the receptionist and everything
337// it contains. The configuration should take place after everything
338// has been added but before the initialisation.
339void receptionist::configure (const text_t &key, const text_tarray &cfgline) {
340 // configure the receptionist
341 if (cfgline.size() >= 1) {
342 cgiarginfo *info = NULL;
343 if (key == "gsdlhome") configinfo.gsdlhome = cfgline[0];
344 else if (key == "collection") {
345 configinfo.collection = cfgline[0];
346 // also need to set the default arg to this collection
347 if ((info = argsinfo.getarginfo("c")) != NULL) {
348 info->defaultstatus = cgiarginfo::good;
349 info->argdefault = cfgline[0];
350 }
351
352 } else if (key == "collectdir") configinfo.collectdir = cfgline[0];
353 else if (key == "httpprefix") configinfo.httpprefix = cfgline[0];
354 else if (key == "httpimg") configinfo.httpimg = cfgline[0];
355 else if (key == "gwcgi") configinfo.gwcgi = cfgline[0];
356 else if (key == "macrofiles") configinfo.macrofiles = cfgline;
357 else if (key == "saveconf") configinfo.saveconf = cfgline[0];
358 else if (key == "usecookies") configinfo.usecookies = (cfgline[0] == "true");
359 else if (key == "logcgiargs") configinfo.logcgiargs = (cfgline[0] == "true");
360 else if (key == "pageparam") {
361 if (cfgline.size() >= 2) configinfo.pageparams[cfgline[0]] = cfgline[1];
362 else configinfo.pageparams[cfgline[0]] = "";
363 }
364 else if (key == "macroprecedence") configinfo.macroprecedence = cfgline[0];
365
366
367 else if (key == "cgiarg") {
368 // get shortname
369 bool seen_defaultstatus = false;
370 text_t subkey, subvalue;
371 text_t shortname;
372 text_t::const_iterator cfglinesub_here;
373 text_tarray::const_iterator cfgline_here = cfgline.begin();
374 text_tarray::const_iterator cfgline_end = cfgline.end();
375 while (cfgline_here != cfgline_end) {
376 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
377 (*cfgline_here).end(), '=', subkey);
378 if (subkey == "shortname") {
379 shortname = substr (cfglinesub_here, (*cfgline_here).end());
380 }
381 cfgline_here++;
382 }
383
384 // if we found the shortname process the line again filling in values
385 if (!shortname.empty()) {
386 cgiarginfo &chinfo = argsinfo[shortname];
387 chinfo.shortname = shortname; // in case this is a new argument
388
389 cfgline_here = cfgline.begin();
390 while (cfgline_here != cfgline_end) {
391 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
392 (*cfgline_here).end(), '=', subkey);
393 subvalue = substr (cfglinesub_here, (*cfgline_here).end());
394
395 if (subkey == "longname") chinfo.longname = subvalue;
396 else if (subkey == "multiplechar") chinfo.multiplechar = (subvalue == "true");
397 else if (subkey == "defaultstatus") {
398 seen_defaultstatus = true;
399 if (subvalue == "none") chinfo.defaultstatus = cgiarginfo::none;
400 else if (subvalue == "weak") chinfo.defaultstatus = cgiarginfo::weak;
401 else if (subvalue == "good") chinfo.defaultstatus = cgiarginfo::good;
402 else if (subvalue == "config") chinfo.defaultstatus = cgiarginfo::config;
403 else if (subvalue == "imperative") chinfo.defaultstatus = cgiarginfo::imperative;
404 }
405 else if (subkey == "argdefault") {
406 chinfo.argdefault = subvalue;
407 if (!seen_defaultstatus) chinfo.defaultstatus = cgiarginfo::config;
408 }
409 else if (subkey == "savedarginfo") {
410 if (subvalue == "mustnot") chinfo.savedarginfo = cgiarginfo::mustnot;
411 else if (subvalue == "can") chinfo.savedarginfo = cgiarginfo::can;
412 else if (subvalue == "must") chinfo.savedarginfo = cgiarginfo::must;
413 }
414
415 cfgline_here++;
416 }
417 }
418 }
419 }
420
421 // configure the actions
422 actionptrmap::iterator actionhere = actions.begin ();
423 actionptrmap::iterator actionend = actions.end ();
424
425 while (actionhere != actionend) {
426 assert ((*actionhere).second.a != NULL);
427 if ((*actionhere).second.a != NULL)
428 (*actionhere).second.a->configure(key, cfgline);
429
430 actionhere++;
431 }
432
433 // configure the protocols
434 recptprotolistclass::iterator protohere = protocols.begin ();
435 recptprotolistclass::iterator protoend = protocols.end ();
436
437 while (protohere != protoend) {
438 assert ((*protohere).p != NULL);
439 if ((*protohere).p != NULL)
440 (*protohere).p->configure(key, cfgline);
441
442 protohere++;
443 }
444
445 // configure the browsers
446 browserptrmap::iterator browserhere = browsers.begin ();
447 browserptrmap::iterator browserend = browsers.end ();
448
449 while (browserhere != browserend) {
450 assert ((*browserhere).second.b != NULL);
451 if ((*browserhere).second.b != NULL)
452 (*browserhere).second.b->configure(key, cfgline);
453
454 browserhere++;
455 }
456}
457
458
459void receptionist::configure (const text_t &key, const text_t &value) {
460 text_tarray cfgline;
461 cfgline.push_back (value);
462 configure(key, cfgline);
463}
464
465
466// init should be called after all the actions, protocols, and
467// converters have been added to the receptionist and after everything
468// has been configured but before any pages are created.
469// It returns true on success and false on failure. If false is
470// returned getpage should not be called (without producing
471// meaningless output), instead an error page should be
472// produced by the calling code.
473bool receptionist::init (ostream &logout) {
474 // first configure collectdir
475 text_t thecollectdir = configinfo.gsdlhome;
476 if (!configinfo.collection.empty()) {
477 // collection specific mode
478 if (!configinfo.collectdir.empty()) {
479 // has already been configured
480 thecollectdir = configinfo.collectdir;
481 } else {
482 // decide where collectdir is by searching for collect.cfg
483 // look in $GSDLHOME/collect/collection-name/etc/collect.cfg and
484 // then $GSDLHOME/etc/collect.cfg
485 thecollectdir = filename_cat (configinfo.gsdlhome, "collect");
486 thecollectdir = filename_cat (thecollectdir, configinfo.collection);
487 text_t filename = filename_cat (thecollectdir, "etc");
488 filename = filename_cat (filename, "collect.cfg");
489
490 if (!file_exists(filename)) thecollectdir = configinfo.gsdlhome;
491 }
492 }
493 configure("collectdir", thecollectdir);
494
495 // read in the macro files
496 if (!read_macrofiles (logout)) return false;
497
498 // there must be at least one action defined
499 if (actions.empty()) {
500 logout << "Error: no actions have been added to the receptionist\n";
501 return false;
502 }
503
504 // there must be at least one browser defined
505 if (browsers.empty()) {
506 logout << "Error: no browsers have been added to the receptionist\n";
507 return false;
508 }
509
510 // create a saveconf string if there isn't one already
511 if (configinfo.saveconf.empty())
512 configinfo.saveconf = create_save_conf_str (argsinfo, logout);
513
514 // check the saveconf string
515 if (!check_save_conf_str (configinfo.saveconf, argsinfo, logout))
516 return false;
517
518 // set a random seed
519 srand (time(NULL));
520
521 // make the output converters remove all the zero-width spaces
522 convertinfoclass::iterator converthere = converters.begin ();
523 convertinfoclass::iterator convertend = converters.end ();
524 text_t defaultconvertname;
525 while (converthere != convertend) {
526 assert ((*converthere).second.outconverter != NULL);
527 if ((*converthere).second.outconverter != NULL) {
528 (*converthere).second.outconverter->set_rzws(1);
529 if (defaultconvertname.empty())
530 defaultconvertname = (*converthere).second.name;
531 }
532 converthere++;
533 }
534
535 // set default converter if no good one has been defined
536 if (!defaultconvertname.empty()) {
537 cgiarginfo *ainfo = argsinfo.getarginfo ("w");
538 if (ainfo->argdefault != "w") {
539 if ((ainfo != NULL) && (converters.get_outconverter(ainfo->argdefault) == NULL)) {
540 ainfo->defaultstatus = cgiarginfo::good;
541 ainfo->argdefault = defaultconvertname;
542 }
543 }
544 }
545
546 // init the actions
547 actionptrmap::iterator actionhere = actions.begin ();
548 actionptrmap::iterator actionend = actions.end ();
549 while (actionhere != actionend) {
550 if (((*actionhere).second.a == NULL) ||
551 !(*actionhere).second.a->init(logout)) return false;
552 actionhere++;
553 }
554
555 // init the protocols
556 recptprotolistclass::iterator protohere = protocols.begin ();
557 recptprotolistclass::iterator protoend = protocols.end ();
558 while (protohere != protoend) {
559 if (((*protohere).p == NULL) ||
560 !(*protohere).p->init(logout)) return false;
561 protohere++;
562 }
563
564 // init the browsers
565 browserptrmap::iterator browserhere = browsers.begin ();
566 browserptrmap::iterator browserend = browsers.end ();
567 while (browserhere != browserend) {
568 if (((*browserhere).second.b == NULL) ||
569 !(*browserhere).second.b->init(logout)) return false;
570 browserhere++;
571 }
572
573 return true;
574}
575
576
577// parse_cgi_args parses cgi arguments into an argument class.
578// This function should be called for each page request. It returns false
579// if there was a major problem with the cgi arguments.
580bool receptionist::parse_cgi_args (const text_t &argstr, cgiargsclass &args,
581 ostream &logout) {
582 outconvertclass text_t2ascii;
583
584 // get an initial list of cgi arguments
585 args.clear();
586 split_cgi_args (argstr, args);
587
588 // expand the compressed argument (if there was one)
589 if (!expand_save_args (argsinfo, configinfo.saveconf, args, logout)) return false;
590
591 // add the defaults
592 add_default_args (argsinfo, args, logout);
593
594 // get the cookie
595 if (configinfo.usecookies) get_cookie(args["z"]);
596
597 // get the input encoding
598 text_t &arg_w = args["w"];
599 inconvertclass defaultinconvert;
600 inconvertclass *inconvert = converters.get_inconverter (arg_w);
601 if (inconvert == NULL) inconvert = &defaultinconvert;
602
603 // see if the next page will have a different encoding
604 if (args.getarg("nw") != NULL) arg_w = args["nw"];
605
606 // convert arguments which aren't in unicode to unicode
607 args_tounicode (args, *inconvert);
608
609
610 // decide on the output conversion class (needed for checking the external
611 // cgi arguments)
612 rzwsoutconvertclass defaultoutconverter;
613 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
614 if (outconverter == NULL) outconverter = &defaultoutconverter;
615 outconverter->reset();
616
617 // check the main cgi arguments
618 if (!check_mainargs (args, logout)) return false;
619
620 // check the arguments for the action
621 action *a = actions.getaction (args["a"]);
622 if (a != NULL) {
623 if (!a->check_cgiargs (argsinfo, args, logout)) return false;
624 } else {
625 // the action was not found!!
626 logout << text_t2ascii << "Error: the action \"" << args["a"]
627 << "\" could not be found.\n";
628 return false;
629 }
630
631 // check external cgi arguments for each action
632 actionptrmap::iterator actionhere = actions.begin ();
633 actionptrmap::iterator actionend = actions.end ();
634 while (actionhere != actionend) {
635 assert ((*actionhere).second.a != NULL);
636 if ((*actionhere).second.a != NULL) {
637 if (!(*actionhere).second.a->check_external_cgiargs (argsinfo, args, *outconverter,
638 configinfo.saveconf, logout))
639 return false;
640 }
641 actionhere++;
642 }
643
644 // the action might have changed but we will assume that
645 // the cgiargs were checked properly when the change was made
646
647 return true;
648}
649
650// returns true if cookie already existed, false
651// if it was generated
652bool receptionist::get_cookie (text_t &cookie) {
653
654 char *c = getenv("HTTP_COOKIE");
655 if (c != NULL) {
656 text_t cookiestring = c;
657
658 text_t::const_iterator end = cookiestring.end();
659 text_t::const_iterator here = findchar (cookiestring.begin(), end, 'G');
660
661 if (substr(here, here+8) == "GSDL_UID") {
662 cookie = substr (here+9, findchar (here+9, end, ';'));
663 return true;
664 }
665 }
666
667 cookie.clear();
668 char *host = getenv("REMOTE_ADDR");
669 time_t ttime = time(NULL);
670 if (host != NULL) {
671 cookie += host;
672 cookie.push_back ('-');
673 }
674 cookie += text_t(ttime);
675
676 return false;
677}
678
679// as above but just tests if cookie exists
680bool receptionist::get_cookie () {
681
682 char *c = getenv("HTTP_COOKIE");
683 if (c != NULL) {
684 text_t cookiestring = c;
685
686 text_t::const_iterator end = cookiestring.end();
687 text_t::const_iterator here = findchar (cookiestring.begin(), end, 'G');
688
689 if (substr(here, here+8) == "GSDL_UID")
690 return true;
691 }
692 return false;
693}
694
695bool receptionist::log_cgi_args (cgiargsclass &args, ostream &logout) {
696
697 // see if we want to log the cgi arguments
698 if (!configinfo.logcgiargs) return true;
699
700 char *host;
701 host = getenv("REMOTE_HOST");
702 if (host == NULL) host = getenv ("REMOTE_ADDR");
703 if (host == NULL) host = "";
704 char *browser = getenv("HTTP_USER_AGENT");
705 if (browser == NULL) browser = "";
706 time_t ttime = time(NULL);
707
708 cgiargsclass::const_iterator args_here = args.begin();
709 cgiargsclass::const_iterator args_end = args.end();
710
711 text_t argstr;
712 bool first = true;
713 while (args_here != args_end) {
714 if (!first) argstr += ", ";
715 argstr += (*args_here).first + "=" + (*args_here).second.value;
716 first = false;
717 args_here ++;
718 }
719
720 text_t logfile = filename_cat (configinfo.gsdlhome, "etc");
721 logfile = filename_cat (logfile, "usage.txt");
722
723 text_t logstr = host;
724 logstr += " [";
725 logstr += ttime;
726 logstr += "] (" + argstr + ") \"";
727 logstr += browser;
728 logstr += "\"\n";
729
730 return append_logstr (logfile, logstr, logout);
731}
732
733bool receptionist::append_logstr (const text_t &filename, const text_t &logstr,
734 ostream &logout) {
735
736 utf8outconvertclass text_t2utf8;
737 char *lfile = filename.getcstr();
738 ofstream log (lfile, ios::app);
739
740 if (!log) {
741 logout << "Error: Couldn't open file " << lfile << "\n";
742 delete lfile;
743 return false;
744 }
745
746 int fd = GSDL_GET_FILEDESC(log);
747
748 // lock_val is set to 0 if file is locked successfully
749 int lock_val = 1;
750 GSDL_LOCK_FILE (fd);
751 if (lock_val == 0) {
752 log << text_t2utf8 << logstr;
753 GSDL_UNLOCK_FILE (fd);
754 } else {
755 logout << "Error: Couldn't lock file " << lfile << "\n";
756 log.close();
757 delete lfile;
758 return false;
759 }
760
761 log.close();
762
763 delete lfile;
764 return true;
765}
766
767// produce_cgi_page will call get_cgihead_info and
768// produce_content in the appropriate way to output a cgi header and
769// the page content (if needed). If a page could not be created it
770// will return false
771bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout,
772 ostream &logout) {
773 outconvertclass text_t2ascii;
774
775 response_t response;
776 text_t response_data;
777
778 // produce cgi header
779 get_cgihead_info (args, response, response_data, logout);
780 if (response == location) {
781 // location response (url may contain macros!!)
782 text_t ex_location;
783 action *a = actions.getaction (args["a"]);
784 prepare_page (a, args, text_t2ascii, logout);
785 disp.expandstring ("Global", response_data, ex_location);
786 contentout << text_t2ascii << "Location: " << ex_location << "\n\n";
787 contentout << flush;
788 return true;
789 } else if (response == content) {
790 // content response
791 contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
792 } else {
793 // unknown response
794 logout << "Error: get_cgihead_info returned an unknown response type.\n";
795 return false;
796 }
797
798 // produce cgi page
799 if (!produce_content (args, contentout, logout)) return false;
800
801 // flush contentout
802 contentout << flush;
803 return true;
804}
805
806
807// get_cgihead_info determines the cgi header information for
808// a set of cgi arguments. If response contains location then
809// response_data contains the redirect address. If reponse
810// contains content then reponse_data contains the content-type.
811// Note that images can now be produced by the receptionist.
812void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
813 text_t &response_data, ostream &logout) {
814 outconvertclass text_t2ascii;
815
816 // get the action
817 action *a = actions.getaction (args["a"]);
818 if (a != NULL) {
819 a->get_cgihead_info (args, &protocols, response, response_data, logout);
820
821 } else {
822 // the action was not found!!
823 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
824 << args["a"] << "\" could not be found.\n";
825 response = content;
826 response_data = "text/html";
827 }
828
829 // add the encoding information
830 if (response == content) {
831 if (args["w"] == "u") {
832 response_data += "; charset=UTF-8";
833 } else if (args["w"] == "g") {
834 response_data += "; charset=GBK";
835 } else {
836 response_data += "; charset=ISO-8859-1";
837 }
838
839 // add cookie if required
840 if (configinfo.usecookies && !get_cookie())
841 response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
842 + "; expires=25-Dec-37 00:00:00 GMT";
843 }
844}
845
846
847// produce the page content
848bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
849 ostream &logout) {
850 // decide on the output conversion class
851 text_t &arg_w = args["w"];
852 rzwsoutconvertclass defaultoutconverter;
853 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
854 if (outconverter == NULL) outconverter = &defaultoutconverter;
855 outconverter->reset();
856
857 recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
858 if (collectproto != NULL) {
859 // get browsers to process OID
860 text_t OID = args["d"];
861 if (OID.empty()) OID = args["cl"];
862 if (!OID.empty()) {
863 text_tset metadata;
864 text_tarray OIDs;
865 OIDs.push_back (OID);
866 if (!is_top(OID)) OIDs.push_back (OID + ".pr");
867 FilterResponse_t response;
868 metadata.insert ("childtype");
869 if (get_info (OIDs, args["c"], metadata, false, collectproto, response, logout)) {
870 text_t classifytype;
871 if (!response.docInfo[0].metadata["childtype"].values[0].empty())
872 classifytype = response.docInfo[0].metadata["childtype"].values[0];
873 else if (!is_top (OID)) {
874 if (!response.docInfo[1].metadata["childtype"].values[0].empty())
875 classifytype = response.docInfo[1].metadata["childtype"].values[0];
876 }
877 browserclass *b = browsers.getbrowser (classifytype);
878 b->processOID (args, collectproto, logout);
879 }
880 }
881
882 // translate "d" and "cl" arguments if required
883 translate_OIDs (args, collectproto, logout);
884 }
885
886 // produce the page using the desired action
887 action *a = actions.getaction (args["a"]);
888 if (a != NULL) {
889 if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
890 if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
891 return false;
892
893 } else {
894 // the action was not found!!
895 outconvertclass text_t2ascii;
896
897 logout << text_t2ascii << "Error receptionist::produce_content: the action \""
898 << args["a"] << "\" could not be found.\n";
899
900 contentout << (*outconverter)
901 << "<html>\n"
902 << "<head>\n"
903 << "<title>Error</title>\n"
904 << "</head>\n"
905 << "<body>\n"
906 << "<h2>Oops!</h2>\n"
907 << "Undefined Page. The action \""
908 << args["a"] << "\" could not be found.\n"
909 << "</body>\n"
910 << "</html>\n";
911 }
912
913 return true;
914}
915
916
917// returns the compressed argument ("e") corresponding to the argument
918// list. This can be used to save preferences between sessions.
919text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
920 // decide on the output conversion class
921 text_t &arg_w = args["w"];
922 rzwsoutconvertclass defaultoutconverter;
923 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
924 if (outconverter == NULL) outconverter = &defaultoutconverter;
925 outconverter->reset();
926
927 text_t compressed_args;
928 if (compress_save_args (argsinfo, configinfo.saveconf, args,
929 compressed_args, *outconverter, logout))
930 return compressed_args;
931
932 return "";
933}
934
935
936// will read in all the macro files. If one is not found an
937// error message will be written to logout and the method will
938// return false.
939bool receptionist::read_macrofiles (ostream &logout) {
940 outconvertclass text_t2ascii;
941
942 // redirect the error output to logout
943 disp.setlogout (&logout);
944
945 // load up the default macro files, the collection directory
946 // is searched first for the file (if this is being used in
947 // collection specific mode) and then the main directory
948 text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
949 text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
950 text_tarray::iterator arrhere = configinfo.macrofiles.begin();
951 text_tarray::iterator arrend = configinfo.macrofiles.end();
952 text_t filename;
953 while (arrhere != arrend) {
954 // filename is used as a flag to indicate whether
955 // the macro file has been found
956 filename.clear();
957
958 // try in the collection directory if this is being
959 // run in collection specific mode
960 if (!configinfo.collection.empty()) {
961 filename = filename_cat (colmacrodir, *arrhere);
962 if (!file_exists (filename)) filename.clear ();
963 }
964
965 // if we haven't found the macro file yet try in
966 // the main macro directory
967 if (filename.empty()) {
968 filename = filename_cat (gsdlmacrodir, *arrhere);
969 if (!file_exists (filename)) filename.clear ();
970 }
971
972 // see if we found the file or not
973 if (filename.empty()) {
974 logout << text_t2ascii
975 << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
976 if (configinfo.collection.empty()) {
977 logout << text_t2ascii
978 << "It should be in " << gsdlmacrodir << ".\n\n";
979 } else {
980 logout << text_t2ascii
981 << "It should be in either " << colmacrodir << " or in "
982 << gsdlmacrodir << ".\n\n";
983 }
984 return false;
985
986 } else { // found the file
987 disp.loaddefaultmacros(filename);
988 }
989
990 arrhere++;
991 }
992
993 // success
994 return true;
995}
996
997
998// check_mainargs will check all the main arguments. If a major
999// error is found it will return false and no cgi page should
1000// be created using the arguments.
1001bool receptionist::check_mainargs (cgiargsclass &args, ostream &/*logout*/) {
1002 // if this receptionist is running in collection dependant mode
1003 // then it should always set the collection argument to the
1004 // collection
1005 if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
1006
1007 // argument "v" can only be 0 or 1. Use the default value
1008 // if it is out of range
1009 int arg_v = args.getintarg ("v");
1010 if (arg_v != 0 && arg_v != 1) {
1011 cgiarginfo *vinfo = argsinfo.getarginfo ("v");
1012 if (vinfo != NULL) args["v"] = vinfo->argdefault;
1013 }
1014
1015 // argument "f" can only be 0 or 1. Use the default value
1016 // if it is out of range
1017 int arg_f = args.getintarg ("f");
1018 if (arg_f != 0 && arg_f != 1) {
1019 cgiarginfo *finfo = argsinfo.getarginfo ("f");
1020 if (finfo != NULL) args["f"] = finfo->argdefault;
1021 }
1022
1023 return true;
1024}
1025
1026// translate_OIDs translates the "d" and "cl" arguments to their correct values
1027// if they use the tricky ".fc", ".lc" type syntax.
1028void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto,
1029 ostream &logout) {
1030
1031 FilterResponse_t response;
1032 FilterRequest_t request;
1033 comerror_t err;
1034 text_t &arg_d = args["d"];
1035 text_t &arg_cl = args["cl"];
1036 text_t &collection = args["c"];
1037
1038 // do a call to translate OIDs if required
1039 request.filterName = "NullFilter";
1040 request.filterResultOptions = FROID;
1041 if (!arg_d.empty() && needs_translating (arg_d)) {
1042 request.docSet.push_back (arg_d);
1043 collectproto->filter (collection, request, response, err, logout);
1044 arg_d = response.docInfo[0].OID;
1045 request.clear();
1046 }
1047 // we'll also check here that the "cl" argument has a "classify" doctype
1048 // (in case ".fc" or ".lc" have screwed up)
1049 if (needs_translating (arg_cl)) {
1050 request.fields.insert ("doctype");
1051 request.docSet.push_back (arg_cl);
1052 request.filterResultOptions = FRmetadata;
1053 collectproto->filter (collection, request, response, err, logout);
1054 // set to original value (without .xx stuff) if doctype isn't "classify"
1055 if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
1056 strip_suffix (arg_cl);
1057 else
1058 arg_cl = response.docInfo[0].OID;
1059 }
1060}
1061
1062// prepare_page sets up page parameters, sets display macros
1063// and opens the page ready for output
1064void receptionist::prepare_page (action *a, cgiargsclass &args,
1065 outconvertclass &outconvert,
1066 ostream &logout) {
1067 // set up page parameters
1068 text_t pageparams;
1069 bool first = true;
1070
1071 text_tmap::iterator params_here = configinfo.pageparams.begin();
1072 text_tmap::iterator params_end = configinfo.pageparams.end();
1073 while (params_here != params_end) {
1074 if (args[(*params_here).first] != (*params_here).second) {
1075 if (!first) pageparams += ",";
1076 first = false;
1077 pageparams += (*params_here).first;
1078 pageparams += "=";
1079 pageparams += args[(*params_here).first];
1080 }
1081
1082 params_here++;
1083 }
1084
1085
1086 // open the page
1087 disp.openpage(pageparams, configinfo.macroprecedence);
1088
1089
1090 // define external macros for each action
1091 actionptrmap::iterator actionhere = actions.begin ();
1092 actionptrmap::iterator actionend = actions.end ();
1093
1094 while (actionhere != actionend) {
1095 assert ((*actionhere).second.a != NULL);
1096 if ((*actionhere).second.a != NULL)
1097 (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
1098 actionhere++;
1099 }
1100
1101 // define internal macros for the current action
1102 a->define_internal_macros (disp, args, &protocols, logout);
1103
1104 // define general macros. the defining of general macros is done here so that
1105 // the last possible version of the cgi arguments are used
1106 define_general_macros (args, outconvert, logout);
1107}
1108
1109void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/,
1110 ostream &logout) {
1111
1112 text_t &collection = args["c"];
1113
1114 disp.setmacro ("gsdlhome", "Global", configinfo.gsdlhome);
1115 disp.setmacro ("gwcgi", "Global", configinfo.gwcgi);
1116 disp.setmacro ("httpimg", "Global", configinfo.httpimg);
1117 disp.setmacro ("httpprefix", "Global", configinfo.httpprefix);
1118 disp.setmacro ("compressedoptions", "Global", get_compressed_arg(args, logout));
1119
1120 // set _cgiargX_ macros for each cgi argument
1121 cgiargsclass::const_iterator argshere = args.begin();
1122 cgiargsclass::const_iterator argsend = args.end();
1123 while (argshere != argsend) {
1124 if (((*argshere).first == "q") ||
1125 ((*argshere).first == "qa") ||
1126 ((*argshere).first == "qtt") ||
1127 ((*argshere).first == "qty") ||
1128 ((*argshere).first == "qp") ||
1129 ((*argshere).first == "qpl") ||
1130 ((*argshere).first == "qr") ||
1131 ((*argshere).first == "q2"))
1132 // need to escape special characters from query string
1133 disp.setmacro ("cgiarg" + (*argshere).first,
1134 "Global", html_safe((*argshere).second.value));
1135 else
1136 disp.setmacro ("cgiarg" + (*argshere).first, "Global", (*argshere).second.value);
1137 argshere ++;
1138 }
1139
1140 // set collection specific macros
1141 if (!collection.empty()) {
1142 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1143 if (collectproto != NULL) {
1144 FilterResponse_t response;
1145 text_tset metadata;
1146 get_info ("collection", collection, metadata, false,
1147 collectproto, response, logout);
1148
1149 if (!response.docInfo[0].metadata.empty()) {
1150 MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
1151 MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
1152 while (here != end) {
1153 if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
1154 ((*here).first != "hasprevious")) {
1155 disp.setmacro ((*here).first, "Global", (*here).second.values[0]);
1156 }
1157 here ++;
1158 }
1159 }
1160 }
1161 }
1162}
Note: See TracBrowser for help on using the repository browser.