source: trunk/gsdl/src/recpt/cgiwrapper.cpp@ 928

Last change on this file since 928 was 872, checked in by sjboddie, 24 years ago

few changes to get fastcgi to work properly

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
RevLine 
[144]1/**********************************************************************
2 *
3 * cgiwrapper.cpp -- output pages using the cgi protocol
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
[533]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.
[144]9 *
[533]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 *
[144]24 * $Id: cgiwrapper.cpp 872 2000-01-25 22:45:59Z sjboddie $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
[872]30 Revision 1.21 2000/01/25 22:45:59 sjboddie
31 few changes to get fastcgi to work properly
32
[533]33 Revision 1.20 1999/09/07 04:56:53 sjboddie
34 added GPL notice
35
[508]36 Revision 1.19 1999/09/02 00:24:36 rjmcnab
37 fixed bug in getting POST arguments
38
[465]39 Revision 1.18 1999/08/20 01:02:07 sjboddie
40 added some usage logging
41
[389]42 Revision 1.17 1999/07/15 06:03:15 rjmcnab
43 Moved the adding of the actions to librarymain so that they can be
44 overriden easily.
45
[387]46 Revision 1.16 1999/07/14 08:31:05 rjmcnab
47 Fixed a small bug in the POST implementation.
48
[377]49 Revision 1.15 1999/07/13 23:32:17 rjmcnab
50 Added authenaction and usersaction
51
[365]52 Revision 1.14 1999/07/11 01:03:37 rjmcnab
53 Added ability to receive POST cgi form data.
54
[284]55 Revision 1.13 1999/06/24 05:12:18 sjboddie
56 lots of small changes
57
[248]58 Revision 1.12 1999/04/30 01:59:40 sjboddie
59 lots of stuff - getting documentaction working (documentaction replaces
60 old browseaction)
61
[210]62 Revision 1.11 1999/03/25 03:12:01 sjboddie
63
64 subjectbrowseaction was replaced with browseaction
65
[189]66 Revision 1.10 1999/03/05 03:53:54 sjboddie
67
68 fixed some bugs
69
[188]70 Revision 1.9 1999/03/04 22:38:21 sjboddie
71
72 Added subjectbrowseaction. - Doesn't do anything yet.
73
[173]74 Revision 1.8 1999/02/28 20:00:13 rjmcnab
75
76
77 Fixed a few things.
78
[165]79 Revision 1.7 1999/02/21 22:33:53 rjmcnab
80
81 Lots of stuff :-)
82
[160]83 Revision 1.6 1999/02/12 02:40:17 sjboddie
84
85 Added page action
86
[158]87 Revision 1.5 1999/02/11 01:24:04 rjmcnab
88
89 Fixed a few compiler warnings.
90
[155]91 Revision 1.4 1999/02/08 01:28:01 rjmcnab
92
93 Got the receptionist producing something using the statusaction.
94
[150]95 Revision 1.3 1999/02/05 10:42:44 rjmcnab
96
97 Continued working on receptionist
98
[146]99 Revision 1.2 1999/02/04 10:00:56 rjmcnab
100
101 Developed the idea of an "action" and having them define the cgi arguments
102 which they need and how those cgi arguments function.
103
[144]104 Revision 1.1 1999/02/04 01:16:17 rjmcnab
105
106 Initial revision.
107
108 Revision 1.5 1999/01/19 01:38:18 rjmcnab
109
110 Made the source more portable.
111
112 Revision 1.4 1999/01/12 01:51:04 rjmcnab
113
114 Standard header.
115
116 */
117
118
119#include "gsdlconf.h"
120#include "cgiwrapper.h"
[150]121#include "recptconfig.h"
[144]122#include <stdlib.h>
[872]123#include <assert.h>
[144]124
[150]125
[508]126// get GSDL_GSDLHOME
127#include "gsdlhome.h"
128
129
[144]130#if defined(GSDL_USE_OBJECTSPACE)
131# include <ospace/std/iostream>
132# include <ospace/std/fstream>
133#elif defined(GSDL_USE_IOS_H)
134# include <iostream.h>
135# include <fstream.h>
136#else
137# include <iostream>
138# include <fstream>
139#endif
140
141#ifdef USE_FASTCGI
142#include "fcgiapp.h"
143#endif
144
[387]145
[144]146#ifdef USE_FASTCGI
147// used to output the text from receptionist
148class fcgistreambuf : public streambuf {
149public:
150 fcgistreambuf ();
151 int sync ();
152 int overflow (int ch);
153 int underflow () {return EOF;}
154
155 void fcgisbreset() {fcgx_stream = NULL; other_ostream = NULL;};
156 void set_fcgx_stream(FCGX_Stream *newone) {fcgx_stream=newone;};
157 void set_other_ostream(ostream *newone) {other_ostream=newone;};
158
159private:
160 FCGX_Stream *fcgx_stream;
161 ostream *other_ostream;
162};
163
164fcgistreambuf::fcgistreambuf() {
165 fcgisbreset();
166 if (base() == ebuf()) allocate();
167 setp (base(), ebuf());
168};
169
170int fcgistreambuf::sync () {
171 if ((fcgx_stream != NULL) &&
172 (FCGX_PutStr (pbase(), out_waiting(), fcgx_stream) < 0)) {
173 fcgx_stream = NULL;
174 }
175
176 if (other_ostream != NULL) {
177 char *thepbase=pbase();
178 for (int i=0;i<out_waiting();i++) (*other_ostream).put(thepbase[i]);
179 }
180
181 setp (pbase(), epptr());
182
183 return 0;
184}
185
186int fcgistreambuf::overflow (int ch) {
187 if (sync () == EOF) return EOF;
188 if (ch != EOF) sputc (ch);
189 return 0;
190}
191
192#endif
193
194
[155]195static void page_errorsitecfg (const text_t &gsdlhome, const text_t &collection,
196 text_t &errorpage) {
197 errorpage += "Content-type: text/html\n\n";
[144]198
[155]199 errorpage += "<html>\n";
200 errorpage += "<head>\n";
201 errorpage += "<title>Error</title>\n";
202 errorpage += "</head>\n";
203 errorpage += "<body>\n";
204 errorpage += "<h2>Oops!</h2>\n";
205 errorpage += "The site.cfg configuration file could not be found. This file\n";
206 errorpage += "should contain configuration information relating to this\n";
207 errorpage += "site's setup. ";
208 if (collection.empty()) {
209 errorpage += "As this cgi script is not being run in collection specific mode,\n";
210 errorpage += "the file should reside at ";
211 errorpage += gsdlhome;
212 errorpage += "/etc/site.cfg.\n";
213 } else {
214 errorpage += "As this cgi script is being run in collection specific mode,\n";
215 errorpage += "the file can reside in ";
216 errorpage += gsdlhome;
217 errorpage += "/collect/";
218 errorpage += collection;
219 errorpage += "/etc/site.cfg or ";
220 errorpage += gsdlhome;
221 errorpage += "/etc/site.cfg.\n";
222 }
223 errorpage += "</body>\n";
224 errorpage += "</html>\n";
225}
[144]226
[155]227
228static void page_errormaincfg (const text_t &gsdlhome, const text_t &collection,
229 text_t &errorpage) {
230 errorpage += "Content-type: text/html\n\n";
231
232 errorpage += "<html>\n";
233 errorpage += "<head>\n";
234 errorpage += "<title>Error</title>\n";
235 errorpage += "</head>\n";
236 errorpage += "<body>\n";
237 errorpage += "<h2>Oops!</h2>\n";
[144]238 if (collection.empty()) {
[155]239 errorpage += "The main.cfg configuration file could not be found. This file\n";
240 errorpage += "should contain configuration information relating to the\n";
241 errorpage += "setup of the interface. As this cgi script is not being run\n";
242 errorpage += "in collection specific mode the file should reside at\n";
243 errorpage += gsdlhome;
244 errorpage += "/etc/main.cfg.\n";
[144]245 } else {
[155]246 errorpage += "Neither the collect.cfg or main.cfg configuration files could\n";
247 errorpage += "not be found. This file should contain configuration information\n";
248 errorpage += "relating to the setup of the interface. As this cgi script is\n";
249 errorpage += "being run in collection specific mode the file should reside\n";
250 errorpage += "at either ";
251 errorpage += gsdlhome;
252 errorpage += "/collect/";
253 errorpage += collection;
254 errorpage += "/etc/collect.cfg, ";
255 errorpage += gsdlhome;
256 errorpage += "/etc/collect.cfg or ";
257 errorpage += gsdlhome;
258 errorpage += "/etc/main.cfg.\n";
[144]259 }
[155]260 errorpage += "</body>\n";
261 errorpage += "</html>\n";
[144]262}
263
264
[158]265static void page_errorinit (const text_t &/*gsdlhome*/, text_t &errorpage) {
[155]266 errorpage += "Content-type: text/html\n\n";
[150]267
[155]268 errorpage += "<html>\n";
269 errorpage += "<head>\n";
270 errorpage += "<title>Error</title>\n";
271 errorpage += "</head>\n";
272 errorpage += "<body>\n";
273 errorpage += "<h2>Oops!</h2>\n";
274 errorpage += "An error occurred during the initialisation of the Greenstone Digital\n";
275 errorpage += "Library software. It is likely that the software has not been setup\n";
276 errorpage += "correctly.\n";
[144]277
[155]278 ifstream initin (GSDL_GSDLHOME "/etc/initout.txt");
279 if (initin) {
280 errorpage += "The initialisation error log, " GSDL_GSDLHOME "/etc/initout.txt, contains the\n";
281 errorpage += "following information:\n\n";
282 errorpage += "<pre>\n";
283
284 char c;
285 initin.get(c);
286 while (!initin.eof ()) {
287 errorpage.push_back(c);
288 initin.get(c);
289 }
290
291 errorpage += "</pre>\n";
292
293 initin.close();
294
[150]295 } else {
[155]296 errorpage += "Please consult " GSDL_GSDLHOME "/etc/initout.txt for more information.\n";
[150]297 }
[155]298
299 errorpage += "</body>\n";
300 errorpage += "</html>\n";
[150]301}
302
[158]303static void page_errorparseargs (const text_t &/*gsdlhome*/, text_t &errorpage) {
[155]304 errorpage += "Content-type: text/html\n\n";
[150]305
[155]306 errorpage += "<html>\n";
307 errorpage += "<head>\n";
308 errorpage += "<title>Error</title>\n";
309 errorpage += "</head>\n";
310 errorpage += "<body>\n";
311 errorpage += "<h2>Oops!</h2>\n";
312 errorpage += "An error occurred during the parsing of the cgi arguments.\n";
[150]313
[155]314 ifstream errin (GSDL_GSDLHOME "/etc/errout.txt");
315 if (errin) {
316 errorpage += "The error log, " GSDL_GSDLHOME "/etc/errout.txt, contains the\n";
317 errorpage += "following information:\n\n";
318 errorpage += "<pre>\n";
[150]319
[155]320 char c;
321 errin.get(c);
322 while (!errin.eof ()) {
323 errorpage.push_back(c);
324 errin.get(c);
325 }
326 errorpage += "</pre>\n";
327 errin.close();
328
329 } else {
330 errorpage += "Please consult " GSDL_GSDLHOME "/etc/errout.txt for more information.\n";
331 }
332
333 errorpage += "</body>\n";
334 errorpage += "</html>\n";
[144]335}
336
[158]337static void page_errorcgipage (const text_t &/*gsdlhome*/, text_t &errorpage) {
[155]338 errorpage += "Content-type: text/html\n\n";
[144]339
[155]340 errorpage += "<html>\n";
341 errorpage += "<head>\n";
342 errorpage += "<title>Error</title>\n";
343 errorpage += "</head>\n";
344 errorpage += "<body>\n";
345 errorpage += "<h2>Oops!</h2>\n";
346 errorpage += "An error occurred during the construction of the cgi page.\n";
347
348 ifstream errin (GSDL_GSDLHOME "/etc/errout.txt");
349 if (errin) {
350 errorpage += "The error log, " GSDL_GSDLHOME "/etc/errout.txt, contains the\n";
351 errorpage += "following information:\n\n";
352 errorpage += "<pre>\n";
353
354 char c;
355 errin.get(c);
356 while (!errin.eof ()) {
357 errorpage.push_back(c);
358 errin.get(c);
359 }
360 errorpage += "</pre>\n";
361 errin.close();
362
363 } else {
364 errorpage += "Please consult " GSDL_GSDLHOME "/etc/errout.txt for more information.\n";
365 }
366
367 errorpage += "</body>\n";
368 errorpage += "</html>\n";
369}
370
371
[144]372// cgiwrapper does everything necessary to output a page
373// using the cgi protocol. If this is being run for a particular
374// collection then "collection" should be set, otherwise it
375// should equal "".
[189]376void cgiwrapper (receptionist &recpt, text_t collection) {
[144]377#ifdef USE_FASTCGI
378 fcgistreambuf outbuf;
379#endif
380
[155]381 // init stuff - we can't output error pages directly with
382 // fastcgi so the pages are stored until we can output them
383 text_t errorpage;
384 outconvertclass text_t2ascii;
[144]385
386 // set defaults
387 int maxrequests = 10000;
[165]388 recpt.configure ("gsdlhome", GSDL_GSDLHOME);
389 recpt.configure ("collection", collection);
390 recpt.configure ("httpimg", "/gsdl/images");
[144]391 char *script_name = getenv("SCRIPT_NAME");
[165]392 if (script_name != NULL) recpt.configure("gwcgi", script_name);
393 else recpt.configure("gwcgi", "/cgi-bin/gw");
[144]394
[150]395 // read in the configuration files.
396 if (!site_cfg_read (recpt, GSDL_GSDLHOME, collection, maxrequests)) {
397 // couldn't find the site configuration file
[155]398 page_errorsitecfg (GSDL_GSDLHOME, collection, errorpage);
399 } else if (!main_cfg_read (recpt, GSDL_GSDLHOME, collection)) {
[150]400 // couldn't find the main configuration file
[155]401 page_errormaincfg (GSDL_GSDLHOME, collection, errorpage);
[144]402 }
403
404 // initialise the library software
[155]405 if (errorpage.empty()) {
406 ofstream initout (GSDL_GSDLHOME "/etc/initout.txt");
407 if (!recpt.init(initout)) {
408 // an error occurred during the initialisation
409 initout.close();
410 page_errorinit(GSDL_GSDLHOME, errorpage);
411 }
[144]412 initout.close();
413 }
414
415 // find out whether this is being run as a cgi-script
416 // or a fastcgi script
417 int numrequests = 0;
418#ifdef USE_FASTCGI
419 int isfastcgi = !FCGX_IsCGI();
420 FCGX_Stream *fcgiin, *fcgiout, *fcgierr;
421 FCGX_ParamArray fcgienvp;
422#else
423 int isfastcgi = 0;
424#endif
425
426 // get the query string if it is not being run as a fastcgi
427 // script
428 text_t argstr = "";
[155]429 cgiargsclass args;
[144]430 char *aURIStr;
431 if (!isfastcgi) {
[365]432 char *request_method_str = getenv("REQUEST_METHOD");
433 char *content_length_str = getenv("CONTENT_LENGTH");
434 if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
[508]435 content_length_str != NULL) {
[365]436 // POST form data
[387]437 int content_length = text_t(content_length_str).getint();
[508]438 if (content_length > 0) {
439 char c;
440 do {
441 cin.get(c);
442 if (cin.eof()) break;
443 argstr.push_back (c);
444 content_length--;
445 } while (content_length > 0);
[365]446 }
447
448 } else {
449 aURIStr = getenv("QUERY_STRING");
[508]450 if ((request_method_str != NULL && strcmp(request_method_str, "GET") == 0)
451 || aURIStr != NULL) {
[365]452 // GET form data
[508]453 if (aURIStr != NULL) argstr = aURIStr;
[365]454 } else {
455 // debugging from command line
456 char cinURIStr[1024];
457 cin.get(cinURIStr, 1024);
458 argstr = cinURIStr;
459 }
[144]460 }
[365]461
462 // cgi scripts only deal with one request
[144]463 maxrequests = 1;
464 }
465
466 // Page-request loop. If this is not being run as a fastcgi
467 // process then only one request will be processed and then
468 // the process will exit.
469 while (numrequests < maxrequests) {
470#ifdef USE_FASTCGI
471 if (isfastcgi) {
472 if (FCGX_Accept(&fcgiin, &fcgiout, &fcgierr, &fcgienvp) < 0) break;
473 aURIStr = FCGX_GetParam("QUERY_STRING", fcgienvp);
474 if (aURIStr != NULL) argstr = aURIStr;
475 else argstr = "";
476 }
477#endif
478
479 // get output streams ready
480#ifdef USE_FASTCGI
481 outbuf.fcgisbreset ();
482 if (isfastcgi) outbuf.set_fcgx_stream (fcgiout);
483 else outbuf.set_other_ostream (&cout);
484 ostream pageout (&outbuf);
485#else
486#define pageout cout
487#endif
[155]488
[872]489 // if using fastcgi we'll load environment into a map,
490 // otherwise simply pass empty map (can't get environment
491 // variables using getenv() while using FCGX versions
492 // of fastcgi - at least I can't ;-) - Stefan)
493 text_tmap fastcgienv;
494#ifdef USE_FASTCGI
495 if (isfastcgi) {
496 for(; *fcgienvp != NULL; fcgienvp++) {
497 text_t fvalue = *fcgienvp;
498 text_t::const_iterator begin = fvalue.begin();
499 text_t::const_iterator end = fvalue.end();
500 text_t::const_iterator equals_sign = findchar (begin, end, '=');
501 if (equals_sign != end)
502 fastcgienv[substr(begin, equals_sign)] = substr(equals_sign+1, end);
503 }
504 }
505#endif
506
507 // temporarily need to configure gwcgi here when using fastcgi as I can't
508 // get it to pass the SCRIPT_NAME environment variable to the initial
509 // environment (if anyone can work out how to do this using the apache
510 // server, let me know). Note that this overrides the gwcgi field in
511 // site.cfg (which it shouldn't do) but I can't at present set gwcgi
512 // from site.cfg as I have old receptionists laying around that wouldn't
513 // appreciate it. The following 5 lines of code should be deleted once
514 // I either a: get the server to pass SCRIPT_NAME at initialization
515 // time or b: convert all the collections using old receptionists over
516 // to this version and uncomment gwcgi in the site.cfg file -- Stefan.
517#ifdef USE_FASTCGI
518 if (isfastcgi) {
519 recpt.configure("gwcgi", fastcgienv["SCRIPT_NAME"]);
520 }
521#endif
522
523
[155]524 if (errorpage.empty()) {
525 ofstream errout (GSDL_GSDLHOME "/etc/errout.txt");
526 cerr = errout;
[144]527
[155]528 // parse the cgi arguments and produce the resulting page if there
529 // has been no errors so far
[872]530 if (!recpt.parse_cgi_args (argstr, args, errout, fastcgienv)) {
[155]531 errout.close ();
532 page_errorparseargs(GSDL_GSDLHOME, errorpage);
533 } else {
[872]534 if (!recpt.produce_cgi_page (args, pageout, errout, fastcgienv)) {
[155]535 errout.close ();
536 page_errorcgipage(GSDL_GSDLHOME, errorpage);
537 } else {
538 errout.close ();
539 }
[872]540 recpt.log_cgi_args (args, errout, fastcgienv);
[155]541 }
542 }
543 // there was an error, output the error page
544 if (!errorpage.empty()) {
545 pageout << text_t2ascii << errorpage;
546 errorpage.clear();
547 numrequests = maxrequests; // make this the last page
548 }
549 pageout << flush;
[144]550
551 // finish with the output streams
552#ifdef USE_FASTCGI
553 if (isfastcgi) FCGX_Finish();
554#endif
555
556 numrequests++;
557 }
558
559 return;
560}
561
Note: See TracBrowser for help on using the repository browser.