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

Last change on this file since 935 was 935, checked in by sjboddie, 24 years ago

made compressedoptions macro dm_safe - added SCRIPT_NAME to log string

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