Diff for /parser3/src/targets/cgi/parser3.C between versions 1.166 and 1.283

version 1.166, 2002/04/04 07:57:50 version 1.283, 2019/12/27 14:37:17
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
 //#define DEBUG_STRING_APPENDS_VS_EXPANDS  
   
 #ifdef DEBUG_STRING_APPENDS_VS_EXPANDS  // comment remove me after debugging
 extern ulong   //#define PA_DEBUG_CGI_ENTRY_EXIT       "parser3-debug.log"
         string_piece_appends,   
         wcontext_result_size,  #if defined(_MSC_VER) && !defined(_DEBUG)
         total_alloc_size,  #       define PA_SUPPRESS_SYSTEM_EXCEPTION
         string_string_shortcut_economy;  
 #endif  #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  
 };  
   
 const char *PARSER_ROOT_CONFIG_ENV_NAME="HTTP_PARSER_ROOT_CONFIG";  
 const char *PARSER_SITE_CONFIG_ENV_NAME="HTTP_PARSER_SITE_CONFIG";  
   
 /// 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* argv0;
 Pool_storage pool_storage;  static const char* config_filespec_cstr=0;
 Pool pool(&pool_storage); // global pool [dont describe to doxygen: it confuses it with param names]  static bool fail_on_config_read_problem=true;
 bool cgi; ///< we were started as CGI?  
   static int args_skip=1;
   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 110  static void log(const char *fmt, va_list Line 128  static void log(const char *fmt, va_list
                 fflush(f);                  fflush(f);
 }  }
   
 // appends to parser3.log located beside my binary if openable, to stderr otherwize  #ifdef PA_DEBUG_CGI_ENTRY_EXIT
 void SAPI::log(Pool& , const char *fmt, ...) {  static void log(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, ...) {  
 #ifdef DEBUG_POOL_MALLOC  
         extern void log_pool_stats(Pool& pool);  
         log_pool_stats(pool);  
 #endif  #endif
   
     va_list args;  // appends to parser3.log located beside my binary if openable, to stderr otherwize
   void SAPI::log(SAPI_Info&, const char* fmt, ...) {
           va_list args;
         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);
   }
   
   static void die_or_abort(const char* fmt, va_list args, bool write_core) {
         // inform user          // inform user
   
         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 & try to produce core dump[unix] or invoke debugger[Win32 Debug version]
           if(write_core) {
   #ifdef WIN32
                   // IIS with abort failes to show STDOUT, it just barks "abnormal program termination"
                   exit(1);
   #else
                   abort();
   #endif
           } else
                   exit(1);
   }
   
   void SAPI::die(const char* fmt, ...) {
           va_list args;
   
           // logging first, can't log inside die_or_abort due to vsnprintf (bug #106)
           va_start(args,fmt);
           ::log(fmt, args);
           va_end(args);
   
           va_start(args, fmt);
           die_or_abort(fmt, args, false /*write core?*/);
   //      va_end(args);
   }
   
         exit(1);  void SAPI::abort(const char* fmt, ...) {
           va_list args;
   
           // logging first, can't log inside die_or_abort due to vsnprintf (bug #106)
           va_start(args,fmt);
           ::log(fmt, args);
           va_end(args);
   
           va_start(args, fmt);
           die_or_abort(fmt, args, true /*write core?*/);
   //      va_end(args);
   }
   
   char* SAPI::Env::get(SAPI_Info& , const char* name) {
           if(char *local=getenv(name))
                   return pa_strdup(local);
           else
                   return 0;
 }  }
   
 const char *SAPI::get_env(Pool& , const char *name) {  const char* const *SAPI::Env::get(SAPI_Info&) {
         return getenv(name);  #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 174  size_t SAPI::read_post(Pool& , char *buf Line 234  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)
 void full_file_spec(const char *file_name, char *buf, size_t buf_size) {                  if(file_name[0]=='/' 
         if(file_name && !strchr(file_name, '/')) {  #ifdef WIN32
                 char cwd[MAX_STRING];  getcwd(cwd, MAX_STRING);                          || file_name[0] && file_name[1]==':'
                 snprintf(buf, buf_size, "%s/%s", cwd, file_name);  #endif
         } else {                  ){
                 strncpy(buf, file_name, buf_size);                          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  #ifdef WIN32
         back_slashes_to_slashes(buf);          back_slashes_to_slashes(buf);
 #endif  #endif
 }  }
   
   static void log_signal(const char* signal_name) {
           if(request_info)
                   SAPI::log(SAPI_info, "%s received while %s. uri=%s, method=%s, cl=%u",
                           signal_name,
                           request ? "executing code" : "reading data",
                           request_info->uri,
                           request_info->method,
                           request_info->content_length);
           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
   
   #ifdef SIGPIPE
   #define SIGPIPE_NAME "SIGPIPE"
   static const String sigpipe_name(SIGPIPE_NAME);
   static void SIGPIPE_handler(int /*sig*/){
           Value* sigpipe=0;
           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 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;
           }
   };
   
   
 /**  /**
 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));
         char document_root_buf[MAX_STRING];          char document_root_buf[MAX_STRING];
   
           request_info.path_translated = filespec_to_process;
           request_info.method = request_method ? request_method : "GET";
           request_info.query_string = MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(getenv("QUERY_STRING"));
   
         if(cgi) {          if(cgi) {
                 if(const char *env_document_root=SAPI::get_env(pool, "DOCUMENT_ROOT"))                  // few absolute obligatory
                         request_info.document_root=env_document_root;                  const char* path_info=getenv("PATH_INFO");
                 else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO")) {                  if(!path_info)
                           SAPI::die("CGI: illegal call (missing PATH_INFO)");
                   
                   const char* script_name=getenv("SCRIPT_NAME");
                   if(!script_name)
                                   SAPI::die("CGI: illegal call (missing SCRIPT_NAME)");
                   
                   request_info.document_root = getenv("DOCUMENT_ROOT");
                   if(!request_info.document_root) {
                         // IIS                          // IIS
                         size_t len=min(sizeof(document_root_buf)-1, strlen(filespec_to_process)-strlen(path_info));                          size_t len=min(sizeof(document_root_buf)-1, strlen(filespec_to_process)-strlen(path_info));
                         memcpy(document_root_buf, filespec_to_process, len); document_root_buf[len]=0;                          memcpy(document_root_buf, filespec_to_process, len); document_root_buf[len]=0;
                         request_info.document_root=document_root_buf;                          request_info.document_root=document_root_buf;
                 } else                  }
                         throw Exception("parser.runtime",  
                                 0,                  request_info.uri=request_info.strip_absolute_uri(getenv("REQUEST_URI"));
                                 "CGI: no PATH_INFO defined(in reinventing DOCUMENT_ROOT)");                  if(request_info.uri) { // apache & others stuck to standards
         } else {                          /*
                 full_file_spec("", document_root_buf, sizeof(document_root_buf));                                  http://parser3/env.html?123  =OK
                 request_info.document_root=document_root_buf;                                  $request:uri=/env.html?123
         }                                  REQUEST_URI='/env.html?123'
         request_info.path_translated=filespec_to_process;                                  SCRIPT_NAME='/cgi-bin/parser3'
         request_info.method=request_method ? request_method : "GET";                                  PATH_INFO='/env.html'
         const char *query_string=SAPI::get_env(pool, "QUERY_STRING");  
         request_info.query_string=query_string;                                  http://parser3/cgi-bin/parser3/env.html?123 =ERROR
         if(cgi) {                                  $request:uri=/cgi-bin/parser3/env.html?123
                 if(const char *env_request_uri=SAPI::get_env(pool, "REQUEST_URI"))                                  REQUEST_URI='/cgi-bin/parser3/env.html?123'
                         request_info.uri=env_request_uri;                                  SCRIPT_NAME='/cgi-bin/parser3'
                 else if(const char *path_info=SAPI::get_env(pool, "PATH_INFO"))                                  PATH_INFO='/env.html'
                         if(query_string) {                          */
                                 char *reconstructed_uri=(char *)pool.malloc(                          size_t script_name_len=strlen(script_name);
                                         strlen(path_info)+1/*'?'*/+                          size_t uri_len=strlen(request_info.uri);
                                         strlen(query_string)+1/*0*/);                          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 { // seen on IIS5
   
                           if(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("parser.runtime",  
                                         0,  
                                         "CGI: no PATH_INFO defined(in reinventing REQUEST_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  
         const char *root_config_filespec;  
         if(const char *root_config_by_env=getenv(PARSER_ROOT_CONFIG_ENV_NAME))  
                 root_config_filespec=root_config_by_env;  
         else {  
 #ifdef ROOT_CONFIG_DIR  
                 root_config_filespec=ROOT_CONFIG_DIR "/" CONFIG_FILE_NAME;  
 #else  
 #       ifdef WIN32  
                 // c:\windows  
                 char windows_dir[MAX_STRING];  
                 GetWindowsDirectory(windows_dir, MAX_STRING);  
   
                 char buf[MAX_STRING];                          /*
                 snprintf(buf, MAX_STRING,                                   http://nestle/env.html?123 =OK
                         "%s/%s",                                   $request:uri=/env.html?123
                         windows_dir, CONFIG_FILE_NAME);                                  REQUEST_URI=''
                                                   SCRIPT_NAME='/env.html'
                 root_config_filespec=buf;                                  PATH_INFO='/env.html'
 #       else  
 #error must be compiled either configure/make or MSVC++                                  http://nestle/cgi-bin/parser3.exe/env.html =ERROR
 #       endif                                  $request:uri=/env.html
 #endif                                  REQUEST_URI=''
         }                                  SCRIPT_NAME='/cgi-bin/parser3.exe'
                                           PATH_INFO='/env.html'
         const char *site_config_filespec;                          */
         if(const char *site_config_by_env=getenv(PARSER_SITE_CONFIG_ENV_NAME))                          if(strcmp(script_name, path_info)!=0)
                 site_config_filespec=site_config_by_env;                                  SAPI::die("CGI: illegal call (2)");
         else {  
         // beside by binary  
         // @todo full path, not ./!  
                 char beside_binary_path[MAX_STRING];  
                 strncpy(beside_binary_path, argv0, 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  
                         beside_binary_path[0]='.'; beside_binary_path[1]=0;  
                 }                  }
                 char buf[MAX_STRING];          } else{
                 snprintf(buf, MAX_STRING,                   full_file_spec("", document_root_buf, sizeof(document_root_buf));
                         "%s/%s",                   request_info.document_root=document_root_buf;
                         beside_binary_path, CONFIG_FILE_NAME);                  request_info.uri="";
                 site_config_filespec=buf;  
         }          }
                   
         // process the request          request_info.content_type = getenv("CONTENT_TYPE");
         request.core(          const char* content_length = getenv("CONTENT_LENGTH");
                 root_config_filespec, false /*don't fail_on_read_problem*/,          request_info.content_length = content_length ? atoi(content_length) : 0;
                 site_config_filespec, false /*don't fail_on_read_problem*/,          request_info.cookie = getenv("HTTP_COOKIE");
                 header_only);          request_info.mail_received = mail_received;
           
         //          request_info.argv = argv_all;
         done_socks();          request_info.args_skip = args_skip;
           
 #ifdef DEBUG_POOL_MALLOC          // get request_info ptr for signal handlers
         extern void log_pool_stats(Pool& pool);          ::request_info = &request_info;
         log_pool_stats(pool);          if(execution_canceled)
                   SAPI::die("Execution canceled");
   
   #ifdef PA_DEBUG_CGI_ENTRY_EXIT
           log("request_info: method=%s, uri=%s, q=%s, dr=%s, pt=%s, cookies=%s, cl=%u", 
                   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
   
 #ifdef DEBUG_STRING_APPENDS_VS_EXPANDS          // prepare to process request
         SAPI::log(pool,           Request request(SAPI_info, request_info, cgi ? String::Language(String::L_HTML|String::L_OPTIMIZE_BIT) : String::L_AS_IS);
                 "string piece appends=%lu, wcontext_result_size=%lu, string_string_shortcut_economy_closer=%lu, total_alloc_size=%lu",   
                 string_piece_appends,          {
                 wcontext_result_size,                  // get ::request ptr for signal handlers
                 string_string_shortcut_economy,                  RequestController rc(&request);
                 total_alloc_size);  
 #endif                  char config_filespec_buf[MAX_STRING];
                   if(!config_filespec_cstr) {
                           const char* config_by_env=getenv(PARSER_CONFIG_ENV_NAME);
                           if(!config_by_env)
                                   config_by_env=getenv(REDIRECT_PREFIX PARSER_CONFIG_ENV_NAME);
                           if(config_by_env)
                                   config_filespec_cstr=config_by_env;
                           else {
                                   // beside by binary
                                   char beside_binary_path[MAX_STRING];
                                   strncpy(beside_binary_path, argv0, 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;
                                   }
                                   snprintf(config_filespec_buf, MAX_STRING, "%s/%s", beside_binary_path, AUTO_FILE_NAME);
                                   config_filespec_cstr=config_filespec_buf;
                                   fail_on_config_read_problem=entry_exists(config_filespec_cstr);
                           }
                   }
                   
                   // 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(  
                         filespec_to_process,  
                         request_method, header_only);  
                   
 #if _MSC_VER && !defined(_DEBUG)  
         } __except (          } __except (
                 (system_exception=GetExceptionInformation()),                   (system_exception=GetExceptionInformation()), 
                 EXCEPTION_EXECUTE_HANDLER) {                  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,                                  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, "Exception <no exception record>");  
                         else                          else
                                 throw Exception(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
           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;
         argv0=argv[0];          argv0=argv[0];
   
         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 459  int main(int argc, char *argv[]) { Line 655  int main(int argc, char *argv[]) {
   
         // Set flag to the new value          // Set flag to the new value
         _CrtSetDbgFlag( tmpFlag );          _CrtSetDbgFlag( tmpFlag );
 //      _CrtSetBreakAlloc(471);  
   
 #endif  
   
           _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
           _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
 #endif  #endif
   
 #ifdef HAVE_SET_NEW_HANDLER  
         std::set_new_handler(failed_new);  
 #endif  
   
         char *raw_filespec_to_process=cgi?getenv("PATH_TRANSLATED"):argv[1];  
         char filespec_to_process[MAX_STRING];          char filespec_to_process[MAX_STRING];
         full_file_spec(raw_filespec_to_process, filespec_to_process, sizeof(filespec_to_process));          full_file_spec(raw_filespec_to_process, filespec_to_process, sizeof(filespec_to_process));
   
         const char *request_method=getenv("REQUEST_METHOD");          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,  
                         request_method, header_only);  
         } catch(const Exception& e) { // global problem           } catch(const Exception& e) { // global problem 
                 // don't allocate anything on pool here:                  // don't allocate anything on pool here:
                 //   possible pool' exception not catch-ed now                  //   possible pool' exception not catch-ed now
                 //   and there could be out-of-memory exception                  //   and there could be out-of-memory exception
                   char buf[MAX_STRING];
                   snprintf(buf, MAX_STRING, "Unhandled exception %s", e.comment());
                   // log it
                   SAPI::log(SAPI_info, "%s", buf);
   
                   //
                   int content_length=strlen(buf);
   
                   // prepare header
                   // capitalized headers are used for preventing malloc during capitalization
                   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];
                   snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length);
                   SAPI::add_header_attribute(SAPI_info, HTTP_CONTENT_LENGTH_CAPITALIZED, content_length_cstr);
   
                   // send header
                   SAPI::send_header(SAPI_info);
   
                   // send body
                   if(!header_only)
                           SAPI::send_body(SAPI_info, buf, content_length);
   
                 SAPI::die("exception in request exception handler: %s", e.comment());                  // unsuccessful finish
 #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
 //_asm int 3;          return SAPI_info.http_response_code < 100 ? SAPI_info.http_response_code : 0;
         return 0;  
 }  }

Removed from v.1.166  
changed lines
  Added in v.1.283


E-mail: