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

Last change on this file since 793 was 789, checked in by sjboddie, 25 years ago

fixed bug in logout

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