Annotation of parser3/src/targets/cgi/parser3.C, revision 1.120
1.27 paf 1: /** @file
2: Parser: scripting and CGI main.
3:
1.43 paf 4: Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
1.113 parser 5: Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
1.27 paf 6:
1.120 ! parser 7: $Id: parser3.C,v 1.119 2001/10/09 14:30:19 parser Exp $
1.1 paf 8: */
9:
1.40 paf 10: #include "pa_config_includes.h"
1.3 paf 11:
12: #ifdef WIN32
13: # include <windows.h>
14: #endif
1.27 paf 15:
1.37 paf 16: #include "pa_sapi.h"
1.76 paf 17: #include "classes.h"
1.24 paf 18: #include "pa_common.h"
1.2 paf 19: #include "pa_request.h"
1.57 paf 20: #include "pa_socks.h"
1.68 paf 21: #include "pa_version.h"
1.69 paf 22:
1.120 ! parser 23: #ifdef XML
! 24: #include <XalanTransformer/XalanCAPI.h>
! 25: #endif
! 26:
1.84 parser 27: //#define DEBUG_POOL_MALLOC
28:
1.109 parser 29: // consts
1.113 parser 30:
31: extern const char *main_RCSIds[];
1.116 parser 32: #ifdef USE_SMTP
1.113 parser 33: extern const char *smtp_RCSIds[];
1.114 parser 34: #endif
1.113 parser 35: extern const char *gd_RCSIds[];
36: extern const char *classes_RCSIds[];
37: extern const char *types_RCSIds[];
1.115 parser 38: extern const char *parser3_RCSIds[];
1.119 parser 39: #ifdef XML
40: extern const char *xalan_patched_RCSIds[];
41: #endif
1.113 parser 42: const char **RCSIds[]={
43: main_RCSIds,
1.116 parser 44: #ifdef USE_SMTP
1.113 parser 45: smtp_RCSIds,
1.114 parser 46: #endif
1.113 parser 47: gd_RCSIds,
48: classes_RCSIds,
49: types_RCSIds,
1.115 parser 50: parser3_RCSIds,
1.118 parser 51: #ifdef XML
52: xalan_patched_RCSIds,
53: #endif
1.113 parser 54: 0
55: };
1.84 parser 56:
1.42 paf 57: /// IIS refuses to read bigger chunks
58: const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M
59:
1.45 paf 60: const char *argv0;
1.42 paf 61: Pool pool(0); // global pool [dont describe to doxygen: it confuses it with param names]
1.27 paf 62: bool cgi; ///< we were started as CGI?
1.5 paf 63:
1.40 paf 64: #ifdef WIN32
65: /// global system errors into parser exceptions converter
1.43 paf 66: static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {
1.5 paf 67: char buf[MAX_STRING];
68: if(ExceptionInfo && ExceptionInfo->ExceptionRecord) {
1.36 paf 69: struct _EXCEPTION_RECORD *er=ExceptionInfo->ExceptionRecord;
1.46 paf 70: snprintf(buf, MAX_STRING, "Exception 0x%X at %p",
1.31 paf 71: er->ExceptionCode,
72: er->ExceptionAddress);
1.5 paf 73: } else
74: strcpy(buf, "Exception <unknown>");
75:
76: PTHROW(0, 0,
77: 0,
78: buf);
79:
80: return EXCEPTION_EXECUTE_HANDLER; // never reached
81: }
1.27 paf 82: #endif
83:
1.46 paf 84: // SAPI
1.86 parser 85:
86: // appends to parser3.log located beside my binary if openable, to stderr otherwize
1.61 paf 87: void SAPI::log(Pool& pool, const char *fmt, ...) {
88: bool opened;
89: FILE *f=0;
90:
91: if(argv0) {
92: // beside by binary
93: char file_spec[MAX_STRING];
1.98 parser 94: strncpy(file_spec, argv0, MAX_STRING-1); file_spec[MAX_STRING-1]=0; // filespec of my binary
1.61 paf 95: rsplit(file_spec, '/'); rsplit(file_spec, '\\');// strip filename
96: strcat(file_spec, "/parser3.log");
97: f=fopen(file_spec, "at");
98: }
99: opened=f!=0;
100: if(!opened)
101: f=stderr;
102:
103: // prefix
104: time_t t=time(0);
105: const char *stamp=ctime(&t);
106: fprintf(f, "[%.*s] ", strlen(stamp)-1, stamp);
107: // message
108: va_list args;
109: va_start(args,fmt);
1.117 parser 110:
111: char buf[MAX_STRING];
112: size_t size=vsnprintf(buf, MAX_STRING, fmt, args);
113: remove_crlf(buf, buf+size);
114:
115: fwrite(buf, size, 1, f);
1.61 paf 116: va_end(args);
117: // newline
118: fprintf(f, "\n");
119:
120: if(opened)
121: fclose(f);
1.85 parser 122: else
123: fflush(f);
1.61 paf 124: }
125:
1.37 paf 126: const char *SAPI::get_env(Pool& pool, const char *name) {
1.109 parser 127: return getenv(name);
1.28 paf 128: }
129:
1.59 paf 130: size_t SAPI::read_post(Pool& pool, char *buf, size_t max_bytes) {
131: size_t read_size=0;
1.12 paf 132: do {
1.36 paf 133: int chunk_size=read(fileno(stdin),
1.42 paf 134: buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));
1.12 paf 135: if(chunk_size<0)
136: break;
137: read_size+=chunk_size;
138: } while(read_size<max_bytes);
139:
140: return read_size;
1.10 paf 141: }
142:
1.37 paf 143: void SAPI::add_header_attribute(Pool& pool, const char *key, const char *value) {
1.68 paf 144: if(cgi)
1.20 paf 145: printf("%s: %s\n", key, value);
1.19 paf 146: }
147:
1.56 paf 148: /// @todo intelligent cache-control
1.37 paf 149: void SAPI::send_header(Pool& pool) {
1.33 paf 150: if(cgi) {
1.55 paf 151: puts("expires: Fri, 23 Mar 2001 09:32:23 GMT");
1.33 paf 152:
153: // header | body delimiter
1.20 paf 154: puts("");
1.33 paf 155: }
1.30 paf 156: }
1.20 paf 157:
1.53 paf 158: void SAPI::send_body(Pool& pool, const void *buf, size_t size) {
1.19 paf 159: stdout_write(buf, size);
1.58 paf 160: }
161:
1.97 parser 162: //
163:
164: char *full_file_spec(char *file_name) {
1.108 parser 165: if(file_name && !strchr(file_name, '/')) {
1.97 parser 166: static char cwd[MAX_STRING]; getcwd(cwd, MAX_STRING);
167: static char buf[MAX_STRING];
168: snprintf(buf, MAX_STRING, "%s/%s", cwd, file_name);
169: return buf;
170: }
171: return file_name;
172: }
173:
1.40 paf 174: /**
175: main workhorse
1.19 paf 176:
1.56 paf 177: @todo
1.40 paf 178: IIS: remove trailing default-document[index.html] from $request.uri.
179: to do that we need to consult metabase,
180: wich is tested but seems slow.
181: */
1.5 paf 182: int main(int argc, char *argv[]) {
1.109 parser 183: int result;
1.45 paf 184: argv0=argv[0];
185:
1.32 paf 186: umask(2);
187:
1.3 paf 188: // were we started as CGI?
1.20 paf 189: cgi=
1.109 parser 190: getenv("SERVER_SOFTWARE") ||
191: getenv("SERVER_NAME") ||
192: getenv("GATEWAY_INTERFACE") ||
193: getenv("REQUEST_METHOD");
1.5 paf 194:
1.10 paf 195: if(!cgi) {
196: if(argc<2) {
1.69 paf 197: printf(
1.100 parser 198: "Parser/%s Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)\n"
199: "Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)\n"
200: "\n"
201: "Usage: %s <file>\n",
1.69 paf 202: PARSER_VERSION,
203: argv0?argv0:"parser3");
1.67 paf 204: return 1;
1.10 paf 205: }
206: }
207:
1.100 parser 208: #ifdef WIN32
209: setmode(fileno(stdin), _O_BINARY);
210: setmode(fileno(stdout), _O_BINARY);
211: setmode(fileno(stderr), _O_BINARY);
212: #endif
213:
1.109 parser 214: char *filespec_to_process=cgi?getenv("PATH_TRANSLATED"):argv[1];
1.36 paf 215: #ifdef WIN32
1.43 paf 216: back_slashes_to_slashes(filespec_to_process);
1.36 paf 217: #endif
1.97 parser 218: filespec_to_process=full_file_spec(filespec_to_process);
1.10 paf 219:
1.109 parser 220: const char *request_method=getenv("REQUEST_METHOD");
1.35 paf 221: bool header_only=request_method && strcasecmp(request_method, "HEAD")==0;
1.5 paf 222: PTRY { // global try
223: // must be first in PTRY{}PCATCH
1.40 paf 224: #ifdef WIN32
1.5 paf 225: SetUnhandledExceptionFilter(&TopLevelExceptionFilter);
226: #endif
1.62 paf 227: // init socks
1.57 paf 228: init_socks(pool);
1.120 ! parser 229:
! 230: #ifdef XML
! 231: /**
! 232: * Initialize Xerces and Xalan.
! 233: *
! 234: * Should be called only once per process before making
! 235: * any other API calls.
! 236: */
! 237: //_asm int 3;
! 238: XalanInitialize();
! 239: #endif
1.57 paf 240:
1.73 paf 241: // init global classes
242: init_methoded_array(pool);
1.10 paf 243: // init global variables
1.37 paf 244: pa_globals_init(pool);
1.10 paf 245:
1.13 paf 246: if(!filespec_to_process)
247: PTHROW(0, 0,
248: 0,
1.68 paf 249: "Parser/%s", PARSER_VERSION);
1.13 paf 250:
1.10 paf 251: // Request info
252: Request::Info request_info;
1.39 paf 253: if(cgi) {
1.51 paf 254: if(const char *env_document_root=SAPI::get_env(pool, "DOCUMENT_ROOT"))
1.39 paf 255: request_info.document_root=env_document_root;
1.51 paf 256: else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO")) {
1.40 paf 257: // IIS
1.39 paf 258: size_t len=strlen(filespec_to_process)-strlen(path_info);
1.40 paf 259: char *buf=(char *)pool.malloc(len+1);
1.98 parser 260: memcpy(buf, filespec_to_process, len); buf[len]=0;
1.39 paf 261: request_info.document_root=buf;
262: } else
263: PTHROW(0, 0,
264: 0,
1.43 paf 265: "CGI: no PATH_INFO defined(in reinventing DOCUMENT_ROOT)");
1.39 paf 266: } else {
267: static char buf[MAX_STRING];
1.98 parser 268: strncpy(buf, filespec_to_process, MAX_STRING-1); buf[MAX_STRING-1]=0;
1.70 paf 269: if(rsplit(buf, '/') || rsplit(buf, '\\')) // strip filename
270: request_info.document_root=buf;
271: else
272: request_info.document_root="";
1.13 paf 273: }
1.10 paf 274: request_info.path_translated=filespec_to_process;
1.107 parser 275: request_info.method=request_method ? request_method : "GET";
1.51 paf 276: const char *query_string=SAPI::get_env(pool, "QUERY_STRING");
1.40 paf 277: request_info.query_string=query_string;
1.68 paf 278: if(cgi) {
1.51 paf 279: if(const char *env_request_uri=SAPI::get_env(pool, "REQUEST_URI"))
1.42 paf 280: request_info.uri=env_request_uri;
1.51 paf 281: else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO"))
1.42 paf 282: if(query_string) {
283: char *reconstructed_uri=(char *)malloc(
284: strlen(path_info)+1/*'?'*/+
285: strlen(query_string)+1/*0*/);
286: strcpy(reconstructed_uri, path_info);
287: strcat(reconstructed_uri, "?");
288: strcat(reconstructed_uri, query_string);
289: request_info.uri=reconstructed_uri;
290: } else
291: request_info.uri=path_info;
292: else
293: PTHROW(0, 0,
294: 0,
1.43 paf 295: "CGI: no PATH_INFO defined(in reinventing REQUEST_URI)");
1.68 paf 296:
1.72 paf 297: if(const char *script_name=SAPI::get_env(pool, "SCRIPT_NAME")) {
298: size_t script_name_len=strlen(script_name);
299: size_t uri_len=strlen(request_info.uri);
300: if(strncmp(request_info.uri,script_name, script_name_len)==0 &&
301: script_name_len != uri_len) // under IIS they are the same
302: PTHROW(0, 0,
303: 0,
304: "CGI: illegal call");
305: }
1.68 paf 306: } else
1.42 paf 307: request_info.uri=0;
1.40 paf 308:
1.51 paf 309: request_info.content_type=SAPI::get_env(pool, "CONTENT_TYPE");
310: const char *content_length=SAPI::get_env(pool, "CONTENT_LENGTH");
1.10 paf 311: request_info.content_length=(content_length?atoi(content_length):0);
1.51 paf 312: request_info.cookie=SAPI::get_env(pool, "HTTP_COOKIE");
313: request_info.user_agent=SAPI::get_env(pool, "HTTP_USER_AGENT");
1.10 paf 314:
1.5 paf 315: // prepare to process request
1.38 paf 316: Request request(pool,
1.10 paf 317: request_info,
1.91 parser 318: cgi ? String::UL_USER_HTML : String::UL_AS_IS
1.5 paf 319: );
320:
321: // some root-controlled location
1.102 parser 322: #ifdef SYSCONFDIR
1.111 parser 323: const char *root_config_filespec=SYSCONFDIR "/" CONFIG_FILE_NAME;
1.102 parser 324: #else
325: # ifdef WIN32
1.10 paf 326: // c:\windows
1.111 parser 327: char root_config_path[MAX_STRING];
328: GetWindowsDirectory(root_config_path, MAX_STRING);
329:
330: char root_config_filespec[MAX_STRING];
331: snprintf(root_config_filespec, MAX_STRING,
332: "%s/%s",
333: root_config_path, CONFIG_FILE_NAME);
1.101 parser 334: # else
1.102 parser 335: #error must be compiled either configure/make or MSVC++
1.101 parser 336: # endif
1.5 paf 337: #endif
338:
339: // beside by binary
1.111 parser 340: // @todo full path, not ./!
341: static char site_config_path[MAX_STRING];
342: strncpy(site_config_path, argv0, MAX_STRING-1); site_config_path[MAX_STRING-1]=0; // filespec of my binary
1.88 parser 343: if(!(
1.111 parser 344: rsplit(site_config_path, '/') ||
345: rsplit(site_config_path, '\\'))) { // strip filename
1.88 parser 346: // no path, just filename
1.111 parser 347: site_config_path[0]='.'; site_config_path[1]=0;
1.88 parser 348: }
1.111 parser 349:
350: char site_config_filespec[MAX_STRING];
351: snprintf(site_config_filespec, MAX_STRING,
352: "%s/%s",
353: site_config_path, CONFIG_FILE_NAME);
1.5 paf 354:
355: // process the request
1.35 paf 356: request.core(
1.111 parser 357: root_config_filespec, false,
358: site_config_filespec, false,
1.35 paf 359: header_only);
1.57 paf 360:
361: //
362: done_socks();
1.84 parser 363:
364: #ifdef DEBUG_POOL_MALLOC
1.78 parser 365: extern void log_pool_stats(Pool& pool);
366: log_pool_stats(pool);
1.84 parser 367: #endif
1.16 paf 368:
1.22 paf 369: // must be last in PTRY{}PCATCH
1.40 paf 370: #ifdef WIN32
1.5 paf 371: SetUnhandledExceptionFilter(0);
1.25 paf 372: #endif
1.100 parser 373:
1.16 paf 374: // successful finish
1.109 parser 375: result=0;
1.16 paf 376: } PCATCH(e) { // global problem
1.43 paf 377: // must be first in PCATCH{}
378: #ifdef WIN32
379: SetUnhandledExceptionFilter(0);
380: #endif
1.44 paf 381: // don't allocate anything on pool here:
382: // possible pool' exception not catch-ed now
383: // and there could be out-of-memory exception
1.43 paf 384:
1.19 paf 385: const char *body=e.comment();
1.44 paf 386: // log it
387: SAPI::log(pool, "exception in request exception handler: %s", body);
388:
389: //
1.19 paf 390: int content_length=strlen(body);
1.5 paf 391:
1.35 paf 392: // prepare header
1.37 paf 393: SAPI::add_header_attribute(pool, "content-type", "text/plain");
1.19 paf 394: char content_length_cstr[MAX_NUMBER];
1.60 paf 395: snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length);
1.37 paf 396: SAPI::add_header_attribute(pool, "content-length", content_length_cstr);
1.35 paf 397:
398: // send header
1.37 paf 399: SAPI::send_header(pool);
1.19 paf 400:
401: // body
1.35 paf 402: if(!header_only)
1.37 paf 403: SAPI::send_body(pool, body, content_length);
1.94 parser 404:
1.16 paf 405: // unsuccessful finish
1.109 parser 406: result=1;
1.16 paf 407: }
408: PEND_CATCH
1.109 parser 409:
410: #ifndef WIN32
411: //
412: if(!cgi)
413: SAPI::send_body(pool, "\n", 1);
414: #endif
415: return result;
1.1 paf 416: }
E-mail: