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

Last change on this file since 757 was 731, checked in by sjboddie, 25 years ago

yet another problem with calling browserclass
processOID functions correctly

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