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

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

altered receptionist slightly so it now passes *collectproto to
define_internal_macros and define_external_macros - need it
for browseaction

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