source: gsdl/trunk/runtime-src/src/recpt/receptionist.cpp@ 18824

Last change on this file since 18824 was 18824, checked in by mdewsnip, 15 years ago

Fixed get_cookie() so that it checks that cookiestring isn't empty before trying to fiddle with it, to prevent crashes on Windows.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 56.1 KB
RevLine 
[108]1/**********************************************************************
2 *
3 * receptionist.cpp -- a web interface for the gsdl
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
[533]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.
[108]9 *
[533]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 *
[108]24 *********************************************************************/
25
[2379]26// following line required to get fstream.filedesc() on darwin (Mac OS X)
[2386]27// gcc 2.91 automatically defines this in stream.h
[2379]28#define _STREAM_COMPAT 1
29
[108]30#include "receptionist.h"
[15418]31#include "recptprototools.h"
[108]32#include "fileutil.h"
[155]33#include "cgiutils.h"
[263]34#include "htmlutils.h"
[1148]35#include "gsdltools.h"
[1779]36#include "gsdltimes.h"
[248]37#include "OIDtools.h"
[108]38#include <assert.h>
[150]39#include <time.h>
[3007]40#include <stdio.h> // for open()
41#include <fcntl.h> // for open() flags
[2608]42// following 2 are for printing Last-Modified http header.
[2386]43#include <sys/stat.h>
44#include <time.h>
45
[1170]46#if defined (GSDL_USE_IOS_H)
[463]47#include <fstream.h>
[1170]48#else
49#include <fstream>
50#endif
[108]51
[526]52void recptconf::clear () {
53 gsdlhome.clear();
[16310]54 collecthome.clear();
[15589]55 dbhome.clear();
[806]56 collectinfo.erase(collectinfo.begin(), collectinfo.end());
[526]57 collection.clear();
58 collectdir.clear();
59 httpprefix.clear();
[1193]60 httpimg = "/images";
[526]61 gwcgi.clear();
62 macrofiles.erase(macrofiles.begin(), macrofiles.end());
63 saveconf.clear();
64 usecookies = false;
65 logcgiargs = false;
[1779]66 LogDateFormat = LocalTime;
[530]67
[1762]68 maintainer.clear();
69 MailServer.clear();
70 LogEvents = Disabled;
71 EmailEvents = Disabled;
72 EmailUserEvents = false;
73
[1856]74 languages.erase(languages.begin(), languages.end());
75 encodings.erase(encodings.begin(), encodings.end());
76
[5018]77 site_auth = false;
[4905]78 HomePageType = "images";
79 HomePageCols = 3;
80
[530]81 // these default page parameters can always be overriden
82 // in the configuration file
83 pageparams.erase(pageparams.begin(), pageparams.end());
84 pageparams["c"] = "";
85 pageparams["l"] = "en";
86
87#ifdef MACROPRECEDENCE
88 macroprecedence = MACROPRECEDENCE;
89#else
90 macroprecedence.clear();
91#endif
[526]92}
93
[1270]94void collectioninfo_t::clear () {
95 gsdl_gsdlhome.clear();
[15589]96 gsdl_dbhome.clear();
[526]97
[1270]98 info_loaded = false;
99 info.clear();
100}
[526]101
[1856]102void languageinfo_t::clear () {
103 longname.clear();
104 defaultencoding.clear();
105}
106
[388]107receptionist::receptionist () {
108 // create a list of cgi arguments
109 // this must be done before the configuration
110
111 cgiarginfo ainfo;
112
113 ainfo.shortname = "e";
114 ainfo.longname = "compressed arguments";
115 ainfo.multiplechar = true;
116 ainfo.defaultstatus = cgiarginfo::good;
[7421]117 ainfo.argdefault = g_EmptyText;
[388]118 ainfo.savedarginfo = cgiarginfo::mustnot;
119 argsinfo.addarginfo (NULL, ainfo);
120
121 ainfo.shortname = "a";
122 ainfo.longname = "action";
123 ainfo.multiplechar = true;
124 ainfo.defaultstatus = cgiarginfo::none;
[7421]125 ainfo.argdefault = g_EmptyText;
[388]126 ainfo.savedarginfo = cgiarginfo::must;
127 argsinfo.addarginfo (NULL, ainfo);
128
129 // w=western
130 ainfo.shortname = "w";
131 ainfo.longname = "encoding";
132 ainfo.multiplechar = true;
[1856]133 ainfo.defaultstatus = cgiarginfo::none;
[7421]134 ainfo.argdefault = g_EmptyText;
[388]135 ainfo.savedarginfo = cgiarginfo::must;
136 argsinfo.addarginfo (NULL, ainfo);
137
138 ainfo.shortname = "nw";
139 ainfo.longname = "new encoding";
140 ainfo.multiplechar = true;
141 ainfo.defaultstatus = cgiarginfo::none;
[7421]142 ainfo.argdefault = g_EmptyText;
[388]143 ainfo.savedarginfo = cgiarginfo::mustnot;
144 argsinfo.addarginfo (NULL, ainfo);
145
146 ainfo.shortname = "c";
147 ainfo.longname = "collection";
148 ainfo.multiplechar = true;
149 ainfo.defaultstatus = cgiarginfo::none;
[7421]150 ainfo.argdefault = g_EmptyText;
[388]151 ainfo.savedarginfo = cgiarginfo::must;
152 argsinfo.addarginfo (NULL, ainfo);
153
154 // the interface language name should use the ISO 639
155 // standard
156 ainfo.shortname = "l";
157 ainfo.longname = "interface language";
158 ainfo.multiplechar = true;
159 ainfo.defaultstatus = cgiarginfo::weak;
160 ainfo.argdefault = "en";
161 ainfo.savedarginfo = cgiarginfo::must;
162 argsinfo.addarginfo (NULL, ainfo);
[1856]163
164 ainfo.shortname = "nl";
165 ainfo.longname = "new language";
166 ainfo.multiplechar = false;
167 ainfo.defaultstatus = cgiarginfo::none;
168 ainfo.argdefault = "0";
169 ainfo.savedarginfo = cgiarginfo::mustnot;
170 argsinfo.addarginfo (NULL, ainfo);
171
[463]172 // the GSDL_UID (cookie)
173 ainfo.shortname = "z";
174 ainfo.longname = "gsdl uid";
175 ainfo.multiplechar = true;
176 ainfo.defaultstatus = cgiarginfo::none;
[7421]177 ainfo.argdefault = g_EmptyText;
[463]178 ainfo.savedarginfo = cgiarginfo::mustnot;
179 argsinfo.addarginfo (NULL, ainfo);
[530]180}
[463]181
[530]182
183void receptionist::add_action (action *theaction) {
184 // make sure we have an action to add
185 if (theaction == NULL) return;
186
187 // add this action to the list of actions
188 actions.addaction(theaction);
189
190 // add the cgi arguments from this action
[12509]191 argsinfo.addarginfo (NULL, *(theaction->getargsinfo()));
[388]192}
193
194
[649]195void receptionist::add_browser (browserclass *thebrowser) {
196 // make sure we have a browser to add
197 if (thebrowser == NULL) return;
198
199 // add this browser to the list of browsers
200 browsers.addbrowser(thebrowser);
201}
202
203
204void receptionist::setdefaultbrowser (const text_t &browsername) {
205 browsers.setdefaultbrowser (browsername);
206}
207
208
[165]209// configure should be called for each line in the
210// configuration files to configure the receptionist and everything
211// it contains. The configuration should take place after everything
212// has been added but before the initialisation.
[5017]213
[165]214void receptionist::configure (const text_t &key, const text_tarray &cfgline) {
215 // configure the receptionist
[1870]216
[5017]217
218
[165]219 if (cfgline.size() >= 1) {
[388]220 cgiarginfo *info = NULL;
[2561]221 if (key == "gsdlhome") {
222 configinfo.gsdlhome = cfgline[0];
[15589]223 if (configinfo.dbhome.empty()) configinfo.dbhome = cfgline[0];
[2561]224 }
[16310]225 else if (key == "collecthome") configinfo.collecthome = cfgline[0];
[15589]226 else if (key == "gdbmhome") configinfo.dbhome = cfgline[0];
[388]227 else if (key == "collection") {
228 configinfo.collection = cfgline[0];
229 // also need to set the default arg to this collection
230 if ((info = argsinfo.getarginfo("c")) != NULL) {
231 info->defaultstatus = cgiarginfo::good;
232 info->argdefault = cfgline[0];
233 }
234
[16310]235 }
236 else if (key == "collectdir") configinfo.collectdir = cfgline[0];
[297]237 else if (key == "httpprefix") configinfo.httpprefix = cfgline[0];
[165]238 else if (key == "httpimg") configinfo.httpimg = cfgline[0];
239 else if (key == "gwcgi") configinfo.gwcgi = cfgline[0];
[799]240 else if (key == "macrofiles") {
241 // want to append to macrofiles (i.e. may be several config files
242 // contributing, maybe from several collections).
243 text_tarray::const_iterator here = cfgline.begin();
244 text_tarray::const_iterator end = cfgline.end();
245 while (here != end) {
246 configinfo.macrofiles.insert (*here);
[7443]247 ++here;
[799]248 }
249 }
[165]250 else if (key == "saveconf") configinfo.saveconf = cfgline[0];
[526]251 else if (key == "usecookies") configinfo.usecookies = (cfgline[0] == "true");
252 else if (key == "logcgiargs") configinfo.logcgiargs = (cfgline[0] == "true");
[1762]253 else if (key == "maintainer") configinfo.maintainer = cfgline[0];
254 else if (key == "MailServer") configinfo.MailServer = cfgline[0];
[1779]255 else if (key == "LogDateFormat") {
256 if (cfgline[0] == "UTCTime") configinfo.LogDateFormat = UTCTime;
257 else if (cfgline[0] == "Absolute") configinfo.LogDateFormat = Absolute;
258 }
[1762]259 else if (key == "LogEvents") {
260 if (cfgline[0] == "CollectorEvents") configinfo.LogEvents = CollectorEvents;
261 else if (cfgline[0] == "AllEvents") configinfo.LogEvents = AllEvents;
262 }
263 else if (key == "EmailEvents") {
264 if (cfgline[0] == "CollectorEvents") configinfo.EmailEvents = CollectorEvents;
265 else if (cfgline[0] == "AllEvents") configinfo.EmailEvents = AllEvents;
266 }
267 else if (key == "EmailUserEvents") configinfo.EmailUserEvents = (cfgline[0] == "true");
[530]268 else if (key == "pageparam") {
269 if (cfgline.size() >= 2) configinfo.pageparams[cfgline[0]] = cfgline[1];
270 else configinfo.pageparams[cfgline[0]] = "";
271 }
272 else if (key == "macroprecedence") configinfo.macroprecedence = cfgline[0];
[799]273 else if (key == "collectinfo") {
[16310]274 if (cfgline.size() == 3) {
275 // for backwards compatability with older collections that only use
276 // gsdlhome and dbhome
[799]277 collectioninfo_t cinfo;
278 cinfo.gsdl_gsdlhome = cfgline[1];
[16310]279 cinfo.gsdl_collecthome = filename_cat(cfgline[1],"collect");
[15589]280 cinfo.gsdl_dbhome = cfgline[2];
[799]281 configinfo.collectinfo[cfgline[0]] = cinfo;
282 }
[16310]283 else if (cfgline.size() >= 4) {
284 collectioninfo_t cinfo;
285 cinfo.gsdl_gsdlhome = cfgline[1];
286 cinfo.gsdl_collecthome = cfgline[2];
287 cinfo.gsdl_dbhome = cfgline[3];
288 configinfo.collectinfo[cfgline[0]] = cinfo;
289 }
[799]290 }
[4905]291
[5017]292 // Read in the value for the site_auth directive either true or false
293 else if (key == "site_auth") configinfo.site_auth = (cfgline[0] == "true");
294
295 else if (key == "site_group")
296 joinchar(cfgline,',',configinfo.site_group);
297
[4905]298 else if (key == "SiteFormat") {
299 if (cfgline[0] == "HomePageType") {
300 configinfo.HomePageType = cfgline[1];
301 } else if (cfgline[0] == "HomePageCols") {
302 configinfo.HomePageCols = cfgline[1].getint();
303 }
304 }
305
[530]306 else if (key == "cgiarg") {
307 // get shortname
308 bool seen_defaultstatus = false;
309 text_t subkey, subvalue;
310 text_t shortname;
311 text_t::const_iterator cfglinesub_here;
312 text_tarray::const_iterator cfgline_here = cfgline.begin();
313 text_tarray::const_iterator cfgline_end = cfgline.end();
314 while (cfgline_here != cfgline_end) {
315 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
316 (*cfgline_here).end(), '=', subkey);
317 if (subkey == "shortname") {
318 shortname = substr (cfglinesub_here, (*cfgline_here).end());
319 }
[7443]320 ++cfgline_here;
[388]321 }
[530]322
323 // if we found the shortname process the line again filling in values
324 if (!shortname.empty()) {
325 cgiarginfo &chinfo = argsinfo[shortname];
326 chinfo.shortname = shortname; // in case this is a new argument
327
328 cfgline_here = cfgline.begin();
329 while (cfgline_here != cfgline_end) {
330 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
331 (*cfgline_here).end(), '=', subkey);
332 subvalue = substr (cfglinesub_here, (*cfgline_here).end());
333
334 if (subkey == "longname") chinfo.longname = subvalue;
335 else if (subkey == "multiplechar") chinfo.multiplechar = (subvalue == "true");
336 else if (subkey == "defaultstatus") {
337 seen_defaultstatus = true;
338 if (subvalue == "none") chinfo.defaultstatus = cgiarginfo::none;
339 else if (subvalue == "weak") chinfo.defaultstatus = cgiarginfo::weak;
340 else if (subvalue == "good") chinfo.defaultstatus = cgiarginfo::good;
341 else if (subvalue == "config") chinfo.defaultstatus = cgiarginfo::config;
342 else if (subvalue == "imperative") chinfo.defaultstatus = cgiarginfo::imperative;
343 }
344 else if (subkey == "argdefault") {
345 chinfo.argdefault = subvalue;
346 if (!seen_defaultstatus) chinfo.defaultstatus = cgiarginfo::config;
347 }
348 else if (subkey == "savedarginfo") {
349 if (subvalue == "mustnot") chinfo.savedarginfo = cgiarginfo::mustnot;
350 else if (subvalue == "can") chinfo.savedarginfo = cgiarginfo::can;
351 else if (subvalue == "must") chinfo.savedarginfo = cgiarginfo::must;
352 }
353
[7443]354 ++cfgline_here;
[530]355 }
356 }
[1856]357
358 } else if (key == "Encoding") {
359
[1870]360 configure_encoding (cfgline);
361
[1856]362 } else if (key == "Language") {
[1870]363 text_t subkey, subvalue, shortname;
364 languageinfo_t lang;
[1856]365 text_t::const_iterator cfglinesub_here;
366 text_tarray::const_iterator cfgline_here = cfgline.begin();
367 text_tarray::const_iterator cfgline_end = cfgline.end();
368 while (cfgline_here != cfgline_end) {
369 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
370 (*cfgline_here).end(), '=', subkey);
371 if (subkey == "shortname") {
372 shortname = substr (cfglinesub_here, (*cfgline_here).end());
373 } else if (subkey == "longname") {
[1870]374 lang.longname = substr (cfglinesub_here, (*cfgline_here).end());
[1856]375 } else if (subkey == "default_encoding") {
[1870]376 lang.defaultencoding = substr (cfglinesub_here, (*cfgline_here).end());
[1856]377 }
[7443]378 ++cfgline_here;
[1856]379 }
380 if (!shortname.empty()) {
[1870]381 if (lang.longname.empty()) lang.longname = shortname;
[1856]382 configinfo.languages[shortname] = lang;
383 }
[388]384 }
[165]385 }
[530]386
[165]387 // configure the actions
388 actionptrmap::iterator actionhere = actions.begin ();
389 actionptrmap::iterator actionend = actions.end ();
[108]390
[165]391 while (actionhere != actionend) {
392 assert ((*actionhere).second.a != NULL);
393 if ((*actionhere).second.a != NULL)
394 (*actionhere).second.a->configure(key, cfgline);
[108]395
[7421]396 ++actionhere;
[165]397 }
[108]398
[165]399 // configure the protocols
400 recptprotolistclass::iterator protohere = protocols.begin ();
401 recptprotolistclass::iterator protoend = protocols.end ();
[108]402
[165]403 while (protohere != protoend) {
404 assert ((*protohere).p != NULL);
[2113]405 comerror_t err;
[165]406 if ((*protohere).p != NULL)
[2113]407 (*protohere).p->configure(key, cfgline, err);
[165]408
[7443]409 ++protohere;
[155]410 }
[649]411
412 // configure the browsers
413 browserptrmap::iterator browserhere = browsers.begin ();
414 browserptrmap::iterator browserend = browsers.end ();
415
416 while (browserhere != browserend) {
417 assert ((*browserhere).second.b != NULL);
418 if ((*browserhere).second.b != NULL)
419 (*browserhere).second.b->configure(key, cfgline);
420
[7443]421 ++browserhere;
[649]422 }
[108]423}
424
[649]425
[165]426void receptionist::configure (const text_t &key, const text_t &value) {
427 text_tarray cfgline;
428 cfgline.push_back (value);
429 configure(key, cfgline);
430}
431
432
[1870]433// init should be called after all the actions and protocols have been
434// added to the receptionist and after everything has been configured but
435// before any pages are created. It returns true on success and false on
436// failure. If false is returned getpage should not be called (without
437// producing meaningless output), instead an error page should be produced
438// by the calling code.
[150]439bool receptionist::init (ostream &logout) {
[1870]440
[165]441 // first configure collectdir
442 if (!configinfo.collection.empty()) {
[16310]443
[165]444 // collection specific mode
[16310]445
446 text_t collectdir = configinfo.gsdlhome;
447
[165]448 if (!configinfo.collectdir.empty()) {
449 // has already been configured
[16310]450 collectdir = configinfo.collectdir;
[165]451 } else {
[16310]452
[165]453 // decide where collectdir is by searching for collect.cfg
454 // look in $GSDLHOME/collect/collection-name/etc/collect.cfg and
455 // then $GSDLHOME/etc/collect.cfg
[16310]456 collectdir = filename_cat (configinfo.gsdlhome, "collect");
457 collectdir = filename_cat (collectdir, configinfo.collection);
458 text_t filename = filename_cat (collectdir, "etc");
[165]459 filename = filename_cat (filename, "collect.cfg");
[16310]460
461 if (!file_exists(filename)) collectdir = configinfo.gsdlhome;
462 }
[418]463
[16310]464 configure("collectdir", collectdir);
465
[165]466 }
[16310]467 else {
[165]468
[16310]469 text_t collecthome;
470 if (configinfo.collecthome.empty()) {
471 collecthome = filename_cat(configinfo.gsdlhome,"collect");
472 }
473 else {
474 collecthome = configinfo.collecthome;
475 }
476
477 configure("collecthome", collecthome);
478
479 // for backwards compatability collectdir set to gsdlhome
480 // (possible it could now be removed)
481 configure("collectdir", configinfo.gsdlhome);
482 }
483
484
[155]485 // read in the macro files
486 if (!read_macrofiles (logout)) return false;
[108]487
[165]488 // there must be at least one action defined
489 if (actions.empty()) {
490 logout << "Error: no actions have been added to the receptionist\n";
491 return false;
492 }
493
[649]494 // there must be at least one browser defined
495 if (browsers.empty()) {
496 logout << "Error: no browsers have been added to the receptionist\n";
497 return false;
498 }
499
[155]500 // create a saveconf string if there isn't one already
[165]501 if (configinfo.saveconf.empty())
502 configinfo.saveconf = create_save_conf_str (argsinfo, logout);
[108]503
[155]504 // check the saveconf string
[165]505 if (!check_save_conf_str (configinfo.saveconf, argsinfo, logout))
[155]506 return false;
507
508 // set a random seed
509 srand (time(NULL));
510
[1762]511 // if maintainer email address is something dodgy (for now I'll define
512 // dodgy as being anything that doesn't contain '@') disable EmailEvents
[1778]513 // and EmailUserEvents (we don't strictly need to disable EmailUserEvents
514 // in this case but we will as it seems likely that MailServer will also
515 // be screwed up if maintainer is).
[1762]516 text_t::const_iterator maintainer_end = configinfo.maintainer.end ();
[2937]517 text_t::const_iterator maintainer_here = findchar ((text_t::const_iterator)configinfo.maintainer.begin(),
[1762]518 maintainer_end, '@');
519 if (maintainer_here == maintainer_end) {
520 configinfo.EmailEvents = Disabled;
521 configinfo.EmailUserEvents = Disabled;
522 } else {
523 // if MailServer isn't set it should default to mail.maintainer-domain
524 if (configinfo.MailServer.empty()) {
[1778]525 configinfo.MailServer = "mail." + substr (maintainer_here+1, maintainer_end);
[1762]526 }
527 }
528
[165]529 // init the actions
530 actionptrmap::iterator actionhere = actions.begin ();
531 actionptrmap::iterator actionend = actions.end ();
532 while (actionhere != actionend) {
533 if (((*actionhere).second.a == NULL) ||
534 !(*actionhere).second.a->init(logout)) return false;
[7421]535 ++actionhere;
[165]536 }
537
538 // init the protocols
539 recptprotolistclass::iterator protohere = protocols.begin ();
540 recptprotolistclass::iterator protoend = protocols.end ();
541 while (protohere != protoend) {
[2113]542 comerror_t err;
[165]543 if (((*protohere).p == NULL) ||
[2113]544 !(*protohere).p->init(err, logout)) return false;
[7443]545 ++protohere;
[165]546 }
547
[649]548 // init the browsers
549 browserptrmap::iterator browserhere = browsers.begin ();
550 browserptrmap::iterator browserend = browsers.end ();
551 while (browserhere != browserend) {
552 if (((*browserhere).second.b == NULL) ||
553 !(*browserhere).second.b->init(logout)) return false;
[7443]554 ++browserhere;
[649]555 }
556
[146]557 return true;
[108]558}
559
[1856]560// get the default encoding for the given language - if it fails for any
561// reason return ""
562text_t receptionist::get_default_encoding (const text_t &language) {
563
564 // make sure language is valid
565 if (configinfo.languages.find(language) == configinfo.languages.end()) return "";
[108]566
[1856]567 text_t default_encoding = configinfo.languages[language].defaultencoding;
568
569 // make sure the encoding is valid
[10563]570 if (converters.find(default_encoding) == converters.end()) {
571 // we don't support the encoding specified as default for this language
572 if (configinfo.encodings.size()==1) {
573 // only 1 encoding specified in main.cfg, so use it
574 return configinfo.encodings.begin()->second;
575 }
576 return "";
577 }
[1856]578
579 return default_encoding;
580}
581
[155]582// parse_cgi_args parses cgi arguments into an argument class.
583// This function should be called for each page request. It returns false
584// if there was a major problem with the cgi arguments.
[12509]585bool receptionist::parse_cgi_args (const text_t &argstr,
586 fileupload_tmap &fileuploads,
587 cgiargsclass &args,
[864]588 ostream &logout, text_tmap &fcgienv) {
[155]589
590 // get an initial list of cgi arguments
591 args.clear();
[776]592 split_cgi_args (argsinfo, argstr, args);
[155]593
594 // expand the compressed argument (if there was one)
[165]595 if (!expand_save_args (argsinfo, configinfo.saveconf, args, logout)) return false;
[155]596
597 // add the defaults
598 add_default_args (argsinfo, args, logout);
599
[12509]600 // add any file upload arguments
601 add_fileupload_args(argsinfo, args, fileuploads, logout);
602
[463]603 // get the cookie
[864]604 if (configinfo.usecookies) get_cookie(args["z"], fcgienv);
[362]605
[1856]606 // if we're changing languages, set the encoding to the default for the new language
607 if (args["nl"] == "1") {
608 args["nw"] = get_default_encoding(args["l"]);
609 }
610
[362]611 // get the input encoding
[1856]612 // if encoding isn't set, set it to the default for the current language
613 if ((args.getarg("w") == NULL) || args["w"].empty()) {
614 args["w"] = get_default_encoding(args["l"]);
615 }
616
[362]617 text_t &arg_w = args["w"];
[1856]618
[362]619 inconvertclass defaultinconvert;
620 inconvertclass *inconvert = converters.get_inconverter (arg_w);
621 if (inconvert == NULL) inconvert = &defaultinconvert;
622
623 // see if the next page will have a different encoding
624 if (args.getarg("nw") != NULL) arg_w = args["nw"];
625
626 // convert arguments which aren't in unicode to unicode
627 args_tounicode (args, *inconvert);
628
629
630 // decide on the output conversion class (needed for checking the external
631 // cgi arguments)
632 rzwsoutconvertclass defaultoutconverter;
633 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
634 if (outconverter == NULL) outconverter = &defaultoutconverter;
635 outconverter->reset();
636
[155]637 // check the main cgi arguments
638 if (!check_mainargs (args, logout)) return false;
639
640 // check the arguments for the action
641 action *a = actions.getaction (args["a"]);
642 if (a != NULL) {
[3546]643 if (!a->check_cgiargs (argsinfo, args, &protocols, logout)) return false;
[155]644 } else {
645 // the action was not found!!
[1856]646 outconvertclass text_t2ascii;
[155]647 logout << text_t2ascii << "Error: the action \"" << args["a"]
648 << "\" could not be found.\n";
649 return false;
650 }
651
[362]652 // check external cgi arguments for each action
653 actionptrmap::iterator actionhere = actions.begin ();
654 actionptrmap::iterator actionend = actions.end ();
655 while (actionhere != actionend) {
656 assert ((*actionhere).second.a != NULL);
657 if ((*actionhere).second.a != NULL) {
658 if (!(*actionhere).second.a->check_external_cgiargs (argsinfo, args, *outconverter,
659 configinfo.saveconf, logout))
660 return false;
661 }
[7421]662 ++actionhere;
[362]663 }
[165]664
[362]665 // the action might have changed but we will assume that
666 // the cgiargs were checked properly when the change was made
[165]667
[155]668 return true;
669}
670
[463]671// returns true if cookie already existed, false
672// if it was generated
[864]673bool receptionist::get_cookie (text_t &cookie, text_tmap &fcgienv) {
[155]674
[864]675 text_t cookiestring = gsdl_getenv ("HTTP_COOKIE", fcgienv);
[18824]676 if (!cookiestring.empty())
677 {
678 text_t::const_iterator end = cookiestring.end();
679 text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
[7065]680
[18824]681 while (here+9 < end) {
[463]682
[18824]683 if (substr(here, here+8) == "GSDL_UID") {
684 cookie = substr (here+9, findchar (here+9, end, ';'));
685 return true;
686 }
687 ++here;
688 here = findchar (here, end, 'G');
[463]689 }
690 }
691
[526]692 cookie.clear();
[864]693 text_t host = gsdl_getenv("REMOTE_ADDR", fcgienv);
[463]694 time_t ttime = time(NULL);
[864]695 if (!host.empty()) {
[463]696 cookie += host;
697 cookie.push_back ('-');
698 }
699 cookie += text_t(ttime);
700
701 return false;
702}
703
704// as above but just tests if cookie exists
[864]705bool receptionist::get_cookie (text_tmap &fcgienv) {
[463]706
[864]707 text_t c = gsdl_getenv("HTTP_COOKIE", fcgienv);
708 if (!c.empty()) {
[463]709 text_t cookiestring = c;
710
711 text_t::const_iterator end = cookiestring.end();
[2937]712 text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
[864]713
714 while (here+9 < end) {
715 if (substr(here, here+8) == "GSDL_UID") return true;
[7443]716 ++here;
[7065]717 here = findchar (here, end, 'G');
[864]718 }
[463]719 }
720 return false;
721}
722
[864]723bool receptionist::log_cgi_args (cgiargsclass &args, ostream &logout, text_tmap &fcgienv) {
[463]724
[526]725 // see if we want to log the cgi arguments
726 if (!configinfo.logcgiargs) return true;
727
[864]728 text_t host = gsdl_getenv ("REMOTE_HOST", fcgienv);
[935]729 text_t script_name = gsdl_getenv ("SCRIPT_NAME", fcgienv);
[864]730 if (host.empty()) host = gsdl_getenv ("REMOTE_ADDR", fcgienv);
731 text_t browser = gsdl_getenv ("HTTP_USER_AGENT", fcgienv);
[463]732
733 cgiargsclass::const_iterator args_here = args.begin();
734 cgiargsclass::const_iterator args_end = args.end();
735
736 text_t argstr;
737 bool first = true;
738 while (args_here != args_end) {
739 if (!first) argstr += ", ";
740 argstr += (*args_here).first + "=" + (*args_here).second.value;
741 first = false;
[7443]742 ++args_here;
[463]743 }
744
[15589]745 text_t logfile = filename_cat (configinfo.dbhome, "etc", "usage.txt");
[595]746
[935]747 text_t logstr = script_name;
748 logstr += " " + host;
[595]749 logstr += " [";
[1779]750 if (configinfo.LogDateFormat == UTCTime) {
751 logstr += get_date (false);
752 } else if (configinfo.LogDateFormat == Absolute) {
753 time_t ttime = time(NULL);
754 logstr += ttime;
755 } else {
756 // LocalTime
757 logstr += get_date (true);
758 }
[595]759 logstr += "] (" + argstr + ") \"";
760 logstr += browser;
761 logstr += "\"\n";
762
763 return append_logstr (logfile, logstr, logout);
764}
765
766bool receptionist::append_logstr (const text_t &filename, const text_t &logstr,
767 ostream &logout) {
768
769 utf8outconvertclass text_t2utf8;
770 char *lfile = filename.getcstr();
[1170]771
[3154]772 int fd = open(lfile, O_WRONLY | O_APPEND);
[463]773
[3007]774 if (fd == -1) {
[463]775 logout << "Error: Couldn't open file " << lfile << "\n";
[7421]776 delete []lfile;
[463]777 return false;
[595]778 }
[463]779
[595]780 // lock_val is set to 0 if file is locked successfully
781 int lock_val = 1;
[606]782 GSDL_LOCK_FILE (fd);
[595]783 if (lock_val == 0) {
[3036]784 text_t tmp_log_str(logstr); // so we don't pass a const to setinput...
785 text_t2utf8.setinput(&tmp_log_str);
[3007]786 char *buffer=new char[logstr.size()];
787 size_t num_chars;
788 convertclass::status_t status;
789 text_t2utf8.convert(buffer, logstr.size(), num_chars, status);
790 // ignore status - assume it is "finished" as buffer is big enough
791 write(fd, buffer, num_chars);
[606]792 GSDL_UNLOCK_FILE (fd);
[7421]793 delete []buffer;
[606]794 } else {
[595]795 logout << "Error: Couldn't lock file " << lfile << "\n";
[3007]796 close(fd);
[7421]797 delete []lfile;
[606]798 return false;
799 }
[595]800
[3007]801 close(fd);
[463]802
[7421]803 delete []lfile;
[463]804 return true;
805}
806
[799]807text_t receptionist::expandmacros (const text_t &astring, cgiargsclass &args,
808 ostream &logout) {
809 text_t outstring;
810 outconvertclass text_t2ascii;
811
812 action *a = actions.getaction (args["a"]);
813 prepare_page (a, args, text_t2ascii, logout);
[7421]814 disp.expandstring (displayclass::defaultpackage, astring, outstring);
[799]815 return outstring;
816}
817
[155]818// produce_cgi_page will call get_cgihead_info and
[145]819// produce_content in the appropriate way to output a cgi header and
[155]820// the page content (if needed). If a page could not be created it
821// will return false
822bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout,
[864]823 ostream &logout, text_tmap &fcgienv) {
[155]824 outconvertclass text_t2ascii;
[108]825
[155]826 response_t response;
827 text_t response_data;
[108]828
[155]829 // produce cgi header
[864]830 get_cgihead_info (args, response, response_data, logout, fcgienv);
[155]831 if (response == location) {
[769]832 // location response (url may contain macros!!)
[799]833 response_data = expandmacros (response_data, args, logout);
[16819]834
[799]835 contentout << text_t2ascii << "Location: " << response_data << "\n\n";
[463]836 contentout << flush;
[16819]837
[155]838 return true;
839 } else if (response == content) {
840 // content response
[2386]841
[3663]842#ifdef GSDL_NOCACHE
843 contentout << "Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"; // date in the past
844 tm *tm_ptr = NULL;
845 time_t t = time(NULL);
846 tm_ptr = gmtime (&t);
847 if (tm_ptr != NULL) {
848 char *timestr = new char[128];
849 strftime (timestr, 128, "%a, %d %b %Y %H:%M:%S", tm_ptr);
850 contentout << "Last-Modified: " << timestr << " GMT\n"; // always modified
[7443]851 delete []timestr;
[3663]852 }
853 contentout << "Cache-Control: no-cache, must-revalidate\n"; // HTTP/1.1
854 contentout << "Pragma: no-cache\n"; // HTTP/1.0
855
856#else
857
[2386]858 // use the later of build.cfg and collect.cfg modification times
[2608]859 // as the Last-Modified: header, for caching values
[2386]860 struct stat file_info;
861 time_t latest=0;
862
863 text_t collectname="";
864 collectname=args["c"];
865 if (collectname != "") {
[16310]866
867 text_t collecthome;
868 if (!configinfo.collecthome.empty()) {
869 collecthome = configinfo.collecthome;
870 }
871 else {
872 collecthome=filename_cat(configinfo.gsdlhome,"collect");
873 }
874 text_t collectdir=filename_cat(collecthome,collectname);
875
[2386]876 text_t buildcfg=filename_cat(collectdir,"index");
877 buildcfg=filename_cat(buildcfg,"build.cfg");
878 char *buildcfg_ptr=buildcfg.getcstr();
879 text_t collectcfg=filename_cat(collectdir,"etc");
880 collectcfg=filename_cat(collectcfg,"collect.cfg");
881 char *collectcfg_ptr=collectcfg.getcstr();
882
883 if (stat(buildcfg_ptr, &file_info)) {
884 // we got an error. Currently don't handle error :(
885 // logout <<
886 } else {
887 latest=file_info.st_mtime;
888 }
889
890 if (stat(collectcfg_ptr, &file_info)) {
891 // error - unhandled for now
892 } else {
893 if (latest<file_info.st_mtime) latest=file_info.st_mtime;
894 }
[7421]895 delete []buildcfg_ptr;
896 delete []collectcfg_ptr;
[2386]897
898 if (latest>0) {
899 // print out modified time, "DDD, dd MMM YYYY hh:mm:ss" format
900 // c library takes care of mem for this string... (has \n at end!!!!)
[2660]901 // latest is currently local time, convert to UTC.
902 struct tm* utc_latest;
903 utc_latest=gmtime(&latest);
904 contentout << "Last-Modified: " << asctime(utc_latest);
[2386]905 }
[3007]906 } // end of collection != ""
[2386]907
[3663]908#endif
[2957]909
[155]910 contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
[7594]911 }
912 else if (response == undecided_location) {
913 // Wait until later to output the target location
914 // Used for the "I'm feeling lucky" functionality
915 }
916 else {
[155]917 // unknown response
918 logout << "Error: get_cgihead_info returned an unknown response type.\n";
919 return false;
920 }
921
922 // produce cgi page
923 if (!produce_content (args, contentout, logout)) return false;
924
925 // flush contentout
926 contentout << flush;
927 return true;
[108]928}
929
930
[145]931// get_cgihead_info determines the cgi header information for
932// a set of cgi arguments. If response contains location then
933// response_data contains the redirect address. If reponse
934// contains content then reponse_data contains the content-type.
935// Note that images can now be produced by the receptionist.
[7347]936// Note also, alternative for get_cgihead_info below which
937// stores the information in a text_tmap so it is more easily digested
938
[146]939void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
[864]940 text_t &response_data, ostream &logout,
941 text_tmap &fcgienv) {
[155]942 outconvertclass text_t2ascii;
943
944 // get the action
945 action *a = actions.getaction (args["a"]);
946 if (a != NULL) {
[760]947 a->get_cgihead_info (args, &protocols, response, response_data, logout);
[155]948
949 } else {
950 // the action was not found!!
951 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
952 << args["a"] << "\" could not be found.\n";
953 response = content;
954 response_data = "text/html";
955 }
[388]956
957 // add the encoding information
958 if (response == content) {
[1870]959 if (converters.find(args["w"]) != converters.end()) {
960 response_data += "; charset=" + args["w"];
[388]961 } else {
[1856]962 // default to latin 1
963 response_data += "; charset=ISO-8859-1";
[388]964 }
[463]965
966 // add cookie if required
[864]967 if (configinfo.usecookies && !get_cookie(fcgienv))
[463]968 response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
[18818]969 + "; expires=Fri, 25-Dec-2037 00:00:00 GMT";
[388]970 }
[108]971}
972
973
[7347]974// Alternative version of get_cgihead_info, stores fielded infomation
975// in text_tmap rather than concatenated string
976void receptionist::get_cgihead_info (cgiargsclass &args, text_tmap &headers,
977 ostream &logout, text_tmap &fcgienv) {
978
979 response_t response;
980 text_t response_data;
981
982 // get the action
983 action *a = actions.getaction (args["a"]);
984 if (a != NULL) {
985 a->get_cgihead_info (args, &protocols, response, response_data, logout);
986
987 } else {
988 // the action was not found!!
989 outconvertclass text_t2ascii;
990 logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
991 << args["a"] << "\" could not be found.\n";
992 response = content;
993 response_data = "text/html";
994 }
995
996 if (response == location) {
997 response_data = expandmacros(response_data, args, logout);
998 headers["Location"] = response_data;
999 return;
1000 }
1001
1002 // add the encoding information
1003 if (response == content) {
1004
1005 if (converters.find(args["w"]) != converters.end()) {
1006 headers["content-encoding"] = args["w"];
1007 response_data += "; charset=" + args["w"];
1008 } else {
1009 // default to utf-8
1010 headers["content-encoding"] = "utf-8";
1011 response_data += "; charset=utf-8";
1012 }
1013
1014 headers["content-type"] = response_data;
1015
1016 }
1017
1018}
1019
1020
1021
[145]1022// produce the page content
[155]1023bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
[145]1024 ostream &logout) {
[1170]1025
[155]1026 // decide on the output conversion class
[165]1027 text_t &arg_w = args["w"];
1028 rzwsoutconvertclass defaultoutconverter;
1029 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
1030 if (outconverter == NULL) outconverter = &defaultoutconverter;
1031 outconverter->reset();
[155]1032
[3668]1033 // needed for 16-bit unicode only - big endian marker 0xfeff (RFC 2781)
[9674]1034 if (arg_w=="utf-16be") {
[3668]1035 contentout << '\xfe' << '\xff' ;
1036 }
[799]1037
[760]1038 recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
1039 if (collectproto != NULL) {
[669]1040 // get browsers to process OID
1041 text_t OID = args["d"];
1042 if (OID.empty()) OID = args["cl"];
1043 if (!OID.empty()) {
1044 text_tset metadata;
1045 text_tarray OIDs;
1046 OIDs.push_back (OID);
1047 if (!is_top(OID)) OIDs.push_back (OID + ".pr");
1048 FilterResponse_t response;
1049 metadata.insert ("childtype");
[7420]1050 if (get_info (OIDs, args["c"], args["l"], metadata, false, collectproto, response, logout)) {
[712]1051 text_t classifytype;
[731]1052 if (!response.docInfo[0].metadata["childtype"].values[0].empty())
1053 classifytype = response.docInfo[0].metadata["childtype"].values[0];
1054 else if (!is_top (OID)) {
[6479]1055 // not sure why this is occasionally not set, but it will
1056 // cause a segfault... possibly if built with no_text? jrm21
1057 if (response.docInfo[1].metadata.find("childtype")
1058 == response.docInfo[1].metadata.end()) {
1059 cerr << "receptionist: no childtype element in metadata map!"
1060 << endl;
1061 } else {
1062 if (!response.docInfo[1].metadata["childtype"].values[0].empty())
1063 classifytype = response.docInfo[1].metadata["childtype"].values[0];
1064 }
[712]1065 }
[669]1066 browserclass *b = browsers.getbrowser (classifytype);
1067 b->processOID (args, collectproto, logout);
1068 }
1069 }
[760]1070
[712]1071 // translate "d" and "cl" arguments if required
[669]1072 translate_OIDs (args, collectproto, logout);
[649]1073 }
[760]1074
[155]1075 // produce the page using the desired action
1076 action *a = actions.getaction (args["a"]);
1077 if (a != NULL) {
[760]1078 if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
1079 if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
[155]1080 return false;
1081 } else {
1082 // the action was not found!!
[165]1083 outconvertclass text_t2ascii;
1084
[155]1085 logout << text_t2ascii << "Error receptionist::produce_content: the action \""
1086 << args["a"] << "\" could not be found.\n";
1087
[165]1088 contentout << (*outconverter)
1089 << "<html>\n"
1090 << "<head>\n"
1091 << "<title>Error</title>\n"
1092 << "</head>\n"
1093 << "<body>\n"
1094 << "<h2>Oops!</h2>\n"
1095 << "Undefined Page. The action \""
1096 << args["a"] << "\" could not be found.\n"
1097 << "</body>\n"
1098 << "</html>\n";
[155]1099 }
1100 return true;
[108]1101}
1102
1103
[145]1104// returns the compressed argument ("e") corresponding to the argument
1105// list. This can be used to save preferences between sessions.
[362]1106text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
1107 // decide on the output conversion class
1108 text_t &arg_w = args["w"];
1109 rzwsoutconvertclass defaultoutconverter;
1110 rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
1111 if (outconverter == NULL) outconverter = &defaultoutconverter;
1112 outconverter->reset();
1113
[248]1114 text_t compressed_args;
1115 if (compress_save_args (argsinfo, configinfo.saveconf, args,
[362]1116 compressed_args, *outconverter, logout))
[248]1117 return compressed_args;
[362]1118
[7421]1119 return g_EmptyText;
[108]1120}
[155]1121
1122
1123// will read in all the macro files. If one is not found an
1124// error message will be written to logout and the method will
1125// return false.
1126bool receptionist::read_macrofiles (ostream &logout) {
1127 outconvertclass text_t2ascii;
1128
1129 // redirect the error output to logout
[789]1130 ostream *savedlogout = disp.setlogout (&logout);
[155]1131
[6022]1132 // unload any macros that were previously loaded - this allows us to call
1133 // this function a second time to reload all the macro files (useful for
1134 // reading in changed macro files in server versions of greenstone)
1135 disp.unloaddefaultmacros();
1136
[155]1137 // load up the default macro files, the collection directory
1138 // is searched first for the file (if this is being used in
[799]1139 // collection specific mode) and then the main directory(s)
[165]1140 text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
[799]1141
[1089]1142 text_tset maindirs;
[165]1143 text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
[1089]1144 maindirs.insert (gsdlmacrodir);
[799]1145 colinfo_tmap::iterator colhere = configinfo.collectinfo.begin();
1146 colinfo_tmap::iterator colend = configinfo.collectinfo.end();
1147 while (colhere != colend) {
[1270]1148 if (!((*colhere).second.gsdl_gsdlhome).empty()) {
1149 gsdlmacrodir = filename_cat ((*colhere).second.gsdl_gsdlhome, "macros");
1150 maindirs.insert (gsdlmacrodir);
1151 }
[7421]1152 ++colhere;
[799]1153 }
1154
1155 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1156 text_tset::iterator arrend = configinfo.macrofiles.end();
[155]1157 text_t filename;
1158 while (arrhere != arrend) {
[799]1159 bool foundfile = false;
[155]1160
1161 // try in the collection directory if this is being
1162 // run in collection specific mode
[165]1163 if (!configinfo.collection.empty()) {
[155]1164 filename = filename_cat (colmacrodir, *arrhere);
[799]1165 if (file_exists (filename)) {
1166 disp.loaddefaultmacros(filename);
1167 foundfile = true;
1168 }
[155]1169 }
1170
1171 // if we haven't found the macro file yet try in
[799]1172 // the main macro directory(s)
1173 // if file is found in more than one main directory
1174 // we'll load all copies
1175 if (!foundfile) {
[1089]1176 text_tset::const_iterator dirhere = maindirs.begin();
1177 text_tset::const_iterator dirend = maindirs.end();
[799]1178 while (dirhere != dirend) {
1179 filename = filename_cat (*dirhere, *arrhere);
1180 if (file_exists (filename)) {
1181 disp.loaddefaultmacros(filename);
1182 foundfile = true;
1183 }
[7421]1184 ++dirhere;
[799]1185 }
[155]1186 }
1187
1188 // see if we found the file or not
[799]1189 if (!foundfile) {
[155]1190 logout << text_t2ascii
1191 << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
[165]1192 if (configinfo.collection.empty()) {
[799]1193 text_t dirs;
1194 joinchar (maindirs, ", ", dirs);
1195 logout << text_t2ascii
1196 << "It should be in either of the following directories ("
1197 << dirs << ").\n\n";
1198
[155]1199 } else {
1200 logout << text_t2ascii
1201 << "It should be in either " << colmacrodir << " or in "
1202 << gsdlmacrodir << ".\n\n";
1203 }
[10636]1204 // don't crap out if a macro file is missing
1205 //disp.setlogout (savedlogout);
1206 //return false;
[155]1207 }
[7421]1208 ++arrhere;
[155]1209 }
1210
1211 // success
[789]1212
1213 // reset logout to what it was
1214 disp.setlogout (savedlogout);
[155]1215 return true;
1216}
1217
1218
[7390]1219
1220
1221// Go through the list of macro files looking to see
1222// if any exist in the collectoin specific area. If they
1223// do then read them in and add them to the set of existing
1224// current macros
1225
1226void receptionist::read_collection_macrofiles (const text_t& collection, ostream &logout)
1227{
1228 outconvertclass text_t2ascii;
1229
[9615]1230 // disp.unloadcollectionmacros();
[7390]1231
1232 // redirect the error output to logout
1233 ostream *savedlogout = disp.setlogout (&logout);
1234
1235 text_t colmacrodir
[16310]1236 = filename_cat (configinfo.collecthome,collection, "macros");
[7390]1237
[7509]1238 if (directory_exists (colmacrodir)) {
[7390]1239
1240 text_tset::iterator arrhere = configinfo.macrofiles.begin();
1241 text_tset::iterator arrend = configinfo.macrofiles.end();
1242 text_t filename;
1243 while (arrhere != arrend) {
1244
1245 filename = filename_cat (colmacrodir, *arrhere);
1246 if (file_exists (filename)) {
1247 disp.loadcollectionmacros(filename);
1248 }
1249
[7421]1250 ++arrhere;
[7390]1251 }
1252 }
1253
1254 // reset logout to what it was
1255 disp.setlogout (savedlogout);
1256}
1257
1258
1259
1260
[155]1261// check_mainargs will check all the main arguments. If a major
1262// error is found it will return false and no cgi page should
1263// be created using the arguments.
[4974]1264
[799]1265bool receptionist::check_mainargs (cgiargsclass &args, ostream &logout) {
[5017]1266
1267 if(configinfo.site_auth)
1268 {
1269 args["uan"] = "1";
1270 args["ug"] = configinfo.site_group;
1271 }
1272
1273
1274 // if this receptionist is running in collection dependant mode
1275 // then it should always set the collection argument to the
1276 // collection
1277 if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
1278
1279 // if current collection uses ccscols make sure
1280 // "ccs" argument is set and make "cc" default to
1281 // all collections in "ccs"
1282 if (args["a"] != "config" && !args["c"].empty()) {
1283
1284 text_t &arg_c = args["c"];
1285 recptproto *collectproto = protocols.getrecptproto (arg_c, logout);
1286 if (collectproto == NULL) {
1287 // oops, this collection isn't valid
1288 outconvertclass text_t2ascii;
1289 logout << text_t2ascii << "ERROR: Invalid collection: " << arg_c << "\n";
[5512]1290 // args["c"].clear();
[5017]1291
1292 } else {
1293
1294 ColInfoResponse_t *cinfo = get_collectinfo_ptr (collectproto, arg_c, logout);
1295
[5024]1296 if(cinfo->authenticate == "collection")
[5017]1297 {
1298 args["uan"] = "1";
1299 args["ug"] = cinfo->auth_group;
1300 }
1301
1302
[1270]1303 if (cinfo != NULL) {
[5017]1304 if (!cinfo->ccsCols.empty()) {
1305 args["ccs"] = 1;
1306 if (args["cc"].empty()) {
1307 text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
1308 text_tarray::const_iterator col_end = cinfo->ccsCols.end();
1309 bool first = true;
1310 while (col_here != col_end) {
1311 // make sure it's a valid collection
1312 if (protocols.getrecptproto (*col_here, logout) != NULL) {
1313 if (!first) args["cc"].push_back (',');
1314 args["cc"] += *col_here;
1315 first = false;
1316 }
[7421]1317 ++col_here;
[1268]1318 }
[904]1319 }
[799]1320 }
[1270]1321 } else {
1322 logout << "ERROR (receptionist::check_mainargs): get_collectinfo_ptr returned NULL\n";
[799]1323 }
1324 }
1325 }
1326
[155]1327 // argument "v" can only be 0 or 1. Use the default value
1328 // if it is out of range
1329 int arg_v = args.getintarg ("v");
1330 if (arg_v != 0 && arg_v != 1) {
1331 cgiarginfo *vinfo = argsinfo.getarginfo ("v");
1332 if (vinfo != NULL) args["v"] = vinfo->argdefault;
1333 }
1334
1335 // argument "f" can only be 0 or 1. Use the default value
1336 // if it is out of range
1337 int arg_f = args.getintarg ("f");
1338 if (arg_f != 0 && arg_f != 1) {
1339 cgiarginfo *finfo = argsinfo.getarginfo ("f");
1340 if (finfo != NULL) args["f"] = finfo->argdefault;
1341 }
1342
1343 return true;
1344}
[173]1345
[712]1346// translate_OIDs translates the "d" and "cl" arguments to their correct values
[669]1347// if they use the tricky ".fc", ".lc" type syntax.
[649]1348void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto,
1349 ostream &logout) {
[438]1350
1351 FilterResponse_t response;
1352 FilterRequest_t request;
1353 comerror_t err;
1354 text_t &arg_d = args["d"];
1355 text_t &arg_cl = args["cl"];
1356 text_t &collection = args["c"];
1357
1358 // do a call to translate OIDs if required
1359 request.filterName = "NullFilter";
1360 request.filterResultOptions = FROID;
[649]1361 if (!arg_d.empty() && needs_translating (arg_d)) {
[468]1362 request.docSet.push_back (arg_d);
[438]1363 collectproto->filter (collection, request, response, err, logout);
1364 arg_d = response.docInfo[0].OID;
1365 request.clear();
1366 }
1367 // we'll also check here that the "cl" argument has a "classify" doctype
1368 // (in case ".fc" or ".lc" have screwed up)
1369 if (needs_translating (arg_cl)) {
[649]1370 request.fields.insert ("doctype");
[468]1371 request.docSet.push_back (arg_cl);
[438]1372 request.filterResultOptions = FRmetadata;
1373 collectproto->filter (collection, request, response, err, logout);
1374 // set to original value (without .xx stuff) if doctype isn't "classify"
[649]1375 if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
[438]1376 strip_suffix (arg_cl);
1377 else
1378 arg_cl = response.docInfo[0].OID;
1379 }
1380}
1381
[456]1382// prepare_page sets up page parameters, sets display macros
1383// and opens the page ready for output
[760]1384void receptionist::prepare_page (action *a, cgiargsclass &args,
1385 outconvertclass &outconvert,
1386 ostream &logout) {
[173]1387 // set up page parameters
1388 text_t pageparams;
1389 bool first = true;
[512]1390
[530]1391 text_tmap::iterator params_here = configinfo.pageparams.begin();
1392 text_tmap::iterator params_end = configinfo.pageparams.end();
1393 while (params_here != params_end) {
[8470]1394 // page params are those from main.cfg (eg pageparam v 0) plus
1395 // two defaults set in recptconf.clear() (c="" and l=en)
1396 // This used to check if the current value of the page param
1397 // == the default value, then don't add in it the list
1398 // but if l=en, and there is a macro with [l=en], then it doesn't
1399 // find it.
1400 // so now all page params will go into the list. I assume this will
1401 // mean more attempts to find each macro, but nothing worsee than
1402 // that. --kjdon
1403 //if (args[(*params_here).first] != (*params_here).second) {
[8486]1404 if (first)
1405 first = false;
1406 else
1407 pageparams += ",";
1408
[530]1409 pageparams += (*params_here).first;
1410 pageparams += "=";
1411 pageparams += args[(*params_here).first];
[8470]1412 // }
[530]1413
[7421]1414 ++params_here;
[530]1415 }
1416
[418]1417
[173]1418 // open the page
[530]1419 disp.openpage(pageparams, configinfo.macroprecedence);
[173]1420
[9615]1421 disp.unloadcollectionmacros();
1422
[9032]1423 text_t collection = args["c"];
1424 if (!collection.empty()) {
1425 read_collection_macrofiles(collection,logout);
1426 }
1427
[173]1428 // define external macros for each action
1429 actionptrmap::iterator actionhere = actions.begin ();
1430 actionptrmap::iterator actionend = actions.end ();
1431
1432 while (actionhere != actionend) {
1433 assert ((*actionhere).second.a != NULL);
[1860]1434 if ((*actionhere).second.a != NULL) {
[760]1435 (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
[1860]1436 }
[7421]1437 ++actionhere;
[173]1438 }
1439
[7390]1440
[173]1441 // define internal macros for the current action
[760]1442 a->define_internal_macros (disp, args, &protocols, logout);
[512]1443
1444 // define general macros. the defining of general macros is done here so that
1445 // the last possible version of the cgi arguments are used
[760]1446 define_general_macros (args, outconvert, logout);
[173]1447}
1448
[760]1449void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/,
1450 ostream &logout) {
[456]1451
1452 text_t &collection = args["c"];
1453
[7421]1454 disp.setmacro ("gsdlhome", displayclass::defaultpackage, dm_safe(configinfo.gsdlhome));
1455 disp.setmacro ("gwcgi", displayclass::defaultpackage, configinfo.gwcgi);
1456 disp.setmacro ("httpimg", displayclass::defaultpackage, configinfo.httpimg);
1457 disp.setmacro ("httpprefix", displayclass::defaultpackage, configinfo.httpprefix);
[1860]1458
[8486]1459
[864]1460 text_t compressedoptions = get_compressed_arg(args, logout);
[7421]1461 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
[864]1462 // need a decoded version of compressedoptions for use within forms
1463 // as browsers encode values from forms before sending to server
1464 // (e.g. %25 becomes %2525)
[13463]1465 decode_cgi_arg (compressedoptions);
1466 if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
1467 // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
1468 compressedoptions = to_uni(compressedoptions);
1469 }
1470 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
[173]1471
[1305]1472#if defined (__WIN32__)
[7421]1473 disp.setmacro ("win32", displayclass::defaultpackage, "1");
[1305]1474#endif
1475
[173]1476 // set _cgiargX_ macros for each cgi argument
1477 cgiargsclass::const_iterator argshere = args.begin();
1478 cgiargsclass::const_iterator argsend = args.end();
1479 while (argshere != argsend) {
[512]1480 if (((*argshere).first == "q") ||
1481 ((*argshere).first == "qa") ||
1482 ((*argshere).first == "qtt") ||
1483 ((*argshere).first == "qty") ||
1484 ((*argshere).first == "qp") ||
1485 ((*argshere).first == "qpl") ||
1486 ((*argshere).first == "qr") ||
1487 ((*argshere).first == "q2"))
[263]1488 // need to escape special characters from query string
[512]1489 disp.setmacro ("cgiarg" + (*argshere).first,
[7421]1490 displayclass::defaultpackage, html_safe((*argshere).second.value));
[5020]1491 else if ((*argshere).first == "hp") {
[7421]1492 disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, (*argshere).second.value);
[5020]1493 } else {
[7421]1494 disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, dm_safe((*argshere).second.value));
[5020]1495 }
[7421]1496 ++argshere;
[173]1497 }
[456]1498
1499 // set collection specific macros
[760]1500 if (!collection.empty()) {
1501 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1502 if (collectproto != NULL) {
1503 FilterResponse_t response;
1504 text_tset metadata;
[7420]1505 get_info ("collection", collection, args["l"], metadata, false,
[760]1506 collectproto, response, logout);
1507
1508 if (!response.docInfo[0].metadata.empty()) {
1509 MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
1510 MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
1511 while (here != end) {
1512 if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
1513 ((*here).first != "hasprevious")) {
[2773]1514 // check for args in form name:lang
[7421]1515 text_t name = g_EmptyText;
1516 text_t lang = g_EmptyText;
[2773]1517 bool colonfound=false;
1518 text_t::const_iterator a = (*here).first.begin();
1519 text_t::const_iterator b = (*here).first.end();
1520 while (a !=b) {
1521 if (*a==':') {
1522 colonfound=true;
1523 }
1524 else {
1525 if (colonfound)
1526 lang.push_back(*a);
1527 else name.push_back(*a);
1528 }
[7421]1529 ++a;
[2773]1530 }
1531 if (!lang.empty()) {
1532 if (args["l"]==lang) {
[9931]1533 disp.setcollectionmacro(displayclass::defaultpackage, name, "", (*here).second.values[0]);
[2773]1534 }
1535 }
1536 else { // the default one
[9931]1537 disp.setcollectionmacro(displayclass::defaultpackage, (*here).first, "", (*here).second.values[0]);
[2773]1538 }
[760]1539 }
[7421]1540 ++here;
[456]1541 }
1542 }
[1860]1543
1544 text_t iconcollection;
[7421]1545 disp.expandstring (displayclass::defaultpackage, "_iconcollection_", iconcollection);
[1860]1546 if (!iconcollection.empty())
1547 {
[1877]1548 ColInfoResponse_t cinfo;
1549 comerror_t err;
1550 collectproto->get_collectinfo (collection, cinfo, err, logout);
1551 if (iconcollection[0]=='/' && !cinfo.httpdomain.empty())
[1860]1552 {
1553 // local but with full path
1554 iconcollection = "http://" + cinfo.httpdomain + iconcollection;
[7421]1555 disp.setmacro("iconcollection", displayclass::defaultpackage, iconcollection);
[1860]1556 }
1557 }
[456]1558 }
1559 }
[9931]1560
1561 if (!collection.empty()) {
1562 ColInfoResponse_t cinfo;
1563 comerror_t err;
1564 recptproto *collectproto = protocols.getrecptproto (collection, logout);
1565 if (collectproto != NULL) {
1566 collectproto->get_collectinfo (collection, cinfo, err, logout);
1567 text_t httpcollection;
1568 if (!cinfo.httpdomain.empty()) httpcollection = "http://";
1569 httpcollection += cinfo.httpdomain + cinfo.httpprefix + "/collect/"
1570 + collection;
1571 disp.setmacro ("httpcollection", displayclass::defaultpackage,
1572 httpcollection);
1573 // as of gsdl 2.53, collect.cfg can specify macros
1574 if (cinfo.collection_macros.size() > 0) {
[11964]1575 collectionmeta_map::const_iterator this_macro=cinfo.collection_macros.begin();
1576 collectionmeta_map::const_iterator done_macro=cinfo.collection_macros.end();
[9931]1577 while (this_macro != done_macro) {
[10931]1578 text_t package = "Global";
1579 text_t macroname = this_macro->first;
1580 // if this macro name is AAA:bbb then extract the package name
1581 text_t::const_iterator thischar, donechar;
1582 thischar = macroname.begin();
1583 donechar = macroname.end();
1584 while (thischar < donechar) {
1585 if (*thischar == ':') {
1586 package = substr(macroname.begin(),thischar);
1587 macroname = substr(thischar+1,donechar);
1588 break;
1589 }
1590 ++thischar;
1591 }
[9931]1592
[11964]1593 text_tmap params_map = this_macro->second;
1594 text_tmap::const_iterator this_param = params_map.begin();
1595 text_tmap::const_iterator done_param = params_map.end();
1596 while (this_param != done_param) {
1597 disp.setcollectionmacro(package,
1598 macroname,
1599 this_param->first,
1600 this_param->second);
1601 ++this_param;
1602 }
1603
[9931]1604 ++this_macro;
1605 }
1606 } // col macros
1607 } // collectproto != NULL
1608 }
1609
[173]1610}
[1270]1611
1612// gets collection info from cache if found or
1613// calls collection server (and updates cache)
1614// returns NULL if there's an error
1615ColInfoResponse_t *receptionist::get_collectinfo_ptr (recptproto *collectproto,
1616 const text_t &collection,
1617 ostream &logout) {
1618
1619 // check the cache
1620 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1621 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1622 // found it
1623 return &((*it).second.info);
1624 }
1625
1626 // not cached, get info from collection server
1627 if (collectproto == NULL) {
1628 logout << "ERROR: receptionist::get_collectinfo_ptr passed null collectproto\n";
1629 return NULL;
1630 }
1631
1632 comerror_t err;
1633 if (it == configinfo.collectinfo.end()) {
1634 collectioninfo_t cinfo;
1635 collectproto->get_collectinfo (collection, cinfo.info, err, logout);
1636 if (err != noError) {
1637 outconvertclass text_t2ascii;
1638 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1639 << get_comerror_string (err) << "\"while getting collectinfo\n";
1640 return NULL;
1641 }
1642 cinfo.info_loaded = true;
1643 configinfo.collectinfo[collection] = cinfo;
1644 return &(configinfo.collectinfo[collection].info);
1645 } else {
1646 collectproto->get_collectinfo (collection, (*it).second.info, err, logout);
1647 if (err != noError) {
1648 outconvertclass text_t2ascii;
1649 logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1650 << get_comerror_string (err) << "\"while getting collectinfo\n";
1651 return NULL;
1652 }
1653 (*it).second.info_loaded = true;
1654 return &((*it).second.info);
1655 }
1656}
[1860]1657
[2218]1658// removes a collection from the cache so that the next
1659// call to get_collectinfo_ptr() for that collection will
1660// retrieve the collection info from the collection server
1661void receptionist::uncache_collection (const text_t &collection) {
1662
1663 colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1664 if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1665
1666 (*it).second.info_loaded = false;
1667
1668 }
1669}
1670
[1870]1671// Handles an "Encoding" line from a configuration file - note that the
1672// configinfo.encodings map is a bit of a hack (to be fixed when the
1673// configuration files are tidied up).
1674void receptionist::configure_encoding (const text_tarray &cfgline) {
[1860]1675
[1870]1676 text_t subkey, subvalue, shortname, longname, mapfile;
[1927]1677 int multibyte = 0;
[1870]1678 text_t::const_iterator cfglinesub_here;
1679 text_tarray::const_iterator cfgline_here = cfgline.begin();
1680 text_tarray::const_iterator cfgline_end = cfgline.end();
1681 while (cfgline_here != cfgline_end) {
[1927]1682 if (*cfgline_here == "multibyte") {
1683 multibyte = 1;
1684 } else {
1685 cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
1686 (*cfgline_here).end(), '=', subkey);
1687 if (subkey == "shortname") {
1688 shortname = substr (cfglinesub_here, (*cfgline_here).end());
1689 } else if (subkey == "longname") {
1690 longname = substr (cfglinesub_here, (*cfgline_here).end());
1691 } else if (subkey == "map") {
1692 mapfile = substr (cfglinesub_here, (*cfgline_here).end());
1693 }
[1870]1694 }
[7443]1695 ++cfgline_here;
[1870]1696 }
1697 if (!shortname.empty()) {
1698 if (longname.empty()) longname = shortname;
1699
1700 // add the converter
1701 if (shortname == "utf-8") {
1702 utf8inconvertclass *utf8inconvert = new utf8inconvertclass();
1703 utf8outconvertclass *utf8outconvert = new utf8outconvertclass();
1704 utf8outconvert->set_rzws(1);
1705 add_converter (shortname, utf8inconvert, utf8outconvert);
1706 configinfo.encodings[longname] = shortname;
1707
[9674]1708 } else if (shortname == "utf-16be") {
[3668]1709 // we use the default input converter as this shouldn't ever be used
1710 // for converting from unicode...
1711 inconvertclass *inconverter = new inconvertclass();
1712 utf16outconvertclass *outconverter = new utf16outconvertclass();
1713 add_converter (shortname, inconverter, outconverter);
1714 configinfo.encodings[longname] = shortname;
1715
[1870]1716 } else if (!mapfile.empty()) {
1717
1718 if (mapfile == "8859_1.ump") {
1719 // iso-8859-1 is a special case as it'll always be supported by the
1720 // standard converter class and therefore doesn't need to use its
1721 // mapping file
1722 inconvertclass *inconvert = new inconvertclass();
1723 rzwsoutconvertclass *outconvert = new rzwsoutconvertclass();
1724 outconvert->set_rzws(1);
1725 add_converter (shortname, inconvert, outconvert);
1726 configinfo.encodings[longname] = shortname;
1727
1728 } else {
1729 text_t to_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "to_uc", mapfile);
1730 text_t from_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "from_uc", mapfile);
1731 if (file_exists(to_uc_map) && file_exists(from_uc_map)) {
1732
1733 mapinconvertclass *mapinconvert = new mapinconvertclass();
1734 mapinconvert->setmapfile (to_uc_map, 0x003F);
[1927]1735 mapinconvert->set_multibyte (multibyte);
[1870]1736 mapoutconvertclass *mapoutconvert = new mapoutconvertclass();
1737 mapoutconvert->setmapfile (from_uc_map, 0x3F);
[1927]1738 mapoutconvert->set_multibyte (multibyte);
[1870]1739 mapoutconvert->set_rzws(1);
1740 add_converter (shortname, mapinconvert, mapoutconvert);
1741 configinfo.encodings[longname] = shortname;
1742 }
1743 }
1744 }
1745 }
1746}
Note: See TracBrowser for help on using the repository browser.