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

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

fixed a couple of version conflicts - tidied up some small things

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 20.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 * PUT COPYRIGHT NOTICE HERE
7 *
8 * $Id: receptionist.cpp 297 1999-06-27 21:49:03Z sjboddie $
9 *
10 *********************************************************************/
11
12/*
13 $Log$
14 Revision 1.16 1999/06/27 21:49:03 sjboddie
15 fixed a couple of version conflicts - tidied up some small things
16
17 Revision 1.15 1999/06/26 01:14:32 rjmcnab
18 Made a couple of changes to handle different encodings.
19
20 Revision 1.14 1999/06/09 00:08:36 sjboddie
21 query string macro (_cgiargq_) is now made html safe before being set
22
23 Revision 1.13 1999/06/08 04:29:31 sjboddie
24 added argsinfo to the call to check_cgiargs to make it easy to set
25 args to their default if they're found to be screwed up
26
27 Revision 1.12 1999/04/30 01:59:42 sjboddie
28 lots of stuff - getting documentaction working (documentaction replaces
29 old browseaction)
30
31 Revision 1.11 1999/03/25 03:06:43 sjboddie
32
33 altered receptionist slightly so it now passes *collectproto to
34 define_internal_macros and define_external_macros - need it
35 for browseaction
36
37 Revision 1.10 1999/03/05 03:53:54 sjboddie
38
39 fixed some bugs
40
41 Revision 1.9 1999/02/28 20:00:16 rjmcnab
42
43
44 Fixed a few things.
45
46 Revision 1.8 1999/02/25 21:58:59 rjmcnab
47
48 Merged sources.
49
50 Revision 1.7 1999/02/21 22:33:55 rjmcnab
51
52 Lots of stuff :-)
53
54 Revision 1.6 1999/02/11 01:24:05 rjmcnab
55
56 Fixed a few compiler warnings.
57
58 Revision 1.5 1999/02/08 01:28:02 rjmcnab
59
60 Got the receptionist producing something using the statusaction.
61
62 Revision 1.4 1999/02/05 10:42:46 rjmcnab
63
64 Continued working on receptionist
65
66 Revision 1.3 1999/02/04 10:00:56 rjmcnab
67
68 Developed the idea of an "action" and having them define the cgi arguments
69 which they need and how those cgi arguments function.
70
71 Revision 1.2 1999/02/04 01:17:27 rjmcnab
72
73 Got it outputing something.
74
75
76 */
77
78
79#include "receptionist.h"
80#include "fileutil.h"
81#include "cgiutils.h"
82#include "htmlutils.h"
83#include "OIDtools.h"
84#include <assert.h>
85#include <time.h>
86
87
88
89// configure should be called for each line in the
90// configuration files to configure the receptionist and everything
91// it contains. The configuration should take place after everything
92// has been added but before the initialisation.
93void receptionist::configure (const text_t &key, const text_tarray &cfgline) {
94 // configure the receptionist
95 if (cfgline.size() >= 1) {
96 if (key == "gsdlhome") configinfo.gsdlhome = cfgline[0];
97 else if (key == "collection") configinfo.collection = cfgline[0];
98 else if (key == "collectdir") configinfo.collectdir = cfgline[0];
99 else if (key == "httpprefix") configinfo.httpprefix = cfgline[0];
100 else if (key == "httpimg") configinfo.httpimg = cfgline[0];
101 else if (key == "gwcgi") configinfo.gwcgi = cfgline[0];
102 else if (key == "macrofiles") configinfo.macrofiles = cfgline;
103 else if (key == "saveconf") configinfo.saveconf = cfgline[0];
104 }
105
106 // configure the actions
107 actionptrmap::iterator actionhere = actions.begin ();
108 actionptrmap::iterator actionend = actions.end ();
109
110 while (actionhere != actionend) {
111 assert ((*actionhere).second.a != NULL);
112 if ((*actionhere).second.a != NULL)
113 (*actionhere).second.a->configure(key, cfgline);
114
115 actionhere++;
116 }
117
118 // configure the protocols
119 recptprotolistclass::iterator protohere = protocols.begin ();
120 recptprotolistclass::iterator protoend = protocols.end ();
121
122 while (protohere != protoend) {
123 assert ((*protohere).p != NULL);
124 if ((*protohere).p != NULL)
125 (*protohere).p->configure(key, cfgline);
126
127 protohere++;
128 }
129}
130
131void receptionist::configure (const text_t &key, const text_t &value) {
132 text_tarray cfgline;
133 cfgline.push_back (value);
134 configure(key, cfgline);
135}
136
137
138// init should be called after all the actions, protocols, and
139// converters have been added to the receptionist and after everything
140// has been configured but before any pages are created.
141// It returns true on success and false on failure. If false is
142// returned getpage should not be called (without producing
143// meaningless output), instead an error page should be
144// produced by the calling code.
145bool receptionist::init (ostream &logout) {
146 // first configure collectdir
147 text_t thecollectdir = configinfo.gsdlhome;
148 if (!configinfo.collection.empty()) {
149 // collection specific mode
150 if (!configinfo.collectdir.empty()) {
151 // has already been configured
152 thecollectdir = configinfo.collectdir;
153 } else {
154 // decide where collectdir is by searching for collect.cfg
155 // look in $GSDLHOME/collect/collection-name/etc/collect.cfg and
156 // then $GSDLHOME/etc/collect.cfg
157 thecollectdir = filename_cat (configinfo.gsdlhome, "collect");
158 thecollectdir = filename_cat (thecollectdir, configinfo.collection);
159 text_t filename = filename_cat (thecollectdir, "etc");
160 filename = filename_cat (filename, "collect.cfg");
161 if (!file_exists(filename)) thecollectdir = configinfo.gsdlhome;
162 }
163 }
164 configure("collectdir", thecollectdir);
165
166 // read in the macro files
167 if (!read_macrofiles (logout)) return false;
168
169 // defined the main cgi arguments
170 if (!define_mainargs (logout)) return false;
171
172 // there must be at least one action defined
173 if (actions.empty()) {
174 logout << "Error: no actions have been added to the receptionist\n";
175 return false;
176 }
177
178 // add the cgi arguments from the actions
179 actionptrmap::iterator here = actions.begin ();
180 actionptrmap::iterator end = actions.end ();
181 while (here != end) {
182 assert ((*here).second.a != NULL);
183 if ((*here).second.a != NULL) {
184 if (!argsinfo.addarginfo (&logout, (*here).second.a->getargsinfo()))
185 return false;
186 }
187 here++;
188 }
189
190 // create a saveconf string if there isn't one already
191 if (configinfo.saveconf.empty())
192 configinfo.saveconf = create_save_conf_str (argsinfo, logout);
193
194 // check the saveconf string
195 if (!check_save_conf_str (configinfo.saveconf, argsinfo, logout))
196 return false;
197
198 // set a random seed
199 srand (time(NULL));
200
201 // make the output converters remove all the zero-width spaces
202 convertinfoclass::iterator converthere = converters.begin ();
203 convertinfoclass::iterator convertend = converters.end ();
204 text_t defaultconvertname;
205 while (converthere != convertend) {
206 assert ((*converthere).second.outconverter != NULL);
207 if ((*converthere).second.outconverter != NULL) {
208 (*converthere).second.outconverter->set_rzws(1);
209 if (defaultconvertname.empty())
210 defaultconvertname = (*converthere).second.name;
211 }
212 converthere++;
213 }
214
215 // set default converter if no good one has been defined
216 if (!defaultconvertname.empty()) {
217 cgiarginfo *ainfo = argsinfo.getarginfo ("w");
218 if ((ainfo != NULL) && (ainfo->defaultstatus < cgiarginfo::good)) {
219 ainfo->defaultstatus = cgiarginfo::good;
220 ainfo->argdefault = defaultconvertname;
221 }
222 }
223
224 // init the actions
225 actionptrmap::iterator actionhere = actions.begin ();
226 actionptrmap::iterator actionend = actions.end ();
227 while (actionhere != actionend) {
228 if (((*actionhere).second.a == NULL) ||
229 !(*actionhere).second.a->init(logout)) return false;
230 actionhere++;
231 }
232
233 // init the protocols
234 recptprotolistclass::iterator protohere = protocols.begin ();
235 recptprotolistclass::iterator protoend = protocols.end ();
236 while (protohere != protoend) {
237 if (((*protohere).p == NULL) ||
238 !(*protohere).p->init(logout)) return false;
239 protohere++;
240 }
241
242 return true;
243}
244
245
246// parse_cgi_args parses cgi arguments into an argument class.
247// This function should be called for each page request. It returns false
248// if there was a major problem with the cgi arguments.
249bool receptionist::parse_cgi_args (const text_t &argstr, cgiargsclass &args,
250 ostream &logout) {
251 outconvertclass text_t2ascii;
252
253 // get an initial list of cgi arguments
254 args.clear();
255 split_cgi_args (argstr, args);
256
257 // expand the compressed argument (if there was one)
258 if (!expand_save_args (argsinfo, configinfo.saveconf, args, logout)) return false;
259
260 // add the defaults
261 add_default_args (argsinfo, args, logout);
262
263 // check the main cgi arguments
264 if (!check_mainargs (args, logout)) return false;
265
266 // check the arguments for the action
267 action *a = actions.getaction (args["a"]);
268 if (a != NULL) {
269 if (!a->check_cgiargs (argsinfo, args, logout)) return false;
270 } else {
271 // the action was not found!!
272 logout << text_t2ascii << "Error: the action \"" << args["a"]
273 << "\" could not be found.\n";
274 return false;
275 }
276
277 // get the input encoding
278 text_t &arg_w = args["w"];
279 inconvertclass defaultinconvert;
280 inconvertclass *inconvert = converters.get_inconverter (arg_w);
281 if (inconvert == NULL) inconvert = &defaultinconvert;
282
283 // see if the next page will have a different encoding
284 if (args.getarg("nw") != NULL) args["w"] = args["nw"];
285
286 // convert arguments which aren't in unicode to unicode
287 args_tounicode (args, *inconvert);
288
289 return true;
290}
291
292
293// produce_cgi_page will call get_cgihead_info and
294// produce_content in the appropriate way to output a cgi header and
295// the page content (if needed). If a page could not be created it
296// will return false
297bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout,
298 ostream &logout) {
299 outconvertclass text_t2ascii;
300
301 response_t response;
302 text_t response_data;
303
304 // produce cgi header
305 get_cgihead_info (args, response, response_data, logout);
306 if (response == location) {
307 // I've forgotten how to do this :-/
308 return true;
309 } else if (response == content) {
310 // content response
311 contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
312 } else {
313 // unknown response
314 logout << "Error: get_cgihead_info returned an unknown response type.\n";
315 return false;
316 }
317
318 // produce cgi page
319 if (!produce_content (args, contentout, logout)) return false;
320
321 // flush contentout
322 contentout << flush;
323 return true;
324}
325
326
327// get_cgihead_info determines the cgi header information for
328// a set of cgi arguments. If response contains location then
329// response_data contains the redirect address. If reponse
330// contains content then reponse_data contains the content-type.
331// Note that images can now be produced by the receptionist.
332void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
333 text_t &response_data, ostream &logout) {
334 outconvertclass text_t2ascii;
335
336 // get the action
337 action *a = actions.getaction (args["a"]);
338 if (a != NULL) {
339 a->get_cgihead_info (args, response, response_data, logout);
340
341 } else {
342 // the action was not found!!
343 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
344 << args["a"] << "\" could not be found.\n";
345 response = content;
346 response_data = "text/html";
347 }
348}
349
350
351// produce the page content
352bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
353 ostream &logout) {
354 // decide on the output conversion class
355 text_t &arg_w = args["w"];
356 rzwsoutconvertclass defaultoutconverter;
357 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
358 if (outconverter == NULL) outconverter = &defaultoutconverter;
359 outconverter->reset();
360
361 // decide on the protocol used for communicating with
362 // the collection server
363 recptproto *collectproto = NULL;
364 if (!args["c"].empty()) {
365 collectproto = protocols.getrecptproto (args["c"], logout);
366 }
367
368 // produce the page using the desired action
369 action *a = actions.getaction (args["a"]);
370 if (a != NULL) {
371 if (a->uses_display(args)) prepare_page (a, args, collectproto, (*outconverter), logout);
372 if (!a->do_action (args, collectproto, disp, (*outconverter), contentout, logout))
373 return false;
374
375 } else {
376 // the action was not found!!
377 outconvertclass text_t2ascii;
378
379 logout << text_t2ascii << "Error receptionist::produce_content: the action \""
380 << args["a"] << "\" could not be found.\n";
381
382 contentout << (*outconverter)
383 << "<html>\n"
384 << "<head>\n"
385 << "<title>Error</title>\n"
386 << "</head>\n"
387 << "<body>\n"
388 << "<h2>Oops!</h2>\n"
389 << "Undefined Page. The action \""
390 << args["a"] << "\" could not be found.\n"
391 << "</body>\n"
392 << "</html>\n";
393 }
394
395 return true;
396}
397
398
399// returns the compressed argument ("e") corresponding to the argument
400// list. This can be used to save preferences between sessions.
401text_t receptionist::get_compressed_arg (cgiargsclass &args, outconvertclass &outconvert,
402 ostream &logout) {
403
404 text_t compressed_args;
405 if (compress_save_args (argsinfo, configinfo.saveconf, args,
406 compressed_args, outconvert, logout))
407 return compressed_args;
408 else
409 return "";
410}
411
412
413// will read in all the macro files. If one is not found an
414// error message will be written to logout and the method will
415// return false.
416bool receptionist::read_macrofiles (ostream &logout) {
417 outconvertclass text_t2ascii;
418
419 // redirect the error output to logout
420 disp.setlogout (&logout);
421
422 // load up the default macro files, the collection directory
423 // is searched first for the file (if this is being used in
424 // collection specific mode) and then the main directory
425 text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
426 text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
427 text_tarray::iterator arrhere = configinfo.macrofiles.begin();
428 text_tarray::iterator arrend = configinfo.macrofiles.end();
429 text_t filename;
430 while (arrhere != arrend) {
431 // filename is used as a flag to indicate whether
432 // the macro file has been found
433 filename.clear();
434
435 // try in the collection directory if this is being
436 // run in collection specific mode
437 if (!configinfo.collection.empty()) {
438 filename = filename_cat (colmacrodir, *arrhere);
439 if (!file_exists (filename)) filename.clear ();
440 }
441
442 // if we haven't found the macro file yet try in
443 // the main macro directory
444 if (filename.empty()) {
445 filename = filename_cat (gsdlmacrodir, *arrhere);
446 if (!file_exists (filename)) filename.clear ();
447 }
448
449 // see if we found the file or not
450 if (filename.empty()) {
451 logout << text_t2ascii
452 << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
453 if (configinfo.collection.empty()) {
454 logout << text_t2ascii
455 << "It should be in " << gsdlmacrodir << ".\n\n";
456 } else {
457 logout << text_t2ascii
458 << "It should be in either " << colmacrodir << " or in "
459 << gsdlmacrodir << ".\n\n";
460 }
461 return false;
462
463 } else { // found the file
464 disp.loaddefaultmacros(filename);
465 }
466
467 arrhere++;
468 }
469
470 // success
471 return true;
472}
473
474
475// Will define the main general arguments used by the receptionist.
476// If an error occurs a message will be written to logout and the
477// method will return false.
478bool receptionist::define_mainargs (ostream &logout) {
479 // create a list of cgi arguments
480 cgiarginfo ainfo;
481
482 ainfo.shortname = "e";
483 ainfo.longname = "compressed arguments";
484 ainfo.multiplechar = true;
485 ainfo.defaultstatus = cgiarginfo::good;
486 ainfo.argdefault = "";
487 ainfo.savedarginfo = cgiarginfo::mustnot;
488 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
489
490 ainfo.shortname = "a";
491 ainfo.longname = "action";
492 ainfo.multiplechar = true;
493 ainfo.defaultstatus = cgiarginfo::none;
494 ainfo.argdefault = "";
495 ainfo.savedarginfo = cgiarginfo::must;
496 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
497
498 // w=western
499 ainfo.shortname = "w";
500 ainfo.longname = "encoding";
501 ainfo.multiplechar = true;
502 ainfo.defaultstatus = cgiarginfo::weak;
503 ainfo.argdefault = "w";
504 ainfo.savedarginfo = cgiarginfo::must;
505 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
506
507 ainfo.shortname = "nw";
508 ainfo.longname = "new encoding";
509 ainfo.multiplechar = true;
510 ainfo.defaultstatus = cgiarginfo::none;
511 ainfo.argdefault = "";
512 ainfo.savedarginfo = cgiarginfo::mustnot;
513 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
514
515 ainfo.shortname = "c";
516 ainfo.longname = "collection";
517 ainfo.multiplechar = true;
518 if (configinfo.collection.empty()) {
519 ainfo.defaultstatus = cgiarginfo::none;
520 ainfo.argdefault = "";
521 ainfo.savedarginfo = cgiarginfo::must;
522 } else {
523 ainfo.defaultstatus = cgiarginfo::good;
524 ainfo.argdefault = configinfo.collection;
525 ainfo.savedarginfo = cgiarginfo::can;
526 }
527 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
528
529 // 0=text+graphics, 1=text
530 ainfo.shortname = "v";
531 ainfo.longname = "version";
532 ainfo.multiplechar = false;
533 ainfo.defaultstatus = cgiarginfo::weak;
534 ainfo.argdefault = "0";
535 ainfo.savedarginfo = cgiarginfo::can;
536 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
537
538 // 0=normal, 1=big
539 ainfo.shortname = "f";
540 ainfo.longname = "query box size";
541 ainfo.multiplechar = false;
542 ainfo.defaultstatus = cgiarginfo::weak;
543 ainfo.argdefault = "0";
544 ainfo.savedarginfo = cgiarginfo::can;
545 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
546
547 // the interface language name should use the ISO 639
548 // standard
549 ainfo.shortname = "l";
550 ainfo.longname = "interface language";
551 ainfo.multiplechar = true;
552 ainfo.defaultstatus = cgiarginfo::weak;
553 ainfo.argdefault = "en";
554 ainfo.savedarginfo = cgiarginfo::must;
555 if (!argsinfo.addarginfo (&logout, ainfo)) return false;
556
557 return true;
558}
559
560
561// check_mainargs will check all the main arguments. If a major
562// error is found it will return false and no cgi page should
563// be created using the arguments.
564bool receptionist::check_mainargs (cgiargsclass &args, ostream &/*logout*/) {
565 // if this receptionist is running in collection dependant mode
566 // then it should always set the collection argument to the
567 // collection
568 if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
569
570 // argument "v" can only be 0 or 1. Use the default value
571 // if it is out of range
572 int arg_v = args.getintarg ("v");
573 if (arg_v != 0 && arg_v != 1) {
574 cgiarginfo *vinfo = argsinfo.getarginfo ("v");
575 if (vinfo != NULL) args["v"] = vinfo->argdefault;
576 }
577
578 // argument "f" can only be 0 or 1. Use the default value
579 // if it is out of range
580 int arg_f = args.getintarg ("f");
581 if (arg_f != 0 && arg_f != 1) {
582 cgiarginfo *finfo = argsinfo.getarginfo ("f");
583 if (finfo != NULL) args["f"] = finfo->argdefault;
584 }
585
586 return true;
587}
588
589// prepare_page sets up page parameters, sets display macros
590// and opens the page ready for output
591void receptionist::prepare_page (action *a, cgiargsclass &args, recptproto *collectproto,
592 outconvertclass &outconvert, ostream &logout) {
593 // set up page parameters
594 text_t pageparams;
595
596 bool first = true;
597 if (!args["c"].empty()) {
598 pageparams += "collection=" + args["c"]; first = false;}
599 if (args.getintarg("u") == 1)
600 if (first) {pageparams += "style=htmlonly"; first = false;}
601 else pageparams += ",style=htmlonly";
602 if (args.getintarg("v") == 1)
603 if (first) {pageparams += "version=text"; first = false;}
604 else pageparams += ",version=text";
605 if (args.getintarg("f") == 1)
606 if (first) {pageparams += ",queryversion=big"; first = false;}
607 else pageparams += ",queryversion=big";
608 if (args["l"] != "en")
609 if (first) pageparams += ",language=" + args["l"];
610 else pageparams += ",language=" + args["l"];
611
612 // open the page
613 disp.openpage(pageparams, MACROPRECEDENCE);
614
615
616 // define general macros
617 define_general_macros (args, outconvert, logout);
618
619
620 // define external macros for each action
621 actionptrmap::iterator actionhere = actions.begin ();
622 actionptrmap::iterator actionend = actions.end ();
623
624 while (actionhere != actionend) {
625 assert ((*actionhere).second.a != NULL);
626 if ((*actionhere).second.a != NULL)
627 (*actionhere).second.a->define_external_macros (disp, args, collectproto, logout);
628 actionhere++;
629 }
630
631
632 // define internal macros for the current action
633 a->define_internal_macros (disp, args, collectproto, logout);
634}
635
636void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &outconvert,
637 ostream &logout) {
638 disp.setmacro ("gwcgi", "Global", configinfo.gwcgi);
639 disp.setmacro ("httpimg", "Global", configinfo.httpimg);
640 disp.setmacro ("httpprefix", "Global", configinfo.httpprefix);
641 disp.setmacro("compressedoptions", "Global", get_compressed_arg(args, outconvert, logout));
642
643 // set _cgiargX_ macros for each cgi argument
644 cgiargsclass::const_iterator argshere = args.begin();
645 cgiargsclass::const_iterator argsend = args.end();
646 while (argshere != argsend) {
647 if ((*argshere).first == "q")
648 // need to escape special characters from query string
649 disp.setmacro ("cgiargq", "Global", html_safe((*argshere).second));
650 else
651 disp.setmacro ("cgiarg" + (*argshere).first, "Global", (*argshere).second);
652 argshere ++;
653 }
654}
Note: See TracBrowser for help on using the repository browser.