source: trunk/gsdl/src/w32server/cgiwrapper.cpp@ 611

Last change on this file since 611 was 611, checked in by sjboddie, 25 years ago

initial commit of windows server code

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1#include <windows.h>
2#include <string.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <assert.h>
6#include <direct.h>
7#include "cgiwrapper.h"
8#include "netio.h"
9#include "wincgiutils.h"
10#include "settings.h"
11
12#include "gsdlconf.h"
13#include "gsdlhome.h"
14#include "recptconfig.h"
15
16#if defined (GSDL_USE_OBJECTSPACE)
17#include <ospace\std\iostream>
18#include <ospace\std\fstream>
19#elif defined (GSDL_USE_IOS_H)
20#include <iostream.h>
21#include <fstream.h>
22#else
23#include <iostream>
24#include <fstream>
25#endif
26
27#include "receptionist.h"
28#include "nullproto.h"
29#include "collectserver.h"
30#include "infodbclass.h"
31#include "mggdbmsource.h"
32
33
34// actions
35#include "pageaction.h"
36#include "pingaction.h"
37#include "queryaction.h"
38#include "documentaction.h"
39#include "statusaction.h"
40#include "usersaction.h"
41#include "authenaction.h"
42#include "tipaction.h"
43
44// filters
45#include "filter.h"
46#include "browsefilter.h"
47#include "queryfilter.h"
48#include "phrasequeryfilter.h"
49
50// the number of times the library has been accessed
51int libaccessnum = 0;
52
53// used to output the text from receptionist
54class textstreambuf : public streambuf
55{
56public:
57 textstreambuf ();
58 int sync ();
59 int overflow (int ch);
60 int underflow () {return EOF;}
61
62 void tsbreset() {RInfo=NULL;casostr=NULL;}
63 void setrequestinfo (RequestInfoT *theRInfo) {RInfo=theRInfo;}
64 void cascadeoutput (ostream *thecasostr) {casostr=thecasostr;}
65
66private:
67 RequestInfoT *RInfo;
68 ostream *casostr;
69};
70
71textstreambuf::textstreambuf() {
72 tsbreset();
73 if (base() == ebuf()) allocate();
74 setp (base(), ebuf());
75};
76
77int textstreambuf::sync () {
78 if ((RInfo != NULL) &&
79 (Send_String_N(pbase(), out_waiting(), RInfo) < 0)) {
80 RInfo = NULL;
81 }
82
83 if (casostr != NULL) {
84 char *thepbase=pbase();
85 for (int i=0;i<out_waiting();i++) (*casostr).put(thepbase[i]);
86 }
87
88 setp (pbase(), epptr());
89
90 return 0;
91}
92
93int textstreambuf::overflow (int ch) {
94 if (sync () == EOF) return EOF;
95 if (ch != EOF) sputc (ch);
96 return 0;
97}
98
99
100// used to output all the log and error messages
101// from receptionist
102class logstreambuf : public streambuf
103{
104public:
105 logstreambuf ();
106 int sync ();
107 int overflow (int ch);
108 int underflow () {return EOF;}
109};
110
111logstreambuf::logstreambuf () {
112 if (base() == ebuf()) allocate();
113 setp (base(), ebuf());
114}
115
116int logstreambuf::sync () {
117 if (gs_keep_log || gs_show_console) {
118 log_message ("LOCAL LIB MESSAGE: ");
119 log_message_N (pbase(), out_waiting());
120 }
121
122 setp (pbase(), epptr());
123 return 0;
124}
125
126int logstreambuf::overflow (int ch) {
127 if (sync () == EOF) return EOF;
128 if (ch != EOF) sputc (ch);
129 return 0;
130}
131
132
133
134#ifndef MAX_FILENAME_SIZE
135#define MAX_FILENAME_SIZE 2048
136#endif
137
138
139receptionist recpt;
140nullproto nproto;
141textstreambuf textstream;
142logstreambuf logstream;
143DWORD lastlibaccesstime;
144DWORD baseavailvirtual;
145
146
147static void page_errorsitecfg (const text_t &gsdlhome, const text_t &collection) {
148
149 text_t message = "Error\n\n"
150 "The site.cfg configuration file could not be found. This file\n"
151 "should contain configuration information relating to this sites\n"
152 "setup.\n";
153
154 if (collection.empty()) {
155 message += "As this program is not being run in collection specific mode,\n"
156 "the file should reside at " + gsdlhome + "\\etc\\site.cfg.\n";
157 } else {
158 message += "As this program is being run in collection specific mode,\n"
159 "the file can reside at " + gsdlhome + "\\collect\\" + collection +
160 "\\etc\\site.cfg or " + gsdlhome + "\\etc\\site.cfg.\n";
161 }
162
163 MessageBox(NULL, message.getcstr(),
164 "Greenstone Digital Library Software"
165 ,MB_OK|MB_SYSTEMMODAL);
166}
167
168static void page_errormaincfg (const text_t &gsdlhome, const text_t &collection) {
169
170 if (collection.empty()) {
171 text_t message = "Error\n\n"
172 "The main.cfg configuration file could not be found. This file\n"
173 "should contain configuration information relating to the\n"
174 "setup of the interface. As this program is not being run\n"
175 "in collection specific mode the file should reside at\n" +
176 gsdlhome + "\\etc\\main.cfg.\n";
177
178 MessageBox(NULL, message.getcstr(),
179 "Greenstone Digital Library Software"
180 ,MB_OK|MB_SYSTEMMODAL);
181 } else {
182 text_t message = "Neither the collect.cfg or main.cfg configuration files could\n"
183 "be found. This file should contain configuration information\n"
184 "relating to the setup of the interface. As this cgi script is\n"
185 "being run in collection specific mode the file should reside\n"
186 "at either " + gsdlhome + "\\collect\\" + collection + "\\etc\\collect.cfg,\n" +
187 gsdlhome + "\\etc\\collect.cfg or " + gsdlhome + "\\etc\\main.cfg.\n";
188
189 MessageBox(NULL, message.getcstr(),
190 "Greenstone Digital Library Software"
191 ,MB_OK|MB_SYSTEMMODAL);
192 }
193}
194
195static void page_errorinit (const text_t &/*gsdlhome*/) {
196
197 text_t message = "Error\n\n"
198 "An error occurred during the initialisation of the Greenstone Digital\n"
199 "Library software. It is likely that the software has not been setup\n"
200 "correctly.\n";
201
202 MessageBox(NULL, message.getcstr(),
203 "Greenstone Digital Library Software"
204 ,MB_OK|MB_SYSTEMMODAL);
205}
206
207static void page_errorparseargs (const text_t &gsdlhome) {
208
209 text_t message = "Error\n\n"
210 "An error occurred during the parsing of the cgi arguments.\n";
211
212 MessageBox(NULL, message.getcstr(),
213 "Greenstone Digital Library Software"
214 ,MB_OK|MB_SYSTEMMODAL);
215}
216
217static void page_errorcgipage (const text_t &gsdlhome) {
218
219 text_t message = "Error\n\n"
220 "An error occurred during the construction of the cgi page.\n";
221
222 MessageBox(NULL, message.getcstr(),
223 "Greenstone Digital Library Software"
224 ,MB_OK|MB_SYSTEMMODAL);
225}
226
227// returns 0 if the directories can't be found
228// and the user wants to quit (it returns 1
229// if everything is ok)
230int checkdir (const text_t &thedir) {
231 UINT curerrormode;
232 int drive = _getdrive();
233 char cwd[1024];
234 char rootpath[4];
235 UINT drivetype;
236 char *cstrthedir = thedir.getcstr();
237 int returnvalue = 1;
238
239 // make sure no rude error messages are presented to the user
240 curerrormode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
241
242 // get the drive name
243 if (thedir.size() >= 2 && thedir[1] == ':') {
244 if (thedir[0] >= 'a' && thedir[0] <= 'z') {
245 drive = thedir[0] - 'a' + 1;
246
247 } else if (thedir[0] >= 'A' && thedir[0] <= 'Z') {
248 drive = thedir[0] - 'A' + 1;
249 }
250 }
251
252 // find the drive type
253 rootpath[0] = drive + 'A' - 1;
254 rootpath[1] = ':';
255 rootpath[2] = '\\';
256 rootpath[3] = '\0';
257 drivetype = GetDriveType (rootpath);
258
259 // try and set this directory to be the current working
260 // directory
261 _getcwd(cwd, 1024);
262 while (_chdir(cstrthedir) != 0) {
263 // failed
264 if (drivetype == DRIVE_CDROM) {
265 // message to insert the cdrom
266 if (MessageBox (NULL,
267 "Please insert the Greenstone Digital Library\n"
268 "CD-ROM into the CD-ROM drive and press 'OK'.\n\n"
269 "If you don't have the CD-ROM press 'Cancel' to exit\n"
270 "this program.", "Greenstone Digital Library Software",
271 MB_OKCANCEL | MB_TASKMODAL) == IDCANCEL) {
272
273 returnvalue = 0;
274 break;
275 }
276
277 } else {
278 // message saying that the system was unable
279 // to find a certain directory
280 text_t message = "Failed to find the directory:\n\n" + thedir;
281 message += "\n\n"
282 "This directory is needed for the successful operation\n"
283 "of this software. Make sure it hasn't been deleted or\n"
284 "moved, and restart the software. You may need to\n"
285 "reinstall this software to correct the problem.";
286 char *cstrmessage = message.getcstr();
287
288 MessageBox (NULL, cstrmessage, "Greenstone Digital Library Software",
289 MB_OK | MB_TASKMODAL);
290
291 delete cstrmessage;
292
293 returnvalue = 0;
294 break;
295 }
296 }
297
298 // revert to the previous error and cwd states
299 _chdir(cwd);
300 SetErrorMode(curerrormode);
301
302 // free the allocated C string
303 delete cstrthedir;
304
305 return returnvalue;
306}
307
308
309// c-string version of checkdir for the outside
310// world
311int cstrcheckdir (char *cstrthedir) {
312 return checkdir (cstrthedir);
313}
314
315
316// returns 1 if successful, 0 if unsuccessful
317int gsdl_init () {
318 cerr = &logstream;
319 cout = &textstream;
320
321 // collection should be set to "" unless in
322 // collection specific mode
323 text_t collection = "";
324
325 // note the current time
326 lastlibaccesstime = GetTickCount();
327
328 // before we do the init we should make sure
329 // that we can find the relevant directories
330 if (!checkdir (GSDL_GSDLHOME "\\")) return 0;
331 if (!checkdir (GSDL_GSDLHOME "\\macros\\")) return 0;
332
333 // add a collection server for each collection
334 text_tarray collections;
335 ifstream collist (GSDL_GSDLHOME "/etc/collections.txt");
336 if (!collist) {
337 exit(1);
338 }
339 char col[100];
340 while (!collist.eof()) {
341 collist.getline (col, 100);
342 if (col[0] != '\0')
343 collections.push_back (col);
344 }
345 collist.close();
346
347 text_tarray::const_iterator thiscol = collections.begin();
348 text_tarray::const_iterator endcol = collections.end();
349
350 while (thiscol != endcol) {
351
352 // this memory is created but never destroyed
353 // we're also not doing any error checking to make sure we didn't
354 // run out of memory
355 collectserver *cserver = new collectserver();
356 gdbmclass *gdbmhandler = new gdbmclass();
357 mgsearchclass *mgsearch = new mgsearchclass();
358
359 // add a null filter
360 filterclass *filter = new filterclass();
361 cserver->add_filter (filter);
362
363 // add a browse filter
364 browsefilterclass *browsefilter = new browsefilterclass();
365 browsefilter->set_gdbmptr (gdbmhandler);
366 cserver->add_filter (browsefilter);
367
368 // add a query filter
369 queryfilterclass *queryfilter = new queryfilterclass();
370 queryfilter->set_gdbmptr (gdbmhandler);
371 queryfilter->set_mgsearchptr (mgsearch);
372 cserver->add_filter (queryfilter);
373
374 // add an mg and gdbm source
375 mggdbmsourceclass *mggdbmsource = new mggdbmsourceclass();
376 mggdbmsource->set_gdbmptr (gdbmhandler);
377 mggdbmsource->set_mgsearchptr (mgsearch);
378 cserver->add_source (mggdbmsource);
379
380 // inform collection server and everything it contains about
381 // its collection name
382 cserver->configure ("collection", *thiscol);
383
384 nproto.add_collectserver (cserver);
385
386 thiscol ++;
387 }
388
389 // add the protocol to the receptionist
390 recpt.add_protocol (&nproto);
391
392 // add other converters
393 utf8inconvertclass *utf8inconvert = new utf8inconvertclass();
394 utf8outconvertclass *utf8outconvert = new utf8outconvertclass();
395 recpt.add_converter ("u", utf8inconvert, utf8outconvert);
396
397 mapinconvertclass *gbinconvert = new mapinconvertclass();
398 gbinconvert->setmapfile (GSDL_GSDLHOME, "gbku", 0x25a1);
399 mapoutconvertclass *gboutconvert = new mapoutconvertclass();
400 gboutconvert->setmapfile (GSDL_GSDLHOME, "ugbk", 0xa1f5);
401 recpt.add_converter ("g", gbinconvert, gboutconvert);
402
403 // the list of actions.
404 statusaction *astatusaction = new statusaction();
405 astatusaction->set_receptionist (&recpt);
406 recpt.add_action (astatusaction);
407
408 pageaction *apageaction = new pageaction();
409 apageaction->set_receptionist (&recpt);
410 recpt.add_action (apageaction);
411
412 pingaction *apingaction = new pingaction();
413 recpt.add_action (apingaction);
414
415 queryaction *aqueryaction = new queryaction();
416 recpt.add_action (aqueryaction);
417
418 documentaction *adocumentaction = new documentaction();
419 recpt.add_action (adocumentaction);
420
421 usersaction *ausersaction = new usersaction();
422 recpt.add_action (ausersaction);
423
424 authenaction *aauthenaction = new authenaction();
425 aauthenaction->set_receptionist(&recpt);
426 recpt.add_action (aauthenaction);
427
428 tipaction *atipaction = new tipaction();
429 recpt.add_action (atipaction);
430
431 // set defaults
432 recpt.configure ("gsdlhome", GSDL_GSDLHOME);
433 recpt.configure ("collection", collection);
434
435 int maxrequests = 1;
436 if (!site_cfg_read (recpt, GSDL_GSDLHOME, collection, maxrequests)) {
437 // couldn't find the site configuration file
438 page_errorsitecfg (GSDL_GSDLHOME, collection);
439 return 0;
440 } else if (!main_cfg_read (recpt, GSDL_GSDLHOME, collection)) {
441 // couldn't find the main configuration file
442 page_errormaincfg (GSDL_GSDLHOME, collection);
443 return 0;
444 }
445
446 // w32server relies on gwcgi being set to "gw"
447 recpt.configure ("gwcgi", "gw");
448
449 // initialise the library software
450 if (!recpt.init(cerr)) {
451 // an error occurred during the initialisation
452 page_errorinit(GSDL_GSDLHOME);
453 return 0;
454 }
455
456 // get memory information
457 MEMORYSTATUS memstatus;
458 memstatus.dwLength = sizeof(MEMORYSTATUS);
459 GlobalMemoryStatus(&memstatus);
460 baseavailvirtual = memstatus.dwAvailVirtual; // save for later comparison
461
462 return 1;
463}
464
465
466static void rememberpref (char *tailstr) {
467 strcpy(gs_enterlib, tailstr);
468
469 // add "a=p&p=home"
470 strcat(gs_enterlib, "&a=p&p=home");
471}
472
473
474static void send_file_from_disk(char *filename,
475 RequestInfoT *RInfo,
476 RequestFieldsT *RFields)
477{
478 int len, nr; char *tail, *kind;
479 FILE *thefile;
480
481 // select appropriate mime type from file name
482 len = strlen(filename);
483 while (len > 0 && filename[len-1] != '.') len--;
484 kind = "unknown";
485 if (len > 0) {
486 tail = &filename[len];
487 if (stricmp("gif",tail) == 0) kind = "image/gif";
488 else if (stricmp("jpg",tail) == 0 ||
489 stricmp("jpeg",tail) == 0) kind = "image/jpeg";
490 else if (stricmp("htm",tail) == 0 ||
491 stricmp("html",tail) == 0) kind = "text/html";
492 }
493
494 // set up file name
495 text_t filenamet = text_t(GSDL_GSDLHOME) + filename;
496 text_t::iterator here = filenamet.begin();
497 text_t::iterator end = filenamet.end();
498 while (here != end) {
499 if (*here == '/') *here = '\\';
500 here ++;
501 }
502
503 filename = filenamet.getcstr();
504
505 // try to open it
506 thefile = fopen(filename, "rb");
507 if (thefile == NULL) {
508 log_message("file not found\n");
509 send_retrieve_error(404, "File not found",
510 "Could not find the local file requested", RInfo);
511 return;
512 }
513
514 char buffer[2048];
515 // send back required information
516 if (send_header(kind, RInfo) >= 0) {
517 if (strcmpi(RFields->MethodStr, "HEAD") != 0) {
518 for (;;) {
519 nr = fread(buffer, 1, 2048, thefile);
520 if (nr <= 0) break;
521 if (SendData(RInfo->ClientSocket,
522 (BYTE *)buffer, nr,
523 RInfo->ThreadNum) < 0) break;
524 }
525 }
526 }
527 fclose(thefile);
528}
529
530static void handle_library_request(char *TailStr, RequestInfoT *RInfo,
531 RequestFieldsT *RequestFields) {
532 // check for a "?" at the start of the tail string
533 if (*TailStr == '?') TailStr++;
534
535 outconvertclass text_t2ascii;
536
537 // parse the cgi arguments and produce the resulting page if there
538 // has been no errors so far
539 cgiargsclass args;
540 if (!recpt.parse_cgi_args (TailStr, args, cerr)) {
541 page_errorparseargs(GSDL_GSDLHOME);
542 return;
543 }
544
545 // produce cgi header
546 response_t response;
547 text_t response_data;
548
549 recpt.get_cgihead_info (args, response, response_data, cerr);
550 char *response_data_c = response_data.getcstr();
551
552 if (response == location) {
553 // location response
554 if (send_header(strcat("@", response_data_c), RInfo) < 0) return;
555 // textstream << text_t2ascii << "Location: " << response_data << "\n\n";
556 } else if (response == content) {
557 // content response
558 if (send_header(response_data_c, RInfo) < 0) return;
559 } else {
560 // unknown response
561 cerr << "Error: get_cgihead_info returned an unknown response type.\n";
562 return;
563 }
564
565 textstream.tsbreset();
566 textstream.setrequestinfo (RInfo);
567 delete response_data_c;
568
569 if (!recpt.produce_content (args, cout, cerr)) {
570 page_errorcgipage(GSDL_GSDLHOME);
571 return;
572 }
573 recpt.log_cgi_args (args, cerr);
574
575 cout << flush;
576 cerr << flush;
577
578 libaccessnum++;
579}
580
581static void handle_server_request(char *initialTailStr,
582 RequestInfoT *RequestInfo,
583 RequestFieldsT *RequestFields) {
584 char tmpstr[1024];
585 char tailstr[MAX_URL_SIZE];
586
587 // do any url adjustments necessary
588 if (strcmp(initialTailStr, "/") == 0) {
589 strcpy (tailstr, "/cgi-bin/gw");
590 } else strcpy (tailstr, initialTailStr);
591
592 // test to see if this is a library request or a local
593 // file request
594 if ((strncmp(tailstr, "/cgi-bin/gw", 11) == 0) &&
595 ((tailstr[11] == '\0') || (tailstr[11] == '?'))) {
596 // library request
597
598 // log the difference in access times
599 DWORD thislibaccesstime = GetTickCount();
600 if (gs_keep_log||gs_show_console) {
601 sprintf(tmpstr, "DELTA LIB ACCESS TIME: %i\n", (int)(thislibaccesstime - lastlibaccesstime));
602 log_message (tmpstr);
603 }
604 lastlibaccesstime = thislibaccesstime;
605
606 // log this request
607 if (gs_keep_log||gs_show_console) {
608 sprintf (tmpstr, "LOCAL LIB: %s\n", tailstr);
609 log_message (tmpstr);
610 }
611
612 handle_library_request (&tailstr[11], RequestInfo, RequestFields);
613
614 // remember the preferences
615 rememberpref (tailstr);
616
617 // log memory information
618 if (gs_keep_log||gs_show_console) {
619 MEMORYSTATUS memstatus;
620 memstatus.dwLength = sizeof(MEMORYSTATUS);
621 GlobalMemoryStatus(&memstatus);
622 sprintf (tmpstr, "BDELTA AVAIL VIRTUAL: %i K\n",
623 (int)((baseavailvirtual - memstatus.dwAvailVirtual)/1024));
624 log_message (tmpstr);
625 }
626
627 } else {
628 // local file
629 if (gs_keep_log||gs_show_console) {
630 sprintf (tmpstr, "LOCAL FILE: %s\n", tailstr);
631 log_message (tmpstr);
632 }
633 send_file_from_disk (tailstr, RequestInfo, RequestFields);
634 }
635}
636
637static void fix_prefix(char *dest, char *pref, char *suff)
638{
639 strcpy(dest,pref);
640 if (*suff != '/') strcat(dest,"/");
641 if (strlen(dest) + strlen(suff) + 1 > MAX_URL_SIZE)
642 strcpy(dest,"http://gsdl/name-too-long");
643 else
644 strcat(dest,suff);
645}
646
647int ExamineURIStr(char *URIStr,RequestInfoT *RequestInfo,
648 RequestFieldsT *RequestFields)
649{
650 char *protocol, *machine, *rest; int port;
651 char URICopyA[MAX_URL_SIZE], URICopyB[MAX_URL_SIZE];
652 strcpy(URICopyA,URIStr);
653
654 if (parse_url(URICopyA,&protocol,&machine,&port,&rest)!=http_ok) {
655 /* Alter local file request to address 'gsdl' */
656 fix_prefix(URICopyB, "http://gsdl", URIStr);
657 URIStr = URICopyB;
658 strcpy(URICopyA, URICopyB);
659 parse_url(URICopyA,&protocol,&machine,&port,&rest);
660 }
661
662 if (strncmp(machine, "gsdl", 5) == 0) {
663 // a local file request
664 handle_server_request(rest, RequestInfo, RequestFields);
665
666 } else {
667 send_retrieve_error(404, "File not found",
668 "Could not find the local file requested", RequestInfo);
669 }
670
671 return 1;
672}
Note: See TracBrowser for help on using the repository browser.