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

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

gsdlhome now comes from gsdlsite.cfg

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