Diff for /parser3/src/targets/cgi/parser3.C between versions 1.330 and 1.363

version 1.330, 2020/12/15 17:23:56 version 1.363, 2024/12/03 23:48:43
Line 1 Line 1
 /** @file  /** @file
         Parser: scripting and CGI main.          Parser: scripting and CGI main.
   
         Copyright (c) 2001-2020 Art. Lebedev Studio (http://www.artlebedev.com)          Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
 */  */
   
 volatile const char * IDENT_PARSER3_C="$Id$";  volatile const char * IDENT_PARSER3_C="$Id$";
Line 14  volatile const char * IDENT_PARSER3_C="$ Line 14  volatile const char * IDENT_PARSER3_C="$
 #include "pa_common.h"  #include "pa_common.h"
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_version.h"  #include "pa_version.h"
   #include "pa_threads.h"
 #include "pa_vconsole.h"  #include "pa_vconsole.h"
 #include "pa_sapi_info.h"  #include "pa_sapi_info.h"
   
Line 33  extern "C" int GC_pthread_create(pthread Line 34  extern "C" int GC_pthread_create(pthread
 // defines  // defines
   
 // comment remove me after debugging  // comment remove me after debugging
 //#define PA_DEBUG_CGI_ENTRY_EXIT "parser3-debug.log"  //#define PA_DEBUG_CGI_ENTRY_EXIT
   
 #if defined(_MSC_VER) && !defined(_DEBUG)  #if defined(_MSC_VER) && !defined(_DEBUG)
 #       define PA_SUPPRESS_SYSTEM_EXCEPTION  #       define PA_SUPPRESS_SYSTEM_EXCEPTION
Line 45  extern "C" int GC_pthread_create(pthread Line 46  extern "C" int GC_pthread_create(pthread
 #define PARSER_CONFIG_ENV_NAME "CGI_PARSER_CONFIG"  #define PARSER_CONFIG_ENV_NAME "CGI_PARSER_CONFIG"
 #define PARSER_LOG_ENV_NAME "CGI_PARSER_LOG"  #define PARSER_LOG_ENV_NAME "CGI_PARSER_LOG"
   
   SAPI_Info sapi_console;
   SAPI_Info_CGI sapi_cgi;
   
   static SAPI_Info *sapi_info = &sapi_cgi;
   static THREAD_LOCAL SAPI_Info *sapi_info_4log = NULL; // global for correct send error in die()
   
 static const char* filespec_to_process = 0; // [file]  static const char* filespec_to_process = 0; // [file]
 static const char* httpd_host_port = 0; // -p option  static const char* httpd_host_port = 0; // -p option
 static const char* config_filespec = 0; // -f option or from env or next to the executable if exists  static const char* config_filespec = 0; // -f option or from env or next to the executable if exists
   static const char* log_filespec = 0; // -l option
 static bool mail_received = false; // -m option? [asked to parse incoming message to $mail:received]  static bool mail_received = false; // -m option? [asked to parse incoming message to $mail:received]
 static char* parser3_filespec = 0; // argv[0]  static const char* parser3_filespec = 0; // argv[0]
 static char** argv_extra = NULL;  static char** argv_extra = NULL;
   
 // for error logging  // for error logging
 static THREAD_LOCAL Request_info request_info; // global for correct log() reporting  static THREAD_LOCAL Request_info *request_info_4log = NULL; // global for correct log() reporting
 static const char* filespec_4log = NULL; // null only if system-wide auto.p used  static const char* filespec_4log = NULL; // null only if system-wide auto.p used
   
   template <typename T> static T *dir_pos(T *fname){
           T *result=NULL;
           while (fname=strpbrk(fname, "/\\")){
                   result=fname;
                   fname++;
           }
           return result;
   }
   
 // SAPI  // SAPI
   
 static void log(const char* fmt, va_list args) {  static void pa_log(const char* fmt, va_list args) {
         bool opened=false;  
         FILE *f=0;          FILE *f=0;
           const char* slog=log_filespec;
   
         const char* log_by_env=getenv(PARSER_LOG_ENV_NAME);          if(!slog)
         if(!log_by_env)                  slog=getenv(PARSER_LOG_ENV_NAME);
                 log_by_env=getenv(REDIRECT_PREFIX PARSER_LOG_ENV_NAME);          if(!slog)
         if(log_by_env) {                  slog=getenv(REDIRECT_PREFIX PARSER_LOG_ENV_NAME);
                 f=fopen(log_by_env, "at");  
                 opened=f!=0;          if(slog) {
         }                  f=strcmp(slog,"-") ? fopen(slog, "at") : stderr;
 #ifdef PA_DEBUG_CGI_ENTRY_EXIT          } else  if(filespec_4log) {
         f=fopen(PA_DEBUG_CGI_ENTRY_EXIT, "at");                  char log_spec[MAX_STRING + 12 /* '/parser3.log' */];
         opened=f!=0;                  pa_strncpy(log_spec, filespec_4log, MAX_STRING);
 #endif  
   
         if(!opened && filespec_4log) {                  if(char* log_dir_pos=dir_pos(log_spec)){
                 char beside_config_path[MAX_STRING];                          strcpy(log_dir_pos, "/parser3.log");
                 strncpy(beside_config_path, filespec_4log, MAX_STRING-1);  beside_config_path[MAX_STRING-1]=0;                  } else {
                 if(!(rsplit(beside_config_path, '/') || rsplit(beside_config_path, '\\'))) { // strip filename  
                         // no path, just filename                          // no path, just filename
                         strcpy(beside_config_path, ".");                          strcpy(log_spec, "./parser3.log");
                 }                  }
                   f=fopen(log_spec, "at");
                 char file_spec[MAX_STRING];  
                 snprintf(file_spec, MAX_STRING, "%s/parser3.log", beside_config_path);  
                 f=fopen(file_spec, "at");  
                 opened=f!=0;  
         }          }
   
         // fallback to stderr          // fallback to stderr
         if(!opened)          if(!f)
                 f=stderr;                  f=stderr;
   
         // use no memory [so that we could log out-of-memory error]          // use no memory [so that we could log out-of-memory error]
Line 98  static void log(const char* fmt, va_list Line 110  static void log(const char* fmt, va_list
         time_t t=time(0);          time_t t=time(0);
         if(const char* stamp=ctime(&t)) { // never saw that          if(const char* stamp=ctime(&t)) { // never saw that
                 if(size_t len=strlen(stamp)) // saw once stamp being =""                  if(size_t len=strlen(stamp)) // saw once stamp being =""
                         fprintf(f, "[%.*s] [%u] ", (int)len-1, stamp, (unsigned int)getpid() );                          fprintf(f, "[%.*s] [%u] ", (int)len-1, stamp, (unsigned int)pa_get_thread_id() );
         }          }
         // message          // message
   
Line 107  static void log(const char* fmt, va_list Line 119  static void log(const char* fmt, va_list
         size=remove_crlf(buf, buf+size);          size=remove_crlf(buf, buf+size);
         fwrite(buf, size, 1, f);          fwrite(buf, size, 1, f);
   
         if(request_info.method) {          if(request_info_4log && request_info_4log->method) {
                 fprintf(f, " [uri=%s, method=%s, cl=%lu]\n", request_info.uri ? request_info.uri : "<unknown>", request_info.method, (unsigned long)request_info.content_length);                  fprintf(f, " [uri=%s, method=%s, cl=%lu]\n", request_info_4log->uri ? request_info_4log->uri : "<unknown>", request_info_4log->method, (unsigned long)request_info_4log->content_length);
         } else          } else
                 fputs(" [no request info]\n", f);                  fputs(" [no request info]\n", f);
   
         if(opened)          if(f!=stderr)
                 fclose(f);                  fclose(f);
         else          else
                 fflush(f);                  fflush(f);
 }  }
   
 #ifdef PA_DEBUG_CGI_ENTRY_EXIT  void pa_log(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);          pa_log(fmt, args);
         va_end(args);          va_end(args);
 }  }
 #endif  
   
 // appends to parser3.log located next to the config file if openable, to stderr otherwize  // appends to parser3.log located next to the config file if openable, to stderr otherwize
 void SAPI::log(SAPI_Info&, 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);          pa_log(fmt, args);
         va_end(args);          va_end(args);
 }  }
   
Line 140  void SAPI::die(const char* fmt, ...) { Line 150  void SAPI::die(const char* fmt, ...) {
   
         // logging first, first vsnprintf          // logging first, first vsnprintf
         va_start(args,fmt);          va_start(args,fmt);
         ::log(fmt, args);          pa_log(fmt, args);
         va_end(args);          va_end(args);
   
         // inform user, second vsnprintf          // inform user, second vsnprintf
Line 148  void SAPI::die(const char* fmt, ...) { Line 158  void SAPI::die(const char* fmt, ...) {
         char message[MAX_STRING];          char message[MAX_STRING];
         vsnprintf(message, MAX_STRING, fmt, args);          vsnprintf(message, MAX_STRING, fmt, args);
   
         SAPI::send_error(*sapiInfo, message);          SAPI::send_error(sapi_info_4log ? *sapi_info_4log : *sapi_info, message);
         exit(1);          exit(1);
 //      va_end(args);  //      va_end(args);
 }  }
   
   void SAPI::send_error(SAPI_Info& info, const char *exception_cstr, const char *status){
           info.send_error(exception_cstr, status);
   }
   
 char* SAPI::Env::get(SAPI_Info& info, const char* name) {  char* SAPI::Env::get(SAPI_Info& info, const char* name) {
         return info.get_env(name);          return info.get_env(name);
 }  }
Line 165  const char* const *SAPI::Env::get(SAPI_I Line 179  const char* const *SAPI::Env::get(SAPI_I
         return info.get_env();          return info.get_env();
 }  }
   
 size_t SAPI::read_post(SAPI_Info& info, char *buf, size_t max_bytes) {  size_t SAPI::read_post(SAPI_Info& info, char* buf, size_t max_bytes) {
         return info.read_post(buf, max_bytes);          return info.read_post(buf, max_bytes);
 }  }
   
 void SAPI::add_header_attribute(SAPI_Info& info, const char* dont_store_key, const char* dont_store_value) {  void SAPI::add_header_attribute(SAPI_Info& info, const char* dont_store_key, const char* dont_store_value) {
         info.add_header_attribute(dont_store_key, dont_store_value);          info.add_header(dont_store_key, dont_store_value);
 }  }
   
 void SAPI::send_header(SAPI_Info& info) {  void SAPI::send_headers(SAPI_Info& info) {
         info.send_header();          info.send_headers();
   }
   
   void SAPI::clear_headers(SAPI_Info& info) {
           info.clear_headers();
 }  }
   
 size_t SAPI::send_body(SAPI_Info& info, const void *buf, size_t size) {  size_t SAPI::send_body(SAPI_Info& info, const void *buf, size_t size) {
         return info.send_body(buf, size);          return info.send_body(buf, size);
 }  }
   
 static void full_disk_path(const char* file_name, char *buf, size_t buf_size) {  static const char* full_disk_path(const char* file_name = "") {
         if(file_name[0]=='/'           char* result;
           if(file_name[0]=='/'
 #ifdef WIN32  #ifdef WIN32
                 || file_name[0] && file_name[1]==':'                  || file_name[0] && file_name[1]==':'
 #endif  #endif
         ){          ){
                 strncpy(buf, file_name, buf_size-1); buf[buf_size-1]=0;                  result=pa_strdup(file_name);
         } else {          } else {
                 char cwd[MAX_STRING];                  char cwd[MAX_STRING];
                 snprintf(buf, buf_size, "%s/%s", getcwd(cwd, MAX_STRING) ? cwd : "", file_name);                  result=pa_strcat(getcwd(cwd, MAX_STRING) ? cwd : "", "/", file_name);
         }          }
 #ifdef WIN32  #ifdef WIN32
         back_slashes_to_slashes(buf);          back_slashes_to_slashes(result);
 #endif  #endif
           return result;
 }  }
   
 static void log_signal(const char* signal_name) {  static void log_signal(const char* signal_name) {
         SAPI::log(*sapiInfo, "%s received %s processing request", signal_name, request ? "while" : "before or after");          pa_log("%s received %s processing request", signal_name, request ? "while" : "before or after");
 }  
   
 #ifdef SIGUSR1  
 static void SIGUSR1_handler(int /*sig*/){  
         log_signal("SIGUSR1");  
 }  }
 #endif  
   
 #ifdef SIGPIPE  #ifdef SIGPIPE
 #define SIGPIPE_NAME "SIGPIPE"  #define SIGPIPE_NAME "SIGPIPE"
Line 223  static void SIGPIPE_handler(int /*sig*/) Line 237  static void SIGPIPE_handler(int /*sig*/)
 #endif  #endif
   
 // requires pa_thread_request() in entry_exists() under Windows  // requires pa_thread_request() in entry_exists() under Windows
 static const char *locate_config(const char *config_filespec_option, const char *executable_path){  static const char* locate_config(const char* config_filespec_option, const char* executable_path){
         filespec_4log=config_filespec_option;          filespec_4log=config_filespec_option;
         if(!filespec_4log)          if(!filespec_4log)
                 filespec_4log=getenv(PARSER_CONFIG_ENV_NAME);                  filespec_4log=getenv(PARSER_CONFIG_ENV_NAME);
         if(!filespec_4log)          if(!filespec_4log)
                 filespec_4log=getenv(REDIRECT_PREFIX PARSER_CONFIG_ENV_NAME);                  filespec_4log=getenv(REDIRECT_PREFIX PARSER_CONFIG_ENV_NAME);
         if(!filespec_4log){          if(!filespec_4log){
                         // next to the executable                          const char* exec_dir_pos = dir_pos(executable_path);
                         char *beside_executable_path = pa_strdup(executable_path);  #ifdef SYSTEM_CONFIG_FILE
                         bool stripped_filename = rsplit(beside_executable_path, '/') || rsplit(beside_executable_path, '\\');                          if(exec_dir_pos){
                         filespec_4log = pa_strcat(stripped_filename ? beside_executable_path : "." /* no path, just filename */ , "/" AUTO_FILE_NAME);  #endif
                         if(entry_exists(filespec_4log))                                  // next to the executable
                                 return filespec_4log;                                  if(!exec_dir_pos || (exec_dir_pos==executable_path+1 && *executable_path=='.')){
                                           // when just parser3 or ./parser3 full path should be used to avoid "parser already configured"
                                           filespec_4log=full_disk_path(AUTO_FILE_NAME);
                                   } else {
                                           filespec_4log=pa_strcat(pa_strdup(executable_path, exec_dir_pos - executable_path), "/" AUTO_FILE_NAME);
                                   }
                                   if(entry_exists(filespec_4log))
                                           return filespec_4log;
 #ifdef SYSTEM_CONFIG_FILE  #ifdef SYSTEM_CONFIG_FILE
                           }
                         if(entry_exists(SYSTEM_CONFIG_FILE)){                          if(entry_exists(SYSTEM_CONFIG_FILE)){
                                 filespec_4log=NULL;                                  filespec_4log=NULL;
                                 return SYSTEM_CONFIG_FILE;                                  return SYSTEM_CONFIG_FILE;
Line 248  static const char *locate_config(const c Line 270  static const char *locate_config(const c
 }  }
   
 #ifdef WIN32  #ifdef WIN32
 const char* maybe_reconstruct_IIS_status_in_qs(const char* original) {  static const char* maybe_reconstruct_IIS_status_in_qs(const char* original) {
         // 404;http://servername/page[?param=value...]          // 404;http://servername/page[?param=value...]
         // ';' should be urlencoded by HTTP standard, so we shouldn't get it from browser           // ';' 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          // and can consider that as an indication that this is IIS way to report errors
Line 280  const char* maybe_reconstruct_IIS_status Line 302  const char* maybe_reconstruct_IIS_status
         return original;          return original;
 }  }
   
   static const char* maybe_back_slashes_to_slashes(const char* original){
           char *result=pa_strdup(original);
           back_slashes_to_slashes(result);
           return result;
   }
   
 #define MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(s) maybe_reconstruct_IIS_status_in_qs(s)  #define MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(s) maybe_reconstruct_IIS_status_in_qs(s)
 #else   #define MAYBE_BACK_SLASHES_TO_SLASHES(s) maybe_back_slashes_to_slashes(s)
   
   #else
   
 #define MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(s) s  #define MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(s) s
 #endif  #define MAYBE_BACK_SLASHES_TO_SLASHES(s) s
   
   #endif
   
 class RequestController {  class RequestController {
 public:  public:
         RequestController(Request* r){          RequestController(Request* r){
                 ::request=r;                  request=r;
         }          }
         ~RequestController(){          ~RequestController(){
                 ::request=0;                  request=0;
           }
   };
   
   class RequestInfoController {
   public:
           RequestInfoController(Request_info* rinfo, SAPI_Info* sinfo){
                   request_info_4log=rinfo;
                   sapi_info_4log=sinfo;
           }
           ~RequestInfoController(){
                   request_info_4log=0;
                   sapi_info_4log=0;
         }          }
 };  };
   
   /** httpd support */
   static const String httpd_class_name("httpd");
   
 static void config_handler(SAPI_Info &info) {  static void config_handler(SAPI_Info &info) {
         char document_root_buf[MAX_STRING];          Request_info request_info;
         full_disk_path("", document_root_buf, sizeof(document_root_buf));          RequestInfoController ric(&request_info, &info);
   
         request_info.document_root = document_root_buf;          request_info.document_root = full_disk_path();
         request_info.uri = "";          request_info.uri = "";
         request_info.argv = argv_extra;          request_info.argv = argv_extra;
   
Line 313  static void config_handler(SAPI_Info &in Line 360  static void config_handler(SAPI_Info &in
 }  }
   
 static void connection_handler(SAPI_Info_HTTPD &info, HTTPD_Connection &connection) {  static void connection_handler(SAPI_Info_HTTPD &info, HTTPD_Connection &connection) {
           Request_info request_info;
           RequestInfoController ric(&request_info, &info);
   
         try {          try {
                 connection.read_header();                  if(!connection.read_header())
                           return; // ignore "void" connections
                 info.populate_env();                  info.populate_env();
   
                 char document_root_buf[MAX_STRING];                  request_info.document_root = full_disk_path();
                 full_disk_path("", document_root_buf, sizeof(document_root_buf));  
   
                 memset(&request_info, 0, sizeof(request_info));  
                 request_info.document_root = document_root_buf;  
                 request_info.path_translated = filespec_to_process;                  request_info.path_translated = filespec_to_process;
                 request_info.method = connection.method();                  request_info.method = connection.method();
                 request_info.query_string = connection.query();                  request_info.query_string = connection.query();
Line 335  static void connection_handler(SAPI_Info Line 382  static void connection_handler(SAPI_Info
                 // prepare to process request                  // prepare to process request
                 Request r(info, request_info, String::Language(String::L_HTML|String::L_OPTIMIZE_BIT));                  Request r(info, request_info, String::Language(String::L_HTML|String::L_OPTIMIZE_BIT));
                 // process the request                  // process the request
                 r.core(config_filespec, strcasecmp(request_info.method, "HEAD")==0, String("httpd-main"));                  r.core(config_filespec, strcasecmp(request_info.method, "HEAD")==0, main_method_name, &httpd_class_name);
         } catch(const Exception& e) { // exception in connection handling or unhandled exception          } catch(const Exception& e) { // exception in connection handling
                 SAPI::log(info, "%s", e.comment());                  SAPI::log(info, "%s", e.comment());
                 SAPI::send_error(info, e.comment(), info.exception_http_status(e.type()));                  const char* status = info.exception_http_status(e.type());
                   if(*status)
                           SAPI::send_error(info, e.comment(), status);
         }          }
 }  }
   
Line 353  static void *connection_thread(void *arg Line 402  static void *connection_thread(void *arg
         try {          try {
                 connection_handler(info, connection);                  connection_handler(info, connection);
         } catch(const Exception& e) { // exception in send_error          } catch(const Exception& e) { // exception in send_error
                 SAPI::log(*sapiInfo, "%s", e.comment());                  pa_log("%s", e.comment());
         }          }
   
         delete(&connection);          delete(&connection);
Line 361  static void *connection_thread(void *arg Line 410  static void *connection_thread(void *arg
 }  }
   
 static void httpd_mode() {  static void httpd_mode() {
         config_handler(*sapiInfo);          config_handler(*sapi_info);
   
         int sock = HTTPD_Server::bind(httpd_host_port);          SOCKET sock = HTTPD_Server::bind(httpd_host_port);
   
 #ifdef SIGCHLD  
         signal(SIGCHLD, SIG_IGN);  
 #endif  
 #ifdef SIGPIPE  #ifdef SIGPIPE
         signal(SIGPIPE, SIG_IGN);          signal(SIGPIPE, SIG_IGN);
 #endif  #endif
Line 375  static void httpd_mode() { Line 421  static void httpd_mode() {
         while(1){          while(1){
 #ifndef _MSC_VER  #ifndef _MSC_VER
                 pid_t pid=1;                  pid_t pid=1;
                   if(HTTPD_Server::mode == HTTPD_Server::PARALLEL)
                           while (waitpid((pid_t)(-1), 0, WNOHANG) > 0);
 #endif  #endif
                 try {                  try {
                         HTTPD_Connection connection;                          HTTPD_Connection connection;
                         if(!connection.accept(sock, 5))                          if(!connection.accept(sock, 500))
                                 continue;                                  continue;
   
                         switch (HTTPD_Server::mode) {                          switch (HTTPD_Server::mode) {
Line 386  static void httpd_mode() { Line 434  static void httpd_mode() {
 #ifdef _MSC_VER  #ifdef _MSC_VER
                                         if (!GC_CreateThread(0, 0, connection_thread, new HTTPD_Connection(connection), 0, 0))                                          if (!GC_CreateThread(0, 0, connection_thread, new HTTPD_Connection(connection), 0, 0))
                                                 throw Exception("httpd.fork", 0, "thread creation failed");                                                  throw Exception("httpd.fork", 0, "thread creation failed");
                                         connection.sock = -1;                                          connection.sock=INVALID_SOCKET;
                                         break;                                          break;
 #else  #else
   #ifdef HAVE_TLS
                                         pthread_t thread;                                          pthread_t thread;
                                         pthread_attr_t attr;                                          pthread_attr_t attr;
                                         pthread_attr_init(&attr);                                          pthread_attr_init(&attr);
Line 396  static void httpd_mode() { Line 445  static void httpd_mode() {
   
                                         if(int result=GC_pthread_create(&thread, &attr, connection_thread, new HTTPD_Connection(connection)))                                          if(int result=GC_pthread_create(&thread, &attr, connection_thread, new HTTPD_Connection(connection)))
                                                 throw Exception("httpd.fork", 0, "thread creation failed (%d)", result);                                                  throw Exception("httpd.fork", 0, "thread creation failed (%d)", result);
                                         connection.sock=-1;                                          connection.sock=INVALID_SOCKET;
                                         break;                                          break;
   #endif
                                 case HTTPD_Server::PARALLEL:                                  case HTTPD_Server::PARALLEL:
                                         pid=fork();                                          pid=fork();
                                         if(pid<0)                                          if(pid<0)
Line 412  static void httpd_mode() { Line 462  static void httpd_mode() {
                         }                          }
                         // closing connection socket in HTTPD_Connection destructor                          // closing connection socket in HTTPD_Connection destructor
                 } catch(const Exception& e) { // exception in accept or send_error                  } catch(const Exception& e) { // exception in accept or send_error
                         SAPI::log(*sapiInfo, "%s", e.comment());                          pa_log("%s", e.comment());
                 }                  }
   
 #ifndef _MSC_VER  #ifndef _MSC_VER
Line 437  static void real_parser_handler(bool cgi Line 487  static void real_parser_handler(bool cgi
         if(!filespec_to_process)          if(!filespec_to_process)
                 SAPI::die("Parser/%s", PARSER_VERSION);                  SAPI::die("Parser/%s", PARSER_VERSION);
                   
         char document_root_buf[MAX_STRING];  
   
         // global request info          // global request info
           Request_info request_info;
           RequestInfoController ric(&request_info, sapi_info);
   
         request_info.path_translated = filespec_to_process;          request_info.path_translated = filespec_to_process;
         request_info.method = request_method ? request_method : "GET";          request_info.method = request_method ? request_method : "GET";
         request_info.query_string = MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(getenv("QUERY_STRING"));          request_info.query_string = MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(getenv("QUERY_STRING"));
Line 448  static void real_parser_handler(bool cgi Line 499  static void real_parser_handler(bool cgi
                 // obligatory                  // obligatory
                 const char* path_info=getenv("PATH_INFO");                  const char* path_info=getenv("PATH_INFO");
                 if(!path_info)                  if(!path_info)
                         SAPI::die("CGI: illegal call (missing PATH_INFO)");                          SAPI::die("parser3: illegal CGI call (missing PATH_INFO)");
                                   
                 request_info.document_root = getenv("DOCUMENT_ROOT");                  request_info.document_root = getenv("DOCUMENT_ROOT");
                 if(!request_info.document_root) {                  if(!request_info.document_root) {
                         // IIS or fcgiwrap minimalistic setup                          // IIS or fcgiwrap minimalistic setup
                         ssize_t prefix_len = strlen(filespec_to_process) - strlen(path_info);                          ssize_t prefix_len = strlen(filespec_to_process) - strlen(path_info);
                         if(prefix_len < 0 || strcmp(filespec_to_process + prefix_len, path_info) != 0)                          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)");                                  SAPI::die("parser3: illegal CGI call (invalid PATH_INFO in reinventing DOCUMENT_ROOT)");
   
                         char* document_root = new(PointerFreeGC) char[prefix_len + 1/*0*/];                          char* document_root = new(PointerFreeGC) char[prefix_len + 1/*0*/];
                         memcpy(document_root, filespec_to_process, prefix_len); document_root[prefix_len] = 0;                          memcpy(document_root, filespec_to_process, prefix_len); document_root[prefix_len] = 0;
Line 467  static void real_parser_handler(bool cgi Line 518  static void real_parser_handler(bool cgi
                         // another obligatory                          // another obligatory
                         const char* script_name = getenv("SCRIPT_NAME");                          const char* script_name = getenv("SCRIPT_NAME");
                         if(!script_name)                          if(!script_name)
                                 SAPI::die("CGI: illegal call (missing SCRIPT_NAME)");                                  SAPI::die("parser3: illegal CGI call (missing SCRIPT_NAME)");
                         /*                          /*
                                 http://parser3/env.html?123  =OK                                  http://parser3/env.html?123  =OK
                                 $request:uri=/env.html?123                                  $request:uri=/env.html?123
Line 484  static void real_parser_handler(bool cgi Line 535  static void real_parser_handler(bool cgi
                         size_t script_name_len = strlen(script_name);                          size_t script_name_len = strlen(script_name);
                         size_t uri_len = strlen(request_info.uri);                          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                          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)");                                  SAPI::die("parser3: illegal CGI call (REQUEST_URI starts with SCRIPT_NAME)");
                 } else { // fcgiwrap minimalistic setup                  } else { // fcgiwrap minimalistic setup
                         request_info.uri = request_info.query_string && *request_info.query_string ? pa_strcat(path_info, "?", request_info.query_string) : path_info;                          request_info.uri = request_info.query_string && *request_info.query_string ? pa_strcat(path_info, "?", request_info.query_string) : path_info;
                 }                  }
         } else{          } else{
                 full_disk_path("", document_root_buf, sizeof(document_root_buf));                  request_info.document_root = full_disk_path();
                 request_info.document_root = document_root_buf;  
                 request_info.uri = "";                  request_info.uri = "";
         }          }
                   
         request_info.content_type = getenv("CONTENT_TYPE");          request_info.content_type = getenv("CONTENT_TYPE");
         request_info.content_length = (size_t)pa_atoul(getenv("CONTENT_LENGTH"));          request_info.content_length = cgi ? (size_t)pa_atoul(getenv("CONTENT_LENGTH")) : 0; // only SAPI_Info_CGI can read POST
         request_info.cookie = getenv("HTTP_COOKIE");          request_info.cookie = getenv("HTTP_COOKIE");
         request_info.mail_received = mail_received;          request_info.mail_received = mail_received;
   
         request_info.argv = argv_extra;          request_info.argv = argv_extra;
   
 #ifdef PA_DEBUG_CGI_ENTRY_EXIT  #ifdef PA_DEBUG_CGI_ENTRY_EXIT
         log("request_info: method=%s, uri=%s, q=%s, dr=%s, pt=%s", request_info.method, request_info.uri, request_info.query_string, request_info.document_root, request_info.path_translated);          pa_log("request_info: method=%s, uri=%s, q=%s, dr=%s, pt=%s", request_info.method, request_info.uri, request_info.query_string, request_info.document_root, request_info.path_translated);
 #endif  #endif
   
         // prepare to process request          // prepare to process request
         Request r(*sapiInfo, request_info, cgi ? String::Language(String::L_HTML|String::L_OPTIMIZE_BIT) : String::L_AS_IS);          Request r(*sapi_info, request_info, cgi ? String::Language(String::L_HTML|String::L_OPTIMIZE_BIT) : String::L_AS_IS);
         {          {
                 // initing ::request ptr for signal handlers                  // initing ::request ptr for signal handlers
                 RequestController rc(&r);                  RequestController rc(&r);
Line 558  static void call_real_parser_handler__su Line 608  static void call_real_parser_handler__su
 static void usage(const char* program) {  static void usage(const char* program) {
         printf(          printf(
                 "Parser/%s\n"                  "Parser/%s\n"
                 "Copyright (c) 2001-2020 Art. Lebedev Studio (http://www.artlebedev.com)\n"                  "Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com)\n"
                 "Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)\n"                  "Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>\n"
                 "\n"                  "\n"
                 "Usage: %s [options] [file]\n"                  "Usage: %s [options] [file]\n"
                 "Options are:\n"                  "Options are:\n"
Line 577  static void usage(const char* program) { Line 627  static void usage(const char* program) {
   
 int main(int argc, char *argv[]) {  int main(int argc, char *argv[]) {
 #ifdef PA_DEBUG_CGI_ENTRY_EXIT  #ifdef PA_DEBUG_CGI_ENTRY_EXIT
         log("main: entry");          pa_log("main: entry");
 #endif  #endif
   
         parser3_filespec = argv[0];          parser3_filespec = argc && argv[0] ? MAYBE_BACK_SLASHES_TO_SLASHES(argv[0]) : "parser3";
         umask(2);          umask(2);
   
         // were we started as CGI?          // were we started as CGI?
         bool cgi=(getenv("SERVER_SOFTWARE") || getenv("SERVER_NAME") || getenv("GATEWAY_INTERFACE") || getenv("REQUEST_METHOD")) && !getenv("PARSER_VERSION");          bool cgi=(getenv("SERVER_SOFTWARE") || getenv("SERVER_NAME") || getenv("GATEWAY_INTERFACE") || getenv("REQUEST_METHOD")) && !getenv("PARSER_VERSION");
         sapiInfo = cgi ? new SAPI_Info_CGI() : new SAPI_Info();          if(!cgi)
                   sapi_info = &sapi_console;
   
 #ifdef SIGUSR1  
         signal(SIGUSR1, SIGUSR1_handler);  
 #endif  
 #ifdef SIGPIPE  #ifdef SIGPIPE
         signal(SIGPIPE, SIGPIPE_handler);          signal(SIGPIPE, SIGPIPE_handler);
 #endif  #endif
   
         char *raw_filespec_to_process = NULL;          char* raw_filespec_to_process = NULL;
         if(cgi) {          if(cgi) {
                 raw_filespec_to_process=getenv("PATH_TRANSLATED");                  raw_filespec_to_process=getenv("PATH_TRANSLATED");
                 argv_extra=argv + 1;                  argv_extra=argv + 1;
         } else {          } else {
                 int optind=1;                  int optind=1;
                 while(optind < argc){                  while(optind < argc){
                         char *carg = argv[optind];                          char* carg = argv[optind];
                         if(carg[0] != '-')                          if(carg[0] != '-')
                                 break;                                  break;
   
Line 609  int main(int argc, char *argv[]) { Line 657  int main(int argc, char *argv[]) {
                                 char c = carg[k];                                  char c = carg[k];
                                 switch (c) {                                  switch (c) {
                                         case 'h':                                          case 'h':
                                                 usage(argv[0]);                                                  usage(parser3_filespec);
                                                 break;                                                  break;
                                         case 'f':                                          case 'f':
                                                 if(optind < argc - 1){                                                  if(optind < argc - 1){
Line 617  int main(int argc, char *argv[]) { Line 665  int main(int argc, char *argv[]) {
                                                         config_filespec=argv[optind];                                                          config_filespec=argv[optind];
                                                 }                                                  }
                                                 break;                                                  break;
                                           case 'l':
                                                   if(optind < argc - 1){
                                                           optind++;
                                                           log_filespec=argv[optind];
                                                   }
                                                   break;
                                         case 'p':                                          case 'p':
                                                 if(optind < argc - 1){                                                  if(optind < argc - 1){
                                                         optind++;                                                          optind++;
Line 629  int main(int argc, char *argv[]) { Line 683  int main(int argc, char *argv[]) {
                                                 break;                                                  break;
 #endif  #endif
                                         default:                                          default:
                                                 fprintf(stderr, "%s: invalid option '%c'\n", argv[0], c);                                                  fprintf(stderr, "%s: invalid option '%c'\n", parser3_filespec, c);
                                                 usage(argv[0]);                                                  usage(parser3_filespec);
                                                 break;                                                  break;
                                 }                                  }
                         }                          }
Line 639  int main(int argc, char *argv[]) { Line 693  int main(int argc, char *argv[]) {
                                   
                 if (optind > argc - 1) {                  if (optind > argc - 1) {
                         if(!httpd_host_port) {                          if(!httpd_host_port) {
                                 fprintf(stderr, "%s: file not specified\n", argv[0]);                                  fprintf(stderr, "%s: file not specified\n", parser3_filespec);
                                 usage(argv[0]);                                  usage(parser3_filespec);
                         }                          }
                 } else {                  } else {
                         raw_filespec_to_process=argv[optind];                          raw_filespec_to_process=argv[optind];
                 }                  }
   
                 if (httpd_host_port && mail_received) {                  if (httpd_host_port && mail_received) {
                                 fprintf(stderr, "%s: -p and -m options should not be used together\n", argv[0]);                                  fprintf(stderr, "%s: -p and -m options should not be used together\n", parser3_filespec);
                                 usage(argv[0]);                                  usage(parser3_filespec);
                 }                  }
   
                 argv_extra=argv + optind;                  argv_extra=argv + optind;
Line 675  int main(int argc, char *argv[]) { Line 729  int main(int argc, char *argv[]) {
 #endif  #endif
   
         try { // global try          try { // global try
                 char filespec_to_process_buf[MAX_STRING];  
                 if(raw_filespec_to_process && *raw_filespec_to_process){                  if(raw_filespec_to_process && *raw_filespec_to_process){
                         full_disk_path(raw_filespec_to_process, filespec_to_process_buf, sizeof(filespec_to_process_buf));                          filespec_to_process=full_disk_path(raw_filespec_to_process);
                         filespec_to_process=filespec_to_process_buf;  
                 }                  }
   
                 REAL_PARSER_HANDLER(cgi);                  REAL_PARSER_HANDLER(cgi);
         } catch(const Exception& e) { // exception in unhandled exception          } catch(const Exception& e) { // exception in config_handler
                 SAPI::die("%s", e.comment());                  SAPI::log(*sapi_info, "%s", e.comment());
                   SAPI::send_error(*sapi_info, e.comment(), strcmp(e.type(), "file.missing") ? "500" : "404");
         }          }
   
 #ifdef PA_DEBUG_CGI_ENTRY_EXIT  #ifdef PA_DEBUG_CGI_ENTRY_EXIT
         log("main: successful return");          pa_log("main: successful return");
 #endif  #endif
         return sapiInfo->http_response_code < 100 ? sapiInfo->http_response_code : 0;          return sapi_info->http_response_code < 100 ? sapi_info->http_response_code : 0;
 }  }

Removed from v.1.330  
changed lines
  Added in v.1.363


E-mail: