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

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

added GPL notice

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