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

Last change on this file since 22142 was 22142, checked in by davidb, 14 years ago

For the CGI 'e' variable to be inter-changable between mod_gsdl and library.cgi then they need to have exactly the same actions. The code has been refactored so they now use a shared function to do this, the ensure this is the case.

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