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

Last change on this file since 872 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
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 * $Id: cgiwrapper.cpp 872 2000-01-25 22:45:59Z sjboddie $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.21 2000/01/25 22:45:59 sjboddie
31 few changes to get fastcgi to work properly
32
33 Revision 1.20 1999/09/07 04:56:53 sjboddie
34 added GPL notice
35
36 Revision 1.19 1999/09/02 00:24:36 rjmcnab
37 fixed bug in getting POST arguments
38
39 Revision 1.18 1999/08/20 01:02:07 sjboddie
40 added some usage logging
41
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
46 Revision 1.16 1999/07/14 08:31:05 rjmcnab
47 Fixed a small bug in the POST implementation.
48
49 Revision 1.15 1999/07/13 23:32:17 rjmcnab
50 Added authenaction and usersaction
51
52 Revision 1.14 1999/07/11 01:03:37 rjmcnab
53 Added ability to receive POST cgi form data.
54
55 Revision 1.13 1999/06/24 05:12:18 sjboddie
56 lots of small changes
57
58 Revision 1.12 1999/04/30 01:59:40 sjboddie
59 lots of stuff - getting documentaction working (documentaction replaces
60 old browseaction)
61
62 Revision 1.11 1999/03/25 03:12:01 sjboddie
63
64 subjectbrowseaction was replaced with browseaction
65
66 Revision 1.10 1999/03/05 03:53:54 sjboddie
67
68 fixed some bugs
69
70 Revision 1.9 1999/03/04 22:38:21 sjboddie
71
72 Added subjectbrowseaction. - Doesn't do anything yet.
73
74 Revision 1.8 1999/02/28 20:00:13 rjmcnab
75
76
77 Fixed a few things.
78
79 Revision 1.7 1999/02/21 22:33:53 rjmcnab
80
81 Lots of stuff :-)
82
83 Revision 1.6 1999/02/12 02:40:17 sjboddie
84
85 Added page action
86
87 Revision 1.5 1999/02/11 01:24:04 rjmcnab
88
89 Fixed a few compiler warnings.
90
91 Revision 1.4 1999/02/08 01:28:01 rjmcnab
92
93 Got the receptionist producing something using the statusaction.
94
95 Revision 1.3 1999/02/05 10:42:44 rjmcnab
96
97 Continued working on receptionist
98
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
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"
121#include "recptconfig.h"
122#include <stdlib.h>
123#include <assert.h>
124
125
126// get GSDL_GSDLHOME
127#include "gsdlhome.h"
128
129
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
145
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
195static void page_errorsitecfg (const text_t &gsdlhome, const text_t &collection,
196 text_t &errorpage) {
197 errorpage += "Content-type: text/html\n\n";
198
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}
226
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";
238 if (collection.empty()) {
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";
245 } else {
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";
259 }
260 errorpage += "</body>\n";
261 errorpage += "</html>\n";
262}
263
264
265static void page_errorinit (const text_t &/*gsdlhome*/, text_t &errorpage) {
266 errorpage += "Content-type: text/html\n\n";
267
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";
277
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
295 } else {
296 errorpage += "Please consult " GSDL_GSDLHOME "/etc/initout.txt for more information.\n";
297 }
298
299 errorpage += "</body>\n";
300 errorpage += "</html>\n";
301}
302
303static void page_errorparseargs (const text_t &/*gsdlhome*/, text_t &errorpage) {
304 errorpage += "Content-type: text/html\n\n";
305
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";
313
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";
319
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";
335}
336
337static void page_errorcgipage (const text_t &/*gsdlhome*/, text_t &errorpage) {
338 errorpage += "Content-type: text/html\n\n";
339
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
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 "".
376void cgiwrapper (receptionist &recpt, text_t collection) {
377#ifdef USE_FASTCGI
378 fcgistreambuf outbuf;
379#endif
380
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;
385
386 // set defaults
387 int maxrequests = 10000;
388 recpt.configure ("gsdlhome", GSDL_GSDLHOME);
389 recpt.configure ("collection", collection);
390 recpt.configure ("httpimg", "/gsdl/images");
391 char *script_name = getenv("SCRIPT_NAME");
392 if (script_name != NULL) recpt.configure("gwcgi", script_name);
393 else recpt.configure("gwcgi", "/cgi-bin/gw");
394
395 // read in the configuration files.
396 if (!site_cfg_read (recpt, GSDL_GSDLHOME, collection, maxrequests)) {
397 // couldn't find the site configuration file
398 page_errorsitecfg (GSDL_GSDLHOME, collection, errorpage);
399 } else if (!main_cfg_read (recpt, GSDL_GSDLHOME, collection)) {
400 // couldn't find the main configuration file
401 page_errormaincfg (GSDL_GSDLHOME, collection, errorpage);
402 }
403
404 // initialise the library software
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 }
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 = "";
429 cgiargsclass args;
430 char *aURIStr;
431 if (!isfastcgi) {
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 &&
435 content_length_str != NULL) {
436 // POST form data
437 int content_length = text_t(content_length_str).getint();
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);
446 }
447
448 } else {
449 aURIStr = getenv("QUERY_STRING");
450 if ((request_method_str != NULL && strcmp(request_method_str, "GET") == 0)
451 || aURIStr != NULL) {
452 // GET form data
453 if (aURIStr != NULL) argstr = aURIStr;
454 } else {
455 // debugging from command line
456 char cinURIStr[1024];
457 cin.get(cinURIStr, 1024);
458 argstr = cinURIStr;
459 }
460 }
461
462 // cgi scripts only deal with one request
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
488
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
524 if (errorpage.empty()) {
525 ofstream errout (GSDL_GSDLHOME "/etc/errout.txt");
526 cerr = errout;
527
528 // parse the cgi arguments and produce the resulting page if there
529 // has been no errors so far
530 if (!recpt.parse_cgi_args (argstr, args, errout, fastcgienv)) {
531 errout.close ();
532 page_errorparseargs(GSDL_GSDLHOME, errorpage);
533 } else {
534 if (!recpt.produce_cgi_page (args, pageout, errout, fastcgienv)) {
535 errout.close ();
536 page_errorcgipage(GSDL_GSDLHOME, errorpage);
537 } else {
538 errout.close ();
539 }
540 recpt.log_cgi_args (args, errout, fastcgienv);
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;
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.