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

Last change on this file since 183 was 173, checked in by rjmcnab, 25 years ago

Fixed a few things.

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