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

Revision 1927, 44.4 KB (checked in by sjboddie, 19 years ago)

Fixed a bug in the C++ encoding support - 8 bit encodings like windows-1251
were being treated as 16 bit encodings in some places

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