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

Last change on this file since 2379 was 2379, checked in by jrm21, 23 years ago

added #define line for iostream stuff to work under Mac OS X.

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