Annotation of parser3/src/targets/cgi/parser3.C, revision 1.161
1.27 paf 1: /** @file
2: Parser: scripting and CGI main.
3:
1.155 paf 4: Copyright(c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)
1.154 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.27 paf 6:
1.161 ! paf 7: $Id: parser3.C,v 1.160 2002/02/20 09:13:08 paf Exp $
1.1 paf 8: */
9:
1.40 paf 10: #include "pa_config_includes.h"
1.3 paf 11:
1.139 paf 12: #if _MSC_VER
1.131 paf 13: # include <new.h>
1.148 paf 14: # include <crtdbg.h>
1.151 paf 15: #else
16: # include "pa_config_paths.h"
1.3 paf 17: #endif
1.27 paf 18:
1.37 paf 19: #include "pa_sapi.h"
1.76 paf 20: #include "classes.h"
1.24 paf 21: #include "pa_common.h"
1.2 paf 22: #include "pa_request.h"
1.57 paf 23: #include "pa_socks.h"
1.68 paf 24: #include "pa_version.h"
1.125 parser 25: #include "pool_storage.h"
1.69 paf 26:
1.149 paf 27: #ifdef WIN32
28: # include <windows.h>
1.120 parser 29: #endif
30:
1.158 paf 31: //#define DEBUG_POOL_MALLOC
1.160 paf 32: #define DEBUG_STRING_APPENDS_VS_EXPANDS
33:
34: #ifdef DEBUG_STRING_APPENDS_VS_EXPANDS
35: extern ulong
36: string_piece_appends,
37: wcontext_result_size,
38: total_alloc_size,
39: string_string_shortcut_economy;
40: #endif
1.84 parser 41:
1.109 parser 42: // consts
1.113 parser 43:
44: extern const char *main_RCSIds[];
1.116 parser 45: #ifdef USE_SMTP
1.113 parser 46: extern const char *smtp_RCSIds[];
1.114 parser 47: #endif
1.113 parser 48: extern const char *gd_RCSIds[];
49: extern const char *classes_RCSIds[];
50: extern const char *types_RCSIds[];
1.115 parser 51: extern const char *parser3_RCSIds[];
1.113 parser 52: const char **RCSIds[]={
53: main_RCSIds,
1.116 parser 54: #ifdef USE_SMTP
1.113 parser 55: smtp_RCSIds,
1.114 parser 56: #endif
1.113 parser 57: gd_RCSIds,
58: classes_RCSIds,
59: types_RCSIds,
1.115 parser 60: parser3_RCSIds,
1.113 parser 61: 0
62: };
1.84 parser 63:
1.159 paf 64: const char *PARSER_ROOT_CONFIG_ENV_NAME="PARSER_ROOT_CONFIG";
65:
1.42 paf 66: /// IIS refuses to read bigger chunks
67: const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M
68:
1.45 paf 69: const char *argv0;
1.125 parser 70: Pool_storage pool_storage;
71: Pool pool(&pool_storage); // global pool [dont describe to doxygen: it confuses it with param names]
1.27 paf 72: bool cgi; ///< we were started as CGI?
1.5 paf 73:
1.46 paf 74: // SAPI
1.86 parser 75:
1.124 parser 76: static void log(const char *fmt, va_list args) {
1.61 paf 77: bool opened;
78: FILE *f=0;
79:
80: if(argv0) {
81: // beside by binary
82: char file_spec[MAX_STRING];
1.98 parser 83: strncpy(file_spec, argv0, MAX_STRING-1); file_spec[MAX_STRING-1]=0; // filespec of my binary
1.61 paf 84: rsplit(file_spec, '/'); rsplit(file_spec, '\\');// strip filename
85: strcat(file_spec, "/parser3.log");
86: f=fopen(file_spec, "at");
87: }
88: opened=f!=0;
89: if(!opened)
90: f=stderr;
91:
92: // prefix
93: time_t t=time(0);
94: const char *stamp=ctime(&t);
95: fprintf(f, "[%.*s] ", strlen(stamp)-1, stamp);
96: // message
1.117 parser 97:
98: char buf[MAX_STRING];
99: size_t size=vsnprintf(buf, MAX_STRING, fmt, args);
100: remove_crlf(buf, buf+size);
101:
102: fwrite(buf, size, 1, f);
1.61 paf 103: // newline
104: fprintf(f, "\n");
105:
106: if(opened)
107: fclose(f);
1.85 parser 108: else
109: fflush(f);
1.124 parser 110: }
111:
112: // appends to parser3.log located beside my binary if openable, to stderr otherwize
113: void SAPI::log(Pool& , const char *fmt, ...) {
114: va_list args;
115: va_start(args,fmt);
116: ::log(fmt, args);
117: va_end(args);
118: }
119:
120: void SAPI::die(const char *fmt, ...) {
1.137 paf 121: #ifdef DEBUG_POOL_MALLOC
122: extern void log_pool_stats(Pool& pool);
123: log_pool_stats(pool);
124: #endif
125:
1.144 paf 126: va_list args;
127: va_start(args,fmt);
1.138 paf 128: // log
129:
130: // logging is more important than user
131: // she can cancel download, we'd get SIG_PIPE,
132: // nothing would be logged then
1.124 parser 133: ::log(fmt, args);
134:
1.138 paf 135: // inform user
136:
1.134 paf 137: char body[MAX_STRING];
1.138 paf 138: int content_length=vsnprintf(body, MAX_STRING, fmt, args);
1.134 paf 139:
1.144 paf 140: va_end(args);
141:
1.134 paf 142: // prepare header
143: // let's be honest, that's bad we couldn't produce valid output
144: SAPI::add_header_attribute(pool, "status", "500");
145: SAPI::add_header_attribute(pool, "content-type", "text/plain");
146: char content_length_cstr[MAX_NUMBER];
147: snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length);
148: SAPI::add_header_attribute(pool, "content-length", content_length_cstr);
149:
150: // send header
151: SAPI::send_header(pool);
152:
153: // body
154: SAPI::send_body(pool, body, content_length);
155:
1.124 parser 156: exit(1);
1.61 paf 157: }
158:
1.122 parser 159: const char *SAPI::get_env(Pool& , const char *name) {
1.109 parser 160: return getenv(name);
1.28 paf 161: }
162:
1.122 parser 163: size_t SAPI::read_post(Pool& , char *buf, size_t max_bytes) {
1.59 paf 164: size_t read_size=0;
1.12 paf 165: do {
1.36 paf 166: int chunk_size=read(fileno(stdin),
1.42 paf 167: buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));
1.129 paf 168: if(chunk_size<=0)
1.12 paf 169: break;
170: read_size+=chunk_size;
171: } while(read_size<max_bytes);
172:
173: return read_size;
1.10 paf 174: }
175:
1.122 parser 176: void SAPI::add_header_attribute(Pool& , const char *key, const char *value) {
1.68 paf 177: if(cgi)
1.20 paf 178: printf("%s: %s\n", key, value);
1.19 paf 179: }
180:
1.56 paf 181: /// @todo intelligent cache-control
1.122 parser 182: void SAPI::send_header(Pool& ) {
1.33 paf 183: if(cgi) {
1.147 paf 184: // puts("expires: Fri, 23 Mar 2001 09:32:23 GMT");
1.33 paf 185:
186: // header | body delimiter
1.20 paf 187: puts("");
1.33 paf 188: }
1.30 paf 189: }
1.20 paf 190:
1.122 parser 191: void SAPI::send_body(Pool& , const void *buf, size_t size) {
1.19 paf 192: stdout_write(buf, size);
1.58 paf 193: }
194:
1.97 parser 195: //
196:
197: char *full_file_spec(char *file_name) {
1.108 parser 198: if(file_name && !strchr(file_name, '/')) {
1.97 parser 199: static char cwd[MAX_STRING]; getcwd(cwd, MAX_STRING);
200: static char buf[MAX_STRING];
201: snprintf(buf, MAX_STRING, "%s/%s", cwd, file_name);
202: return buf;
203: }
204: return file_name;
205: }
206:
1.40 paf 207: /**
1.122 parser 208: main workhorse
1.19 paf 209:
1.122 parser 210: @todo
1.40 paf 211: IIS: remove trailing default-document[index.html] from $request.uri.
212: to do that we need to consult metabase,
213: wich is tested but seems slow.
1.144 paf 214: IIS5 todo find out proper 'illegal call' check
1.40 paf 215: */
1.122 parser 216: void real_parser_handler(
217: const char *filespec_to_process,
218: const char *request_method, bool header_only) {
219: // init socks
220: init_socks(pool);
221:
222: // init global classes
223: init_methoded_array(pool);
224: // init global variables
225: pa_globals_init(pool);
226:
227: if(!filespec_to_process)
1.144 paf 228: SAPI::die("Parser/%s", PARSER_VERSION);
1.122 parser 229:
230: // Request info
231: Request::Info request_info;
232: if(cgi) {
233: if(const char *env_document_root=SAPI::get_env(pool, "DOCUMENT_ROOT"))
234: request_info.document_root=env_document_root;
235: else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO")) {
236: // IIS
237: size_t len=strlen(filespec_to_process)-strlen(path_info);
238: char *buf=(char *)pool.malloc(len+1);
239: memcpy(buf, filespec_to_process, len); buf[len]=0;
240: request_info.document_root=buf;
241: } else
242: throw Exception(0, 0,
243: 0,
244: "CGI: no PATH_INFO defined(in reinventing DOCUMENT_ROOT)");
245: } else {
246: static char buf[MAX_STRING];
247: strncpy(buf, filespec_to_process, MAX_STRING-1); buf[MAX_STRING-1]=0;
248: if(rsplit(buf, '/') || rsplit(buf, '\\')) // strip filename
249: request_info.document_root=buf;
250: else
251: request_info.document_root="";
252: }
253: request_info.path_translated=filespec_to_process;
254: request_info.method=request_method ? request_method : "GET";
255: const char *query_string=SAPI::get_env(pool, "QUERY_STRING");
256: request_info.query_string=query_string;
257: if(cgi) {
258: if(const char *env_request_uri=SAPI::get_env(pool, "REQUEST_URI"))
259: request_info.uri=env_request_uri;
260: else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO"))
261: if(query_string) {
262: char *reconstructed_uri=(char *)pool.malloc(
263: strlen(path_info)+1/*'?'*/+
264: strlen(query_string)+1/*0*/);
265: strcpy(reconstructed_uri, path_info);
266: strcat(reconstructed_uri, "?");
267: strcat(reconstructed_uri, query_string);
268: request_info.uri=reconstructed_uri;
269: } else
270: request_info.uri=path_info;
271: else
272: throw Exception(0, 0,
273: 0,
274: "CGI: no PATH_INFO defined(in reinventing REQUEST_URI)");
1.145 paf 275:
276: #ifndef WIN32
277: // they've changed this under IIS5.
1.122 parser 278: if(const char *script_name=SAPI::get_env(pool, "SCRIPT_NAME")) {
279: size_t script_name_len=strlen(script_name);
280: size_t uri_len=strlen(request_info.uri);
281: if(strncmp(request_info.uri,script_name, script_name_len)==0 &&
282: script_name_len != uri_len) // under IIS they are the same
1.144 paf 283: SAPI::die("CGI: illegal call");
1.122 parser 284: }
1.145 paf 285: #endif
1.122 parser 286: } else
287: request_info.uri=0;
288:
289: request_info.content_type=SAPI::get_env(pool, "CONTENT_TYPE");
290: const char *content_length=SAPI::get_env(pool, "CONTENT_LENGTH");
291: request_info.content_length=(content_length?atoi(content_length):0);
292: request_info.cookie=SAPI::get_env(pool, "HTTP_COOKIE");
293: request_info.user_agent=SAPI::get_env(pool, "HTTP_USER_AGENT");
294:
295: // prepare to process request
296: Request request(pool,
297: request_info,
1.143 paf 298: #ifdef _DEBUG
299: String::UL_HTML|String::UL_OPTIMIZE_BIT
300: #else
301: cgi ? String::UL_HTML|String::UL_OPTIMIZE_BIT : String::UL_AS_IS
302: #endif
303: ,
1.130 paf 304: true /* status_allowed */);
1.122 parser 305:
306: // some root-controlled location
1.159 paf 307: const char *root_config_filespec;
308: if(const char *root_config_by_env=getenv(PARSER_ROOT_CONFIG_ENV_NAME))
309: root_config_filespec=root_config_by_env;
310: else {
1.152 paf 311: #ifdef ROOT_CONFIG_DIR
1.159 paf 312: root_config_filespec=ROOT_CONFIG_DIR "/" CONFIG_FILE_NAME;
1.122 parser 313: #else
314: # ifdef WIN32
1.159 paf 315: // c:\windows
316: char windows_dir[MAX_STRING];
317: GetWindowsDirectory(windows_dir, MAX_STRING);
318:
319: char buf[MAX_STRING];
320: snprintf(buf, MAX_STRING,
321: "%s/%s",
322: windows_dir, CONFIG_FILE_NAME);
323:
324: root_config_filespec=buf;
1.122 parser 325: # else
326: #error must be compiled either configure/make or MSVC++
327: # endif
328: #endif
1.159 paf 329: }
1.122 parser 330:
331: // beside by binary
332: // @todo full path, not ./!
333: static char site_config_path[MAX_STRING];
334: strncpy(site_config_path, argv0, MAX_STRING-1); site_config_path[MAX_STRING-1]=0; // filespec of my binary
335: if(!(
336: rsplit(site_config_path, '/') ||
337: rsplit(site_config_path, '\\'))) { // strip filename
338: // no path, just filename
339: site_config_path[0]='.'; site_config_path[1]=0;
340: }
341:
342: char site_config_filespec[MAX_STRING];
343: snprintf(site_config_filespec, MAX_STRING,
344: "%s/%s",
345: site_config_path, CONFIG_FILE_NAME);
346:
347: // process the request
348: request.core(
349: root_config_filespec, false,
350: site_config_filespec, false,
351: header_only);
352:
353: //
354: done_socks();
355:
356: #ifdef DEBUG_POOL_MALLOC
357: extern void log_pool_stats(Pool& pool);
358: log_pool_stats(pool);
359: #endif
1.160 paf 360:
361: #ifdef DEBUG_STRING_APPENDS_VS_EXPANDS
362: SAPI::log(pool,
1.161 ! paf 363: "string piece appends=%lu, wcontext_result_size=%lu, string_string_shortcut_economy_closer=%lu, total_alloc_size=%lu",
1.160 paf 364: string_piece_appends,
365: wcontext_result_size,
366: string_string_shortcut_economy,
367: total_alloc_size);
368: #endif
369:
1.122 parser 370: }
371:
372: void call_real_parser_handler__do_SEH(
373: const char *filespec_to_process,
374: const char *request_method, bool header_only) {
1.133 paf 375: #if _MSC_VER && !defined(_DEBUG)
1.122 parser 376: LPEXCEPTION_POINTERS system_exception=0;
377: __try {
378: #endif
379: real_parser_handler(
380: filespec_to_process,
381: request_method, header_only);
382:
1.133 paf 383: #if _MSC_VER && !defined(_DEBUG)
1.122 parser 384: } __except (
385: (system_exception=GetExceptionInformation()),
386: EXCEPTION_EXECUTE_HANDLER) {
387:
388: if(system_exception)
389: if(_EXCEPTION_RECORD *er=system_exception->ExceptionRecord)
390: throw Exception(0, 0,
391: 0,
392: "Exception 0x%08X at 0x%08X", er->ExceptionCode, er->ExceptionAddress);
393: else
394: throw Exception(0, 0, 0, "Exception <no exception record>");
395: else
396: throw Exception(0, 0, 0, "Exception <no exception information>");
397: }
398: #endif
399: }
400:
1.139 paf 401: #if _MSC_VER
1.135 paf 402: int failed_new(size_t size) {
403: SAPI::die("out of memory in 'new', failed to allocated %u bytes", size);
404: return 0; // not reached
1.131 paf 405: }
1.135 paf 406: #endif
1.131 paf 407:
1.135 paf 408: #ifdef HAVE_SET_NEW_HANDLER
1.134 paf 409: void failed_new() {
1.135 paf 410: SAPI::die("out of memory in 'new'");
1.131 paf 411: }
412: #endif
413:
1.5 paf 414: int main(int argc, char *argv[]) {
1.144 paf 415: // _asm int 3;
1.45 paf 416: argv0=argv[0];
417:
1.32 paf 418: umask(2);
419:
1.3 paf 420: // were we started as CGI?
1.146 paf 421: cgi=
1.109 parser 422: getenv("SERVER_SOFTWARE") ||
423: getenv("SERVER_NAME") ||
424: getenv("GATEWAY_INTERFACE") ||
425: getenv("REQUEST_METHOD");
1.5 paf 426:
1.10 paf 427: if(!cgi) {
428: if(argc<2) {
1.69 paf 429: printf(
1.155 paf 430: "Parser/%s Copyright(c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)\n"
1.154 paf 431: "Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)\n"
1.100 parser 432: "\n"
433: "Usage: %s <file>\n",
1.69 paf 434: PARSER_VERSION,
435: argv0?argv0:"parser3");
1.67 paf 436: return 1;
1.10 paf 437: }
438: }
439:
1.100 parser 440: #ifdef WIN32
441: setmode(fileno(stdin), _O_BINARY);
442: setmode(fileno(stdout), _O_BINARY);
443: setmode(fileno(stderr), _O_BINARY);
444: #endif
445:
1.139 paf 446: #if _MSC_VER
1.138 paf 447: _set_new_handler(failed_new);
1.148 paf 448:
449: #ifdef _DEBUG
450: // Get current flag
451: int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
452:
453: // Turn on leak-checking bit
454: tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
455:
456: // Set flag to the new value
457: _CrtSetDbgFlag( tmpFlag );
458: // _CrtSetBreakAlloc(471);
459:
460: #endif
461:
1.138 paf 462: #endif
463:
464: #ifdef HAVE_SET_NEW_HANDLER
465: std::set_new_handler(failed_new);
466: #endif
467:
1.109 parser 468: char *filespec_to_process=cgi?getenv("PATH_TRANSLATED"):argv[1];
1.36 paf 469: #ifdef WIN32
1.43 paf 470: back_slashes_to_slashes(filespec_to_process);
1.36 paf 471: #endif
1.97 parser 472: filespec_to_process=full_file_spec(filespec_to_process);
1.10 paf 473:
1.109 parser 474: const char *request_method=getenv("REQUEST_METHOD");
1.35 paf 475: bool header_only=request_method && strcasecmp(request_method, "HEAD")==0;
1.131 paf 476:
1.122 parser 477: try { // global try
478: call_real_parser_handler__do_SEH(
479: filespec_to_process,
480: request_method, header_only);
481: } catch(const Exception& e) { // global problem
1.44 paf 482: // don't allocate anything on pool here:
483: // possible pool' exception not catch-ed now
484: // and there could be out-of-memory exception
1.43 paf 485:
1.144 paf 486: SAPI::die("exception in request exception handler: %s", e.comment());
1.134 paf 487: #ifndef _DEBUG
1.131 paf 488: } catch(...) {
1.134 paf 489: SAPI::die("<unknown exception>");
1.133 paf 490: #endif
1.16 paf 491: }
1.122 parser 492:
1.109 parser 493:
494: #ifndef WIN32
495: //
496: if(!cgi)
497: SAPI::send_body(pool, "\n", 1);
498: #endif
1.156 paf 499: //_asm int 3;
1.134 paf 500: return 0;
1.1 paf 501: }
E-mail: