Diff for /parser3/src/main/pa_common.C between versions 1.143.2.21.2.14 and 1.143.2.21.2.25

version 1.143.2.21.2.14, 2003/03/24 17:24:42 version 1.143.2.21.2.25, 2003/04/08 16:11:54
Line 11  static const char* IDENT_COMMON_C="$Date Line 11  static const char* IDENT_COMMON_C="$Date
 #include "pa_exception.h"  #include "pa_exception.h"
 #include "pa_hash.h"  #include "pa_hash.h"
 #include "pa_globals.h"  #include "pa_globals.h"
   #include "pa_request_charsets.h"
   #include "pa_charsets.h"
   
 #define PA_HTTP  #define PA_HTTP
   
Line 61  int PASCAL closesocket(SOCKET); Line 63  int PASCAL closesocket(SOCKET);
 #endif  #endif
   
 // locking constants  // locking constants
   //#define PA_DEBUG_NO_LOCKING
   
   #ifdef PA_DEBUG_NO_LOCKING
   
 #ifdef HAVE_FLOCK  #ifdef HAVE_FLOCK
   
Line 105  static int unlock(int fd) { FLOCK(F_TLOC Line 110  static int unlock(int fd) { FLOCK(F_TLOC
 #endif  #endif
 #endif  #endif
   
   #else
   static int lock_shared_blocking(int fd) { return 0; }
   static int lock_exclusive_blocking(int fd) { return 0; }
   static int lock_exclusive_nonblocking(int fd) { return 0; }
   static int unlock(int fd) { return 0; }
   
   #endif
   
 // defines for globals  // defines for globals
   
 #define FILE_STATUS_NAME  "status"  #define FILE_STATUS_NAME  "status"
Line 119  const String file_status_name(FILE_STATU Line 132  const String file_status_name(FILE_STATU
 #define HTTP_TIMEOUT_NAME    "timeout"  #define HTTP_TIMEOUT_NAME    "timeout"
 #define HTTP_HEADERS_NAME "headers"  #define HTTP_HEADERS_NAME "headers"
 #define HTTP_ANY_STATUS_NAME "any-status"  #define HTTP_ANY_STATUS_NAME "any-status"
   #define HTTP_CHARSET_NAME "charset"
   
 // statics  // statics
   
Line 126  static const String http_method_name(HTT Line 140  static const String http_method_name(HTT
 static const String http_timeout_name(HTTP_TIMEOUT_NAME);  static const String http_timeout_name(HTTP_TIMEOUT_NAME);
 static const String http_headers_name(HTTP_HEADERS_NAME);  static const String http_headers_name(HTTP_HEADERS_NAME);
 static const String http_any_status_name(HTTP_ANY_STATUS_NAME);  static const String http_any_status_name(HTTP_ANY_STATUS_NAME);
   static const String http_charset_name(HTTP_CHARSET_NAME);
   
 // defines  // defines
   
Line 133  static const String http_any_status_name Line 148  static const String http_any_status_name
   
 // functions  // functions
   
 void fix_line_breaks(char* buf, size_t& size) {  void fix_line_breaks(char *str, size_t& length) {
         if(size==0)  
                 return;  
   
         //_asm int 3;          //_asm int 3;
         const char* const eob=buf+size;          const char* const eob=str+length;
         char* dest=buf;          char* dest=str;
         // fix DOS: \r\n -> \n          // fix DOS: \r\n -> \n
         // fix Macintosh: \r -> \n          // fix Macintosh: \r -> \n
         char* bol=buf;          char* bol=str;
         while(char* eol=(char*)memchr(bol, '\r', eob -bol)) {          while(char* eol=(char*)memchr(bol, '\r', eob -bol)) {
                 size_t len=eol-bol;                  size_t len=eol-bol;
                 if(dest!=bol)                  if(dest!=bol)
Line 152  void fix_line_breaks(char* buf, size_t& Line 164  void fix_line_breaks(char* buf, size_t&
   
                 if(&eol[1]<eob && eol[1]=='\n') { // \r, \n = DOS                  if(&eol[1]<eob && eol[1]=='\n') { // \r, \n = DOS
                         bol=eol+2;                          bol=eol+2;
                         size--;                           length--; 
                 } else // \r, not \n = Macintosh                  } else // \r, not \n = Macintosh
                         bol=eol+1;                          bol=eol+1;
         }          }
Line 161  void fix_line_breaks(char* buf, size_t& Line 173  void fix_line_breaks(char* buf, size_t&
                 memcpy(dest, bol, eob-bol);                   memcpy(dest, bol, eob-bol); 
 }  }
   
 char* file_read_text(Charset& charset,   char* file_read_text(Request_charsets& charsets, 
                                          const String& file_spec,                        const String& file_spec, 
                                          bool fail_on_read_problem,                       bool fail_on_read_problem,
                                          HashStringValue* params/*, HashStringValue* * out_fields*/) {                       HashStringValue* params/*, HashStringValue* * out_fields*/) {
         File_read_result file=          File_read_result file=
                 file_read(charset, file_spec, true, params, fail_on_read_problem);                  file_read(charsets, file_spec, true, params, fail_on_read_problem);
         return file.success?file.str:0;          return file.success?file.str:0;
 }  }
   
Line 195  static int http_read_response(String& re Line 207  static int http_read_response(String& re
                 ssize_t size=recv(sock, buf, MAX_STRING, 0);                   ssize_t size=recv(sock, buf, MAX_STRING, 0); 
                 if(size<=0)                  if(size<=0)
                         break;                          break;
                 response.append(buf, size, String::L_TAINTED);                   response.append_strdup(buf, size, String::L_TAINTED); 
                 if(!result && (EOLat=response.pos("\r\n", 2))!=STRING_NOT_FOUND) { // checking status in first response                  if(!result && (EOLat=response.pos("\r\n", 2))!=STRING_NOT_FOUND) { // checking status in first response
                         const String& status_line=response.mid(0, (size_t)EOLat);                          const String& status_line=response.mid(0, (size_t)EOLat);
                         ArrayString astatus;                           ArrayString astatus; 
Line 221  static int http_read_response(String& re Line 233  static int http_read_response(String& re
 /* ********************** request *************************** */  /* ********************** request *************************** */
   
 #if defined(SIGALRM) && defined(HAVE_SIGSETJMP) && defined(HAVE_SIGLONGJMP)  #if defined(SIGALRM) && defined(HAVE_SIGSETJMP) && defined(HAVE_SIGLONGJMP)
 #       define WE_CAN_USE_ALARM  #       define PA_USE_ALARM
 #endif  #endif
   
 #ifdef WE_CAN_USE_ALARM  #ifdef PA_USE_ALARM
 static sigjmp_buf timeout_env;  static sigjmp_buf timeout_env;
 static void timeout_handler(int sig){  static void timeout_handler(int sig){
     siglongjmp(timeout_env, 1);       siglongjmp(timeout_env, 1); 
 }  }
 #endif  #endif
   
 static int http_request(  static int http_request(String& response,
                         String& response,  
                         const char* host, int port,                           const char* host, int port, 
                         const char* request,                           const char* request, 
                         int timeout,                          int timeout,
Line 242  static int http_request( Line 253  static int http_request(
                         0,                           0, 
                         "zero hostname");  //never                          "zero hostname");  //never
   
 #ifdef WE_CAN_USE_ALARM  #ifdef PA_USE_ALARM
     signal(SIGALRM, timeout_handler);           signal(SIGALRM, timeout_handler); 
 #endif  #endif
         int sock=-1;          int sock=-1;
         try {  #ifdef PA_USE_ALARM
                 int result;          if(sigsetjmp(timeout_env, 1)) {
 #ifdef WE_CAN_USE_ALARM                  // stupid gcc [2.95.4] generated bad code
                 if(sigsetjmp(timeout_env, 1))                  // which failed to handle sigsetjmp+throw: crashed inside of pre-throw code.
                         throw Exception("http.timeout",                   // rewritten simplier [though duplicating closesocket code]
                                 0,                   if(sock>=0) 
                                 "timeout occured while retrieving document");                           closesocket(sock); 
                 else {                  throw Exception("http.timeout", 
                         alarm(timeout);                           origin_string, 
                           "timeout occured while retrieving document"); 
                   return 0; // never
           } else {
                   alarm(timeout); 
 #endif  #endif
                   try {
                           int result;
                         struct sockaddr_in dest;                          struct sockaddr_in dest;
                                   
                 if(!set_addr(&dest, host, port))                          if(!set_addr(&dest, host, port))
                                 throw Exception("http.host",                                   throw Exception("http.host", 
                                         0,                                           0, 
                                         "can not resolve hostname \"%s\"", host);                                           "can not resolve hostname \"%s\"", host); 
Line 279  static int http_request( Line 296  static int http_request(
   
                         result=http_read_response(response, sock, fail_on_status_ne_200);                           result=http_read_response(response, sock, fail_on_status_ne_200); 
                         closesocket(sock);                           closesocket(sock); 
 #ifdef WE_CAN_USE_ALARM  #ifdef PA_USE_ALARM
                         alarm(0)                          alarm(0)
                 }  
 #endif  #endif
                 return result;                          return result;
         } catch(...) {                  } catch(...) {
                 if(sock>=0)   #ifdef PA_USE_ALARM
                         closesocket(sock);                           alarm(0)
 #ifdef WE_CAN_USE_ALARM  
                 alarm(0)  
 #endif  #endif
                 rethrow;                          if(sock>=0) 
                                   closesocket(sock); 
                           rethrow;
                   }
   #ifdef PA_USE_ALARM
         }          }
   #endif
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Http_pass_header_info {  struct Http_pass_header_info {
         Charset* charset;          Request_charsets* charsets;
         String* request;          String* request;
         bool user_agent_specified;          bool user_agent_specified;
 };  };
 #endif  #endif
 static void http_pass_header(HashStringValue::key_type key, HashStringValue::value_type value,   static void http_pass_header(HashStringValue::key_type key, 
                                HashStringValue::value_type value, 
                              Http_pass_header_info *info) {                               Http_pass_header_info *info) {
     *info->request <<key<<": "      *info->request <<key<<": "
                 << attributed_meaning_to_string(*value, String::L_HTTP_HEADER, false)          << attributed_meaning_to_string(*value, String::L_HTTP_HEADER, false)
                 << "\n";           << "\n"; 
                   
     if(String(key, String::L_TAINTED).change_case(*info->charset, String::CC_UPPER)=="USER-AGENT")      if(String(key, String::L_TAINTED).change_case(info->charsets->source(), String::CC_UPPER)=="USER-AGENT")
                 info->user_agent_specified=true;          info->user_agent_specified=true;
   }
   
   
   static Charset* detect_charset(Charset& source_charset, const String& content_type_value) {
           const StringBody CONTENT_TYPE_VALUE=
                   content_type_value.change_case(source_charset, String::CC_UPPER);
           // content-type: xxx/xxx; source_charset=WE-NEED-THIS
           // content-type: xxx/xxx; source_charset="WE-NEED-THIS"
           // content-type: xxx/xxx; source_charset="WE-NEED-THIS";
           size_t before_charseteq_pos=CONTENT_TYPE_VALUE.pos("CHARSET=");
           if(before_charseteq_pos!=STRING_NOT_FOUND) {
                   size_t charset_begin=before_charseteq_pos+8/*CHARSET="*/;
                   size_t open_quote_pos=CONTENT_TYPE_VALUE.pos('"', charset_begin);
                   bool quoted=open_quote_pos==charset_begin;
                   if(quoted)
                           charset_begin++; // skip opening '"'
                   size_t charset_end=0;
                   if(quoted) {
                           size_t close_quote_pos=CONTENT_TYPE_VALUE.pos('"', charset_begin);
                           if(close_quote_pos!=STRING_NOT_FOUND)
                                   charset_end=close_quote_pos;
                   } else {
                           size_t delim_pos=CONTENT_TYPE_VALUE.pos(';', charset_begin);
                           if(delim_pos!=STRING_NOT_FOUND)
                                   charset_end=delim_pos;
                   }
                   const StringBody CHARSET_NAME_BODY=
                           CONTENT_TYPE_VALUE.mid(charset_begin, charset_end);
   
                   return &charsets.get(CHARSET_NAME_BODY);
           }
   
           return 0;
 }  }
   
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct File_read_http_result {  struct File_read_http_result {
         char *str; size_t length;          char *str; size_t length;
         HashStringValue* headers;          HashStringValue* headers;
 };   }; 
 #endif  #endif
 static File_read_http_result file_read_http(Charset& charset, const String& file_spec,   static File_read_http_result file_read_http(Request_charsets& charsets, 
                                               const String& file_spec, 
                                             HashStringValue *options=0) {                                              HashStringValue *options=0) {
         File_read_http_result result;          File_read_http_result result;
         char host[MAX_STRING];           char host[MAX_STRING]; 
Line 326  static File_read_http_result file_read_h Line 382  static File_read_http_result file_read_h
         int timeout=2;          int timeout=2;
         bool fail_on_status_ne_200=true;          bool fail_on_status_ne_200=true;
         Value* vheaders=0;          Value* vheaders=0;
           Charset *asked_remote_charset=0;
   
         String& connect_string=*new String;          String& connect_string=*new String;
         // not in ^sql{... L_SQL ...} spirit, but closer to ^file::load one          // not in ^sql{... L_SQL ...} spirit, but closer to ^file::load one
Line 363  static File_read_http_result file_read_h Line 420  static File_read_http_result file_read_h
                         valid_options++;                          valid_options++;
                         fail_on_status_ne_200=!vany_status->as_bool();                           fail_on_status_ne_200=!vany_status->as_bool(); 
                 }                  }
                   if(Value* vcharset_name=options->get(http_charset_name)) {
                           valid_options++;
                           asked_remote_charset=&::charsets.get(vcharset_name->as_string().
                                   change_case(charsets.source(), String::CC_UPPER));
                   }
   
                 if(valid_options!=options->count())                  if(valid_options!=options->count())
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 0,                                  0,
                                 "invalid option passed");                                  "invalid option passed");
         }           }
           if(!asked_remote_charset) // defaulting to $request:charset
                   asked_remote_charset=&charsets.source();
   
         //making request          //making request
         String request;          String request;
Line 376  static File_read_http_result file_read_h Line 440  static File_read_http_result file_read_h
                 request<<method;                  request<<method;
         else          else
                 request<<"GET";                  request<<"GET";
         request<< " "<< uri <<" HTTP/1.0\nHost: "<< host<<"\n";           request<< " "<< uri <<" HTTP/1.0\n"
                   "host: "<< host<<"\n"; 
         bool user_agent_specified=false;          bool user_agent_specified=false;
         if(vheaders && !vheaders->is_string()) { // allow empty          if(vheaders && !vheaders->is_string()) { // allow empty
                 if(HashStringValue *headers=vheaders->get_hash()) {                  if(HashStringValue *headers=vheaders->get_hash()) {
                         Http_pass_header_info info={&charset, &request};                          Http_pass_header_info info={&charsets, &request};
                         headers->for_each(http_pass_header, &info);                           headers->for_each(http_pass_header, &info); 
                         user_agent_specified=info.user_agent_specified;                          user_agent_specified=info.user_agent_specified;
                 } else                  } else
Line 394  static File_read_http_result file_read_h Line 459  static File_read_http_result file_read_h
                   
         //sending request          //sending request
         String response;          String response;
         const char* request_cstr=request.cstr(String::L_UNSPECIFIED);          const char* request_cstr;
           {
                   // influence URLencoding of tainted pieces to String::L_URI lang
                   Temp_client_charset temp(charsets, *asked_remote_charset);
   
                   request_cstr=request.cstr(String::L_UNSPECIFIED);
   
                   // recode pieces not in String::L_URI lang 
                   // [those violating HTTP standard, but widly used]
                   request_cstr=Charset::transcode(
                           String::C(request_cstr, strlen(request_cstr)),
                           charsets.source(),
                           *asked_remote_charset);
           }
         int status_code=http_request(response,          int status_code=http_request(response,
                 host, port, request_cstr,                   host, port, request_cstr, 
                 timeout, fail_on_status_ne_200);                   timeout, fail_on_status_ne_200); 
                   
         //processing results              //processing results    
         size_t pos=response.pos("\r\n\r\n", 4);           size_t pos=response.pos("\r\n\r\n", 4); 
         if(pos==STRING_NOT_FOUND || pos<1){          if(pos==STRING_NOT_FOUND || pos<1) {
                 throw Exception("http.response",                   throw Exception("http.response", 
                         &connect_string,                          &connect_string,
                         "bad response from host - no headers found");                           "bad response from host - no headers found"); 
         }          }
         const String& header_block=response.mid(0, pos);           const String& header_block=response.mid(0, pos); 
         const String& body=response.mid(pos+4, response.length());           const String& raw_body=response.mid(pos+4, response.length()); 
                   
         ArrayString aheaders;          ArrayString aheaders;
         result.headers=new HashStringValue;          result.headers=new HashStringValue;
Line 415  static File_read_http_result file_read_h Line 493  static File_read_http_result file_read_h
         header_block.split(aheaders, pos_after, "\r\n");           header_block.split(aheaders, pos_after, "\r\n"); 
                   
         //processing headers          //processing headers
         for(int i=1; i<aheaders.count(); i++) {          size_t aheaders_count=aheaders.count();
           Charset* real_remote_charset=0; // undetected, yet
           for(size_t i=1; i<aheaders_count; i++) {
                 const String& line=*aheaders.get(i);                  const String& line=*aheaders.get(i);
                 pos=line.pos(": ", 2);                   pos=line.pos(": ", 2); 
                 if(pos==STRING_NOT_FOUND || pos<1)                  if(pos==STRING_NOT_FOUND || pos<1)
                         throw Exception("http.response",                           throw Exception("http.response", 
                                 &connect_string,                                  &connect_string,
                                 "bad response from host - bad header \"%s\"", line.cstr());                                  "bad response from host - bad header \"%s\"", line.cstr());
                                           const StringBody HEADER_NAME=
                 result.headers->put(                          line.mid(0, pos).change_case(charsets.source(), String::CC_UPPER);
                         line.mid(0, pos).change_case(charset, String::CC_UPPER),                   const String& header_value=line.mid(pos+2, line.length());
                         new VString(line.mid(pos+2, line.length())));                  if(HEADER_NAME=="CONTENT_TYPE")
         }                          real_remote_charset=detect_charset(charsets.source(), header_value);
                   result.headers->put(HEADER_NAME, new VString(header_value));
           }
           // defaulting to used-asked charset [it's never empty!]
           if(!real_remote_charset)
                   real_remote_charset=asked_remote_charset;
   
         // output response          // output response
         result.str=body.cstrm();          String::C real_body=Charset::transcode(
         result.length=body.length();                  String::C(raw_body.cstrm()/*must be modifiable*/, raw_body.length()),
                   *real_remote_charset,
                   charsets.source());
   
           result.str=const_cast<char *>(real_body.str); // hacking a little
           result.length=real_body.length;
         result.headers->put(file_status_name, new VInt(status_code));          result.headers->put(file_status_name, new VInt(status_code));
         return result;          return result;
 }  }
Line 467  static void file_read_action( Line 557  static void file_read_action(
                 return;                  return;
         }          }
 }  }
 File_read_result file_read(Charset& charset, const String& file_spec,   File_read_result file_read(Request_charsets& charsets, const String& file_spec, 
                            bool as_text, HashStringValue *params,                             bool as_text, HashStringValue *params,
                            bool fail_on_read_problem) {                             bool fail_on_read_problem) {
         File_read_result result={false};          File_read_result result={false};
 #ifdef PA_HTTP  #ifdef PA_HTTP
         if(file_spec.starts_with("http://")) {          if(file_spec.starts_with("http://")) {
                 // fail on read problem                  // fail on read problem
                 File_read_http_result http=file_read_http(charset, file_spec, params);                  File_read_http_result http=file_read_http(charsets, file_spec, params);
                 result.success=true;                  result.success=true;
                 result.str=http.str;                  result.str=http.str;
                 result.length=http.length;                  result.length=http.length;
Line 493  File_read_result file_read(Charset& char Line 583  File_read_result file_read(Charset& char
                 // UTF-8 signature: EF BB BF                  // UTF-8 signature: EF BB BF
                 if(result.length>=3) {                  if(result.length>=3) {
                         char *in=(char *)result.str;                          char *in=(char *)result.str;
                         if((in[0] == '\xEF') && (in[1] == '\xBB') &&                          if(strncmp(in, "\xEB\xBB\xBF", 3)==0) {
                                 (in[2] == '\xBF')) {  
                                 result.str=in+3; result.length-=3;// skip prefix                                  result.str=in+3; result.length-=3;// skip prefix
                         }                          }
                 }                  }
Line 507  File_read_result file_read(Charset& char Line 596  File_read_result file_read(Charset& char
         return result;          return result;
 }  }
   
   #ifdef PA_SAFE_MODE 
   void check_safe_mode(stat finfo, const String& file_spec, const char* fname) { 
           if(finfo.st_uid/*foreign?*/!=geteuid() 
                   && finfo.st_gid/*foreign?*/!=getegid()) 
                   throw Exception("parser.runtime",  
                           &file_spec,  
                           "parser is in safe mode: " 
                           "reading files of foreign group and user disabled " 
                           "[recompile parser with --disable-safe-mode configure option], " 
                           "actual filename '%s', " 
                           "fuid(%d)!=euid(%d) or fgid(%d)!=egid(%d)",  
                                   fname, 
                                   finfo.st_uid, geteuid(), 
                                   finfo.st_gid, getegid()); 
   } 
   #endif 
   
 bool file_read_action_under_lock(const String& file_spec,   bool file_read_action_under_lock(const String& file_spec, 
                                 const char* action_name, File_read_action action, void *context,                                   const char* action_name, File_read_action action, void *context, 
                                 bool as_text,                                   bool as_text, 
Line 539  bool file_read_action_under_lock(const S Line 645  bool file_read_action_under_lock(const S
                                                 strerror(errno), errno, fname);                                                  strerror(errno), errno, fname);
   
 #ifdef PA_SAFE_MODE  #ifdef PA_SAFE_MODE
                         if(finfo.st_uid/*foreign?*/!=geteuid()                          check_safe_mode(finfo, file_spec, fname);
                                 && finfo.st_gid/*foreign?*/!=getegid())  
                                 throw Exception("parser.runtime",   
                                         &file_spec,   
                                         "parser is in safe mode: reading files of foreign group and user disabled [recompile parser with --disable-safe-mode configure option], actual filename '%s'",   
                                                 fname);  
 #endif  #endif
   
                         action(finfo, f, file_spec, fname, as_text, context);                           action(finfo, f, file_spec, fname, as_text, context); 
Line 749  bool file_stat(const String& file_spec, Line 850  bool file_stat(const String& file_spec,
                            time_t& rctime,                              time_t& rctime, 
                            bool fail_on_read_problem) {                             bool fail_on_read_problem) {
         const char* fname=file_spec.cstr(String::L_FILE_SPEC);           const char* fname=file_spec.cstr(String::L_FILE_SPEC); 
     struct stat finfo;          struct stat finfo;
         if(stat(fname, &finfo)!=0)          if(stat(fname, &finfo)!=0)
                 if(fail_on_read_problem)                  if(fail_on_read_problem)
                         throw Exception("file.missing",                           throw Exception("file.missing", 

Removed from v.1.143.2.21.2.14  
changed lines
  Added in v.1.143.2.21.2.25


E-mail: