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

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

Changes necessary to support new sql-query action

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