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

Last change on this file since 24958 was 24958, checked in by ak19, 12 years ago

First commit to do with Greenstone's support for RSS. Committing Dr Bainbridge's code which was already working for windows. This has now been tested on Linux, where it can be got to work with changes to zextra.dm and base.dm and if the rss-items.rdf file generated by the update to BasePlugout is moved to the index folder). The next set of commits will make the way rssaction.cpp accesses the rss-items.rdf file independent of where the GS server is located, with changes to the protocol class.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 31.7 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)
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#if defined(USE_RSS)
538 rssaction *arssaction = new rssaction();
539 recpt.add_action (arssaction);
540#endif
541
542 queryaction *aqueryaction = new queryaction();
543 aqueryaction->set_receptionist (&recpt);
544 recpt.add_action (aqueryaction);
545
546#if defined(USE_SQLITE)
547 sqlqueryaction *asqlqueryaction = new sqlqueryaction();
548 asqlqueryaction->set_receptionist (&recpt);
549 recpt.add_action (asqlqueryaction);
550#endif
551
552 documentaction *adocumentaction = new documentaction();
553 adocumentaction->set_receptionist (&recpt);
554 recpt.add_action (adocumentaction);
555
556#ifdef GSDL_USE_USERS_ACTION
557 usersaction *ausersaction = new usersaction();
558 ausersaction->set_userdb(udb);
559 recpt.add_action (ausersaction);
560#endif
561
562#ifdef GSDL_USE_EXTLINK_ACTION
563 extlinkaction *aextlinkaction = new extlinkaction();
564 aextlinkaction->set_receptionist(&recpt);
565 recpt.add_action (aextlinkaction);
566#endif
567
568#ifdef GSDL_USE_AUTHEN_ACTION
569 authenaction *aauthenaction = new authenaction();
570 aauthenaction->set_userdb(udb);
571 aauthenaction->set_keydb(kdb);
572 aauthenaction->set_receptionist(&recpt);
573 recpt.add_action (aauthenaction);
574#endif
575
576#ifdef GSDL_USE_COLLECTOR_ACTION
577 collectoraction *acollectoraction = new collectoraction();
578 acollectoraction->set_receptionist (&recpt);
579 recpt.add_action(acollectoraction);
580#endif
581
582#ifdef GSDL_USE_DEPOSITOR_ACTION
583 depositoraction *adepositoraction = new depositoraction();
584 adepositoraction->set_receptionist (&recpt);
585 recpt.add_action(adepositoraction);
586#endif
587
588#ifdef GSDL_USE_BROWSE_ACTION
589 browseaction *abrowseaction = new browseaction();
590 abrowseaction->set_receptionist (&recpt);
591 recpt.add_action(abrowseaction);
592#endif
593
594#ifdef GSDL_USE_PHIND_ACTION
595 // Phind uses MPPP,do we also need to check if ENABLE_MGPP is set??
596 phindaction *aphindaction = new phindaction();
597 recpt.add_action(aphindaction);
598#endif
599
600#ifdef GSDL_USE_GTI_ACTION
601 gtiaction *agtiaction = new gtiaction();
602 agtiaction->set_receptionist(&recpt);
603 recpt.add_action(agtiaction);
604#endif
605
606 dynamicclassifieraction *adynamicclassifieraction = new dynamicclassifieraction();
607 adynamicclassifieraction->set_receptionist(&recpt);
608 recpt.add_action(adynamicclassifieraction);
609
610#if defined(USE_MYSQL) || defined(USE_ACCESS)
611 orderaction *aorderaction = new orderaction();
612 aorderaction->set_receptionist(&recpt);
613 recpt.add_action(aorderaction);
614#endif
615
616 // action that allows collections to be added, released etc. when server
617 // is persistent (e.g. fastcgi or when Greenstone is configured as an
618 // Apache module). Presumably this includes Windows server.exe as well
619
620 // Want to always include it in list of actions even if compiling
621 // Greenstone to be used in a non-persistent way (e.g. library.cgi).
622 // This is so the e-variable that is formed is consistent between the
623 // persisent executable and the non-persistent executable
624 //
625
626 configaction *aconfigaction = new configaction();
627 aconfigaction->set_receptionist(&recpt);
628 recpt.add_action(aconfigaction);
629}
630
631
632
633void add_all_browsers(receptionist& recpt)
634{
635 // list of browsers
636 vlistbrowserclass *avlistbrowserclass = new vlistbrowserclass();
637 avlistbrowserclass->set_receptionist(&recpt);
638 recpt.add_browser (avlistbrowserclass);
639 recpt.setdefaultbrowser ("VList");
640
641 hlistbrowserclass *ahlistbrowserclass = new hlistbrowserclass();
642 ahlistbrowserclass->set_receptionist(&recpt);
643 recpt.add_browser (ahlistbrowserclass);
644
645#ifdef GSDL_USE_DATELIST_BROWSER
646 datelistbrowserclass *adatelistbrowserclass = new datelistbrowserclass();
647 recpt.add_browser (adatelistbrowserclass);
648#endif
649
650 invbrowserclass *ainvbrowserclass = new invbrowserclass();
651 recpt.add_browser (ainvbrowserclass);
652
653#ifdef GSDL_USE_PAGED_BROWSER
654 pagedbrowserclass *apagedbrowserclass = new pagedbrowserclass();
655 recpt.add_browser (apagedbrowserclass);
656#endif
657
658#ifdef GSDL_USE_HTML_BROWSER
659 htmlbrowserclass *ahtmlbrowserclass = new htmlbrowserclass();
660 recpt.add_browser (ahtmlbrowserclass);
661#endif
662
663#ifdef GSDL_USE_PHIND_BROWSER
664 phindbrowserclass *aphindbrowserclass = new phindbrowserclass();;
665 recpt.add_browser (aphindbrowserclass);
666#endif
667}
668
669
670// cgiwrapper does everything necessary to output a page
671// using the cgi protocol. If this is being run for a particular
672// collection then "collection" should be set, otherwise it
673// should equal "".
674void cgiwrapper (receptionist &recpt, text_t collection) {
675 int numrequests = 0;
676 bool debug = false;
677 const recptconf &configinfo = recpt.get_configinfo ();
678
679 // find out whether this is being run as a cgi-script
680 // or a fastcgi script
681#ifdef USE_FASTCGI
682 fcgistreambuf outbuf;
683 int isfastcgi = !FCGX_IsCGI();
684 FCGX_Stream *fcgiin, *fcgiout, *fcgierr;
685 FCGX_ParamArray fcgienvp;
686#else
687 int isfastcgi = 0;
688#endif
689
690 // we need gsdlhome to do fileupload stuff, so moved this configure stuff before the get argstr stuff
691 // init stuff - we can't output error pages directly with
692 // fastcgi so the pages are stored until we can output them
693 text_t errorpage;
694 outconvertclass text_t2ascii;
695
696 // set defaults
697 int maxrequests = 10000;
698 recpt.configure ("collection", collection);
699 char *script_name = getenv("SCRIPT_NAME");
700 if (script_name != NULL) recpt.configure("gwcgi", script_name);
701 else recpt.configure("gwcgi", "/gsdl");
702
703 // read in the configuration files.
704 text_t gsdlhome;
705 text_t collecthome;
706 configurator gsdlconfigurator(&recpt);
707 if (!site_cfg_read (gsdlconfigurator, gsdlhome, collecthome, maxrequests)) {
708 // couldn't find the site configuration file
709 page_errorsitecfg (errorpage, debug, 0);
710 } else if (gsdlhome.empty()) {
711 // no gsdlhome in gsdlsite.cfg
712 page_errorsitecfg (errorpage, debug, 1);
713 } else if (!directory_exists(gsdlhome)) {
714 // gsdlhome not a valid directory
715 page_errorsitecfg (errorpage, debug, 1);
716 } else if (!main_cfg_read (recpt, gsdlhome, collecthome, collection)) {
717 // couldn't find the main configuration file
718 page_errormaincfg (gsdlhome, collection, debug, errorpage);
719 } else if (configinfo.collectinfo.empty() && false) { // commented out for corba
720 // don't have any collections
721 page_errorcollect (gsdlhome, errorpage, debug);
722 }
723
724 // set up the httpweb variable if it hasn't been defined yet
725 if (configinfo.httpweb.empty()) {
726 recpt.configure("httpweb", configinfo.httpprefix+"/web");
727 }
728
729 // get the query string if it is not being run as a fastcgi
730 // script
731 text_t argstr = g_EmptyText;
732 fileupload_tmap fileuploads;
733 cgiargsclass args;
734 char *aURIStr;
735 if (!isfastcgi) {
736 char *request_method_str = getenv("REQUEST_METHOD");
737 char *content_length_str = getenv("CONTENT_LENGTH");
738 if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
739 content_length_str != NULL) {
740 // POST form data
741 long content_length = (content_length_str ? atoi(content_length_str) : 0);
742 if (content_length > 0) {
743#ifdef __WIN32__
744 // On Windows it is important that standard input be read in binary
745 // mode, otherwise end of line "<CR><LF>" is turned into <LF> only
746 // which breaks the MIME standard (and our parsing code!)
747
748 int result = _setmode( _fileno( stdin ), _O_BINARY );
749 if( result == -1 ) {
750 cerr << "Warning: Failed to set standard input to binary mode." << endl;
751 cerr << " Parsing of multi-part MIME will most likely fail" << endl;
752 }
753#endif
754
755 long length = content_length;
756 unsigned char * buffer = new unsigned char[content_length];
757
758 int chars_read = fread(buffer,1,content_length,stdin);
759
760 if (chars_read != content_length) {
761 cerr << "Warning: mismatch between CONTENT_LENGTH and data read from standard in" << endl;
762 }
763
764 argstr.setcarr((char *)buffer, content_length);
765
766 text_t content_type;
767 char *content_type_str = getenv("CONTENT_TYPE");
768 if (content_type_str) content_type = content_type_str;
769 argstr = parse_post_data(content_type, argstr, fileuploads, gsdlhome);
770 }
771 } else {
772 aURIStr = getenv("QUERY_STRING");
773 if ((request_method_str != NULL && strcmp(request_method_str, "GET") == 0)
774 || aURIStr != NULL) {
775 // GET form data
776 if (aURIStr != NULL) argstr = aURIStr;
777 } else {
778 // debugging from command line
779 debug = true;
780 }
781 }
782 }
783
784 if (debug) {
785 cout << "Configuring Greenstone...\n";
786 cout << flush;
787 }
788
789
790 if (errorpage.empty()) {
791
792 // initialise the library software
793 if (debug) {
794 cout << "Initializing...\n";
795 cout << flush;
796 }
797
798 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
799 char *eout = error_file.getcstr();
800 ofstream errout (eout, ios::app);
801 delete []eout;
802 if (!recpt.init(errout)) {
803 // an error occurred during the initialisation
804 errout.close();
805 page_errorinit(gsdlhome, debug, errorpage);
806 }
807 errout.close();
808 }
809
810 if (debug && errorpage.empty()) {
811 // get query string from command line
812 print_debug_info (recpt);
813 char cinURIStr[1024];
814 cin.get(cinURIStr, 1024);
815 argstr = cinURIStr;
816 }
817
818 // cgi scripts only deal with one request
819 if (!isfastcgi) maxrequests = 1;
820
821 // Page-request loop. If this is not being run as a fastcgi
822 // process then only one request will be processed and then
823 // the process will exit.
824 while (numrequests < maxrequests) {
825#ifdef USE_FASTCGI
826 if (isfastcgi) {
827 if (FCGX_Accept(&fcgiin, &fcgiout, &fcgierr, &fcgienvp) < 0) break;
828
829 char *request_method_str = FCGX_GetParam ("REQUEST_METHOD", fcgienvp);
830 char *content_length_str = FCGX_GetParam ("CONTENT_LENGTH", fcgienvp);
831
832 if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
833 content_length_str != NULL) {
834 // POST form data
835 int content_length = text_t(content_length_str).getint();
836 if (content_length > 0) {
837 argstr.clear();
838 int c;
839 do {
840 c = FCGX_GetChar (fcgiin);
841 if (c < 0) break;
842 argstr.push_back (c);
843 --content_length;
844 } while (content_length > 0);
845 }
846
847 } else {
848 // GET form data
849 aURIStr = FCGX_GetParam("QUERY_STRING", fcgienvp);
850 if (aURIStr != NULL) argstr = aURIStr;
851 else argstr = g_EmptyText;
852 }
853 }
854#endif
855
856 // get output streams ready
857#ifdef USE_FASTCGI
858 outbuf.fcgisbreset ();
859 if (isfastcgi) outbuf.set_fcgx_stream (fcgiout);
860 else outbuf.set_other_ostream (&cout);
861 ostream pageout (&outbuf);
862#else
863#define pageout cout
864#endif
865
866 // if using fastcgi we'll load environment into a map,
867 // otherwise simply pass empty map (can't get environment
868 // variables using getenv() while using FCGX versions
869 // of fastcgi - at least I can't ;-) - Stefan)
870 text_tmap fastcgienv;
871#ifdef USE_FASTCGI
872 if (isfastcgi) {
873 for(; *fcgienvp != NULL; ++fcgienvp) {
874 text_t fvalue = *fcgienvp;
875 text_t::const_iterator begin = fvalue.begin();
876 text_t::const_iterator end = fvalue.end();
877 text_t::const_iterator equals_sign = findchar (begin, end, '=');
878 if (equals_sign != end)
879 fastcgienv[substr(begin, equals_sign)] = substr(equals_sign+1, end);
880 }
881 }
882#endif
883
884 // temporarily need to configure gwcgi here when using fastcgi as I can't
885 // get it to pass the SCRIPT_NAME environment variable to the initial
886 // environment (if anyone can work out how to do this using the apache
887 // server, let me know). Note that this overrides the gwcgi field in
888 // site.cfg (which it shouldn't do) but I can't at present set gwcgi
889 // from site.cfg as I have old receptionists laying around that wouldn't
890 // appreciate it. The following 5 lines of code should be deleted once
891 // I either a: get the server to pass SCRIPT_NAME at initialization
892 // time or b: convert all the collections using old receptionists over
893 // to this version and uncomment gwcgi in the site.cfg file -- Stefan.
894#ifdef USE_FASTCGI
895 if (isfastcgi) {
896 recpt.configure("gwcgi", fastcgienv["SCRIPT_NAME"]);
897 }
898#endif
899
900
901 // if there has been no error so far, perform the production of the
902 // output page
903 if (errorpage.empty()) {
904 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
905 char *eout = error_file.getcstr();
906 ofstream errout (eout, ios::app);
907 delete []eout;
908
909#if defined(__WIN32__) && defined(GSDL_USE_IOS_H)
910 // old Windows compilers (VC++4.2)
911 cerr = errout;
912#else
913 // can't do this anymore according to c++ standard...
914 // cerr = errout;
915 // ... but can do this instead
916 streambuf* errbuf = cerr.rdbuf(errout.rdbuf());
917#endif
918
919 // parse the cgi arguments and produce the resulting page if there
920 // has been no errors so far
921 if (!recpt.parse_cgi_args (argstr, fileuploads, args, errout, fastcgienv)) {
922 errout.close ();
923 page_errorparseargs(gsdlhome, debug, errorpage);
924 } else {
925 // produce the output page
926
927 if (!recpt.produce_cgi_page (args, pageout, errout, fastcgienv)) {
928 errout.close ();
929 page_errorcgipage(gsdlhome, debug, errorpage);
930 }
931 recpt.log_cgi_args (args, errout, fastcgienv);
932 errout.close ();
933 }
934
935#if !defined(__WIN32__) || !defined(GSDL_USE_IOS_H)
936 // restore the cerr buffer
937 cerr.rdbuf(errbuf);
938#endif
939 }
940 // clean up any files that were uploaded
941 fileupload_tmap::const_iterator this_file = fileuploads.begin();
942 fileupload_tmap::const_iterator end_file = fileuploads.end();
943 while (this_file != end_file)
944 {
945 if (file_exists((*this_file).second.tmp_name))
946 {
947 char *thefile = (*this_file).second.tmp_name.getcstr();
948 unlink(thefile);
949 delete [] thefile;
950 }
951 ++this_file;
952 }
953
954 // there was an error, output the error page
955 if (!errorpage.empty()) {
956 pageout << text_t2ascii << errorpage;
957 errorpage.clear();
958 numrequests = maxrequests; // make this the last page
959 }
960 pageout << flush;
961
962 // finish with the output streams
963#ifdef USE_FASTCGI
964 if (isfastcgi) FCGX_Finish();
965#endif
966
967 ++numrequests;
968 }
969
970 return;
971}
Note: See TracBrowser for help on using the repository browser.