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

Last change on this file since 28760 was 27172, checked in by kjdon, 11 years ago

For diego: when doing cross collection searching, now it takes into account authentication directives for the collections in the list. If a user has authenticated to get into the top collection, then his user groups are checked against the groups for all the collections. If he matches any, then they will be searched. But if he is not a member of the right group they will not be searched. If there was no authentication needed to get into top colleciton, then any collections with collection-level authentication will not be searched.

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