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

Last change on this file since 20999 was 19259, checked in by davidb, 15 years ago

Revap of Greenstone as a module for Apache. Initial version coded by DL Consulting

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