Diff for /parser3/src/targets/cgi/parser3.C between versions 1.154 and 1.292

version 1.154, 2002/02/08 08:31:32 version 1.292, 2020/08/13 14:49:13
Line 1 Line 1
 /** @file  /** @file
         Parser: scripting and CGI main.          Parser: scripting and CGI main.
   
         Copyright(c) 2001, 2002 ArtLebedev Group(http://www.artlebedev.com)          Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
         $Id$  
 */  */
   
 #include "pa_config_includes.h"  volatile const char * IDENT_PARSER3_C="$Id$";
   
 #if _MSC_VER  #include "pa_config_includes.h"
 #       include <new.h>  
 #       include <crtdbg.h>  
 #else  
 #       include "pa_config_paths.h"  
 #endif  
   
 #include "pa_sapi.h"  #include "pa_sapi.h"
 #include "classes.h"  #include "classes.h"
 #include "pa_common.h"  #include "pa_common.h"
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_socks.h"  
 #include "pa_version.h"  #include "pa_version.h"
 #include "pool_storage.h"  #include "pa_vconsole.h"
   
 #ifdef WIN32  #ifdef _MSC_VER
 #       include <windows.h>  #include <crtdbg.h>
   #include <windows.h>
   #include <direct.h>
 #endif  #endif
   
 //#define DEBUG_POOL_MALLOC  // defines
   
   // comment remove me after debugging
   //#define PA_DEBUG_CGI_ENTRY_EXIT "parser3-debug.log"
   
   #if defined(_MSC_VER) && !defined(_DEBUG)
   #       define PA_SUPPRESS_SYSTEM_EXCEPTION
   #endif
   
 // consts  // consts
   
 extern const char *main_RCSIds[];  #define REDIRECT_PREFIX "REDIRECT_"
 #ifdef USE_SMTP  #define PARSER_CONFIG_ENV_NAME "CGI_PARSER_CONFIG"
 extern const char *smtp_RCSIds[];  #define PARSER_LOG_ENV_NAME "CGI_PARSER_LOG"
 #endif  
 extern const char *gd_RCSIds[];  
 extern const char *classes_RCSIds[];  
 extern const char *types_RCSIds[];  
 extern const char *parser3_RCSIds[];  
 const char **RCSIds[]={  
         main_RCSIds,  
 #ifdef USE_SMTP  
         smtp_RCSIds,  
 #endif  
         gd_RCSIds,  
         classes_RCSIds,  
         types_RCSIds,  
         parser3_RCSIds,  
         0  
 };  
   
 /// IIS refuses to read bigger chunks  /// IIS refuses to read bigger chunks
 const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M   const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M
   
 const char *argv0;  static const char* config_filespec_cstr=0;
 Pool_storage pool_storage;  
 Pool pool(&pool_storage); // global pool [dont describe to doxygen: it confuses it with param names]  static int args_skip=1;
 bool cgi; ///< we were started as CGI?  static char** argv_all = NULL;
   
   static bool cgi; ///< we were started as CGI?
   static bool mail_received=false; ///< we were started with -m option? [asked to parse incoming message to $mail:received]
   
   // for signal handlers
   Request *request=0;
   Request_info *request_info=0;
   bool execution_canceled=false;
   
 // SAPI  // SAPI
   
 static void log(const char *fmt, va_list args) {  class SAPI_Info {
         bool opened;  public:
           int http_response_code;
   } SAPI_info = { 0 };
   
   static void log(const char* fmt, va_list args) {
           bool opened=false;
         FILE *f=0;          FILE *f=0;
   
         if(argv0) {          const char* log_by_env=getenv(PARSER_LOG_ENV_NAME);
                 // beside by binary          if(!log_by_env)
                   log_by_env=getenv(REDIRECT_PREFIX PARSER_LOG_ENV_NAME);
           if(log_by_env) {
                   f=fopen(log_by_env, "at");
                   opened=f!=0;
           }
   #ifdef PA_DEBUG_CGI_ENTRY_EXIT
           f=fopen(PA_DEBUG_CGI_ENTRY_EXIT, "at");
           opened=f!=0;
   #endif
   
           if(!opened && config_filespec_cstr) {
                   char beside_config_path[MAX_STRING];
                   strncpy(beside_config_path, config_filespec_cstr, MAX_STRING-1);  beside_config_path[MAX_STRING-1]=0;
                   if(!(rsplit(beside_config_path, '/') || rsplit(beside_config_path, '\\'))) { // strip filename
                           // no path, just filename
                           beside_config_path[0]='.'; beside_config_path[1]=0;
                   }
   
                 char file_spec[MAX_STRING];                  char file_spec[MAX_STRING];
                 strncpy(file_spec, argv0, MAX_STRING-1);  file_spec[MAX_STRING-1]=0; // filespec of my binary                  snprintf(file_spec, MAX_STRING, "%s/parser3.log", beside_config_path);
                 rsplit(file_spec, '/');  rsplit(file_spec, '\\');// strip filename  
                 strcat(file_spec, "/parser3.log");  
                 f=fopen(file_spec, "at");                  f=fopen(file_spec, "at");
                   opened=f!=0;
         }          }
         opened=f!=0;          // fallback to stderr
         if(!opened)          if(!opened)
                 f=stderr;                  f=stderr;
   
           // use no memory [so that we could log out-of-memory error]
           setbuf(f, 0); // stderr stream is unbuffered by default, but still...
   
         // prefix          // prefix
         time_t t=time(0);          time_t t=time(0);
         const char *stamp=ctime(&t);          if(const char* stamp=ctime(&t)) { // never saw that
         fprintf(f, "[%.*s] ", strlen(stamp)-1, stamp);                  if(size_t len=strlen(stamp)) // saw once stamp being =""
                           fprintf(f, "[%.*s] [%u] ", (int)len-1, stamp, (unsigned int)getpid() );
           }
         // message          // message
   
         char buf[MAX_STRING];          char buf[MAX_LOG_STRING];
         size_t size=vsnprintf(buf, MAX_STRING, fmt, args);          size_t size=vsnprintf(buf, MAX_LOG_STRING, fmt, args);
         remove_crlf(buf, buf+size);          size=remove_crlf(buf, buf+size);
   
         fwrite(buf, size, 1, f);          fwrite(buf, size, 1, f);
   
           if(request_info)
                   fprintf(f, " [uri=%s, method=%s, cl=%lu]",
                           request_info->uri ? request_info->uri : "<unknown>",
                           request_info->method ? request_info->method : "<unknown>",
                           request_info->content_length);
           else
                   fputs(" [no request info]", f);
   
         // newline          // newline
         fprintf(f, "\n");          fputs("\n", f);
   
         if(opened)          if(opened)
                 fclose(f);                  fclose(f);
Line 98  static void log(const char *fmt, va_list Line 126  static void log(const char *fmt, va_list
                 fflush(f);                  fflush(f);
 }  }
   
   #ifdef PA_DEBUG_CGI_ENTRY_EXIT
   static void log(const char* fmt, ...) {
           va_list args;
           va_start(args,fmt);
           log(fmt, args);
           va_end(args);
   }
   #endif
   
 // appends to parser3.log located beside my binary if openable, to stderr otherwize  // appends to parser3.log located beside my binary if openable, to stderr otherwize
 void SAPI::log(Pool& , const char *fmt, ...) {  void SAPI::log(SAPI_Info&, const char* fmt, ...) {
     va_list args;          va_list args;
         va_start(args,fmt);          va_start(args,fmt);
         ::log(fmt, args);          ::log(fmt, args);
         va_end(args);          va_end(args);
 }  }
   
 void SAPI::die(const char *fmt, ...) {  void SAPI::die(const char* fmt, ...) {
 #ifdef DEBUG_POOL_MALLOC          va_list args;
         extern void log_pool_stats(Pool& pool);  
         log_pool_stats(pool);  
 #endif  
   
     va_list args;          // logging first, first vsnprintf
         va_start(args,fmt);          va_start(args,fmt);
         // log  
   
         // logging is more important than user   
         // she can cancel download, we'd get SIG_PIPE,   
         // nothing would be logged then  
         ::log(fmt, args);          ::log(fmt, args);
           va_end(args);
   
         // inform user          // inform user, second vsnprintf
           va_start(args, fmt);
         char body[MAX_STRING];          char body[MAX_STRING];
         int content_length=vsnprintf(body, MAX_STRING, fmt, args);          int content_length=vsnprintf(body, MAX_STRING, fmt, args);
   
         va_end(args);  
   
         // prepare header          // prepare header
         // let's be honest, that's bad we couldn't produce valid output          // let's be honest, that's bad we couldn't produce valid output
         SAPI::add_header_attribute(pool, "status", "500");          // capitalized headers passed for preventing malloc during capitalization
         SAPI::add_header_attribute(pool, "content-type", "text/plain");          SAPI::add_header_attribute(SAPI_info, HTTP_STATUS_CAPITALIZED, "500");
           SAPI::add_header_attribute(SAPI_info, HTTP_CONTENT_TYPE_CAPITALIZED, "text/plain");
           // don't use 'format' function because it calls malloc
         char content_length_cstr[MAX_NUMBER];          char content_length_cstr[MAX_NUMBER];
         snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length);          snprintf(content_length_cstr, sizeof(content_length_cstr), "%u", content_length);
         SAPI::add_header_attribute(pool, "content-length", content_length_cstr);          SAPI::add_header_attribute(SAPI_info, HTTP_CONTENT_LENGTH_CAPITALIZED, content_length_cstr);
   
         // send header          // send header
         SAPI::send_header(pool);          SAPI::send_header(SAPI_info);
   
         // body          // body
         SAPI::send_body(pool, body, content_length);          SAPI::send_body(SAPI_info, body, content_length);
   
         exit(1);          exit(1);
   //      va_end(args);
 }  }
   
 const char *SAPI::get_env(Pool& , const char *name) {  char* SAPI::Env::get(SAPI_Info& , const char* name) {
         return getenv(name);          if(char *local=getenv(name))
                   return pa_strdup(local);
           else
                   return 0;
   }
   
   const char* const *SAPI::Env::get(SAPI_Info&) {
   #ifdef _MSC_VER
           extern char **_environ;
           return _environ;
   #else
           extern char **environ;
           return environ;
   #endif
 }  }
   
 size_t SAPI::read_post(Pool& , char *buf, size_t max_bytes) {  size_t SAPI::read_post(SAPI_Info& , char *buf, size_t max_bytes) {
         size_t read_size=0;          size_t read_size=0;
         do {          do {
                 int chunk_size=read(fileno(stdin),                   ssize_t chunk_size=read(fileno(stdin), buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));
                         buf+read_size, min(READ_POST_CHUNK_SIZE, max_bytes-read_size));  
                 if(chunk_size<=0)                  if(chunk_size<=0)
                         break;                          break;
                 read_size+=chunk_size;                  read_size+=chunk_size;
Line 162  size_t SAPI::read_post(Pool& , char *buf Line 205  size_t SAPI::read_post(Pool& , char *buf
         return read_size;          return read_size;
 }  }
   
 void SAPI::add_header_attribute(Pool& , const char *key, const char *value) {  void SAPI::add_header_attribute(SAPI_Info& , const char* dont_store_key, const char* dont_store_value) {
         if(cgi)          if(strcasecmp(dont_store_key, HTTP_STATUS)==0)
                 printf("%s: %s\n", key, value);                  SAPI_info.http_response_code=atoi(dont_store_value);
           if( cgi && (!request || !request->console.was_used()) )
                   printf("%s: %s\n", capitalize(dont_store_key), dont_store_value);
 }  }
   
 /// @todo intelligent cache-control  void SAPI::send_header(SAPI_Info& ) {
 void SAPI::send_header(Pool& ) {  
         if(cgi) {          if(cgi) {
 //              puts("expires: Fri, 23 Mar 2001 09:32:23 GMT");  
   
                 // header | body  delimiter  
                 puts("");                  puts("");
         }          }
 }  }
   
 void SAPI::send_body(Pool& , const void *buf, size_t size) {  size_t SAPI::send_body(SAPI_Info& , const void *buf, size_t size) {
         stdout_write(buf, size);          return stdout_write(buf, size);
 }  }
   
 //  static void full_file_spec(const char* file_name, char *buf, size_t buf_size) {
           if(file_name)
                   if(file_name[0]=='/' 
   #ifdef WIN32
                           || file_name[0] && file_name[1]==':'
   #endif
                   ){
                           strncpy(buf, file_name, buf_size-1); buf[buf_size-1]=0;
                   } else {
                           char cwd[MAX_STRING];
                           snprintf(buf, buf_size, "%s/%s", getcwd(cwd, MAX_STRING) ? cwd : "", file_name);
                   }
           else
                   buf[0]=0;
   #ifdef WIN32
           back_slashes_to_slashes(buf);
   #endif
   }
   
   static void log_signal(const char* signal_name) {
           if(request_info)
                   SAPI::log(SAPI_info, "%s received while %s.", signal_name, request ? "executing code" : "reading data");
           else
                   SAPI::log(SAPI_info, "%s received before or after processing request", signal_name);
   }
   
   #ifdef SIGUSR1
   static void SIGUSR1_handler(int /*sig*/){
           log_signal("SIGUSR1");
   }
   #endif
   
 char *full_file_spec(char *file_name) {  #ifdef SIGPIPE
         if(file_name && !strchr(file_name, '/')) {  #define SIGPIPE_NAME "SIGPIPE"
                 static char cwd[MAX_STRING];  getcwd(cwd, MAX_STRING);  static const String sigpipe_name(SIGPIPE_NAME);
                 static char buf[MAX_STRING];  static void SIGPIPE_handler(int /*sig*/){
                 snprintf(buf, MAX_STRING, "%s/%s", cwd, file_name);          Value* sigpipe=0;
                 return buf;          if(request)
                   sigpipe=request->main_class.get_element(sigpipe_name);
           if(sigpipe && sigpipe->as_bool())
                   log_signal(SIGPIPE_NAME);
   
           execution_canceled=true;
           if(request)
                   request->set_skip(Request::SKIP_INTERRUPTED);
   }
   #endif
   
   #ifdef WIN32
   const char* maybe_reconstruct_IIS_status_in_qs(const char* original) {
           // 404;http://servername/page[?param=value...]
           // ';' should be urlencoded by HTTP standard, so we shouldn't get it from browser 
           // and can consider that as an indication that this is IIS way to report errors
   
           if(original && isdigit((unsigned char)original[0]) && isdigit((unsigned char)original[1]) && isdigit((unsigned char)original[2]) && original[3]==';'){
                   size_t original_len=strlen(original);
                   char* reconstructed=new(PointerFreeGC) char[original_len +12/*IIS-STATUS=&*/ +14/*IIS-DOCUMENT=&*/ +1];
                   char* cur=reconstructed;
                   memcpy(cur, "IIS-STATUS=", 11);  cur+=11;
                   memcpy(cur, original, 3); cur+=3;
                   *cur++='&';
   
                   const char* qmark_at=strchr(original, '?');
                   memcpy(cur, "IIS-DOCUMENT=", 13);  cur+=13;
                   {
                           size_t value_len=(qmark_at ? qmark_at-original : original_len)-4;
                           memcpy(cur, original+4, value_len);  cur+=value_len;
                   }
   
                   if(qmark_at) {
                           *cur++='&';
                           strcpy(cur, qmark_at+1/*skip ? itself*/);
                   } else
                           *cur=0;
   
                   return reconstructed;
         }          }
         return file_name;          
           return original;
   }
   
   #define MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(s) maybe_reconstruct_IIS_status_in_qs(s)
   #else 
   #define MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(s) s
   #endif
   
   
   class RequestController {
   public:
           RequestController(Request* r){
                   ::request=r;
           }
           ~RequestController(){
                   ::request=0;
           }
   };
   
   static bool locate_config(){
           if(!config_filespec_cstr) {
                   config_filespec_cstr=getenv(PARSER_CONFIG_ENV_NAME);
                   if(!config_filespec_cstr)
                           config_filespec_cstr=getenv(REDIRECT_PREFIX PARSER_CONFIG_ENV_NAME);
                   if(!config_filespec_cstr){
                           // beside by binary
                           char beside_binary_path[MAX_STRING];
                           strncpy(beside_binary_path, argv_all[0], MAX_STRING-1);  beside_binary_path[MAX_STRING-1]=0; // filespec of my binary
                           if(!(rsplit(beside_binary_path, '/') || rsplit(beside_binary_path, '\\'))) { // strip filename
                                   // no path, just filename
                                   // @todo full path, not ./!
                                   beside_binary_path[0]='.'; beside_binary_path[1]=0;
                           }
                           char config_filespec_buf[MAX_STRING];
                           snprintf(config_filespec_buf, MAX_STRING, "%s/%s", beside_binary_path, AUTO_FILE_NAME);
                           config_filespec_cstr=pa_strdup(config_filespec_buf);
                           return entry_exists(config_filespec_cstr);
                   }
           }
           return true;
 }  }
   
 /**  /**
 main workhorse  main workhorse
   
   @todo     @todo
                 IIS: remove trailing default-document[index.html] from $request.uri.          IIS: remove trailing default-document[index.html] from $request.uri.
                 to do that we need to consult metabase,          to do that we need to consult metabase,
                 wich is tested but seems slow.          wich is tested but seems slow.
                 IIS5 todo find out proper 'illegal call' check   
 */  */
 void real_parser_handler(  static void real_parser_handler(const char* filespec_to_process, const char* request_method, bool header_only) {
                                         const char *filespec_to_process,  
                                         const char *request_method, bool header_only) {          // init libraries
         // init socks          pa_globals_init();
         init_socks(pool);  
           
         // init global classes  
         init_methoded_array(pool);  
         // init global variables  
         pa_globals_init(pool);  
                   
         if(!filespec_to_process)          if(!filespec_to_process || !*filespec_to_process)
                 SAPI::die("Parser/%s", PARSER_VERSION);                  SAPI::die("Parser/%s", PARSER_VERSION);
                   
         // Request info          // Request info
         Request::Info request_info;          Request_info request_info; memset(&request_info, 0, sizeof(request_info));
         if(cgi) {          char document_root_buf[MAX_STRING];
                 if(const char *env_document_root=SAPI::get_env(pool, "DOCUMENT_ROOT"))  
                         request_info.document_root=env_document_root;          request_info.path_translated = filespec_to_process;
                 else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO")) {          request_info.method = request_method ? request_method : "GET";
                         // IIS          request_info.query_string = MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(getenv("QUERY_STRING"));
                         size_t len=strlen(filespec_to_process)-strlen(path_info);  
                         char *buf=(char *)pool.malloc(len+1);  
                         memcpy(buf, filespec_to_process, len); buf[len]=0;  
                         request_info.document_root=buf;  
                 } else  
                         throw Exception(0, 0,  
                         0,  
                         "CGI: no PATH_INFO defined(in reinventing DOCUMENT_ROOT)");  
         } else {  
                 static char buf[MAX_STRING];  
                 strncpy(buf, filespec_to_process, MAX_STRING-1); buf[MAX_STRING-1]=0;  
                 if(rsplit(buf, '/') || rsplit(buf, '\\')) // strip filename  
                         request_info.document_root=buf;  
                 else  
                         request_info.document_root="";  
         }  
         request_info.path_translated=filespec_to_process;  
         request_info.method=request_method ? request_method : "GET";  
         const char *query_string=SAPI::get_env(pool, "QUERY_STRING");  
         request_info.query_string=query_string;  
         if(cgi) {          if(cgi) {
                 if(const char *env_request_uri=SAPI::get_env(pool, "REQUEST_URI"))                  // obligatory
                         request_info.uri=env_request_uri;                  const char* path_info=getenv("PATH_INFO");
                 else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO"))                  if(!path_info)
                         if(query_string) {                          SAPI::die("CGI: illegal call (missing PATH_INFO)");
                                 char *reconstructed_uri=(char *)pool.malloc(                  
                                         strlen(path_info)+1/*'?'*/+                  request_info.document_root = getenv("DOCUMENT_ROOT");
                                         strlen(query_string)+1/*0*/);                  if(!request_info.document_root) {
                           // IIS or fcgiwrap minimalistic setup
                           ssize_t prefix_len = strlen(filespec_to_process) - strlen(path_info);
                           if(prefix_len < 0 || strcmp(filespec_to_process + prefix_len, path_info) != 0)
                                   SAPI::die("CGI: illegal call (invalid PATH_INFO in reinventing DOCUMENT_ROOT)");
   
                           char* document_root = new(PointerFreeGC) char[prefix_len + 1/*0*/];
                           memcpy(document_root, filespec_to_process, prefix_len); document_root[prefix_len] = 0;
                           request_info.document_root = document_root;
                   }
   
                   request_info.uri = request_info.strip_absolute_uri(getenv("REQUEST_URI"));
                   if(request_info.uri) { // apache & others stuck to standards
                           // another obligatory
                           const char* script_name = getenv("SCRIPT_NAME");
                           if(!script_name)
                                   SAPI::die("CGI: illegal call (missing SCRIPT_NAME)");
                           /*
                                   http://parser3/env.html?123  =OK
                                   $request:uri=/env.html?123
                                   REQUEST_URI='/env.html?123'
                                   SCRIPT_NAME='/cgi-bin/parser3'
                                   PATH_INFO='/env.html'
                                   
                                   http://parser3/cgi-bin/parser3/env.html?123 =ERROR
                                   $request:uri=/cgi-bin/parser3/env.html?123
                                   REQUEST_URI='/cgi-bin/parser3/env.html?123'
                                   SCRIPT_NAME='/cgi-bin/parser3'
                                   PATH_INFO='/env.html'
                           */
                           size_t script_name_len = strlen(script_name);
                           size_t uri_len = strlen(request_info.uri);
                           if(strncmp(request_info.uri, script_name, script_name_len)==0 && script_name_len != uri_len) // under IIS they are the same
                                   SAPI::die("CGI: illegal call (1)");
                   } else { // fcgiwrap minimalistic setup
   
                           if(request_info.query_string && *request_info.query_string) {
                                   char* reconstructed_uri = new(PointerFreeGC) char[strlen(path_info) + 1/*'?'*/+ strlen(request_info.query_string) + 1/*0*/];
                                 strcpy(reconstructed_uri, path_info);                                  strcpy(reconstructed_uri, path_info);
                                 strcat(reconstructed_uri, "?");                                  strcat(reconstructed_uri, "?");
                                 strcat(reconstructed_uri, query_string);                                  strcat(reconstructed_uri, request_info.query_string);
                                 request_info.uri=reconstructed_uri;                                  request_info.uri = reconstructed_uri;
                         } else                          } else
                                 request_info.uri=path_info;                                  request_info.uri = path_info;
                         else                  }
                                 throw Exception(0, 0,          } else{
                                 0,                  full_file_spec("", document_root_buf, sizeof(document_root_buf));
                                 "CGI: no PATH_INFO defined(in reinventing REQUEST_URI)");                  request_info.document_root = document_root_buf;
                                           request_info.uri = "";
 #ifndef WIN32  
                         // they've changed this under IIS5.  
                         if(const char *script_name=SAPI::get_env(pool, "SCRIPT_NAME")) {  
                                 size_t script_name_len=strlen(script_name);  
                                 size_t uri_len=strlen(request_info.uri);  
                                 if(strncmp(request_info.uri,script_name, script_name_len)==0 &&  
                                         script_name_len != uri_len) // under IIS they are the same  
                                         SAPI::die("CGI: illegal call");  
                         }  
 #endif  
         } else  
                 request_info.uri=0;  
           
         request_info.content_type=SAPI::get_env(pool, "CONTENT_TYPE");  
         const char *content_length=SAPI::get_env(pool, "CONTENT_LENGTH");  
         request_info.content_length=(content_length?atoi(content_length):0);  
         request_info.cookie=SAPI::get_env(pool, "HTTP_COOKIE");  
         request_info.user_agent=SAPI::get_env(pool, "HTTP_USER_AGENT");  
           
         // prepare to process request  
         Request request(pool,  
                 request_info,  
 #ifdef _DEBUG  
                 String::UL_HTML|String::UL_OPTIMIZE_BIT  
 #else  
                 cgi ? String::UL_HTML|String::UL_OPTIMIZE_BIT : String::UL_AS_IS  
 #endif  
                 ,  
                 true /* status_allowed */);  
           
         // some root-controlled location  
 #ifdef ROOT_CONFIG_DIR  
         const char *root_config_filespec=ROOT_CONFIG_DIR "/" CONFIG_FILE_NAME;  
 #else  
 #       ifdef WIN32  
         // c:\windows  
         char root_config_path[MAX_STRING];  
         GetWindowsDirectory(root_config_path, MAX_STRING);  
           
         char root_config_filespec[MAX_STRING];  
         snprintf(root_config_filespec, MAX_STRING,   
                 "%s/%s",   
                 root_config_path, CONFIG_FILE_NAME);  
 #       else  
 #error must be compiled either configure/make or MSVC++  
 #       endif  
 #endif  
           
         // beside by binary  
         // @todo full path, not ./!  
         static char site_config_path[MAX_STRING];  
         strncpy(site_config_path, argv0, MAX_STRING-1);  site_config_path[MAX_STRING-1]=0; // filespec of my binary  
         if(!(  
                 rsplit(site_config_path, '/') ||   
                 rsplit(site_config_path, '\\'))) { // strip filename  
                 // no path, just filename  
                 site_config_path[0]='.'; site_config_path[1]=0;  
         }          }
                   
         char site_config_filespec[MAX_STRING];          request_info.content_type = getenv("CONTENT_TYPE");
         snprintf(site_config_filespec, MAX_STRING,           const char* content_length = getenv("CONTENT_LENGTH");
                 "%s/%s",           request_info.content_length = content_length ? atoi(content_length) : 0;
                 site_config_path, CONFIG_FILE_NAME);          request_info.cookie = getenv("HTTP_COOKIE");
                   request_info.mail_received = mail_received;
         // process the request  
         request.core(          request_info.argv = argv_all;
                 root_config_filespec, false,          request_info.args_skip = args_skip;
                 site_config_filespec, false,  
                 header_only);          // get request_info ptr for signal handlers
                   ::request_info = &request_info;
         //          if(execution_canceled)
         done_socks();                  SAPI::die("Execution canceled");
           
 #ifdef DEBUG_POOL_MALLOC  #ifdef PA_DEBUG_CGI_ENTRY_EXIT
         extern void log_pool_stats(Pool& pool);          log("request_info: method=%s, uri=%s, q=%s, dr=%s, pt=%s, cookies=%s, cl=%u", 
         log_pool_stats(pool);                  request_info.method,
                   request_info.uri,
                   request_info.query_string,
                   request_info.document_root,
                   request_info.path_translated,
                   request_info.cookie,
                   request_info.content_length);
 #endif  #endif
   
           // prepare to process request
           Request request(SAPI_info, request_info, cgi ? String::Language(String::L_HTML|String::L_OPTIMIZE_BIT) : String::L_AS_IS);
           {
                   // get ::request ptr for signal handlers
                   RequestController rc(&request);
                   bool fail_on_config_read_problem=locate_config();
                   // process the request
                   request.core(config_filespec_cstr, fail_on_config_read_problem, header_only);
                   // ::request cleared in RequestController desctructor to prevent signal handlers from accessing invalid memory
           }
   
           // finalize libraries
           pa_globals_done();
   }
   
   #ifdef PA_SUPPRESS_SYSTEM_EXCEPTION
   static const Exception call_real_parser_handler__do_PEH_return_it(const char* filespec_to_process, const char* request_method, bool header_only) {
           try {
                   real_parser_handler(filespec_to_process, request_method, header_only);
           } catch(const Exception& e) {
                   return e;
           }
   
           return Exception();
 }  }
   
 void call_real_parser_handler__do_SEH(  static void call_real_parser_handler__supress_system_exception(const char* filespec_to_process, const char* request_method, bool header_only) {
                                                                  const char *filespec_to_process,          Exception parser_exception;
                                                                  const char *request_method, bool header_only) {  
 #if _MSC_VER && !defined(_DEBUG)  
         LPEXCEPTION_POINTERS system_exception=0;          LPEXCEPTION_POINTERS system_exception=0;
   
         __try {          __try {
 #endif                  parser_exception=call_real_parser_handler__do_PEH_return_it(filespec_to_process, request_method, header_only);
                 real_parser_handler(          } __except ( (system_exception=GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER) {
                         filespec_to_process,  
                         request_method, header_only);  
                   
 #if _MSC_VER && !defined(_DEBUG)  
         } __except (  
                 (system_exception=GetExceptionInformation()),   
                 EXCEPTION_EXECUTE_HANDLER) {  
                   
                 if(system_exception)                  if(system_exception)
                         if(_EXCEPTION_RECORD *er=system_exception->ExceptionRecord)                          if(_EXCEPTION_RECORD *er=system_exception->ExceptionRecord)
                                 throw Exception(0, 0,                                  throw Exception("system", 0, "0x%08X at 0x%08X", er->ExceptionCode,  er->ExceptionAddress);
                                 0,  
                                 "Exception 0x%08X at 0x%08X", er->ExceptionCode,  er->ExceptionAddress);  
                         else  
                                 throw Exception(0, 0, 0, "Exception <no exception record>");  
                         else                          else
                                 throw Exception(0, 0, 0, "Exception <no exception information>");                                  throw Exception("system", 0, "<no exception record>");
                   else
                           throw Exception("system", 0, "<no exception information>");
         }          }
 #endif  
 }  
   
 #if _MSC_VER          if(parser_exception)
 int failed_new(size_t size) {                  throw Exception(parser_exception);
         SAPI::die("out of memory in 'new', failed to allocated %u bytes", size);  
         return 0; // not reached  
 }  }
   
   #define REAL_PARSER_HANDLER call_real_parser_handler__supress_system_exception
   #else
   #define REAL_PARSER_HANDLER real_parser_handler
 #endif  #endif
   
 #ifdef HAVE_SET_NEW_HANDLER  static void usage(const char* program) {
 void failed_new() {          printf(
     SAPI::die("out of memory in 'new'");                  "Parser/%s\n"
                   "Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com)\n"
                   "Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)\n"
                   "\n"
                   "Usage: %s [options] file\n"
                   "Options are:\n"
   #ifdef WITH_MAILRECEIVE
                   "    -m              Parse mail, put received letter to $mail:received\n"
   #endif
                   "    -f config_file  Use this config file (/path/to/auto.p)\n"
                   "    -h              Display usage information (this message)\n",
                   PARSER_VERSION,
                   program);
           exit(EINVAL);
 }  }
 #endif  
   
 int main(int argc, char *argv[]) {  int main(int argc, char *argv[]) {
 //      _asm int 3;  #ifdef PA_DEBUG_CGI_ENTRY_EXIT
         argv0=argv[0];          log("main: entry");
   #endif
   
   #ifdef SIGUSR1
       if(signal(SIGUSR1, SIGUSR1_handler)==SIG_ERR)
                   SAPI::die("Can not set handler for SIGUSR1");
   #endif
   #ifdef SIGPIPE
       if(signal(SIGPIPE, SIGPIPE_handler)==SIG_ERR)
                   SAPI::die("Can not set handler for SIGPIPE");
   #endif
   
           argv_all=argv;
         umask(2);          umask(2);
   
         // were we started as CGI?          // were we started as CGI?
         cgi=          cgi=(getenv("SERVER_SOFTWARE") || getenv("SERVER_NAME") || getenv("GATEWAY_INTERFACE") || getenv("REQUEST_METHOD")) && !getenv("PARSER_VERSION");
                 getenv("SERVER_SOFTWARE") ||   
                 getenv("SERVER_NAME") ||           char *raw_filespec_to_process;
                 getenv("GATEWAY_INTERFACE") ||           if(cgi) {
                 getenv("REQUEST_METHOD");                  raw_filespec_to_process=getenv("PATH_TRANSLATED");
                           if(raw_filespec_to_process && !*raw_filespec_to_process)
         if(!cgi) {                          raw_filespec_to_process=0;
                 if(argc<2) {          } else {
                         printf(                  int optind=1;
                                 "Parser/%s Copyright(c) 2001, 2002 ArtLebedev Group(http://www.artlebedev.com)\n"                  while(optind < argc){
                                 "Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)\n"                          char *carg = argv[optind];
                                 "\n"                          if(carg[0] != '-')
                                 "Usage: %s <file>\n",                                  break;
                                 PARSER_VERSION,   
                                 argv0?argv0:"parser3");                          for(size_t k = 1; k < strlen(carg); k++){
                         return 1;                                  char c = carg[k];
                                   switch (c) {
                                           case 'h':
                                                   usage(argv[0]);
                                                   break;
                                           case 'f':
                                                   if(optind < argc - 1){
                                                           optind++;
                                                           config_filespec_cstr=argv[optind];
                                                   }
                                                   break;
   #ifdef WITH_MAILRECEIVE
                                           case 'm':
                                                   mail_received=true;
                                                   break;
   #endif
                                           default:
                                                   fprintf(stderr, "%s: invalid option '%c'\n", argv[0], c);
                                                   usage(argv[0]);
                                                   break;
                                   }
                           }
                           optind++;
                   }
                   
                   if (optind > argc - 1) {
                           fprintf(stderr, "%s: file not specified\n", argv[0]);
                           usage(argv[0]);
                 }                  }
                   raw_filespec_to_process=argv[optind];
                   args_skip=optind;
         }          }
   
 #ifdef WIN32  #ifdef _MSC_VER
         setmode(fileno(stdin), _O_BINARY);          setmode(fileno(stdin), _O_BINARY);
         setmode(fileno(stdout), _O_BINARY);          setmode(fileno(stdout), _O_BINARY);
         setmode(fileno(stderr), _O_BINARY);          setmode(fileno(stderr), _O_BINARY);
 #endif  #endif
   
 #if _MSC_VER  #if defined(_MSC_VER) && defined(_DEBUG)
         _set_new_handler(failed_new);  
   
 #ifdef _DEBUG  
         // Get current flag          // Get current flag
         int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );          int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
   
Line 427  int main(int argc, char *argv[]) { Line 599  int main(int argc, char *argv[]) {
   
         // Set flag to the new value          // Set flag to the new value
         _CrtSetDbgFlag( tmpFlag );          _CrtSetDbgFlag( tmpFlag );
 //      _CrtSetBreakAlloc(471);  
   
           _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
           _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
 #endif  #endif
   
 #endif          char filespec_to_process[MAX_STRING];
           full_file_spec(raw_filespec_to_process, filespec_to_process, sizeof(filespec_to_process));
   
 #ifdef HAVE_SET_NEW_HANDLER          const char* request_method=getenv("REQUEST_METHOD");
         std::set_new_handler(failed_new);  
 #endif  
   
         char *filespec_to_process=cgi?getenv("PATH_TRANSLATED"):argv[1];  
 #ifdef WIN32  
         back_slashes_to_slashes(filespec_to_process);  
 #endif  
         filespec_to_process=full_file_spec(filespec_to_process);  
   
         const char *request_method=getenv("REQUEST_METHOD");  
         bool header_only=request_method && strcasecmp(request_method, "HEAD")==0;          bool header_only=request_method && strcasecmp(request_method, "HEAD")==0;
   
         try { // global try          try { // global try
                 call_real_parser_handler__do_SEH(                  REAL_PARSER_HANDLER(filespec_to_process, request_method, header_only);
                         filespec_to_process,          } catch(const Exception& e) { // exception in unhandled exception
                         request_method, header_only);                  SAPI::die("Unhandled exception %s", e.comment());
         } catch(const Exception& e) { // global problem   
                 // don't allocate anything on pool here:  
                 //   possible pool' exception not catch-ed now  
                 //   and there could be out-of-memory exception  
   
                 SAPI::die("exception in request exception handler: %s", e.comment());  
 #ifndef _DEBUG  
         } catch(...) {   
                 SAPI::die("<unknown exception>");  
 #endif  
         }          }
           
   
 #ifndef WIN32  #ifdef PA_DEBUG_CGI_ENTRY_EXIT
         //           log("main: successful return");
         if(!cgi)  
                 SAPI::send_body(pool, "\n", 1);  
 #endif  #endif
         return 0;          return SAPI_info.http_response_code < 100 ? SAPI_info.http_response_code : 0;
 }  }

Removed from v.1.154  
changed lines
  Added in v.1.292


E-mail: