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

version 1.143.2.21.2.25, 2003/04/08 16:11:54 version 1.177, 2004/03/05 10:32:17
Line 1 Line 1
 /** @file  /** @file
         Parser: commonly functions.          Parser: commonly functions.
   
         Copyright(c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)          Copyright(c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
 */  */
   
 static const char* IDENT_COMMON_C="$Date$";   static const char * const IDENT_COMMON_C="$Date$"; 
   
 #include "pa_common.h"  #include "pa_common.h"
 #include "pa_exception.h"  #include "pa_exception.h"
Line 19  static const char* IDENT_COMMON_C="$Date Line 19  static const char* IDENT_COMMON_C="$Date
 #ifdef PA_HTTP  #ifdef PA_HTTP
 #include "pa_vstring.h"  #include "pa_vstring.h"
 #include "pa_vint.h"  #include "pa_vint.h"
   #include "pa_vhash.h"
   #include "pa_vtable.h"
   
 #ifdef CYGWIN  #ifdef CYGWIN
 #define _GNU_H_WINDOWS32_SOCKETS  #define _GNU_H_WINDOWS32_SOCKETS
Line 62  int PASCAL closesocket(SOCKET); Line 64  int PASCAL closesocket(SOCKET);
 #       endif  #       endif
 #endif  #endif
   
 // locking constants  #       ifndef INADDR_NONE
 //#define PA_DEBUG_NO_LOCKING  #               define INADDR_NONE ((ulong) -1)
   #       endif
 #ifdef PA_DEBUG_NO_LOCKING  
   
 #ifdef HAVE_FLOCK  
   
 static int lock_shared_blocking(int fd) { return flock(fd, LOCK_SH); }  
 static int lock_exclusive_blocking(int fd) { return flock(fd, LOCK_EX); }  
 static int lock_exclusive_nonblocking(int fd) { return flock(fd, LOCK_EX || LOCK_NB); }  
 static int unlock(int fd) { return flock(fd, LOCK_UN); }  
   
 #else  
 #ifdef HAVE__LOCKING  
   
 #define FLOCK(operation) lseek(fd, 0, SEEK_SET);  return _locking(fd, operation, 1)  
 static int lock_shared_blocking(int fd) { FLOCK(_LK_LOCK); }  
 static int lock_exclusive_blocking(int fd) { FLOCK(_LK_LOCK); }  
 static int lock_exclusive_nonblocking(int fd) { FLOCK(_LK_NBLCK); }  
 static int unlock(int fd) { FLOCK(_LK_UNLCK); }  
   
 #else  
 #ifdef HAVE_FCNTL  
   
 #define FLOCK(cmd, arg) struct flock ls={arg, SEEK_SET};  return fcntl(fd, cmd, &ls)  
 static int lock_shared_blocking(int fd) { FLOCK(F_SETLKW, F_RDLCK); }  
 static int lock_exclusive_blocking(int fd) { FLOCK(F_SETLKW, F_WRLCK); }  
 static int lock_exclusive_nonblocking(int fd) { FLOCK(F_SETLK, F_RDLCK); }  
 static int unlock(int fd) { FLOCK(F_SETLK, F_UNLCK); }  
   
 #else  
 #ifdef HAVE_LOCKF  
   
 #define FLOCK(fd, operation) lseek(fd, 0, SEEK_SET);  return lockf(fd, operation, 1)  
 static int lock_shared_blocking(int fd) { FLOCK(F_LOCK); } // on intel solaris man doesn't have doc on shared blocking  
 static int lock_exclusive_blocking(int fd) { FLOCK(F_LOCK); }  
 static int lock_exclusive_nonblocking(int fd) { FLOCK(F_TLOCK); }  
 static int unlock(int fd) { FLOCK(F_TLOCK); }  
   
 #else  
   
 #error unable to find file locking func  
   
 #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
   
Line 133  const String file_status_name(FILE_STATU Line 83  const String file_status_name(FILE_STATU
 #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"  #define HTTP_CHARSET_NAME "charset"
   #define HTTP_TABLES_NAME "tables"
   
 // statics  // statics
   
 static const String http_method_name(HTTP_METHOD_NAME);  
 static const String http_timeout_name(HTTP_TIMEOUT_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_charset_name(HTTP_CHARSET_NAME);  
   
 // defines  // defines
   
 #define DEFAULT_USER_AGENT "parser3"  #define DEFAULT_USER_AGENT "parser3"
Line 168  void fix_line_breaks(char *str, size_t& Line 113  void fix_line_breaks(char *str, size_t&
                 } else // \r, not \n = Macintosh                  } else // \r, not \n = Macintosh
                         bol=eol+1;                          bol=eol+1;
         }          }
         // last piece without \r, including terminating 0          // last piece without \r
         if(dest!=bol)          if(dest!=bol)
                 memcpy(dest, bol, eob-bol);                   memcpy(dest, bol, eob-bol); 
           str[length]=0; // terminating
 }  }
   
 char* file_read_text(Request_charsets& charsets,   char* file_read_text(Request_charsets& charsets, 
Line 185  char* file_read_text(Request_charsets& c Line 131  char* file_read_text(Request_charsets& c
 #ifdef PA_HTTP  #ifdef PA_HTTP
 //http request stuff  //http request stuff
   
   #undef CRLF
   #define CRLF "\r\n"
   
 static bool set_addr(struct sockaddr_in *addr, const char* host, const short port){  static bool set_addr(struct sockaddr_in *addr, const char* host, const short port){
     memset(addr, 0, sizeof(*addr));       memset(addr, 0, sizeof(*addr)); 
     addr->sin_family=AF_INET;      addr->sin_family=AF_INET;
     addr->sin_port=htons(port);       addr->sin_port=htons(port); 
     if(host) {      if(host) {
                 if(struct hostent *hostIP=gethostbyname(host))                   ulong packed_ip=inet_addr(host);
                   struct hostent *hostIP = packed_ip==INADDR_NONE?
                           gethostbyname(host)
                           : gethostbyaddr((char *)&packed_ip, sizeof(packed_ip), AF_INET);
                   if(hostIP) 
                         memcpy(&addr->sin_addr, hostIP->h_addr, hostIP->h_length);                           memcpy(&addr->sin_addr, hostIP->h_addr, hostIP->h_length); 
                 else                  else
                         return false;                          return false;
Line 199  static bool set_addr(struct sockaddr_in Line 152  static bool set_addr(struct sockaddr_in
     return true;      return true;
 }  }
   
 static int http_read_response(String& response, int sock, bool fail_on_status_ne_200){  static int http_read_response(char*& response, size_t& response_size, int sock, bool fail_on_status_ne_200){
           response=(char*)pa_malloc_atomic(1/*terminator*/); // setting memory block type
           response[(response_size=0)]=0;
         int result=0;          int result=0;
         size_t EOLat=0;          char* EOLat=0;
         while(true) {          while(true) {
                 char *buf=new(PointerFreeGC) char[MAX_STRING];                   char buf[MAX_STRING*10]; 
                 ssize_t size=recv(sock, buf, MAX_STRING, 0);                   ssize_t received_size=recv(sock, buf, sizeof(buf), 0); 
                 if(size<=0)                  if(received_size<=0)
                         break;                          break;
                 response.append_strdup(buf, size, String::L_TAINTED);                   response=(char*)pa_realloc(response, response_size+received_size+1/*terminator*/);
                 if(!result && (EOLat=response.pos("\r\n", 2))!=STRING_NOT_FOUND) { // checking status in first response                  memcpy(response+response_size, buf, received_size);
                         const String& status_line=response.mid(0, (size_t)EOLat);                  response_size+=received_size;
                   response[response_size]=0;
   
                   if(!result && (EOLat=strstr(response, CRLF))) { // checking status in first response
                           const String status_line(pa_strdup(response, EOLat-response));
                         ArrayString astatus;                           ArrayString astatus; 
                         size_t pos_after=0;                          size_t pos_after=0;
                         status_line.split(astatus, pos_after, " ");                           status_line.split(astatus, pos_after, " "); 
Line 227  static int http_read_response(String& re Line 186  static int http_read_response(String& re
         else          else
                 throw Exception("http.response",                  throw Exception("http.response",
                         0,                          0,
                         "bad response from host - no status found (size=%lu)", response.length());                           "bad response from host - no status found (size=%u)", response_size); 
 }  }
   
 /* ********************** request *************************** */  /* ********************** request *************************** */
Line 243  static void timeout_handler(int sig){ Line 202  static void timeout_handler(int sig){
 }  }
 #endif  #endif
   
 static int http_request(String& response,  static int http_request(char*& response, size_t& response_size,
                         const char* host, int port,                           const char* host, int port, 
                         const char* request,                           const char* request, 
                         int timeout,                          int 
   #ifdef PA_USE_ALARM
                           timeout
   #endif
                           ,
                         bool fail_on_status_ne_200) {                          bool fail_on_status_ne_200) {
         if(!host)          if(!host)
                 throw Exception("http.host",                   throw Exception("http.host", 
Line 294  static int http_request(String& response Line 257  static int http_request(String& response
                                         0,                                           0, 
                                         "error sending request: %s (%d)", strerror(errno), errno);                                           "error sending request: %s (%d)", strerror(errno), errno); 
   
                         result=http_read_response(response, sock, fail_on_status_ne_200);                           result=http_read_response(response, response_size, sock, fail_on_status_ne_200); 
                         closesocket(sock);                           closesocket(sock); 
 #ifdef PA_USE_ALARM  #ifdef PA_USE_ALARM
                         alarm(0)                          alarm(0)
Line 325  static void http_pass_header(HashStringV Line 288  static void http_pass_header(HashStringV
                              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";           << CRLF; 
                   
     if(String(key, String::L_TAINTED).change_case(info->charsets->source(), 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;
Line 333  static void http_pass_header(HashStringV Line 296  static void http_pass_header(HashStringV
   
   
 static Charset* detect_charset(Charset& source_charset, const String& content_type_value) {  static Charset* detect_charset(Charset& source_charset, const String& content_type_value) {
         const StringBody CONTENT_TYPE_VALUE=          const String::Body CONTENT_TYPE_VALUE=
                 content_type_value.change_case(source_charset, String::CC_UPPER);                  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"          // content-type: xxx/xxx; source_charset="WE-NEED-THIS"
Line 345  static Charset* detect_charset(Charset& Line 308  static Charset* detect_charset(Charset&
                 bool quoted=open_quote_pos==charset_begin;                  bool quoted=open_quote_pos==charset_begin;
                 if(quoted)                  if(quoted)
                         charset_begin++; // skip opening '"'                          charset_begin++; // skip opening '"'
                 size_t charset_end=0;                  size_t charset_end=CONTENT_TYPE_VALUE.length();
                 if(quoted) {                  if(quoted) {
                         size_t close_quote_pos=CONTENT_TYPE_VALUE.pos('"', charset_begin);                          size_t close_quote_pos=CONTENT_TYPE_VALUE.pos('"', charset_begin);
                         if(close_quote_pos!=STRING_NOT_FOUND)                          if(close_quote_pos!=STRING_NOT_FOUND)
Line 355  static Charset* detect_charset(Charset& Line 318  static Charset* detect_charset(Charset&
                         if(delim_pos!=STRING_NOT_FOUND)                          if(delim_pos!=STRING_NOT_FOUND)
                                 charset_end=delim_pos;                                  charset_end=delim_pos;
                 }                  }
                 const StringBody CHARSET_NAME_BODY=                  const String::Body CHARSET_NAME_BODY=
                         CONTENT_TYPE_VALUE.mid(charset_begin, charset_end);                          CONTENT_TYPE_VALUE.mid(charset_begin, charset_end);
   
                 return &charsets.get(CHARSET_NAME_BODY);                  return &charsets.get(CHARSET_NAME_BODY);
Line 371  struct File_read_http_result { Line 334  struct File_read_http_result {
         HashStringValue* headers;          HashStringValue* headers;
 };   }; 
 #endif  #endif
   /// @todo build .cookies field. use ^file.tables.SET-COOKIES.menu{ for now
 static File_read_http_result file_read_http(Request_charsets& charsets,   static File_read_http_result file_read_http(Request_charsets& charsets, 
                                             const String& file_spec,                                               const String& file_spec, 
                                             HashStringValue *options=0) {                                              bool as_text,
                                                   HashStringValue *options=0) {
         File_read_http_result result;          File_read_http_result result;
         char host[MAX_STRING];           char host[MAX_STRING]; 
         const char* uri;           const char* uri; 
Line 384  static File_read_http_result file_read_h Line 349  static File_read_http_result file_read_h
         Value* vheaders=0;          Value* vheaders=0;
         Charset *asked_remote_charset=0;          Charset *asked_remote_charset=0;
   
         String& connect_string=*new String;  
         // not in ^sql{... L_SQL ...} spirit, but closer to ^file::load one  
         connect_string.append(file_spec, String::L_URI); // tainted pieces -> URI pieces  
   
         const char* connect_string_cstr=connect_string.cstr(String::L_UNSPECIFIED);   
         const char* current=connect_string_cstr;  
         if(strncmp(current, "http://", 7)!=0)  
                 throw Exception(0,   
                         &connect_string,   
                         "does not start with http://"); //never  
         current+=7;  
   
         strncpy(host, current, sizeof(host)-1);  host[sizeof(host)-1]=0;  
         char* host_uri=lsplit(host, '/');   
         uri=host_uri?current+(host_uri-1-host):"/";   
         char* port_cstr=lsplit(host, ':');   
         char* error_pos=0;  
         port=port_cstr?strtol(port_cstr, &error_pos, 0):80;  
   
         if(options) {          if(options) {
                 int valid_options=0;                  int valid_options=0;
                 if(Value* vmethod=options->get(http_method_name)) {                  if(Value* vmethod=options->get(HTTP_METHOD_NAME)) {
                         valid_options++;                          valid_options++;
                         method=vmethod->as_string().cstr();                          method=vmethod->as_string().cstr();
                 }                  }
                 if(Value* vtimeout=options->get(http_timeout_name)) {                  if(Value* vtimeout=options->get(HTTP_TIMEOUT_NAME)) {
                         valid_options++;                          valid_options++;
                         timeout=vtimeout->as_int();                           timeout=vtimeout->as_int(); 
                 }                  }
                 if(vheaders=options->get(http_headers_name)) {                  if((vheaders=options->get(HTTP_HEADERS_NAME))) {
                         valid_options++;                          valid_options++;
                 }                  }
                 if(Value* vany_status=options->get(http_any_status_name)) {                  if(Value* vany_status=options->get(HTTP_ANY_STATUS_NAME)) {
                         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)) {                  if(Value* vcharset_name=options->get(HTTP_CHARSET_NAME)) {
                         valid_options++;                          valid_options++;
                         asked_remote_charset=&::charsets.get(vcharset_name->as_string().                          asked_remote_charset=&::charsets.get(vcharset_name->as_string().
                                 change_case(charsets.source(), String::CC_UPPER));                                  change_case(charsets.source(), String::CC_UPPER));
Line 434  static File_read_http_result file_read_h Line 380  static File_read_http_result file_read_h
         if(!asked_remote_charset) // defaulting to $request:charset          if(!asked_remote_charset) // defaulting to $request:charset
                 asked_remote_charset=&charsets.source();                  asked_remote_charset=&charsets.source();
   
         //making request          //preparing request
         String request;          String& connect_string=*new String;
         if(method)          // not in ^sql{... L_SQL ...} spirit, but closer to ^file::load one
                 request<<method;          connect_string.append(file_spec, String::L_URI); // tainted pieces -> URI pieces
         else  
                 request<<"GET";  
         request<< " "<< uri <<" HTTP/1.0\n"  
                 "host: "<< host<<"\n";   
         bool user_agent_specified=false;  
         if(vheaders && !vheaders->is_string()) { // allow empty  
                 if(HashStringValue *headers=vheaders->get_hash()) {  
                         Http_pass_header_info info={&charsets, &request};  
                         headers->for_each(http_pass_header, &info);   
                         user_agent_specified=info.user_agent_specified;  
                 } else  
                         throw Exception("parser.runtime",   
                                 &connect_string,  
                                 "headers param must be hash");   
         };  
         if(!user_agent_specified) // defaulting  
                 request << "user-agent: " DEFAULT_USER_AGENT "\n";  
         request<<"\n";   
           
         //sending request  
         String response;  
         const char* request_cstr;          const char* request_cstr;
         {          {
                 // influence URLencoding of tainted pieces to String::L_URI lang                  // influence URLencoding of tainted pieces to String::L_URI lang
                 Temp_client_charset temp(charsets, *asked_remote_charset);                  Temp_client_charset temp(charsets, *asked_remote_charset);
   
                 request_cstr=request.cstr(String::L_UNSPECIFIED);                  const char* connect_string_cstr=connect_string.cstr(String::L_UNSPECIFIED); 
   
                 // recode pieces not in String::L_URI lang                   const char* current=connect_string_cstr;
                 // [those violating HTTP standard, but widly used]                  if(strncmp(current, "http://", 7)!=0)
                 request_cstr=Charset::transcode(                          throw Exception(0, 
                         String::C(request_cstr, strlen(request_cstr)),                                  &connect_string, 
                         charsets.source(),                                  "does not start with http://"); //never
                         *asked_remote_charset);                  current+=7;
   
                   strncpy(host, current, sizeof(host)-1);  host[sizeof(host)-1]=0;
                   char* host_uri=lsplit(host, '/'); 
                   uri=host_uri?current+(host_uri-1-host):"/"; 
                   char* port_cstr=lsplit(host, ':'); 
                   char* error_pos=0;
                   port=port_cstr?strtol(port_cstr, &error_pos, 0):80;
   
                   //making request
                   String request;
                   if(method)
                           request<<method;
                   else
                           request<<"GET";
                   request<< " "<< uri <<" HTTP/1.0" CRLF
                           "host: "<< host << CRLF; 
                   bool user_agent_specified=false;
                   if(vheaders && !vheaders->is_string()) { // allow empty
                           if(HashStringValue *headers=vheaders->get_hash()) {
                                   Http_pass_header_info info={&charsets, &request, false};
                                   headers->for_each(http_pass_header, &info); 
                                   user_agent_specified=info.user_agent_specified;
                           } else
                                   throw Exception("parser.runtime", 
                                           &connect_string,
                                           "headers param must be hash"); 
                   };
                   if(!user_agent_specified) // defaulting
                           request << "user-agent: " DEFAULT_USER_AGENT CRLF;
                   request << CRLF; 
   
                   request_cstr=request.cstr(String::L_UNSPECIFIED);
         }          }
         int status_code=http_request(response,          // recode those pieces which are 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);
           
           //sending request
           char* response;
           size_t response_size;
           int status_code=http_request(response, response_size,
                 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);           char* raw_body; size_t raw_body_size;
         if(pos==STRING_NOT_FOUND || pos<1) {          char* headers_end_at=strstr(response, CRLF CRLF /*change '4' below along!*/); 
           if(headers_end_at) {
                   raw_body=headers_end_at+4;
                   raw_body_size=response_size-(raw_body-response);
           } else
                 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);           *headers_end_at=0;
         const String& raw_body=response.mid(pos+4, response.length());           const String header_block(response, headers_end_at-response, true);
                   
         ArrayString aheaders;          ArrayString aheaders;
         result.headers=new HashStringValue;          result.headers=new HashStringValue;
           VHash* vtables=new VHash;
           result.headers->put(HTTP_TABLES_NAME, vtables);
           HashStringValue& tables=vtables->hash();
   
         size_t pos_after=0;          size_t pos_after=0;
         header_block.split(aheaders, pos_after, "\r\n");           header_block.split(aheaders, pos_after, CRLF); 
                   
         //processing headers          //processing headers
         size_t aheaders_count=aheaders.count();          size_t aheaders_count=aheaders.count();
         Charset* real_remote_charset=0; // undetected, yet          Charset* real_remote_charset=0; // undetected, yet
         for(size_t i=1; i<aheaders_count; i++) {          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);                   size_t 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=                  const String::Body HEADER_NAME=
                         line.mid(0, pos).change_case(charsets.source(), String::CC_UPPER);                          line.mid(0, pos).change_case(charsets.source(), String::CC_UPPER);
                 const String& header_value=line.mid(pos+2, line.length());                  const String& header_value=line.mid(pos+2, line.length());
                 if(HEADER_NAME=="CONTENT_TYPE")                  if(HEADER_NAME=="CONTENT-TYPE")
                         real_remote_charset=detect_charset(charsets.source(), header_value);                          real_remote_charset=detect_charset(charsets.source(), header_value);
   
                   // tables
                   {
                           Value *valready=(Value *)tables.get(HEADER_NAME);
                           bool existed=valready!=0;
                           Table *table;
                           if(existed) {
                                   // second+ appearence
                                   table=valready->get_table();
                           } else {
                                   // first appearence
                                   Table::columns_type columns =new ArrayString(1);
                                   *columns+=new String("value");
                                   table=new Table(columns);
                           }
                           // this string becomes next row
                           ArrayString& row=*new ArrayString(1);
                           row+=&header_value;
                           *table+=&row;
                           // not existed before? add it
                           if(!existed)
                                   tables.put(HEADER_NAME, new VTable(table));
                   }
   
                 result.headers->put(HEADER_NAME, new VString(header_value));                  result.headers->put(HEADER_NAME, new VString(header_value));
         }          }
         // defaulting to used-asked charset [it's never empty!]          // defaulting to used-asked charset [it's never empty!]
Line 514  static File_read_http_result file_read_h Line 514  static File_read_http_result file_read_h
                 real_remote_charset=asked_remote_charset;                  real_remote_charset=asked_remote_charset;
   
         // output response          // output response
         String::C real_body=Charset::transcode(          String::C real_body=String::C(raw_body, raw_body_size);
                 String::C(raw_body.cstrm()/*must be modifiable*/, raw_body.length()),          if(as_text && raw_body_size) // must be checked because transcode returns CONST string in case length==0, which contradicts hacking few lines below
                 *real_remote_charset,                  real_body=Charset::transcode(real_body, *real_remote_charset, charsets.source());
                 charsets.source());  
   
         result.str=const_cast<char *>(real_body.str); // hacking a little          result.str=const_cast<char *>(real_body.str); // hacking a little
         result.length=real_body.length;          result.length=real_body.length;
Line 533  struct File_read_action_info { Line 532  struct File_read_action_info {
 };   }; 
 #endif  #endif
 static void file_read_action(  static void file_read_action(
                                                          struct stat& finfo,                                struct stat& finfo, 
                                                          int f,                                int f, 
                                                          const String& file_spec, const char* fname, bool as_text,                                const String& file_spec, const char* /*fname*/, bool as_text, 
                                                          void *context) {                               void *context) {
         File_read_action_info& info=*static_cast<File_read_action_info *>(context);           File_read_action_info& info=*static_cast<File_read_action_info *>(context); 
         if(size_t to_read_size=(size_t)finfo.st_size) {           if(size_t to_read_size=(size_t)finfo.st_size) { 
                 *info.data=new(PointerFreeGC) char[to_read_size+(as_text?1:0)];                   *info.data=new(PointerFreeGC) char[to_read_size+(as_text?1:0)]; 
Line 545  static void file_read_action( Line 544  static void file_read_action(
                 if(ssize_t(*info.data_size)<0 || *info.data_size>to_read_size)                  if(ssize_t(*info.data_size)<0 || *info.data_size>to_read_size)
                         throw Exception(0,                           throw Exception(0, 
                                 &file_spec,                                   &file_spec, 
                                 "read failed: actually read %lu bytes count not in [0..%lu] valid range",                                   "read failed: actually read %u bytes count not in [0..%u] valid range", 
                                         *info.data_size, to_read_size);                                           *info.data_size, to_read_size); 
         } else { // empty file          } else { // empty file
                 if(as_text) {                  if(as_text) {
Line 560  static void file_read_action( Line 559  static void file_read_action(
 File_read_result file_read(Request_charsets& charsets, 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, 0, 0, 0};
 #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(charsets, file_spec, params);                  File_read_http_result http=file_read_http(charsets, file_spec, as_text, params);
                 result.success=true;                  result.success=true;
                 result.str=http.str;                  result.str=http.str;
                 result.length=http.length;                  result.length=http.length;
                 result.headers=http.headers;                   result.headers=http.headers; 
         } else {          } else {
 #endif  #endif
                   if(params && params->count())
                           throw Exception("parser.runtime",
                                   0,
                                   "invalid option passed");
   
                 File_read_action_info info={&result.str, &result.length};                   File_read_action_info info={&result.str, &result.length}; 
                 result.success=file_read_action_under_lock(file_spec,                   result.success=file_read_action_under_lock(file_spec, 
                         "read", file_read_action, &info,                           "read", file_read_action, &info, 
Line 583  File_read_result file_read(Request_chars Line 587  File_read_result file_read(Request_chars
                 // 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(strncmp(in, "\xEB\xBB\xBF", 3)==0) {                          if(strncmp(in, "\xEF\xBB\xBF", 3)==0) {
                                 result.str=in+3; result.length-=3;// skip prefix                                  result.str=in+3; result.length-=3;// skip prefix
                         }                          }
                 }                  }
   
                 fix_line_breaks((char *)(result.str), result.length);                   fix_line_breaks((char *)(result.str), result.length); 
                 // note: after fixing  
                 ((char*&)(result.str))[result.length]=0;  
         }          }
   
         return result;          return result;
 }  }
   
 #ifdef PA_SAFE_MODE   #ifdef PA_SAFE_MODE 
 void check_safe_mode(stat finfo, const String& file_spec, const char* fname) {   void check_safe_mode(struct stat finfo, const String& file_spec, const char* fname) { 
         if(finfo.st_uid/*foreign?*/!=geteuid()           if(finfo.st_uid/*foreign?*/!=geteuid() 
                 && finfo.st_gid/*foreign?*/!=getegid())                   && finfo.st_gid/*foreign?*/!=getegid()) 
                 throw Exception("parser.runtime",                    throw Exception("parser.runtime",  
Line 631  bool file_read_action_under_lock(const S Line 633  bool file_read_action_under_lock(const S
         // later: it seems, even this does not help sometimes          // later: it seems, even this does not help sometimes
     if((f=open(fname, O_RDONLY|(as_text?_O_TEXT:_O_BINARY)))>=0) {      if((f=open(fname, O_RDONLY|(as_text?_O_TEXT:_O_BINARY)))>=0) {
                 try {                  try {
                         if(lock_shared_blocking(f)!=0)                          if(pa_lock_shared_blocking(f)!=0)
                                 throw Exception("file.lock",                                   throw Exception("file.lock", 
                                                 &file_spec,                                                   &file_spec, 
                                                 "shared lock failed: %s (%d), actual filename '%s'",                                                   "shared lock failed: %s (%d), actual filename '%s'", 
Line 650  bool file_read_action_under_lock(const S Line 652  bool file_read_action_under_lock(const S
   
                         action(finfo, f, file_spec, fname, as_text, context);                           action(finfo, f, file_spec, fname, as_text, context); 
                 } catch(...) {                  } catch(...) {
                         unlock(f);close(f);                           pa_unlock(f);close(f); 
                         if(fail_on_read_problem)                          if(fail_on_read_problem)
                                 rethrow;                                  rethrow;
                         return false;                                             return false;                   
                 }                   } 
   
                 unlock(f);close(f);                   pa_unlock(f);close(f); 
                 return true;                  return true;
     } else {      } else {
                 if(fail_on_read_problem)                  if(fail_on_read_problem)
Line 693  bool file_write_action_under_lock( Line 695  bool file_write_action_under_lock(
                 O_CREAT|O_RDWR                  O_CREAT|O_RDWR
                 |(as_text?_O_TEXT:_O_BINARY)                  |(as_text?_O_TEXT:_O_BINARY)
                 |(do_append?O_APPEND:PA_O_TRUNC), 0664))>=0) {                  |(do_append?O_APPEND:PA_O_TRUNC), 0664))>=0) {
                 if((do_block?lock_exclusive_blocking(f):lock_exclusive_nonblocking(f))!=0) {                  if((do_block?pa_lock_exclusive_blocking(f):pa_lock_exclusive_nonblocking(f))!=0) {
                         Exception e("file.lock",                           Exception e("file.lock", 
                                 &file_spec,                                   &file_spec, 
                                 "shared lock failed: %s (%d), actual filename '%s'",                                   "shared lock failed: %s (%d), actual filename '%s'", 
Line 711  bool file_write_action_under_lock( Line 713  bool file_write_action_under_lock(
                         if(!do_append)                          if(!do_append)
                                 ftruncate(f, lseek(f, 0, SEEK_CUR)); // one can not use O_TRUNC, read lower                                  ftruncate(f, lseek(f, 0, SEEK_CUR)); // one can not use O_TRUNC, read lower
 #endif  #endif
                         unlock(f);close(f);                           pa_unlock(f);close(f); 
                         rethrow;                          rethrow;
                 }                  }
                                   
Line 719  bool file_write_action_under_lock( Line 721  bool file_write_action_under_lock(
                 if(!do_append)                  if(!do_append)
                         ftruncate(f, lseek(f, 0, SEEK_CUR)); // O_TRUNC truncates even exclusevely write-locked file [thanks to Igor Milyakov <virtan@rotabanner.com> for discovering]                          ftruncate(f, lseek(f, 0, SEEK_CUR)); // O_TRUNC truncates even exclusevely write-locked file [thanks to Igor Milyakov <virtan@rotabanner.com> for discovering]
 #endif  #endif
                 unlock(f);close(f);                   pa_unlock(f);close(f); 
                 return true;                  return true;
         } else          } else
                 throw Exception(errno==EACCES?"file.access":0,                   throw Exception(errno==EACCES?"file.access":0, 
Line 765  static void rmdir(const String& file_spe Line 767  static void rmdir(const String& file_spe
                   
         rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::L_FILE_SPEC));           rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::L_FILE_SPEC)); 
 }  }
 bool file_delete(const String& file_spec, bool fail_on_read_problem) {  bool file_delete(const String& file_spec, bool fail_on_problem) {
         const char* fname=file_spec.cstr(String::L_FILE_SPEC);           const char* fname=file_spec.cstr(String::L_FILE_SPEC); 
         if(unlink(fname)!=0)          if(unlink(fname)!=0)
                 if(fail_on_read_problem)                  if(fail_on_problem)
                         throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0,                           throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, 
                                 &file_spec,                                   &file_spec, 
                                 "unlink failed: %s (%d), actual filename '%s'",                                   "unlink failed: %s (%d), actual filename '%s'", 
Line 1059  int __vsnprintf(char* b, size_t s, const Line 1061  int __vsnprintf(char* b, size_t s, const
         // note: on win32& maybe somewhere else          // note: on win32& maybe somewhere else
         // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing          // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing
         --s;          --s;
   
           // clients do not check for negative 's', feature: ignore such prints
           if((ssize_t)s<0)
                   return 0;
   
 #if _MSC_VER  #if _MSC_VER
         /*          /*
         win32:           win32: 
Line 1084  int __vsnprintf(char* b, size_t s, const Line 1091  int __vsnprintf(char* b, size_t s, const
   
         if(r<0)          if(r<0)
                 r=0;                  r=0;
         else if(r>s)          else if((size_t)r>s)
                 r=s;                  r=s;
 #endif  #endif
         b[r]=0;          b[r]=0;
Line 1098  int __snprintf(char* b, size_t s, const Line 1105  int __snprintf(char* b, size_t s, const
     va_end(l);       va_end(l); 
         return r;          return r;
 }  }
   
 int pa_sleep(unsigned long secs, unsigned long usecs) {  
         for (;  usecs >= 1000000; ++secs, usecs -= 1000000);   
   
 #ifdef WIN32  
         Sleep(secs * 1000 + usecs / 1000);   
         return 0;  
 #else  
         struct timeval t;  
         t.tv_sec = secs;  
         t.tv_usec = usecs;  
         return (select(0, NULL, NULL, NULL, &t) == -1 ? errno : 0);   
 #endif  
 }  
   
   

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


E-mail: