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

Last change on this file since 781 was 776, checked in by sjboddie, 25 years ago

added multiplevalue option to cgiarginfo

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