source: main/trunk/greenstone2/runtime-src/src/recpt/mod_gsdl.cpp@ 30465

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

Dr Bainbridge has introduced the isPersistentAction (add the a=is-persistent to the library url). It is true for server.exe and when using mod_gsdl, but false for library.cgi which uses the apache web server (when not using mod_gsdl).

File size: 17.2 KB
Line 
1/**********************************************************************
2 *
3 * mod_gsdl.cpp -- front end for apache 1.3 or 2.x module
4 * Copyright (C) 2003 DL Consulting Ltd
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 <iostream>
27using namespace std;
28
29#include "httpd.h"
30#include "http_core.h"
31#include "http_config.h"
32#include "http_protocol.h"
33#include "http_main.h"
34#include "util_script.h"
35#include "ap_config.h"
36#include "http_log.h"
37
38
39#if _APACHE_MOD >= 2
40
41#include "ap_compat.h"
42
43// In addition to the backward compatible features defined in
44// ap_compat.h, define
45
46typedef apr_pool_t ap_pool; // map old type to new apr_* version
47
48// In Apache 2.x, ap_log_error takes extra parameter -- status code
49// from the previous command; define macro to automatically add in
50// surrogate value of 0 for this
51#define ap_log_error(file,line,level,server,fmt) \
52 ap_log_error(file,line,level,0,server,fmt)
53
54#endif
55
56#if defined(USE_MYSQL)
57#include "mysqlclass.h"
58#endif
59#if defined(USE_ACCESS)
60#include "accessclass.h"
61#endif
62
63#include "maincfg.h"
64#include "string_pool.h"
65
66#include "receptionist.h"
67#include "cgiwrapper.h"
68#include "cgiutils.h"
69#include "fileutil.h"
70#include "nullproto.h"
71#include "collectserver.h"
72#include "filter.h"
73#include "browsefilter.h"
74
75//#ifdef ENABLE_MG
76//#include "mgqueryfilter.h"
77//#include "mgsearch.h"
78//#endif
79
80#include "infodbclass.h"
81#include "collectset.h"
82
83#include "gdbmclass.h"
84
85#include "action.h"
86#include "ispersistentaction.h"
87#include "browserclass.h"
88
89#define GSDL_USE_OSTRINGSTREAM 1
90// ostrstream is deprecated => switch to using ostringstream
91// Doing this with the above define so it is easy to switch
92// back to ostrstream this backwards compatability is needed
93// for some reason.
94
95#if defined(GSDL_USE_OSTRINGSTREAM)
96# include <sstream>
97#else
98# if defined(GSDL_USE_IOS_H)
99# if defined(__WIN32__)
100# include <strstrea.h> // vc4
101# else
102# include <strstream.h>
103# endif
104# else
105# include <strstream>
106# endif
107#endif
108
109receptionist recpt;
110nullproto nproto;
111
112extern "C" module MODULE_VAR_EXPORT gsdl_module;
113
114typedef struct {
115 char *config_gsdlhome;
116 char *config_collecthome;
117 char *config_httpprefix;
118 char *config_httpweb;
119} gsdl_config;
120
121static void *gsdl_create_config(ap_pool *p, server_rec *s) {
122
123#if _APACHE_MOD >= 2
124 gsdl_config *cfg = (gsdl_config *)apr_pcalloc(p, sizeof(gsdl_config));
125#else
126 gsdl_config *cfg = (gsdl_config *)ap_pcalloc(p, sizeof(gsdl_config));
127#endif
128
129 return (void *)cfg;
130}
131
132static void *gsdl_create_dir_config(ap_pool *p, char *path) {
133
134 return (gsdl_config *) gsdl_create_config(p, NULL);
135}
136
137static const char *gsdl_cmd(cmd_parms *cmd, void *mconfig,
138 char *val) {
139
140 server_rec *s = cmd->server;
141 gsdl_config *cfg = (gsdl_config *) ap_get_module_config(s->module_config, &gsdl_module);
142
143 if (strcmp(cmd->cmd->name, "gsdlhome") == 0) {
144 cfg->config_gsdlhome = val;
145 } else if (strcmp(cmd->cmd->name, "collecthome") == 0) {
146 cfg->config_collecthome = val;
147 } else if (strcmp(cmd->cmd->name, "httpprefix") == 0) {
148 cfg->config_httpprefix = val;
149 } else if (strcmp(cmd->cmd->name, "httpweb") == 0) {
150 cfg->config_httpweb = val;
151 }
152
153 return NULL;
154}
155
156#if _APACHE_MOD < 2
157static void gsdl_exit(void* d) {
158#else
159static apr_status_t gsdl_exit(void* d) {
160#endif
161
162#if defined(USE_ACCESS)
163 // shut down com
164 CoUninitialize();
165#endif
166
167#if _APACHE_MOD >= 2
168 return APR_SUCCESS;
169#endif
170}
171
172#if _APACHE_MOD < 2
173static void gsdl_init(server_rec *s, ap_pool *p) {
174#else
175static void gsdl_init(ap_pool *p, server_rec *s) {
176#endif
177
178 // Register gsdl_exit has cleanup operation for when pchild (process id
179 // of child) exits
180#if _APACHE_MOD >= 2
181 apr_pool_cleanup_register(p,s, apr_pool_cleanup_null, gsdl_exit);
182#else
183 ap_register_cleanup(p,s, ap_null_cleanup, gsdl_exit);
184#endif
185
186 recpt.loaded = false;
187
188 gsdl_config *cfg = (gsdl_config *) ap_get_module_config(s->module_config, &gsdl_module);
189
190 if (!cfg->config_gsdlhome) {
191 ap_log_error("", 0, APLOG_ERR, s, "gsdlhome not set\n");
192 return;
193 }
194 text_t gsdlhome = cfg->config_gsdlhome;
195
196 if (!directory_exists(gsdlhome)) {
197 ap_log_error("", 0, APLOG_ERR, s, "gsdlhome directory does not exist\n");
198 return;
199 }
200
201 if (!cfg->config_collecthome) {
202 ap_log_error("", 0, APLOG_ERR, s, "collecthome not set\n");
203 return;
204 }
205 text_t collecthome = cfg->config_collecthome;
206
207 if (!directory_exists(collecthome)) {
208 ap_log_error("", 0, APLOG_ERR, s, "collecthome directory does not exist\n");
209 return;
210 }
211
212
213 if (!cfg->config_httpprefix) {
214 ap_log_error("", 0, APLOG_ERR, s, "httpprefix not set\n");
215 return;
216 }
217
218 if (!cfg->config_httpweb) {
219 ap_log_error("", 0, APLOG_ERR, s, "httpweb not set\n");
220 return;
221 }
222
223 text_t httpprefix = cfg->config_httpprefix;
224 text_t httpweb = cfg->config_httpweb;
225
226 text_t collection = "";
227
228 collectset *cservers = new collectset();
229
230 cservers->add_all_collections(gsdlhome,collecthome);
231
232 // set up the null protocol
233 nproto.set_collectset(cservers);
234
235 // add the protocol to the receptionist
236 recpt.add_protocol(&nproto);
237
238 // add database object to receptionist
239#if defined(USE_MYSQL)
240 mysqlclass *db = new mysqlclass();
241#elif defined(USE_ACCESS)
242 // start up com
243 if(FAILED(CoInitialize(NULL))) {
244 cout << "CoInitialize failed\n";
245 exit(-1);
246 }
247 accessclass *db = new accessclass();
248#else
249
250 // not using either mysql or access database - we'll use the gdbmclass
251 // class as a filler (but most likely won't work) - only useful
252 // if creating a binary that doesn't require access to the database
253 dbclass *db = new gdbmclass(gsdlhome);
254
255#endif
256
257#if defined(USE_MYSQL) || defined(USE_ACCESS)
258 recpt.add_dbclass(db);
259#endif
260
261 userdbclass *udb = new userdbclass(gsdlhome);
262 keydbclass *kdb = new keydbclass(gsdlhome);
263 // do these two database classes ever get deleted in the module version
264 // of Greenstone?
265
266 // librarymain.cpp deletes these when its finished its function
267 // does comparable code need to be put into "gsdl_deinit()" routine
268 // whatever that might be called in reality?
269
270 add_all_actions(recpt,udb,kdb,isPersistent); // apache is a persistent server with mod_gsdl
271 add_all_browsers(recpt);
272
273
274 // configure everything
275 recpt.configure("gsdlhome", gsdlhome);
276 recpt.configure("collecthome", collecthome);
277 recpt.configure("collection", collection);
278 recpt.configure("httpweb", httpweb);
279 recpt.configure("httpprefix", httpprefix);
280 // set httpprefixfull - if httpprefix is a relative path we need to get
281 // the server name and port from the web server environment (note that
282 // gwcgi and gwcgifull will be configured for each page request (in gsdl_handler)
283 // as we need to know that directory info etc.)
284 text_t httpprefixfull = httpprefix;
285 if ((httpprefixfull.size() < 7) ||
286 (substr(httpprefixfull.begin(), httpprefixfull.begin()+7) != "http://")) {
287 if (s->server_hostname && s->port) {
288 httpprefixfull = "http://" + text_t(s->server_hostname) + ":" + text_t(s->port) + httpprefixfull;
289 }
290 }
291 recpt.configure("httpprefixfull", httpprefixfull);
292
293 // read in main.cfg file
294 if (!main_cfg_read(recpt, gsdlhome, collecthome, collection)) {
295 ap_log_error("", 0, APLOG_ERR, s, "failed to read main.cfg file\n");
296 return;
297 }
298
299 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
300 char *eout = error_file.getcstr();
301 ofstream errout (eout, ios::app);
302 delete eout;
303
304 // initialise the library software
305 if (!recpt.init(errout)) {
306 ap_log_error("", 0, APLOG_ERR, s, "Greenstone failed to initialize\n");
307 errout.close();
308 return;
309 }
310
311 errout.close();
312
313 // set flag so gsdl_handler can check that everything initialized
314 // successfully
315 recpt.loaded = true;
316}
317
318
319#if _APACHE_MOD < 2
320
321typedef const char *(*CMD_HAND_TYPE) ();
322static command_rec gsdl_cmds[] = {
323 {"gsdlhome", (CMD_HAND_TYPE)gsdl_cmd,
324 (void *)XtOffsetOf(gsdl_config, config_gsdlhome),
325 OR_ALL, TAKE1, "GSDLHOME directory."},
326 {"collecthome", (CMD_HAND_TYPE)gsdl_cmd,
327 (void *)XtOffsetOf(gsdl_config, config_collecthome),
328 OR_ALL, TAKE1, "COLLECTHOME directory."},
329 {"httpprefix", (CMD_HAND_TYPE)gsdl_cmd,
330 (void *)XtOffsetOf(gsdl_config, config_httpprefix),
331 OR_ALL, TAKE1, "GSDLHOME http path."},
332 {"httpweb", (CMD_HAND_TYPE)gsdl_cmd,
333 (void *)XtOffsetOf(gsdl_config, config_httpweb),
334 OR_ALL, TAKE1, "GSDLHOME http web path."},
335 {NULL, NULL, NULL, 0, cmd_how(0), NULL}
336};
337#else
338
339/*
340static const char
341*set_max_cache_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
342{
343 apr_size_t val;
344
345 return NULL;
346}
347
348 AP_INIT_TAKE1("MCacheSize", set_max_cache_size, NULL, RSRC_CONF,
349 "The maximum amount of memory used by the cache in KBytes"),
350
351*/
352
353// const char *(*cmd_func)();
354
355static const command_rec gsdl_cmds[] = {
356 AP_INIT_TAKE1("gsdlhome", (cmd_func)gsdl_cmd, NULL, OR_ALL,"GSDLHOME directory."),
357 AP_INIT_TAKE1("collecthome", (cmd_func)gsdl_cmd, NULL, OR_ALL,"COLLECTHOME directory."),
358 AP_INIT_TAKE1("httpprefix", (cmd_func)gsdl_cmd, NULL, OR_ALL,"GSDLHOME http path."),
359 AP_INIT_TAKE1("httpweb", (cmd_func)gsdl_cmd, NULL, OR_ALL,"GSDLHOME http web path."),
360 {NULL}
361};
362
363
364#endif
365
366static int gsdl_handler(request_rec *r) {
367
368 if (strcmp(r->handler, "gsdl")) {
369 return DECLINED;
370 }
371
372 // check that everything has been initialized
373 if (!recpt.loaded) {
374 ap_log_error("", 0, APLOG_ERR, r->server, "gsdl_handler: attempt to use uninitialized receptionist - aborting\n");
375 return 500;
376 }
377
378 gsdl_config *cfg = (gsdl_config *) ap_get_module_config(r->server->module_config, &gsdl_module);
379
380 if (!cfg->config_gsdlhome) {
381 ap_log_error("", 0, APLOG_ERR, r->server, "gsdl_handler: gsdlhome not set\n");
382 return 500;
383 }
384
385 if (!cfg->config_collecthome) {
386 ap_log_error("", 0, APLOG_ERR, r->server, "gsdl_handler: collecthome not set\n");
387 return 500;
388 }
389
390 // configure gwcgi and gwcgifull
391 recpt.configure("gwcgi", r->uri);
392 recpt.configure("gwcgifull", "http://" + text_t(r->server->server_hostname) +
393 ":" + text_t(r->server->port) + r->uri);
394
395 string_pool str_pool;
396
397 text_t gsdlhome = cfg->config_gsdlhome;
398
399 text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
400 char *eout = str_pool.get_cstr_from_pool(error_file);
401 ofstream errout (eout, ios::app);
402
403 text_t argstr;
404 fileupload_tmap fileuploads;
405
406 char *request_method_str = getenv("REQUEST_METHOD");
407 char *content_length_str = getenv("CONTENT_LENGTH");
408 if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
409 content_length_str != NULL) {
410 // POST form data
411 long content_length = (content_length_str ? atoi(content_length_str) : 0);
412 if (content_length > 0) {
413#ifdef __WIN32__
414 // On Windows it is important that standard input be read in binary
415 // mode, otherwise end of line "<CR><LF>" is turned into <LF> only
416 // which breaks the MIME standard (and our parsing code!)
417
418 int result = _setmode( _fileno( stdin ), _O_BINARY );
419 if( result == -1 ) {
420 cerr << "Warning: Failed to set standard input to binary mode." << endl;
421 cerr << " Parsing of multi-part MIME will most likely fail" << endl;
422 }
423#endif
424
425 long length = content_length;
426 unsigned char * buffer = new unsigned char[content_length];
427
428 int chars_read = fread(buffer,1,content_length,stdin);
429
430 if (chars_read != content_length) {
431 cerr << "Warning: mismatch between CONTENT_LENGTH and data read from standard in" << endl;
432 }
433
434 argstr.setcarr((char *)buffer, content_length);
435
436 text_t content_type;
437 char *content_type_str = getenv("CONTENT_TYPE");
438 if (content_type_str) content_type = content_type_str;
439 argstr = parse_post_data(content_type, argstr, fileuploads, gsdlhome);
440 }
441 } else {
442
443 if (r->args) argstr = r->args;
444 }
445
446 // parse the cgi arguments and produce the resulting page if there
447 // have been no errors so far
448 cgiargsclass args;
449 text_tmap fastcgienv; // Not being used in this case
450 if (!recpt.parse_cgi_args (argstr, fileuploads, args, errout, fastcgienv)) {
451 errout.close();
452 ap_log_error("", 0, APLOG_ERR, r->server, "parse_cgi_args failed\n");
453 return 500;
454 }
455
456#ifdef GSDL_USE_OSTRINGSTREAM
457 ostringstream pageout;
458#else
459 ostrstream pageout;
460#endif
461
462 // get http headers
463 text_tmap headers;
464 recpt.get_cgihead_info(args, headers, errout, fastcgienv);
465
466 if (headers.find("Location") != headers.end()) {
467 // redirect
468 char *val = str_pool.get_cstr_from_pool(headers.find("Location")->second);
469#if _APACHE_MOD >= 2
470 apr_table_set(r->headers_out, "location", val);
471#else
472 ap_table_set(r->headers_out, "location", val);
473#endif
474 return HTTP_MOVED_TEMPORARILY;
475 }
476
477 text_tmap::iterator here = headers.begin();
478 text_tmap::iterator end = headers.end();
479 while (here != end) {
480 text_t namet = (*here).first;
481 lc(namet);
482 char *name = str_pool.get_cstr_from_pool(namet);
483 char *val = str_pool.get_cstr_from_pool((*here).second);
484
485 if ((*here).first == "content-type") {
486#if _APACHE_MOD < 2
487 r->content_type = val;
488#else
489
490 char* val_copy = strdup(val); // if note freed by r->content_type => Memory leak!
491 ap_set_content_type(r, val_copy);
492#endif
493 } else if ((*here).first == "content-encoding") {
494 r->content_encoding = val;
495 } else {
496#if _APACHE_MOD >= 2
497 apr_table_set(r->headers_out, name, val);
498#else
499 ap_table_set(r->headers_out, name, val);
500#endif
501 }
502
503 here++;
504 }
505 ap_send_http_header(r);
506
507 if (!r->header_only) {
508 if (!recpt.produce_content(args, pageout, errout)) {
509 ap_log_error("", 0, APLOG_ERR, r->server, "produce_content failed\n");
510 return 500;
511 }
512#ifdef GSDL_USE_OSTRINGSTREAM
513 string out = pageout.str();
514 ap_rwrite(out.c_str(), out.size(), r);
515#else
516 char *out = pageout.str();
517 ap_rwrite(out, pageout.pcount(), r);
518 pageout.rdbuf()->freeze(0);
519#endif
520 }
521
522 errout.close();
523
524 return OK;
525}
526
527#if _APACHE_MOD < 2
528
529static const handler_rec gsdl_handlers[] = {
530 { "gsdl", gsdl_handler },
531 { NULL, NULL }
532};
533
534
535extern "C" {
536 module MODULE_VAR_EXPORT gsdl_module = {
537 STANDARD_MODULE_STUFF,
538 NULL, /* module \initializer */
539 gsdl_create_dir_config, /* create per-dir config structures */
540 NULL, /* merge per-dir config structures */
541 gsdl_create_config, /* create per-server config structures */
542 NULL, /* merge per-server config structures */
543 gsdl_cmds, /* table of config file commands */
544 gsdl_handlers, /* [#8] MIME-typed-dispatched handlers */
545 NULL, /* [#1] URI to filename translation */
546 NULL, /* [#4] validate user id from request */
547 NULL, /* [#5] check if the user is ok _here_ */
548 NULL, /* [#3] check access by host address */
549 NULL, /* [#6] determine MIME type */
550 NULL, /* [#7] pre-run fixups */
551 NULL, /* [#9] log a transaction */
552 NULL, /* [#2] header parser */
553 gsdl_init, /* child_init */
554 NULL, /* child_exit */
555 NULL /* [#0] post read-request */
556#ifdef EAPI
557 ,NULL, /* EAPI: add_module */
558 NULL, /* EAPI: remove_module */
559 NULL, /* EAPI: rewrite_command */
560 NULL, /* EAPI: new_connection */
561 NULL /* EAPI: close_connection */
562#endif
563 };
564};
565
566#else
567
568static void gsdl_register_hooks(ap_pool *p)
569{
570 ap_hook_child_init(gsdl_init,NULL,NULL,APR_HOOK_MIDDLE);
571 ap_hook_handler(gsdl_handler, NULL, NULL, APR_HOOK_MIDDLE);
572
573}
574
575extern "C" {
576 module AP_MODULE_DECLARE_DATA gsdl_module = {
577 STANDARD20_MODULE_STUFF,
578 gsdl_create_dir_config, /* create per-dir config structures */
579 NULL, /* merge per-dir config structures */
580 gsdl_create_config, /* create per-server config structures */
581 NULL, /* merge per-server config structures */
582 gsdl_cmds, /* command handlers */
583 gsdl_register_hooks, /* set up other request processing hooks */
584 };
585};
586#endif
Note: See TracBrowser for help on using the repository browser.