root/trunk/gsdl/src/recpt/receptionist.cpp @ 1860

Revision 1860, 43.3 KB (checked in by cs025, 19 years ago)

Included CORBA branch for first time

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1/**********************************************************************
2 *
3 * receptionist.cpp -- a web interface for the gsdl
4 * Copyright (C) 1999  The New Zealand Digital Library Project
5 *
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.
9 *
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 *
24 *********************************************************************/
25
26#include "receptionist.h"
27#include "fileutil.h"
28#include "cgiutils.h"
29#include "htmlutils.h"
30#include "gsdltools.h"
31#include "gsdltimes.h"
32#include "OIDtools.h"
33#include <assert.h>
34#include <time.h>
35#include <stdio.h>
36#if defined (GSDL_USE_IOS_H)
37#include <fstream.h>
38#else
39#include <fstream>
40#endif
41
42#if defined (__WIN32_)
43#include "wincgiutils.h"
44#endif
45
46void recptconf::clear () {
47  gsdlhome.clear();
48  gdbmhome.clear();
49  collectinfo.erase(collectinfo.begin(), collectinfo.end());
50  collection.clear();
51  collectdir.clear();
52  httpprefix.clear();
53  httpimg = "/images";
54  gwcgi.clear();
55  macrofiles.erase(macrofiles.begin(), macrofiles.end());
56  saveconf.clear();
57  usecookies = false;
58  logcgiargs = false;
59  LogDateFormat = LocalTime;
60
61  maintainer.clear();
62  MailServer.clear();
63  LogEvents = Disabled;
64  EmailEvents = Disabled;
65  EmailUserEvents = false;
66
67  languages.erase(languages.begin(), languages.end());
68  encodings.erase(encodings.begin(), encodings.end());
69
70  // these default page parameters can always be overriden
71  // in the configuration file
72  pageparams.erase(pageparams.begin(), pageparams.end());
73  pageparams["c"] = "";
74  pageparams["l"] = "en";
75
76#ifdef MACROPRECEDENCE
77  macroprecedence = MACROPRECEDENCE;
78#else
79  macroprecedence.clear();
80#endif
81}
82
83void collectioninfo_t::clear () {
84  gsdl_gsdlhome.clear();
85  gsdl_gdbmhome.clear();
86
87  info_loaded = false;
88  info.clear();
89}
90
91void languageinfo_t::clear () {
92  longname.clear();
93  defaultencoding.clear();
94}
95
96void encodinginfo_t::clear () {
97  longname.clear();
98  label.clear();
99}
100
101receptionist::receptionist () {
102  // create a list of cgi arguments
103  // this must be done before the configuration
104
105  cgiarginfo ainfo;
106
107  ainfo.shortname = "e";
108  ainfo.longname = "compressed arguments";
109  ainfo.multiplechar = true;
110  ainfo.defaultstatus = cgiarginfo::good;
111  ainfo.argdefault = "";
112  ainfo.savedarginfo = cgiarginfo::mustnot;
113  argsinfo.addarginfo (NULL, ainfo);
114
115  ainfo.shortname = "a";
116  ainfo.longname = "action";
117  ainfo.multiplechar = true;
118  ainfo.defaultstatus = cgiarginfo::none;
119  ainfo.argdefault = "";
120  ainfo.savedarginfo = cgiarginfo::must;
121  argsinfo.addarginfo (NULL, ainfo);
122
123  // w=western
124  ainfo.shortname = "w";
125  ainfo.longname = "encoding";
126  ainfo.multiplechar = true;
127  ainfo.defaultstatus = cgiarginfo::none;
128  ainfo.argdefault = "";
129  ainfo.savedarginfo = cgiarginfo::must;
130  argsinfo.addarginfo (NULL, ainfo);
131 
132  ainfo.shortname = "nw";
133  ainfo.longname = "new encoding";
134  ainfo.multiplechar = true;
135  ainfo.defaultstatus = cgiarginfo::none;
136  ainfo.argdefault = "";
137  ainfo.savedarginfo = cgiarginfo::mustnot;
138  argsinfo.addarginfo (NULL, ainfo);
139 
140  ainfo.shortname = "c";
141  ainfo.longname = "collection";
142  ainfo.multiplechar = true;
143  ainfo.defaultstatus = cgiarginfo::none;
144  ainfo.argdefault = "";
145  ainfo.savedarginfo = cgiarginfo::must;
146  argsinfo.addarginfo (NULL, ainfo);
147 
148  // the interface language name should use the ISO 639
149  // standard
150  ainfo.shortname = "l";
151  ainfo.longname = "interface language";
152  ainfo.multiplechar = true;
153  ainfo.defaultstatus = cgiarginfo::weak;
154  ainfo.argdefault = "en";
155  ainfo.savedarginfo = cgiarginfo::must;
156  argsinfo.addarginfo (NULL, ainfo);
157 
158  ainfo.shortname = "nl";
159  ainfo.longname = "new language";
160  ainfo.multiplechar = false;
161  ainfo.defaultstatus = cgiarginfo::none;
162  ainfo.argdefault = "0";
163  ainfo.savedarginfo = cgiarginfo::mustnot;
164  argsinfo.addarginfo (NULL, ainfo);
165 
166  // the GSDL_UID (cookie)
167  ainfo.shortname = "z";
168  ainfo.longname = "gsdl uid";
169  ainfo.multiplechar = true;
170  ainfo.defaultstatus = cgiarginfo::none;
171  ainfo.argdefault = "";
172  ainfo.savedarginfo = cgiarginfo::mustnot;
173  argsinfo.addarginfo (NULL, ainfo);
174}
175
176
177void receptionist::add_action (action *theaction) {
178  // make sure we have an action to add
179  if (theaction == NULL) return;
180
181  // add this action to the list of actions
182  actions.addaction(theaction);
183 
184  // add the cgi arguments from this action
185  argsinfo.addarginfo (NULL, theaction->getargsinfo());
186}
187
188
189void receptionist::add_browser (browserclass *thebrowser) {
190  // make sure we have a browser to add
191  if (thebrowser == NULL) return;
192
193  // add this browser to the list of browsers
194  browsers.addbrowser(thebrowser);
195}
196
197
198void receptionist::setdefaultbrowser (const text_t &browsername) {
199  browsers.setdefaultbrowser (browsername);
200}
201
202
203// configure should be called for each line in the
204// configuration files to configure the receptionist and everything
205// it contains. The configuration should take place after everything
206// has been added but before the initialisation.
207void receptionist::configure (const text_t &key, const text_tarray &cfgline) {
208  // configure the receptionist
209  if (cfgline.size() >= 1) {
210    cgiarginfo *info = NULL;
211    if (key == "gsdlhome") configinfo.gsdlhome = cfgline[0];
212    else if (key == "collection") {
213      configinfo.collection = cfgline[0];
214      // also need to set the default arg to this collection
215      if ((info = argsinfo.getarginfo("c")) != NULL) {
216    info->defaultstatus = cgiarginfo::good;
217    info->argdefault = cfgline[0];
218      }
219     
220    } else if (key == "collectdir") configinfo.collectdir = cfgline[0];
221    else if (key == "httpprefix") configinfo.httpprefix = cfgline[0];
222    else if (key == "httpimg") configinfo.httpimg = cfgline[0];
223    else if (key == "gwcgi") configinfo.gwcgi = cfgline[0];
224    else if (key == "macrofiles") {
225      // want to append to macrofiles (i.e. may be several config files
226      // contributing, maybe from several collections).
227      text_tarray::const_iterator here = cfgline.begin();
228      text_tarray::const_iterator end = cfgline.end();
229      while (here != end) {
230    configinfo.macrofiles.insert (*here);
231    here ++;
232      }
233    }
234    else if (key == "saveconf") configinfo.saveconf = cfgline[0];
235    else if (key == "usecookies") configinfo.usecookies = (cfgline[0] == "true");
236    else if (key == "logcgiargs") configinfo.logcgiargs = (cfgline[0] == "true");
237    else if (key == "maintainer") configinfo.maintainer = cfgline[0];
238    else if (key == "MailServer") configinfo.MailServer = cfgline[0];
239    else if (key == "LogDateFormat") {
240      if (cfgline[0] == "UTCTime") configinfo.LogDateFormat = UTCTime;
241      else if (cfgline[0] == "Absolute") configinfo.LogDateFormat = Absolute;
242    }
243    else if (key == "LogEvents") {
244      if (cfgline[0] == "CollectorEvents") configinfo.LogEvents = CollectorEvents;
245      else if (cfgline[0] == "AllEvents") configinfo.LogEvents = AllEvents;
246    }
247    else if (key == "EmailEvents") {
248      if (cfgline[0] == "CollectorEvents") configinfo.EmailEvents = CollectorEvents;
249      else if (cfgline[0] == "AllEvents") configinfo.EmailEvents = AllEvents;
250    }
251    else if (key == "EmailUserEvents") configinfo.EmailUserEvents = (cfgline[0] == "true");
252    else if (key == "pageparam") {
253      if (cfgline.size() >= 2) configinfo.pageparams[cfgline[0]] = cfgline[1];
254      else configinfo.pageparams[cfgline[0]] = "";
255    }
256    else if (key == "macroprecedence") configinfo.macroprecedence = cfgline[0];
257    else if (key == "collectinfo") {
258      if (cfgline.size() >= 3) {
259    collectioninfo_t cinfo;
260    cinfo.gsdl_gsdlhome = cfgline[1];
261    cinfo.gsdl_gdbmhome = cfgline[2];
262    configinfo.collectinfo[cfgline[0]] = cinfo;
263      }
264    }
265   
266    else if (key == "cgiarg") {
267      // get shortname
268      bool seen_defaultstatus = false;
269      text_t subkey, subvalue;
270      text_t shortname;
271      text_t::const_iterator cfglinesub_here;
272      text_tarray::const_iterator cfgline_here = cfgline.begin();
273      text_tarray::const_iterator cfgline_end = cfgline.end();
274      while (cfgline_here != cfgline_end) {
275    cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
276                    (*cfgline_here).end(), '=', subkey);
277    if (subkey == "shortname") {
278      shortname = substr (cfglinesub_here, (*cfgline_here).end());
279    }
280    cfgline_here++;
281      }
282
283      // if we found the shortname process the line again filling in values
284      if (!shortname.empty()) {
285    cgiarginfo &chinfo = argsinfo[shortname];
286    chinfo.shortname = shortname; // in case this is a new argument
287   
288    cfgline_here = cfgline.begin();
289    while (cfgline_here != cfgline_end) {
290      cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
291                      (*cfgline_here).end(), '=', subkey);
292      subvalue = substr (cfglinesub_here, (*cfgline_here).end());
293
294      if (subkey == "longname") chinfo.longname = subvalue;
295      else if (subkey == "multiplechar") chinfo.multiplechar = (subvalue == "true");
296      else if (subkey == "defaultstatus") {
297        seen_defaultstatus = true;
298        if (subvalue == "none") chinfo.defaultstatus = cgiarginfo::none;
299        else if (subvalue == "weak") chinfo.defaultstatus = cgiarginfo::weak;
300        else if (subvalue == "good") chinfo.defaultstatus = cgiarginfo::good;
301        else if (subvalue == "config") chinfo.defaultstatus = cgiarginfo::config;
302        else if (subvalue == "imperative") chinfo.defaultstatus = cgiarginfo::imperative;
303      }
304      else if (subkey == "argdefault") {
305        chinfo.argdefault = subvalue;
306        if (!seen_defaultstatus) chinfo.defaultstatus = cgiarginfo::config;
307      }
308      else if (subkey == "savedarginfo") {
309        if (subvalue == "mustnot") chinfo.savedarginfo = cgiarginfo::mustnot;
310        else if (subvalue == "can") chinfo.savedarginfo = cgiarginfo::can;
311        else if (subvalue == "must") chinfo.savedarginfo = cgiarginfo::must;
312      }
313     
314      cfgline_here++;
315    }
316      }
317
318    } else if (key == "Encoding") {
319      text_t subkey, subvalue;
320      text_t shortname, longname, label;
321      text_t::const_iterator cfglinesub_here;
322      text_tarray::const_iterator cfgline_here = cfgline.begin();
323      text_tarray::const_iterator cfgline_end = cfgline.end();
324      while (cfgline_here != cfgline_end) {
325    cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
326                    (*cfgline_here).end(), '=', subkey);
327    if (subkey == "shortname") {
328      shortname = substr (cfglinesub_here, (*cfgline_here).end());
329    } else if (subkey == "longname") {
330      longname = substr (cfglinesub_here, (*cfgline_here).end());
331    } else if (subkey == "label") {
332      label = substr (cfglinesub_here, (*cfgline_here).end());
333    }
334    cfgline_here++;
335      }
336      if (!shortname.empty() && !label.empty()) {
337    encodinginfo_t enc;
338    if (longname.empty()) enc.longname = shortname;
339    else enc.longname = longname;
340    enc.label = label;
341    configinfo.encodings[shortname] = enc;
342      }
343
344    } else if (key == "Language") {
345      text_t subkey, subvalue;
346      text_t shortname, longname, defaultencoding;
347      text_t::const_iterator cfglinesub_here;
348      text_tarray::const_iterator cfgline_here = cfgline.begin();
349      text_tarray::const_iterator cfgline_end = cfgline.end();
350      while (cfgline_here != cfgline_end) {
351    cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
352                    (*cfgline_here).end(), '=', subkey);
353    if (subkey == "shortname") {
354      shortname = substr (cfglinesub_here, (*cfgline_here).end());
355    } else if (subkey == "longname") {
356      longname = substr (cfglinesub_here, (*cfgline_here).end());
357    } else if (subkey == "default_encoding") {
358      defaultencoding = substr (cfglinesub_here, (*cfgline_here).end());
359    }
360    cfgline_here++;
361      }
362      if (!shortname.empty()) {
363    languageinfo_t lang;
364    if (longname.empty()) lang.longname = shortname;
365    else lang.longname = longname;
366    lang.defaultencoding = defaultencoding;
367    configinfo.languages[shortname] = lang;
368      }
369    }
370  }
371 
372  // configure the actions
373  actionptrmap::iterator actionhere = actions.begin ();
374  actionptrmap::iterator actionend = actions.end ();
375
376  while (actionhere != actionend) {
377    assert ((*actionhere).second.a != NULL);
378    if ((*actionhere).second.a != NULL)
379      (*actionhere).second.a->configure(key, cfgline);
380
381    actionhere++;
382  }
383
384  // configure the protocols
385  recptprotolistclass::iterator protohere = protocols.begin ();
386  recptprotolistclass::iterator protoend = protocols.end ();
387
388  while (protohere != protoend) {
389    assert ((*protohere).p != NULL);
390    if ((*protohere).p != NULL)
391      (*protohere).p->configure(key, cfgline);
392   
393    protohere++;
394  }
395
396  // configure the browsers
397  browserptrmap::iterator browserhere = browsers.begin ();
398  browserptrmap::iterator browserend = browsers.end ();
399
400  while (browserhere != browserend) {
401    assert ((*browserhere).second.b != NULL);
402    if ((*browserhere).second.b != NULL)
403      (*browserhere).second.b->configure(key, cfgline);
404   
405    browserhere++;
406  }
407}
408
409
410void receptionist::configure (const text_t &key, const text_t &value) {
411  text_tarray cfgline;
412  cfgline.push_back (value);
413  configure(key, cfgline);
414}
415
416
417// init should be called after all the actions, protocols, and
418// converters have been added to the receptionist and after everything
419// has been configured but before any pages are created.
420// It returns true on success and false on failure. If false is
421// returned getpage should not be called (without producing
422// meaningless output), instead an error page should be
423// produced by the calling code.
424bool receptionist::init (ostream &logout) {
425  // first configure collectdir
426  text_t thecollectdir = configinfo.gsdlhome;
427  if (!configinfo.collection.empty()) {
428    // collection specific mode
429    if (!configinfo.collectdir.empty()) {
430      // has already been configured
431      thecollectdir = configinfo.collectdir;
432    } else {
433      // decide where collectdir is by searching for collect.cfg
434      // look in $GSDLHOME/collect/collection-name/etc/collect.cfg and
435      // then $GSDLHOME/etc/collect.cfg
436      thecollectdir = filename_cat (configinfo.gsdlhome, "collect");
437      thecollectdir = filename_cat (thecollectdir, configinfo.collection);
438      text_t filename = filename_cat (thecollectdir, "etc");
439      filename = filename_cat (filename, "collect.cfg");
440
441      if (!file_exists(filename)) thecollectdir = configinfo.gsdlhome;
442    }
443  }
444  configure("collectdir", thecollectdir);
445
446  // read in the macro files
447  if (!read_macrofiles (logout)) return false;
448
449  // there must be at least one action defined
450  if (actions.empty()) {
451    logout << "Error: no actions have been added to the receptionist\n";
452    return false;
453  }
454
455  // there must be at least one browser defined
456  if (browsers.empty()) {
457    logout << "Error: no browsers have been added to the receptionist\n";
458    return false;
459  }
460
461  // create a saveconf string if there isn't one already
462  if (configinfo.saveconf.empty())
463    configinfo.saveconf = create_save_conf_str (argsinfo, logout);
464
465  // check the saveconf string
466  if (!check_save_conf_str (configinfo.saveconf, argsinfo, logout))
467    return false;
468
469  // set a random seed
470  srand (time(NULL));
471
472  // make the output converters remove all the zero-width spaces
473  convertinfoclass::iterator converthere = converters.begin ();
474  convertinfoclass::iterator convertend = converters.end ();
475  while (converthere != convertend) {
476    assert ((*converthere).second.outconverter != NULL);
477    if ((*converthere).second.outconverter != NULL) {
478      (*converthere).second.outconverter->set_rzws(1);
479    }
480    converthere++;
481  }
482
483  // if maintainer email address is something dodgy (for now I'll define
484  // dodgy as being anything that doesn't contain '@') disable EmailEvents
485  // and EmailUserEvents (we don't strictly need to disable EmailUserEvents
486  // in this case but we will as it seems likely that MailServer will also
487  // be screwed up if maintainer is).
488  text_t::const_iterator maintainer_end = configinfo.maintainer.end ();
489  text_t::const_iterator maintainer_here = findchar (configinfo.maintainer.begin(),
490                             maintainer_end, '@');
491  if (maintainer_here == maintainer_end) {
492    configinfo.EmailEvents = Disabled;
493    configinfo.EmailUserEvents = Disabled;
494  } else {
495    // if MailServer isn't set it should default to mail.maintainer-domain
496    if (configinfo.MailServer.empty()) {
497      configinfo.MailServer = "mail." + substr (maintainer_here+1, maintainer_end);
498    }
499  }
500
501  // init the actions
502  actionptrmap::iterator actionhere = actions.begin ();
503  actionptrmap::iterator actionend = actions.end ();
504  while (actionhere != actionend) {
505    if (((*actionhere).second.a == NULL) ||
506    !(*actionhere).second.a->init(logout)) return false;
507    actionhere++;
508  }
509
510  // init the protocols
511  recptprotolistclass::iterator protohere = protocols.begin ();
512  recptprotolistclass::iterator protoend = protocols.end ();
513  while (protohere != protoend) {
514    if (((*protohere).p == NULL) ||
515    !(*protohere).p->init(logout)) return false;
516    protohere++;
517  }
518
519  // init the browsers
520  browserptrmap::iterator browserhere = browsers.begin ();
521  browserptrmap::iterator browserend = browsers.end ();
522  while (browserhere != browserend) {
523    if (((*browserhere).second.b == NULL) ||
524    !(*browserhere).second.b->init(logout)) return false;
525    browserhere++;
526  }
527
528  return true;
529}
530
531// get the default encoding for the given language - if it fails for any
532// reason return ""
533text_t receptionist::get_default_encoding (const text_t &language) {
534 
535  // make sure language is valid
536  if (configinfo.languages.find(language) == configinfo.languages.end()) return "";
537
538  text_t default_encoding = configinfo.languages[language].defaultencoding;
539
540  // make sure the encoding is valid
541  if (configinfo.encodings.find(default_encoding) == configinfo.encodings.end()) return "";
542
543  return default_encoding;
544}
545
546// parse_cgi_args parses cgi arguments into an argument class.
547// This function should be called for each page request. It returns false
548// if there was a major problem with the cgi arguments.
549bool receptionist::parse_cgi_args (const text_t &argstr, cgiargsclass &args,
550                   ostream &logout, text_tmap &fcgienv) {
551
552  // get an initial list of cgi arguments
553  args.clear();
554  split_cgi_args (argsinfo, argstr, args);
555
556  // expand the compressed argument (if there was one)
557  if (!expand_save_args (argsinfo, configinfo.saveconf, args, logout)) return false;
558
559  // add the defaults
560  add_default_args (argsinfo, args, logout);
561
562  // get the cookie
563  if (configinfo.usecookies) get_cookie(args["z"], fcgienv);
564 
565  // if we're changing languages, set the encoding to the default for the new language
566  if (args["nl"] == "1") {
567    args["nw"] = get_default_encoding(args["l"]);
568  }
569
570  // get the input encoding
571  // if encoding isn't set, set it to the default for the current language
572  if ((args.getarg("w") == NULL) || args["w"].empty()) {
573    args["w"] = get_default_encoding(args["l"]);
574  }
575
576  text_t &arg_w = args["w"];
577
578  inconvertclass defaultinconvert;
579  inconvertclass *inconvert = converters.get_inconverter (arg_w);
580  if (inconvert == NULL) inconvert = &defaultinconvert;
581
582  // see if the next page will have a different encoding
583  if (args.getarg("nw") != NULL) arg_w = args["nw"];
584
585  // convert arguments which aren't in unicode to unicode
586  args_tounicode (args, *inconvert);
587
588
589  // decide on the output conversion class (needed for checking the external
590  // cgi arguments)
591  rzwsoutconvertclass defaultoutconverter;
592  rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
593  if (outconverter == NULL) outconverter = &defaultoutconverter;
594  outconverter->reset();
595
596  // check the main cgi arguments
597  if (!check_mainargs (args, logout)) return false;
598
599  // check the arguments for the action
600  action *a = actions.getaction (args["a"]);
601  if (a != NULL) {
602    if (!a->check_cgiargs (argsinfo, args, logout)) return false;
603  } else {
604    // the action was not found!!
605    outconvertclass text_t2ascii;
606    logout << text_t2ascii << "Error: the action \"" << args["a"]
607       << "\" could not be found.\n";
608    return false;
609  }
610
611  // check external cgi arguments for each action
612  actionptrmap::iterator actionhere = actions.begin ();
613  actionptrmap::iterator actionend = actions.end ();
614  while (actionhere != actionend) {
615    assert ((*actionhere).second.a != NULL);
616    if ((*actionhere).second.a != NULL) {
617      if (!(*actionhere).second.a->check_external_cgiargs (argsinfo, args, *outconverter,
618                               configinfo.saveconf, logout))
619    return false;
620    }
621    actionhere++;
622  }
623
624  // the action might have changed but we will assume that
625  // the cgiargs were checked properly when the change was made
626
627  return true;
628}
629
630// returns true if cookie already existed, false
631// if it was generated
632bool receptionist::get_cookie (text_t &cookie, text_tmap &fcgienv) {
633
634  text_t cookiestring = gsdl_getenv ("HTTP_COOKIE", fcgienv);
635   
636  text_t::const_iterator end = cookiestring.end(); 
637  text_t::const_iterator here = findchar (cookiestring.begin(), end, 'G');
638
639  while (here+9 < end) {
640   
641    if (substr(here, here+8) == "GSDL_UID") {
642      cookie = substr (here+9, findchar (here+9, end, ';'));
643      return true;
644    }
645    here = findchar (cookiestring.begin(), end, 'G');
646  }
647
648  cookie.clear();
649  text_t host = gsdl_getenv("REMOTE_ADDR", fcgienv);
650  time_t ttime = time(NULL);
651  if (!host.empty()) {
652    cookie += host;
653    cookie.push_back ('-');
654  }
655  cookie += text_t(ttime);
656 
657  return false;
658}
659
660// as above but just tests if cookie exists
661bool receptionist::get_cookie (text_tmap &fcgienv) {
662
663  text_t c = gsdl_getenv("HTTP_COOKIE", fcgienv);
664  if (!c.empty()) {
665    text_t cookiestring = c;
666   
667    text_t::const_iterator end = cookiestring.end(); 
668    text_t::const_iterator here = findchar (cookiestring.begin(), end, 'G');
669
670    while (here+9 < end) {
671      if (substr(here, here+8) == "GSDL_UID") return true;
672      here = findchar (cookiestring.begin(), end, 'G');
673    }
674  }
675  return false;
676}
677
678bool receptionist::log_cgi_args (cgiargsclass &args, ostream &logout, text_tmap &fcgienv) {
679
680  // see if we want to log the cgi arguments
681  if (!configinfo.logcgiargs) return true;
682 
683  text_t host = gsdl_getenv ("REMOTE_HOST", fcgienv);
684  text_t script_name = gsdl_getenv ("SCRIPT_NAME", fcgienv);
685  if (host.empty()) host = gsdl_getenv ("REMOTE_ADDR", fcgienv);
686  text_t browser = gsdl_getenv ("HTTP_USER_AGENT", fcgienv);
687
688  cgiargsclass::const_iterator args_here = args.begin();
689  cgiargsclass::const_iterator args_end = args.end();
690
691  text_t argstr;
692  bool first = true;
693  while (args_here != args_end) {
694    if (!first) argstr += ", ";
695    argstr += (*args_here).first + "=" + (*args_here).second.value;
696    first = false;
697    args_here ++;
698  }
699
700  text_t logfile = filename_cat (configinfo.gsdlhome, "etc");
701  logfile = filename_cat (logfile, "usage.txt");
702
703  text_t logstr = script_name;
704  logstr += " " + host;
705  logstr += " [";
706  if (configinfo.LogDateFormat == UTCTime) {
707    logstr += get_date (false);
708  } else if (configinfo.LogDateFormat == Absolute) {
709    time_t ttime = time(NULL);
710    logstr += ttime;
711  } else {
712    // LocalTime
713    logstr += get_date (true);
714  }
715  logstr += "] (" + argstr + ") \"";
716  logstr += browser;
717  logstr += "\"\n";
718
719  return append_logstr (logfile, logstr, logout);
720}
721
722bool receptionist::append_logstr (const text_t &filename, const text_t &logstr,
723                  ostream &logout) {
724
725  utf8outconvertclass text_t2utf8;
726  char *lfile = filename.getcstr();
727
728  ofstream log (lfile, ios::app);
729 
730  if (!log) {
731    logout << "Error: Couldn't open file " << lfile << "\n";
732    delete lfile;
733    return false;
734  }
735
736  int fd = GSDL_GET_FILEDESC(log);
737
738  // lock_val is set to 0 if file is locked successfully
739  int lock_val = 1;
740  GSDL_LOCK_FILE (fd);
741  if (lock_val == 0) {
742    log << text_t2utf8 << logstr;
743    GSDL_UNLOCK_FILE (fd);
744  } else {
745    logout << "Error: Couldn't lock file " << lfile << "\n";
746    log.close();
747    delete lfile;
748    return false;
749  }
750
751  log.close();
752       
753  delete lfile;
754  return true;
755}
756
757text_t receptionist::expandmacros (const text_t &astring, cgiargsclass &args,
758                   ostream &logout) {
759  text_t outstring;
760  outconvertclass text_t2ascii;
761
762  action *a = actions.getaction (args["a"]);
763  prepare_page (a, args, text_t2ascii, logout);
764  disp.expandstring ("Global", astring, outstring);
765  return outstring;
766}
767
768// produce_cgi_page will call get_cgihead_info and
769// produce_content in the appropriate way to output a cgi header and
770// the page content (if needed). If a page could not be created it
771// will return false
772bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout,
773                     ostream &logout, text_tmap &fcgienv) {
774  outconvertclass text_t2ascii;
775
776  response_t response;
777  text_t response_data;
778
779  // produce cgi header
780  get_cgihead_info (args, response, response_data, logout, fcgienv);
781  if (response == location) {
782    // location response (url may contain macros!!)
783    response_data = expandmacros (response_data, args, logout);
784    contentout << text_t2ascii << "Location: " << response_data << "\n\n";
785    contentout << flush;
786    return true;
787  } else if (response == content) {
788    // content response
789    contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
790  } else {
791    // unknown response
792    logout << "Error: get_cgihead_info returned an unknown response type.\n";
793    return false;
794  }
795
796  // produce cgi page
797  if (!produce_content (args, contentout, logout)) return false;
798
799  // flush contentout
800  contentout << flush;
801  return true;
802}
803
804
805// get_cgihead_info determines the cgi header information for
806// a set of cgi arguments. If response contains location then
807// response_data contains the redirect address. If reponse
808// contains content then reponse_data contains the content-type.
809// Note that images can now be produced by the receptionist.
810void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
811                     text_t &response_data, ostream &logout,
812                     text_tmap &fcgienv) {
813  outconvertclass text_t2ascii;
814
815  // get the action
816  action *a = actions.getaction (args["a"]);
817  if (a != NULL) {
818    a->get_cgihead_info (args, &protocols, response, response_data, logout);
819
820  } else {
821    // the action was not found!!
822    logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \""
823       << args["a"] << "\" could not be found.\n";
824    response = content;
825    response_data = "text/html";
826  }
827
828  // add the encoding information
829  if (response == content) {
830    if (configinfo.encodings.find(args["w"]) != configinfo.encodings.end()) {
831      response_data += "; charset=" + configinfo.encodings[args["w"]].label;
832    } else {
833      // default to latin 1
834      response_data += "; charset=ISO-8859-1";
835    }
836
837    // add cookie if required
838    if (configinfo.usecookies && !get_cookie(fcgienv))
839      response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
840    + "; expires=25-Dec-37 00:00:00 GMT";
841  }
842}
843
844
845// produce the page content
846bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
847                    ostream &logout) {
848
849  // decide on the output conversion class
850  text_t &arg_w = args["w"];
851  rzwsoutconvertclass defaultoutconverter;
852  rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
853  if (outconverter == NULL) outconverter = &defaultoutconverter;
854  outconverter->reset();
855
856
857  recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
858  if (collectproto != NULL) {
859    // get browsers to process OID
860    text_t OID = args["d"];
861    if (OID.empty()) OID = args["cl"];
862    if (!OID.empty()) {
863      text_tset metadata;
864      text_tarray OIDs;
865      OIDs.push_back (OID);
866      if (!is_top(OID)) OIDs.push_back (OID + ".pr");
867      FilterResponse_t response;
868      metadata.insert ("childtype");
869      if (get_info (OIDs, args["c"], metadata, false, collectproto, response, logout)) {
870    text_t classifytype;
871    if (!response.docInfo[0].metadata["childtype"].values[0].empty())
872      classifytype = response.docInfo[0].metadata["childtype"].values[0];
873    else if (!is_top (OID)) {
874      if (!response.docInfo[1].metadata["childtype"].values[0].empty())
875        classifytype = response.docInfo[1].metadata["childtype"].values[0];
876    }
877    browserclass *b = browsers.getbrowser (classifytype);
878    b->processOID (args, collectproto, logout);
879      }
880    }
881 
882    // translate "d" and "cl" arguments if required
883    translate_OIDs (args, collectproto, logout);
884  }
885 
886  // produce the page using the desired action
887  action *a = actions.getaction (args["a"]);
888  if (a != NULL) {
889    if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
890    if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
891      return false;
892  } else {
893    // the action was not found!!
894    outconvertclass text_t2ascii;
895
896    logout << text_t2ascii << "Error receptionist::produce_content: the action \""
897       << args["a"] << "\" could not be found.\n";
898   
899    contentout << (*outconverter)
900           << "<html>\n"
901           << "<head>\n"
902           << "<title>Error</title>\n"
903           << "</head>\n"
904           << "<body>\n"
905           << "<h2>Oops!</h2>\n"
906           << "Undefined Page. The action \""
907           << args["a"] << "\" could not be found.\n"
908           << "</body>\n"
909           << "</html>\n";
910  }
911  return true;
912}
913
914
915// returns the compressed argument ("e") corresponding to the argument
916// list. This can be used to save preferences between sessions.
917text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
918  // decide on the output conversion class
919  text_t &arg_w = args["w"];
920  rzwsoutconvertclass defaultoutconverter;
921  rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
922  if (outconverter == NULL) outconverter = &defaultoutconverter;
923  outconverter->reset();
924
925  text_t compressed_args;
926  if (compress_save_args (argsinfo, configinfo.saveconf, args,
927              compressed_args, *outconverter, logout))
928    return compressed_args;
929
930  return "";
931}
932
933
934// will read in all the macro files. If one is not found an
935// error message will be written to logout and the method will
936// return false.
937bool receptionist::read_macrofiles (ostream &logout) {
938  outconvertclass text_t2ascii;
939
940  // redirect the error output to logout
941  ostream *savedlogout = disp.setlogout (&logout);
942
943  // load up the default macro files, the collection directory
944  // is searched first for the file (if this is being used in
945  // collection specific mode) and then the main directory(s)
946  text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
947
948  text_tset maindirs;
949  text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
950  maindirs.insert (gsdlmacrodir);
951  colinfo_tmap::iterator colhere = configinfo.collectinfo.begin();
952  colinfo_tmap::iterator colend = configinfo.collectinfo.end();
953  while (colhere != colend) {
954    if (!((*colhere).second.gsdl_gsdlhome).empty()) {
955      gsdlmacrodir = filename_cat ((*colhere).second.gsdl_gsdlhome, "macros");
956      maindirs.insert (gsdlmacrodir);
957    }
958    colhere ++;
959  }
960
961  text_tset::iterator arrhere = configinfo.macrofiles.begin();
962  text_tset::iterator arrend = configinfo.macrofiles.end();
963  text_t filename;
964  while (arrhere != arrend) {
965    bool foundfile = false;
966
967    // try in the collection directory if this is being
968    // run in collection specific mode
969    if (!configinfo.collection.empty()) {
970      filename = filename_cat (colmacrodir, *arrhere);
971      if (file_exists (filename)) {
972    disp.loaddefaultmacros(filename);
973    foundfile = true;
974      }
975    }
976
977    // if we haven't found the macro file yet try in
978    // the main macro directory(s)
979    // if file is found in more than one main directory
980    // we'll load all copies
981    if (!foundfile) {
982      text_tset::const_iterator dirhere = maindirs.begin();
983      text_tset::const_iterator dirend = maindirs.end();
984      while (dirhere != dirend) {
985    filename = filename_cat (*dirhere, *arrhere);
986    if (file_exists (filename)) {
987      disp.loaddefaultmacros(filename);
988      foundfile = true;
989    }
990    dirhere ++;
991      }
992    }
993
994    // see if we found the file or not
995    if (!foundfile) {
996      logout << text_t2ascii
997         << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
998      if (configinfo.collection.empty()) {
999    text_t dirs;
1000    joinchar (maindirs, ", ", dirs);
1001    logout << text_t2ascii
1002           << "It should be in either of the following directories ("
1003           << dirs << ").\n\n";
1004
1005      } else {
1006    logout << text_t2ascii
1007           << "It should be in either " << colmacrodir << " or in "
1008           << gsdlmacrodir << ".\n\n";
1009      }
1010      // reset logout to what it was
1011      disp.setlogout (savedlogout);
1012      return false;
1013    }
1014    arrhere++;
1015  }
1016
1017  // success
1018
1019  // reset logout to what it was
1020  disp.setlogout (savedlogout);
1021  return true;
1022}
1023
1024
1025// check_mainargs will check all the main arguments. If a major
1026// error is found it will return false and no cgi page should
1027// be created using the arguments.
1028bool receptionist::check_mainargs (cgiargsclass &args, ostream &logout) {
1029  // if this receptionist is running in collection dependant mode
1030  // then it should always set the collection argument to the
1031  // collection
1032  if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
1033
1034  // if current collection uses ccscols make sure
1035  // "ccs" argument is set and make "cc" default to
1036  // all collections in "ccs"
1037  if (!args["c"].empty()) {
1038
1039    text_t &arg_c = args["c"];
1040    recptproto *collectproto = protocols.getrecptproto (arg_c, logout);
1041    if (collectproto == NULL) {
1042      // oops, this collection isn't valid
1043      outconvertclass text_t2ascii;
1044      logout << text_t2ascii << "ERROR: Invalid collection: " << arg_c << "\n";
1045      args["c"].clear();
1046
1047    } else {
1048
1049      ColInfoResponse_t *cinfo = get_collectinfo_ptr (collectproto, arg_c, logout);
1050
1051      if (cinfo != NULL) {
1052    if (!cinfo->ccsCols.empty()) {
1053      args["ccs"] = 1;
1054      if (args["cc"].empty()) {
1055        text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
1056        text_tarray::const_iterator col_end = cinfo->ccsCols.end();
1057        bool first = true;
1058        while (col_here != col_end) {
1059          // make sure it's a valid collection
1060          if (protocols.getrecptproto (*col_here, logout) != NULL) {
1061        if (!first) args["cc"].push_back (',');
1062        args["cc"] += *col_here;
1063        first = false;
1064          }
1065          col_here ++;
1066        }
1067      }
1068    }
1069      } else {
1070    logout << "ERROR (receptionist::check_mainargs): get_collectinfo_ptr returned NULL\n";
1071      }
1072    }
1073  }
1074
1075  // argument "v" can only be 0 or 1. Use the default value
1076  // if it is out of range
1077  int arg_v = args.getintarg ("v");
1078  if (arg_v != 0 && arg_v != 1) {
1079    cgiarginfo *vinfo = argsinfo.getarginfo ("v");
1080    if (vinfo != NULL) args["v"] = vinfo->argdefault;
1081  }
1082
1083  // argument "f" can only be 0 or 1. Use the default value
1084  // if it is out of range
1085  int arg_f = args.getintarg ("f");
1086  if (arg_f != 0 && arg_f != 1) {
1087    cgiarginfo *finfo = argsinfo.getarginfo ("f");
1088    if (finfo != NULL) args["f"] = finfo->argdefault;
1089  }
1090
1091  return true;
1092}
1093
1094// translate_OIDs translates the "d" and "cl" arguments to their correct values
1095// if they use the tricky ".fc", ".lc" type syntax.
1096void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto,
1097                   ostream &logout) {
1098
1099  FilterResponse_t response;
1100  FilterRequest_t request;
1101  comerror_t err;
1102  text_t &arg_d = args["d"];
1103  text_t &arg_cl = args["cl"];
1104  text_t &collection = args["c"];
1105 
1106  // do a call to translate OIDs if required
1107  request.filterName = "NullFilter";
1108  request.filterResultOptions = FROID;
1109  if (!arg_d.empty() && needs_translating (arg_d)) {
1110    request.docSet.push_back (arg_d);
1111    collectproto->filter (collection, request, response, err, logout);
1112    arg_d = response.docInfo[0].OID;
1113    request.clear();
1114  }
1115  // we'll also check here that the "cl" argument has a "classify" doctype
1116  // (in case ".fc" or ".lc" have screwed up)
1117  if (needs_translating (arg_cl)) {
1118    request.fields.insert ("doctype");
1119    request.docSet.push_back (arg_cl);
1120    request.filterResultOptions = FRmetadata;
1121    collectproto->filter (collection, request, response, err, logout);
1122    // set to original value (without .xx stuff) if doctype isn't "classify"
1123    if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
1124      strip_suffix (arg_cl);
1125    else
1126      arg_cl = response.docInfo[0].OID;
1127  }
1128}
1129
1130// prepare_page sets up page parameters, sets display macros
1131// and opens the page ready for output
1132void receptionist::prepare_page (action *a, cgiargsclass &args,
1133                 outconvertclass &outconvert,
1134                 ostream &logout) {
1135  // set up page parameters
1136  text_t pageparams;
1137  bool first = true;
1138
1139  cout << "Action is " << a->get_action_name() << endl;
1140
1141  text_tmap::iterator params_here = configinfo.pageparams.begin();
1142  text_tmap::iterator params_end = configinfo.pageparams.end();
1143  while (params_here != params_end) {
1144    if (args[(*params_here).first] != (*params_here).second) {
1145      if (!first) pageparams += ",";
1146      first = false;
1147      pageparams += (*params_here).first;
1148      pageparams += "=";
1149      pageparams += args[(*params_here).first];
1150    }
1151   
1152    params_here++;
1153  }
1154 
1155
1156  // open the page
1157  disp.openpage(pageparams, configinfo.macroprecedence);
1158
1159  // define external macros for each action
1160  actionptrmap::iterator actionhere = actions.begin ();
1161  actionptrmap::iterator actionend = actions.end ();
1162
1163  while (actionhere != actionend) {
1164    assert ((*actionhere).second.a != NULL);
1165    if ((*actionhere).second.a != NULL) {
1166      (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
1167    }
1168    actionhere++;
1169  }
1170
1171  // define internal macros for the current action
1172  a->define_internal_macros (disp, args, &protocols, logout);
1173 
1174  // define general macros. the defining of general macros is done here so that
1175  // the last possible version of the cgi arguments are used
1176  define_general_macros (args, outconvert, logout);
1177}
1178
1179void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/,
1180                      ostream &logout) {
1181
1182  text_t &collection = args["c"];
1183
1184  disp.setmacro ("gsdlhome", "Global", dm_safe(configinfo.gsdlhome));
1185  disp.setmacro ("gwcgi", "Global", configinfo.gwcgi);
1186  disp.setmacro ("httpimg", "Global", configinfo.httpimg);
1187  disp.setmacro ("httpprefix", "Global", configinfo.httpprefix);
1188
1189  if (!collection.empty()) {
1190    // DB // ****
1191    ColInfoResponse_t cinfo;
1192    comerror_t err;
1193    recptproto *collectproto = protocols.getrecptproto (collection, logout);
1194    if (collectproto != NULL) {
1195      collectproto->get_collectinfo (collection, cinfo, err, logout);
1196      text_t httpcollection
1197    = "http://" + cinfo.httpdomain + cinfo.httpprefix
1198        +"/collect/" + collection;
1199     
1200      disp.setmacro ("httpcollection", "Global", httpcollection);
1201    }
1202  }
1203
1204  text_t compressedoptions = get_compressed_arg(args, logout);
1205  disp.setmacro ("compressedoptions", "Global", dm_safe(compressedoptions));
1206  // need a decoded version of compressedoptions for use within forms
1207  // as browsers encode values from forms before sending to server
1208  // (e.g. %25 becomes %2525)
1209  decode_cgi_arg (compressedoptions);
1210  disp.setmacro ("decodedcompressedoptions", "Global", dm_safe(compressedoptions));
1211
1212#if defined (__WIN32__)
1213  disp.setmacro ("win32", "Global", "1");
1214#endif
1215
1216  // set macron macros if encoding is utf8
1217  if (args["w"] == "u") {
1218    disp.setmacro ("Amn", "Global", "&#256;");
1219    disp.setmacro ("amn", "Global", "&#257;");
1220    disp.setmacro ("Emn", "Global", "&#274;");
1221    disp.setmacro ("emn", "Global", "&#275;");
1222    disp.setmacro ("Imn", "Global", "&#298;");
1223    disp.setmacro ("imn", "Global", "&#299;");
1224    disp.setmacro ("Omn", "Global", "&#332;");
1225    disp.setmacro ("omn", "Global", "&#333;");
1226    disp.setmacro ("Umn", "Global", "&#362;");
1227    disp.setmacro ("umn", "Global", "&#363;");
1228  }
1229
1230  // set _cgiargX_ macros for each cgi argument
1231  cgiargsclass::const_iterator argshere = args.begin();
1232  cgiargsclass::const_iterator argsend = args.end();
1233  while (argshere != argsend) {
1234    if (((*argshere).first == "q") ||
1235    ((*argshere).first == "qa") ||
1236    ((*argshere).first == "qtt") ||
1237    ((*argshere).first == "qty") ||
1238    ((*argshere).first == "qp") ||
1239    ((*argshere).first == "qpl") ||
1240    ((*argshere).first == "qr") ||
1241    ((*argshere).first == "q2"))
1242      // need to escape special characters from query string
1243      disp.setmacro ("cgiarg" + (*argshere).first,
1244             "Global", html_safe((*argshere).second.value));
1245    else
1246      disp.setmacro ("cgiarg" + (*argshere).first, "Global", dm_safe((*argshere).second.value));
1247    argshere ++;
1248  }
1249
1250  // display text right to left if language is arabic (and if browser can support it)
1251  if (args["l"] == "ar")
1252    disp.setmacro ("htmlextra", "Global", " dir=rtl");
1253
1254  // set collection specific macros
1255  if (!collection.empty()) {
1256    recptproto *collectproto = protocols.getrecptproto (collection, logout);
1257    if (collectproto != NULL) {
1258      FilterResponse_t response;
1259      text_tset metadata;
1260      get_info ("collection", collection, metadata, false,
1261        collectproto, response, logout);
1262     
1263      if (!response.docInfo[0].metadata.empty()) {
1264    MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
1265    MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
1266    while (here != end) {
1267      if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
1268          ((*here).first != "hasprevious")) {
1269        disp.setmacro ((*here).first, "Global", (*here).second.values[0]);
1270      }
1271      here ++;
1272    }
1273      }
1274
1275      text_t iconcollection;
1276      disp.expandstring ("Global", "_iconcollection_", iconcollection);
1277      if (!iconcollection.empty())
1278    {
1279      if (iconcollection[0]=='/')
1280        {
1281          // local but with full path
1282          ColInfoResponse_t cinfo;
1283          comerror_t err;
1284          collectproto->get_collectinfo (collection, cinfo, err, logout);
1285          iconcollection = "http://" + cinfo.httpdomain + iconcollection;
1286          disp.setmacro("iconcollection","Global",iconcollection);
1287        }
1288    }
1289    }
1290  }
1291}
1292
1293// gets collection info from cache if found or
1294// calls collection server (and updates cache)
1295// returns NULL if there's an error
1296ColInfoResponse_t *receptionist::get_collectinfo_ptr (recptproto *collectproto,
1297                              const text_t &collection,
1298                              ostream &logout) {
1299 
1300  // check the cache
1301  colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
1302  if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
1303    // found it
1304    return &((*it).second.info);
1305  }
1306
1307  // not cached, get info from collection server
1308  if (collectproto == NULL) {
1309    logout << "ERROR: receptionist::get_collectinfo_ptr passed null collectproto\n";
1310    return NULL;
1311  }
1312   
1313  comerror_t err;
1314  if (it == configinfo.collectinfo.end()) {
1315    collectioninfo_t cinfo;
1316    collectproto->get_collectinfo (collection, cinfo.info, err, logout);
1317    if (err != noError) {
1318      outconvertclass text_t2ascii;
1319      logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1320         << get_comerror_string (err) << "\"while getting collectinfo\n";
1321      return NULL;
1322    }
1323    cinfo.info_loaded = true;
1324    configinfo.collectinfo[collection] = cinfo;
1325    return &(configinfo.collectinfo[collection].info);
1326  } else {
1327    collectproto->get_collectinfo (collection, (*it).second.info, err, logout);
1328    if (err != noError) {
1329      outconvertclass text_t2ascii;
1330      logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
1331         << get_comerror_string (err) << "\"while getting collectinfo\n";
1332      return NULL;
1333    }
1334    (*it).second.info_loaded = true;
1335    return &((*it).second.info);
1336  }
1337}
1338
1339
Note: See TracBrowser for help on using the browser.