source: main/trunk/greenstone2/runtime-src/src/recpt/cgiwrapper.cpp@ 28973

Last change on this file since 28973 was 28973, checked in by ak19, 10 years ago

All the changes required to get the GTI installed on a local greenstone using the latest gs2 src code from svn, since this has the security updates. 1. ApplyXSLT in build-src needed a lot of code additions since runtime-src's gtiaction.cpp needs to send the xml file from stdin using a pipe command. ApplyXSLT used to read stdin from a piped cmd differently, expecting DocStart and DocEnd embedding tags to mark the start and stop of each stream. This is still used by code in BasePlugout, so ApplyXSLT has been modified to take a minus-c parameter when requested to read from stdin without special embedding tag markers, such as when gtiaction calls it. ApplyXSLT uses an internal StreamGobbler class to read the stdin since it takes a while for xml generated by the gti.pl (which is piped in) to come in to ApplyXSLT. 2. The inner StreamGobbler class needed to be added into the ApplyXSLT jar file, so the Makefile.in has been updated. 3. In runtime-src, added in missing header files and updated the code that generated the spreadsheets on GTI, since it was firstly hardcoded to use paths on /home/nzdl, and the code that generated the spreadsheets when running ApplyXSLT/xalan.jar no longer worked as it had been coded.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 31.9 KB
Line 
1/**********************************************************************
2 *
3 * cgiwrapper.cpp -- output pages using the cgi protocol
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 <stdio.h>
27#include <cstring>
28#ifdef __WIN32__
29#include <fcntl.h>
30#endif
31
32#include "gsdlconf.h"
33#include "cgiwrapper.h"
34#include "gsdlsitecfg.h"
35#include "maincfg.h"
36#include "fileutil.h"
37#include "cgiutils.h"
38#include <stdlib.h>
39#include <assert.h>
40
41#if defined(GSDL_USE_OBJECTSPACE)
42# include <ospace/std/iostream>
43# include <ospace/std/fstream>
44#elif defined(GSDL_USE_IOS_H)
45# include <iostream.h>
46# include <fstream.h>
47#else
48# include <iostream>
49# include <fstream>
50#endif
51
52#ifdef USE_FASTCGI
53#include "fcgiapp.h"
54#endif
55
56#include "authenaction.h"
57#include "browseaction.h"
58#include "collectoraction.h"
59#include "depositoraction.h"
60#include "documentaction.h"
61#include "dynamicclassifieraction.h"
62#include "extlinkaction.h"
63#include "pageaction.h"
64#ifdef ENABLE_MGPP
65#include "phindaction.h"
66#endif
67#include "pingaction.h"
68#include "queryaction.h"
69
70#if defined(USE_SQLITE)
71#include "sqlqueryaction.h"
72#endif
73
74#if defined(GSDL_USE_GTI_ACTION)
75#include "gtiaction.h"
76#endif
77
78#if defined(USE_RSS)
79#include "rssaction.h"
80#endif
81
82#include "tipaction.h"
83#include "statusaction.h"
84#include "usersaction.h"
85#include "configaction.h"
86
87#include "vlistbrowserclass.h"
88#include "hlistbrowserclass.h"
89#include "datelistbrowserclass.h"
90#include "invbrowserclass.h"
91#include "pagedbrowserclass.h"
92#include "htmlbrowserclass.h"
93#include "phindbrowserclass.h"
94
95
96#ifdef USE_FASTCGI
97// used to output the text from receptionist
98class fcgistreambuf : public streambuf {
99public:
100 fcgistreambuf ();
101 int sync ();
102 int overflow (int ch);
103 int underflow () {return EOF;}
104
105 void fcgisbreset() {fcgx_stream = NULL; other_ostream = NULL;};
106 void set_fcgx_stream(FCGX_Stream *newone) {fcgx_stream=newone;};
107 void set_other_ostream(ostream *newone) {other_ostream=newone;};
108
109private:
110 FCGX_Stream *fcgx_stream;
111 ostream *other_ostream;
112};
113
114fcgistreambuf::fcgistreambuf() {
115 fcgisbreset();
116 if (base() == ebuf()) allocate();
117 setp (base(), ebuf());
118};
119
120int fcgistreambuf::sync () {
121 if ((fcgx_stream != NULL) &&
122 (FCGX_PutStr (pbase(), out_waiting(), fcgx_stream) < 0)) {
123 fcgx_stream = NULL;
124 }
125
126 if (other_ostream != NULL) {
127 char *thepbase=pbase();
128 for (int i=0;i<out_waiting();++i) (*other_ostream).put(thepbase[i]);
129 }
130
131 setp (pbase(), epptr());
132
133 return 0;
134}
135
136int fcgistreambuf::overflow (int ch) {
137 if (sync () == EOF) return EOF;
138 if (ch != EOF) sputc (ch);
139 return 0;
140}
141
142#endif
143
144static void format_error_string (text_t &errorpage, const text_t &errortext, bool debug) {
145
146 errorpage.clear();
147
148 if (debug) {
149 errorpage += "\n";
150 errorpage += "ERROR: " + errortext;
151 errorpage += "\n";
152
153 } else {
154
155 errorpage += "Content-type: text/html\n\n";
156
157 errorpage += "<html>\n";
158 errorpage += "<head>\n";
159 errorpage += "<title>Error</title>\n";
160 errorpage += "</head>\n";
161 errorpage += "<body>\n";
162 errorpage += "<h2>Oops!</h2>\n";
163 errorpage += errortext;
164 errorpage += "</body>\n";
165 errorpage += "</html>\n";
166 }
167}
168
169static void page_errorcollect (const text_t &gsdlhome, text_t &errorpage, bool debug) {
170
171 text_t collecthome = filename_cat (gsdlhome, "collect");
172
173 text_t errortext = "No valid collections were found: Check that your collect directory\n";
174 errortext += "(" + collecthome + ") is readable and contains at least one valid collection.\n";
175 errortext += "Note that modelcol is NOT a valid collection.\n";
176 errortext += "If the path to your collect directory is wrong edit the 'gsdlhome' field\n";
177 errortext += "in your gsdlsite.cfg configuration file.\n";
178
179 format_error_string (errorpage, errortext, debug);
180}
181
182static void page_errorsitecfg (text_t &errorpage, bool debug, int mode) {
183
184 text_t errortext;
185
186 if (mode == 0) {
187 errortext += "The gsdlsite.cfg configuration file could not be found. This\n";
188 errortext += "file should contain configuration information relating to this\n";
189 errortext += "site's setup.\n";
190
191 } else if (mode == 1) {
192 errortext += "The gsdlsite.cfg configuration file does not contain a valid\n";
193 errortext += "gsdlhome entry.\n";
194 }
195
196 if (debug) {
197 errortext += "gsdlsite.cfg should reside in the directory from which the\n";
198 errortext += "library executable was run.\n";
199 } else {
200 errortext += "gsdlsite.cfg should reside in the same directory as the library\n";
201 errortext += "executable file.\n";
202 }
203
204 format_error_string (errorpage, errortext, debug);
205}
206
207
208static void page_errormaincfg (const text_t &gsdlhome, const text_t &collection,
209 bool debug, text_t &errorpage) {
210
211 text_t errortext;
212
213 if (collection.empty()) {
214 text_t main_cfg_file = filename_cat (gsdlhome, "etc", "main.cfg");
215 errortext += "The main.cfg configuration file could not be found. This file\n";
216 errortext += "should contain configuration information relating to the\n";
217 errortext += "setup of the interface. As this receptionist is not being run\n";
218 errortext += "in collection specific mode the file should reside at\n";
219 errortext += main_cfg_file + ".\n";
220 } else {
221 text_t collect_cfg_file = filename_cat (gsdlhome, "collect", collection, "etc", "collect.cfg");
222 text_t main_collect_cfg_file = filename_cat (gsdlhome, "etc", "collect.cfg");
223 text_t main_cfg_file = filename_cat (gsdlhome, "etc", "main.cfg");
224 errortext += "Either the collect.cfg or main.cfg configuration file could\n";
225 errortext += "not be found. This file should contain configuration information\n";
226 errortext += "relating to the setup of the interface. As this receptionist is\n";
227 errortext += "being run in collection specific mode the file should reside\n";
228 errortext += "at either " + collect_cfg_file + ",\n";
229 errortext += main_collect_cfg_file + " or " + main_cfg_file + ".\n";
230 }
231
232 format_error_string (errorpage, errortext, debug);
233}
234
235
236static void page_errorinit (const text_t &gsdlhome, bool debug, text_t &errorpage) {
237
238 text_t errortext = "An error occurred during the initialisation of the Greenstone Digital\n";
239 errortext += "Library software. It is likely that the software has not been setup\n";
240 errortext += "correctly.\n";
241
242 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
243 // This is all commented out because I think it's a really bad idea
244 // The error.txt file may be very large, causing out of memory problems and even crashing the machine in extreme
245 // cases where multiple processes are causing this type of error (e.g. automated processes that try to "hack"
246 // the Greenstone site by supplying values such as site URLs for the CGI arguments -- this has happened)
247 // Also, the error.txt may contain information that shouldn't be exposed (such as usage or query information)
248 // Maybe this should be configurable through a main.cfg configuration setting, but I don't think it's worth it
249 // The only people who should need the contents of this file should have access to it through the file system
250 // I think you can also view the contents of this file through the statusaction if you have a suitable login
251// char *efile = error_file.getcstr();
252// ifstream errin (efile);
253// delete []efile;
254// if (errin) {
255// errortext += "The error log, " + error_file + ", contains the\n";
256// errortext += "following information:\n\n";
257// if (!debug) errortext += "<pre>\n";
258
259// char c;
260// errin.get(c);
261// while (!errin.eof ()) {
262// errortext.push_back(c);
263// errin.get(c);
264// }
265
266// if (!debug) errortext += "</pre>\n";
267
268// errin.close();
269
270// } else {
271 errortext += "Please consult " + error_file + " for more information.\n";
272// }
273
274 format_error_string (errorpage, errortext, debug);
275}
276
277static void page_errorparseargs (const text_t &gsdlhome, bool debug, text_t &errorpage) {
278
279 text_t errortext = "An error occurred during the parsing of the cgi arguments.\n";
280
281 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
282 // This is all commented out because I think it's a really bad idea
283 // The error.txt file may be very large, causing out of memory problems and even crashing the machine in extreme
284 // cases where multiple processes are causing this type of error (e.g. automated processes that try to "hack"
285 // the Greenstone site by supplying values such as site URLs for the CGI arguments -- this has happened)
286 // Also, the error.txt may contain information that shouldn't be exposed (such as usage or query information)
287 // Maybe this should be configurable through a main.cfg configuration setting, but I don't think it's worth it
288 // The only people who should need the contents of this file should have access to it through the file system
289 // I think you can also view the contents of this file through the statusaction if you have a suitable login
290// char *efile = error_file.getcstr();
291// ifstream errin (efile);
292// delete []efile;
293// if (errin) {
294// errortext += "The error log, " + error_file + ", contains the\n";
295// errortext += "following information:\n\n";
296// if (!debug) errortext += "<pre>\n";
297
298// char c;
299// errin.get(c);
300// while (!errin.eof ()) {
301// errortext.push_back(c);
302// errin.get(c);
303// }
304// if (!debug) errortext += "</pre>\n";
305// errin.close();
306
307// } else {
308 errortext += "Please consult " + error_file + " for more information.\n";
309// }
310
311 format_error_string (errorpage, errortext, debug);
312}
313
314static void page_errorcgipage (const text_t &gsdlhome, bool debug, text_t &errorpage) {
315
316 text_t errortext = "An error occurred during the construction of the cgi page.\n";
317
318 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
319 // This is all commented out because I think it's a really bad idea
320 // The error.txt file may be very large, causing out of memory problems and even crashing the machine in extreme
321 // cases where multiple processes are causing this type of error (e.g. automated processes that try to "hack"
322 // the Greenstone site by supplying values such as site URLs for the CGI arguments -- this has happened)
323 // Also, the error.txt may contain information that shouldn't be exposed (such as usage or query information)
324 // Maybe this should be configurable through a main.cfg configuration setting, but I don't think it's worth it
325 // The only people who should need the contents of this file should have access to it through the file system
326 // I think you can also view the contents of this file through the statusaction if you have a suitable login
327// char *efile = error_file.getcstr();
328// ifstream errin (efile);
329// delete []efile;
330// if (errin) {
331// errortext += "The error log, " + error_file + ", contains the\n";
332// errortext += "following information:\n\n";
333// if (!debug) errortext += "<pre>\n";
334
335// char c;
336// errin.get(c);
337// while (!errin.eof ()) {
338// errortext.push_back(c);
339// errin.get(c);
340// }
341// if (!debug) errortext += "</pre>\n";
342// errin.close();
343
344// } else {
345 errortext += "Please consult " + error_file + " for more information.\n";
346// }
347
348 format_error_string (errorpage, errortext, debug);
349}
350
351static void print_debug_info (receptionist &recpt) {
352
353 outconvertclass text_t2ascii;
354 const recptconf &configinfo = recpt.get_configinfo ();
355 text_t etc_dir = filename_cat (configinfo.gsdlhome, "etc");
356
357 cout << "\n";
358 cout << text_t2ascii
359 << "------------------------------------------------------------\n"
360 << "Configuration and initialization completed successfully.\n"
361 << " Note that more debug information may be available in the\n"
362 << " initialization and error log error.txt in " << etc_dir << ".\n"
363 << "------------------------------------------------------------\n\n";
364
365 bool colspec = false;
366 if (configinfo.collection.empty()) {
367 cout << "Receptionist is running in \"general\" (i.e. not \"collection "
368 << "specific\") mode.\n";
369 } else {
370 cout << text_t2ascii
371 << "Receptionist is running in \"collection specific\" mode.\n"
372 << " collection=" << configinfo.collection << "\n"
373 << " collection directory=" << configinfo.collectdir << "\n";
374 colspec = true;
375 }
376
377 cout << text_t2ascii << " gsdlhome=" << configinfo.gsdlhome << "\n";
378 if (!configinfo.collecthome.empty())
379 cout << text_t2ascii << " collecthome=" << configinfo.collecthome << "\n";
380 if (!configinfo.dbhome.empty())
381 cout << text_t2ascii << " dbhome=" << configinfo.dbhome << "\n";
382 cout << text_t2ascii << " httpprefix=" << configinfo.httpprefix << "\n";
383 cout << text_t2ascii << " httpweb=" << configinfo.httpweb << "\n";
384 cout << text_t2ascii << " gwcgi=" << configinfo.gwcgi << "\n\n"
385 << " Note that unless gwcgi has been set from a configuration\n"
386 << " file it is dependent on environment variables set by your\n"
387 << " webserver. Therefore it may not have the same value when run\n"
388 << " from the command line as it would be when run from your\n"
389 << " web server.\n";
390 if (configinfo.usecookies)
391 cout << "cookies are enabled\n";
392 else
393 cout << "cookies are disabled\n";
394 if (configinfo.logcgiargs)
395 cout << "logging is enabled\n";
396 else
397 cout << "logging is disabled\n";
398 cout << "------------------------------------------------------------\n\n";
399
400 text_tset::const_iterator this_mfile = configinfo.macrofiles.begin();
401 text_tset::const_iterator end_mfile = configinfo.macrofiles.end();
402 cout << "Macro Files:\n"
403 << "------------\n";
404 text_t mfile;
405 bool found;
406 while (this_mfile != end_mfile) {
407 cout << text_t2ascii << *this_mfile;
408 int spaces = (22 - (*this_mfile).size());
409 if (spaces < 2) spaces = 2;
410 text_t outspaces;
411 for (int i = 0; i < spaces; ++i) outspaces.push_back (' ');
412 cout << text_t2ascii << outspaces;
413
414 found = false;
415 if (colspec) {
416 // collection specific - try collectdir/macros first
417 mfile = filename_cat (configinfo.collectdir, "macros", *this_mfile);
418 if (file_exists (mfile)) {
419 cout << text_t2ascii << "found (" << mfile << ")\n";
420 found = true;
421 }
422 }
423
424 if (!found) {
425 // try main macro directory
426 mfile = filename_cat (configinfo.gsdlhome, "macros", *this_mfile);
427 if (file_exists (mfile)) {
428 cout << text_t2ascii << "found (" << mfile << ")\n";
429 found = true;
430 }
431 }
432
433 if (!found)
434 cout << text_t2ascii << "NOT FOUND\n";
435
436 ++this_mfile;
437 }
438
439 cout << "------------------------------------------------------------\n\n"
440 << "Collections:\n"
441 << "------------\n"
442 << " Note that collections will only appear as \"running\" if\n"
443 << " their build.cfg files exist, are readable, contain a valid\n"
444 << " builddate field (i.e. > 0), and are in the collection's\n"
445 << " index directory (i.e. NOT the building directory)\n\n";
446
447 recptprotolistclass *protos = recpt.get_recptprotolist_ptr();
448 recptprotolistclass::iterator rprotolist_here = protos->begin();
449 recptprotolistclass::iterator rprotolist_end = protos->end();
450
451 bool is_z3950 = false;
452 bool found_valid_col = false;
453
454
455 while (rprotolist_here != rprotolist_end) {
456 comerror_t err;
457 if ((*rprotolist_here).p == NULL) continue;
458 else if (is_z3950==false &&
459 (*rprotolist_here).p->get_protocol_name(err) == "z3950proto") {
460 cout << "\nZ39.50 Servers: (always public)\n"
461 << "---------------\n";
462 is_z3950=true;
463 }
464
465 text_tarray collist;
466 (*rprotolist_here).p->get_collection_list (collist, err, cerr);
467 if (err == noError) {
468 text_tarray::iterator collist_here = collist.begin();
469 text_tarray::iterator collist_end = collist.end();
470
471 while (collist_here != collist_end) {
472
473 cout << text_t2ascii << *collist_here;
474
475 int spaces = (22 - (*collist_here).size());
476 if (spaces < 2) spaces = 2;
477 text_t outspaces;
478 for (int i = 0; i < spaces; ++i) outspaces.push_back (' ');
479 cout << text_t2ascii << outspaces;
480
481 ColInfoResponse_t *cinfo = recpt.get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, cerr);
482 if (cinfo != NULL) {
483 if (cinfo->isPublic) cout << "public ";
484 else cout << "private";
485
486 if (cinfo->buildDate > 0) {
487 cout << " running ";
488 found_valid_col = true;
489 } else {
490 cout << " not running";
491 }
492 }
493
494 cout << "\n";
495
496 ++collist_here;
497 }
498 }
499 is_z3950=false;
500 ++rprotolist_here;
501 } // end of while loop
502
503 if (!found_valid_col) {
504 cout << "WARNING: No \"running\" collections were found. You need to\n";
505 cout << " build one of the above collections\n";
506 }
507
508 cout << "\n------------------------------------------------------------\n";
509 cout << "------------------------------------------------------------\n\n";
510 cout << "receptionist running in command line debug mode\n";
511 cout << "enter cgi arguments as name=value pairs (e.g. 'a=p&p=home'):\n";
512
513}
514
515
516
517
518void add_all_actions(receptionist& recpt, userdbclass* udb, keydbclass* kdb, isPersistentEnum isPersistentVal)
519{
520 // the list of actions.
521
522#ifdef GSDL_USE_TIP_ACTION
523 tipaction* atipaction = new tipaction();
524 recpt.add_action (atipaction);
525#endif
526
527#ifdef GSDL_USE_STATUS_ACTION
528 statusaction *astatusaction = new statusaction();
529 astatusaction->set_receptionist (&recpt);
530 recpt.add_action (astatusaction);
531#endif
532
533 pageaction *apageaction = new pageaction();
534 apageaction->set_receptionist (&recpt);
535 recpt.add_action (apageaction);
536
537#ifdef GSDL_USE_PING_ACTION
538 recpt.add_action (new pingaction());
539#endif
540
541 ispersistentaction *aIsPersistentAction = new ispersistentaction(isPersistentVal);
542 recpt.add_action (aIsPersistentAction);
543
544#if defined(USE_RSS)
545 rssaction *arssaction = new rssaction();
546 recpt.add_action (arssaction);
547#endif
548
549 queryaction *aqueryaction = new queryaction();
550 aqueryaction->set_userdb(udb);
551 aqueryaction->set_receptionist (&recpt);
552 recpt.add_action (aqueryaction);
553
554#if defined(USE_SQLITE)
555 sqlqueryaction *asqlqueryaction = new sqlqueryaction();
556 asqlqueryaction->set_receptionist (&recpt);
557 recpt.add_action (asqlqueryaction);
558#endif
559
560 documentaction *adocumentaction = new documentaction();
561 adocumentaction->set_receptionist (&recpt);
562 recpt.add_action (adocumentaction);
563
564#ifdef GSDL_USE_USERS_ACTION
565 usersaction *ausersaction = new usersaction();
566 ausersaction->set_userdb(udb);
567 recpt.add_action (ausersaction);
568#endif
569
570#ifdef GSDL_USE_EXTLINK_ACTION
571 extlinkaction *aextlinkaction = new extlinkaction();
572 aextlinkaction->set_receptionist(&recpt);
573 recpt.add_action (aextlinkaction);
574#endif
575
576#ifdef GSDL_USE_AUTHEN_ACTION
577 authenaction *aauthenaction = new authenaction();
578 aauthenaction->set_userdb(udb);
579 aauthenaction->set_keydb(kdb);
580 aauthenaction->set_receptionist(&recpt);
581 recpt.add_action (aauthenaction);
582#endif
583
584#ifdef GSDL_USE_COLLECTOR_ACTION
585 collectoraction *acollectoraction = new collectoraction();
586 acollectoraction->set_receptionist (&recpt);
587 recpt.add_action(acollectoraction);
588#endif
589
590#ifdef GSDL_USE_DEPOSITOR_ACTION
591 depositoraction *adepositoraction = new depositoraction();
592 adepositoraction->set_receptionist (&recpt);
593 recpt.add_action(adepositoraction);
594#endif
595
596#ifdef GSDL_USE_BROWSE_ACTION
597 browseaction *abrowseaction = new browseaction();
598 abrowseaction->set_receptionist (&recpt);
599 recpt.add_action(abrowseaction);
600#endif
601
602#ifdef GSDL_USE_PHIND_ACTION
603 // Phind uses MPPP,do we also need to check if ENABLE_MGPP is set??
604 phindaction *aphindaction = new phindaction();
605 recpt.add_action(aphindaction);
606#endif
607
608#ifdef GSDL_USE_GTI_ACTION
609 gtiaction *agtiaction = new gtiaction();
610 agtiaction->set_receptionist(&recpt);
611 recpt.add_action(agtiaction);
612#endif
613
614 dynamicclassifieraction *adynamicclassifieraction = new dynamicclassifieraction();
615 adynamicclassifieraction->set_receptionist(&recpt);
616 recpt.add_action(adynamicclassifieraction);
617
618#if defined(USE_MYSQL) || defined(USE_ACCESS)
619 orderaction *aorderaction = new orderaction();
620 aorderaction->set_receptionist(&recpt);
621 recpt.add_action(aorderaction);
622#endif
623
624 // action that allows collections to be added, released etc. when server
625 // is persistent (e.g. fastcgi or when Greenstone is configured as an
626 // Apache module). Presumably this includes Windows server.exe as well
627
628 // Want to always include it in list of actions even if compiling
629 // Greenstone to be used in a non-persistent way (e.g. library.cgi).
630 // This is so the e-variable that is formed is consistent between the
631 // persisent executable and the non-persistent executable
632 //
633
634 configaction *aconfigaction = new configaction();
635 aconfigaction->set_receptionist(&recpt);
636 recpt.add_action(aconfigaction);
637}
638
639
640
641void add_all_browsers(receptionist& recpt)
642{
643 // list of browsers
644 vlistbrowserclass *avlistbrowserclass = new vlistbrowserclass();
645 avlistbrowserclass->set_receptionist(&recpt);
646 recpt.add_browser (avlistbrowserclass);
647 recpt.setdefaultbrowser ("VList");
648
649 hlistbrowserclass *ahlistbrowserclass = new hlistbrowserclass();
650 ahlistbrowserclass->set_receptionist(&recpt);
651 recpt.add_browser (ahlistbrowserclass);
652
653#ifdef GSDL_USE_DATELIST_BROWSER
654 datelistbrowserclass *adatelistbrowserclass = new datelistbrowserclass();
655 recpt.add_browser (adatelistbrowserclass);
656#endif
657
658 invbrowserclass *ainvbrowserclass = new invbrowserclass();
659 recpt.add_browser (ainvbrowserclass);
660
661#ifdef GSDL_USE_PAGED_BROWSER
662 pagedbrowserclass *apagedbrowserclass = new pagedbrowserclass();
663 recpt.add_browser (apagedbrowserclass);
664#endif
665
666#ifdef GSDL_USE_HTML_BROWSER
667 htmlbrowserclass *ahtmlbrowserclass = new htmlbrowserclass();
668 recpt.add_browser (ahtmlbrowserclass);
669#endif
670
671#ifdef GSDL_USE_PHIND_BROWSER
672 phindbrowserclass *aphindbrowserclass = new phindbrowserclass();;
673 recpt.add_browser (aphindbrowserclass);
674#endif
675}
676
677
678// cgiwrapper does everything necessary to output a page
679// using the cgi protocol. If this is being run for a particular
680// collection then "collection" should be set, otherwise it
681// should equal "".
682void cgiwrapper (receptionist &recpt, text_t collection) {
683 int numrequests = 0;
684 bool debug = false;
685 const recptconf &configinfo = recpt.get_configinfo ();
686
687 // find out whether this is being run as a cgi-script
688 // or a fastcgi script
689#ifdef USE_FASTCGI
690 fcgistreambuf outbuf;
691 int isfastcgi = !FCGX_IsCGI();
692 FCGX_Stream *fcgiin, *fcgiout, *fcgierr;
693 FCGX_ParamArray fcgienvp;
694#else
695 int isfastcgi = 0;
696#endif
697
698 // we need gsdlhome to do fileupload stuff, so moved this configure stuff before the get argstr stuff
699 // init stuff - we can't output error pages directly with
700 // fastcgi so the pages are stored until we can output them
701 text_t errorpage;
702 outconvertclass text_t2ascii;
703
704 // set defaults
705 int maxrequests = 10000;
706 recpt.configure ("collection", collection);
707 char *script_name = getenv("SCRIPT_NAME");
708 if (script_name != NULL) recpt.configure("gwcgi", script_name);
709 else recpt.configure("gwcgi", "/gsdl");
710
711 // read in the configuration files.
712 text_t gsdlhome;
713 text_t collecthome;
714 configurator gsdlconfigurator(&recpt);
715 if (!site_cfg_read (gsdlconfigurator, gsdlhome, collecthome, maxrequests)) {
716 // couldn't find the site configuration file
717 page_errorsitecfg (errorpage, debug, 0);
718 } else if (gsdlhome.empty()) {
719 // no gsdlhome in gsdlsite.cfg
720 page_errorsitecfg (errorpage, debug, 1);
721 } else if (!directory_exists(gsdlhome)) {
722 // gsdlhome not a valid directory
723 page_errorsitecfg (errorpage, debug, 1);
724 } else if (!main_cfg_read (recpt, gsdlhome, collecthome, collection)) {
725 // couldn't find the main configuration file
726 page_errormaincfg (gsdlhome, collection, debug, errorpage);
727 } else if (configinfo.collectinfo.empty() && false) { // commented out for corba
728 // don't have any collections
729 page_errorcollect (gsdlhome, errorpage, debug);
730 }
731
732 // set up the httpweb variable if it hasn't been defined yet
733 if (configinfo.httpweb.empty()) {
734 recpt.configure("httpweb", configinfo.httpprefix+"/web");
735 }
736
737 // get the query string if it is not being run as a fastcgi
738 // script
739 text_t argstr = g_EmptyText;
740 fileupload_tmap fileuploads;
741 cgiargsclass args;
742 char *aURIStr;
743 if (!isfastcgi) {
744 char *request_method_str = getenv("REQUEST_METHOD");
745 char *content_length_str = getenv("CONTENT_LENGTH");
746 if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
747 content_length_str != NULL) {
748 // POST form data
749 long content_length = (content_length_str ? atoi(content_length_str) : 0);
750 if (content_length > 0) {
751#ifdef __WIN32__
752 // On Windows it is important that standard input be read in binary
753 // mode, otherwise end of line "<CR><LF>" is turned into <LF> only
754 // which breaks the MIME standard (and our parsing code!)
755
756 int result = _setmode( _fileno( stdin ), _O_BINARY );
757 if( result == -1 ) {
758 cerr << "Warning: Failed to set standard input to binary mode." << endl;
759 cerr << " Parsing of multi-part MIME will most likely fail" << endl;
760 }
761#endif
762
763 long length = content_length;
764 unsigned char * buffer = new unsigned char[content_length];
765
766 int chars_read = fread(buffer,1,content_length,stdin);
767
768 if (chars_read != content_length) {
769 cerr << "Warning: mismatch between CONTENT_LENGTH and data read from standard in" << endl;
770 }
771
772 argstr.setcarr((char *)buffer, content_length);
773
774 text_t content_type;
775 char *content_type_str = getenv("CONTENT_TYPE");
776 if (content_type_str) content_type = content_type_str;
777 argstr = parse_post_data(content_type, argstr, fileuploads, gsdlhome);
778 }
779 } else {
780 aURIStr = getenv("QUERY_STRING");
781 if ((request_method_str != NULL && strcmp(request_method_str, "GET") == 0)
782 || aURIStr != NULL) {
783 // GET form data
784 if (aURIStr != NULL) argstr = aURIStr;
785 } else {
786 // debugging from command line
787 debug = true;
788 }
789 }
790 }
791
792 if (debug) {
793 cout << "Configuring Greenstone...\n";
794 cout << flush;
795 }
796
797
798 if (errorpage.empty()) {
799
800 // initialise the library software
801 if (debug) {
802 cout << "Initializing...\n";
803 cout << flush;
804 }
805
806 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
807 char *eout = error_file.getcstr();
808 ofstream errout (eout, ios::app);
809 delete []eout;
810 if (!recpt.init(errout)) {
811 // an error occurred during the initialisation
812 errout.close();
813 page_errorinit(gsdlhome, debug, errorpage);
814 }
815 errout.close();
816 }
817
818 if (debug && errorpage.empty()) {
819 // get query string from command line
820 print_debug_info (recpt);
821 char cinURIStr[1024];
822 cin.get(cinURIStr, 1024);
823 argstr = cinURIStr;
824 }
825
826 // cgi scripts only deal with one request
827 if (!isfastcgi) maxrequests = 1;
828
829 // Page-request loop. If this is not being run as a fastcgi
830 // process then only one request will be processed and then
831 // the process will exit.
832 while (numrequests < maxrequests) {
833#ifdef USE_FASTCGI
834 if (isfastcgi) {
835 if (FCGX_Accept(&fcgiin, &fcgiout, &fcgierr, &fcgienvp) < 0) break;
836
837 char *request_method_str = FCGX_GetParam ("REQUEST_METHOD", fcgienvp);
838 char *content_length_str = FCGX_GetParam ("CONTENT_LENGTH", fcgienvp);
839
840 if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
841 content_length_str != NULL) {
842 // POST form data
843 int content_length = text_t(content_length_str).getint();
844 if (content_length > 0) {
845 argstr.clear();
846 int c;
847 do {
848 c = FCGX_GetChar (fcgiin);
849 if (c < 0) break;
850 argstr.push_back (c);
851 --content_length;
852 } while (content_length > 0);
853 }
854
855 } else {
856 // GET form data
857 aURIStr = FCGX_GetParam("QUERY_STRING", fcgienvp);
858 if (aURIStr != NULL) argstr = aURIStr;
859 else argstr = g_EmptyText;
860 }
861 }
862#endif
863
864 // get output streams ready
865#ifdef USE_FASTCGI
866 outbuf.fcgisbreset ();
867 if (isfastcgi) outbuf.set_fcgx_stream (fcgiout);
868 else outbuf.set_other_ostream (&cout);
869 ostream pageout (&outbuf);
870#else
871#define pageout cout
872#endif
873
874 // if using fastcgi we'll load environment into a map,
875 // otherwise simply pass empty map (can't get environment
876 // variables using getenv() while using FCGX versions
877 // of fastcgi - at least I can't ;-) - Stefan)
878 text_tmap fastcgienv;
879#ifdef USE_FASTCGI
880 if (isfastcgi) {
881 for(; *fcgienvp != NULL; ++fcgienvp) {
882 text_t fvalue = *fcgienvp;
883 text_t::const_iterator begin = fvalue.begin();
884 text_t::const_iterator end = fvalue.end();
885 text_t::const_iterator equals_sign = findchar (begin, end, '=');
886 if (equals_sign != end)
887 fastcgienv[substr(begin, equals_sign)] = substr(equals_sign+1, end);
888 }
889 }
890#endif
891
892 // temporarily need to configure gwcgi here when using fastcgi as I can't
893 // get it to pass the SCRIPT_NAME environment variable to the initial
894 // environment (if anyone can work out how to do this using the apache
895 // server, let me know). Note that this overrides the gwcgi field in
896 // site.cfg (which it shouldn't do) but I can't at present set gwcgi
897 // from site.cfg as I have old receptionists laying around that wouldn't
898 // appreciate it. The following 5 lines of code should be deleted once
899 // I either a: get the server to pass SCRIPT_NAME at initialization
900 // time or b: convert all the collections using old receptionists over
901 // to this version and uncomment gwcgi in the site.cfg file -- Stefan.
902#ifdef USE_FASTCGI
903 if (isfastcgi) {
904 recpt.configure("gwcgi", fastcgienv["SCRIPT_NAME"]);
905 }
906#endif
907
908
909 // if there has been no error so far, perform the production of the
910 // output page
911 if (errorpage.empty()) {
912 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
913 char *eout = error_file.getcstr();
914 ofstream errout (eout, ios::app);
915 delete []eout;
916
917#if defined(__WIN32__) && defined(GSDL_USE_IOS_H)
918 // old Windows compilers (VC++4.2)
919 cerr = errout;
920#else
921 // can't do this anymore according to c++ standard...
922 // cerr = errout;
923 // ... but can do this instead
924 streambuf* errbuf = cerr.rdbuf(errout.rdbuf());
925#endif
926
927 // parse the cgi arguments and produce the resulting page if there
928 // has been no errors so far
929 if (!recpt.parse_cgi_args (argstr, fileuploads, args, errout, fastcgienv)) {
930 errout.close ();
931 page_errorparseargs(gsdlhome, debug, errorpage);
932 } else {
933 // produce the output page
934
935 if (!recpt.produce_cgi_page (args, pageout, errout, fastcgienv)) {
936 errout.close ();
937 page_errorcgipage(gsdlhome, debug, errorpage);
938 }
939 recpt.log_cgi_args (args, errout, fastcgienv);
940 errout.close ();
941 }
942
943#if !defined(__WIN32__) || !defined(GSDL_USE_IOS_H)
944 // restore the cerr buffer
945 cerr.rdbuf(errbuf);
946#endif
947 }
948 // clean up any files that were uploaded
949 fileupload_tmap::const_iterator this_file = fileuploads.begin();
950 fileupload_tmap::const_iterator end_file = fileuploads.end();
951 while (this_file != end_file)
952 {
953 if (file_exists((*this_file).second.tmp_name))
954 {
955 char *thefile = (*this_file).second.tmp_name.getcstr();
956 unlink(thefile);
957 delete [] thefile;
958 }
959 ++this_file;
960 }
961
962 // there was an error, output the error page
963 if (!errorpage.empty()) {
964 pageout << text_t2ascii << errorpage;
965 errorpage.clear();
966 numrequests = maxrequests; // make this the last page
967 }
968 pageout << flush;
969
970 // finish with the output streams
971#ifdef USE_FASTCGI
972 if (isfastcgi) FCGX_Finish();
973#endif
974
975 ++numrequests;
976 }
977
978 return;
979}
Note: See TracBrowser for help on using the repository browser.