root/main/trunk/greenstone2/runtime-src/src/recpt/cgiwrapper.cpp @ 27172

Revision 27172, 31.9 KB (checked in by kjdon, 7 years ago)

For diego: when doing cross collection searching, now it takes into account authentication directives for the collections in the list. If a user has authenticated to get into the top collection, then his user groups are checked against the groups for all the collections. If he matches any, then they will be searched. But if he is not a member of the right group they will not be searched. If there was no authentication needed to get into top colleciton, then any collections with collection-level authentication will not be searched.

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