source: main/branches/64_bit_Greenstone/greenstone2/runtime-src/src/recpt/cgiwrapper.cpp@ 23508

Last change on this file since 23508 was 23508, checked in by sjm84, 13 years ago

Committing 64 bit changes into the branch

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