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

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

Inclusion of iostream for printing

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