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

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

Merged sources.

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