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

Last change on this file since 673 was 669, checked in by sjboddie, 25 years ago

finished changes to browsing support

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