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

Last change on this file since 668 was 649, checked in by sjboddie, 25 years ago
  • metadata now returns mp rather than array
  • redesigned browsing support (although it's not finished so

won't currently work ;-)

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