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