Diff for /parser3/src/main/pa_common.C between versions 1.185 and 1.246

version 1.185, 2004/04/06 10:08:15 version 1.246, 2009/07/06 08:47:10
Line 1 Line 1
 /** @file  /** @file
         Parser: commonly functions.          Parser: commonly functions.
   
         Copyright(c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com)          Copyright(c) 2001-2009 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)
 */  
    * BASE64 part
    *  Authors: Michael Zucchi <notzed@ximian.com>
    *           Jeffrey Stedfast <fejj@ximian.com>
    *
    *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
    *
    *  This program is free software; you can redistribute it and/or modify
    *  it under the terms of the GNU General Public License as published by
    *  the Free Software Foundation; either version 2 of the License, or
    *  (at your option) any later version.
    *
    *  This program is distributed in the hope that it will be useful,
    *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    *  GNU General Public License for more details.
    *
    *  You should have received a copy of the GNU General Public License
    *  along with this program; if not, write to the Free Software
    *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
    *
    */
   
 static const char * const IDENT_COMMON_C="$Date$";   static const char * const IDENT_COMMON_C="$Date$"; 
   
Line 11  static const char * const IDENT_COMMON_C Line 32  static const char * const IDENT_COMMON_C
 #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"  #include "pa_charsets.h"
   #include "pa_http.h"
 #define PA_HTTP  #include "pa_request_charsets.h"
   #include "pcre.h"
 #ifdef PA_HTTP  #include "pa_request.h"
 #include "pa_vstring.h"  
 #include "pa_vint.h"  
 #include "pa_vhash.h"  
 #include "pa_vtable.h"  
   
 #ifdef CYGWIN  
 #define _GNU_H_WINDOWS32_SOCKETS  
 // for PASCAL  
 #include <windows.h>  
 // SOCKET  
 typedef u_int   SOCKET;  
 int PASCAL closesocket(SOCKET);  
 #else  
 #       if defined(WIN32)  
 #               include <windows.h>  
 #       else  
 #               define closesocket close  
 #       endif  
 #endif  
   
 #else  
   
 #       if defined(WIN32)  
 #               include <windows.h>  
 #       endif  
   
 #endif  
   
 // some maybe-undefined constants  // some maybe-undefined constants
   
Line 64  int PASCAL closesocket(SOCKET); Line 57  int PASCAL closesocket(SOCKET);
 #       endif  #       endif
 #endif  #endif
   
 #       ifndef INADDR_NONE  
 #               define INADDR_NONE ((ulong) -1)  
 #       endif  
   
 // defines for globals  // defines for globals
   
 #define FILE_STATUS_NAME  "status"  #define FILE_STATUS_NAME  "status"
Line 76  int PASCAL closesocket(SOCKET); Line 65  int PASCAL closesocket(SOCKET);
   
 const String file_status_name(FILE_STATUS_NAME);  const String file_status_name(FILE_STATUS_NAME);
   
 // defines  
   
 #define HTTP_METHOD_NAME  "method"  
 #define HTTP_FORM_NAME  "form"  
 #define HTTP_BODY_NAME  "body"  
 #define HTTP_TIMEOUT_NAME    "timeout"  
 #define HTTP_HEADERS_NAME "headers"  
 #define HTTP_ANY_STATUS_NAME "any-status"  
 #define HTTP_CHARSET_NAME "charset"  
 #define HTTP_TABLES_NAME "tables"  
 #define HTTP_USER "user"  
 #define HTTP_PASSWORD "password"  
   
 // defines  
   
 #define DEFAULT_USER_AGENT "parser3"  
   
 // functions  // functions
   
 void fix_line_breaks(char *str, size_t& length) {  void fix_line_breaks(char *str, size_t& length) {
Line 122  void fix_line_breaks(char *str, size_t& Line 94  void fix_line_breaks(char *str, size_t&
 }  }
   
 char* file_read_text(Request_charsets& charsets,   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,
                           bool transcode_result) {
         File_read_result file=          File_read_result file=
                 file_read(charsets, file_spec, true, params, fail_on_read_problem);                  file_read(charsets, file_spec, true, params, fail_on_read_problem, 0, 0, 0, transcode_result);
         return file.success?file.str:0;          return file.success?file.str:0;
 }  }
   
 //http request stuff  char* file_load_text(Request& r, 
 #ifdef PA_HTTP                          const String& file_spec, 
                           bool fail_on_read_problem,
 #undef CRLF                          HashStringValue* params,
 #define CRLF "\r\n"                          bool transcode_result) {
           File_read_result file=
 static bool set_addr(struct sockaddr_in *addr, const char* host, const short port){                  file_load(r, file_spec, true, params, fail_on_read_problem, 0, 0, 0, transcode_result);
     memset(addr, 0, sizeof(*addr));           return file.success?file.str:0;
     addr->sin_family=AF_INET;  
     addr->sin_port=htons(port);   
     if(host) {  
                 ulong packed_ip=inet_addr(host);  
                 if(packed_ip!=INADDR_NONE)  
                         memcpy(&addr->sin_addr, &packed_ip, sizeof(packed_ip));   
                 else {  
                         struct hostent *hostIP=gethostbyname(host);  
                         if(hostIP)   
                                 memcpy(&addr->sin_addr, hostIP->h_addr, hostIP->h_length);   
                         else  
                                 return false;  
                 }   
     } else   
                 addr->sin_addr.s_addr=INADDR_ANY;  
     return true;  
 }  }
   
 static int http_read_response(char*& response, size_t& response_size, int sock, bool fail_on_status_ne_200){  /// these options were handled but not checked elsewhere, now check them
         response=(char*)pa_malloc_atomic(1/*terminator*/); // setting memory block type  int pa_get_valid_file_options_count(HashStringValue& options) {
         response[(response_size=0)]=0;  
         int result=0;          int result=0;
         char* EOLat=0;          if(options.get(PA_SQL_LIMIT_NAME))
         while(true) {                  result++;
                 char buf[MAX_STRING*10];           if(options.get(PA_SQL_OFFSET_NAME))
                 ssize_t received_size=recv(sock, buf, sizeof(buf), 0);                   result++;
                 if(received_size<=0)          if(options.get(PA_COLUMN_SEPARATOR_NAME))
                         break;                  result++;
                 response=(char*)pa_realloc(response, response_size+received_size+1/*terminator*/);          if(options.get(PA_COLUMN_ENCLOSER_NAME))
                 memcpy(response+response_size, buf, received_size);                  result++;
                 response_size+=received_size;          if(options.get(PA_CHARSET_NAME))
                 response[response_size]=0;                  result++;
   
                 if(!result && (EOLat=strstr(response, CRLF))) { // checking status in first response  
                         const String status_line(pa_strdup(response, EOLat-response));  
                         ArrayString astatus;   
                         size_t pos_after=0;  
                         status_line.split(astatus, pos_after, " ");   
                         const String& status_code=*astatus.get(astatus.count()>1?1:0);  
                         result=status_code.as_int();   
   
                         if(fail_on_status_ne_200 && result!=200)  
                                 throw Exception("http.status",  
                                         &status_code,  
                                         "invalid HTTP response status");  
                 }  
         }  
         if(result)  
                 return result;  
         else  
                 throw Exception("http.response",  
                         0,  
                         "bad response from host - no status found (size=%u)", response_size);   
 }  
   
 /* ********************** request *************************** */  
   
 #if defined(SIGALRM) && defined(HAVE_SIGSETJMP) && defined(HAVE_SIGLONGJMP)  
 #       define PA_USE_ALARM  
 #endif  
   
 #ifdef PA_USE_ALARM  
 static sigjmp_buf timeout_env;  
 static void timeout_handler(int sig){  
     siglongjmp(timeout_env, 1);   
 }  
 #endif  
   
 static int http_request(char*& response, size_t& response_size,  
                         const char* host, int port,   
                         const char* request,   
                         int   
 #ifdef PA_USE_ALARM  
                         timeout  
 #endif  
                         ,  
                         bool fail_on_status_ne_200) {  
         if(!host)  
                 throw Exception("http.host",   
                         0,   
                         "zero hostname");  //never  
   
 #ifdef PA_USE_ALARM  
         signal(SIGALRM, timeout_handler);   
 #endif  
         int sock=-1;  
 #ifdef PA_USE_ALARM  
         if(sigsetjmp(timeout_env, 1)) {  
                 // stupid gcc [2.95.4] generated bad code  
                 // which failed to handle sigsetjmp+throw: crashed inside of pre-throw code.  
                 // rewritten simplier [though duplicating closesocket code]  
                 if(sock>=0)   
                         closesocket(sock);   
                 throw Exception("http.timeout",   
                         origin_string,   
                         "timeout occured while retrieving document");   
                 return 0; // never  
         } else {  
                 alarm(timeout);   
 #endif  
                 try {  
                         int result;  
                         struct sockaddr_in dest;  
                   
                         if(!set_addr(&dest, host, port))  
                                 throw Exception("http.host",   
                                         0,   
                                         "can not resolve hostname \"%s\"", host);   
                           
                         if((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP/*0*/))<0)  
                                 throw Exception("http.connect",   
                                         0,   
                                         "can not make socket: %s (%d)", strerror(errno), errno);   
                         if(connect(sock, (struct sockaddr *)&dest, sizeof(dest)))  
                                 throw Exception("http.connect",   
                                         0,   
                                         "can not connect to host \"%s\": %s (%d)", host, strerror(errno), errno);   
                         size_t request_size=strlen(request);  
                         if(send(sock, request, request_size, 0)!=(ssize_t)request_size)  
                                 throw Exception("http.connect",   
                                         0,   
                                         "error sending request: %s (%d)", strerror(errno), errno);   
   
                         result=http_read_response(response, response_size, sock, fail_on_status_ne_200);   
                         closesocket(sock);   
 #ifdef PA_USE_ALARM  
                         alarm(0)  
 #endif  
                         return result;  
                 } catch(...) {  
 #ifdef PA_USE_ALARM  
                         alarm(0)  
 #endif  
                         if(sock>=0)   
                                 closesocket(sock);   
                         rethrow;  
                 }  
 #ifdef PA_USE_ALARM  
         }  
 #endif  
 }  
   
 #ifndef DOXYGEN  
 struct Http_pass_header_info {  
         Request_charsets* charsets;  
         String* request;  
         bool user_agent_specified;  
 };  
 #endif  
 static void http_pass_header(HashStringValue::key_type key,   
                              HashStringValue::value_type value,   
                              Http_pass_header_info *info) {  
     *info->request <<key<<": "  
         << attributed_meaning_to_string(*value, String::L_HTTP_HEADER, false)  
         << CRLF;   
           
     if(String(key, String::L_TAINTED).change_case(info->charsets->source(), String::CC_UPPER)=="USER-AGENT")  
         info->user_agent_specified=true;  
 }  
   
   
 static Charset* detect_charset(Charset& source_charset, const String& content_type_value) {  
         const String::Body 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=CONTENT_TYPE_VALUE.length();  
                 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 String::Body CHARSET_NAME_BODY=  
                         CONTENT_TYPE_VALUE.mid(charset_begin, charset_end);  
   
                 return &charsets.get(CHARSET_NAME_BODY);  
         }  
   
         return 0;  
 }  
   
 static const String* basic_authorization_field(const char* user, const char* pass) {  
         if(!user&& !pass)  
                 return 0;  
   
         String combined;    
         if(user)  
                 combined<<user;  
         combined<<":";  
         if(pass)  
                 combined<<pass;  
           
         String* result=new String("Basic "); *result<<pa_base64(combined.cstr(), combined.length());  
         return result;  
 }  
   
 static void form_string_value2string(  
                                                                   HashStringValue::key_type key,   
                                                                   const String& value,   
                                                                   String& result)   
 {  
         result << String(key, String::L_TAINTED) << "=";  
         result.append(value, String::L_URI, true);  
         result<< "&";  
 }  
 #ifndef DOXYGEN  
 struct Form_table_value2string_info {  
         HashStringValue::key_type key;  
         String& result;  
   
         Form_table_value2string_info(HashStringValue::key_type akey, String& aresult):   
                 key(akey), result(aresult) {}  
 };  
 #endif  
 static void form_table_value2string(Table::element_type row, Form_table_value2string_info* info) {  
         form_string_value2string(info->key, *row->get(0), info->result);  
 }  
 static void form_value2string(  
                                                                   HashStringValue::key_type key,   
                                                                   HashStringValue::value_type value,   
                                                                   String* result)   
 {  
         if(const String* svalue=value->get_string())  
                 form_string_value2string(key, *svalue, *result);  
         else if(Table* tvalue=value->get_table()) {  
                 Form_table_value2string_info info(key, *result);  
                 tvalue->for_each(form_table_value2string, &info);  
         } else  
                 throw Exception(0,  
                         new String(key, String::L_TAINTED),  
                         "is %s, "HTTP_FORM_NAME" option value must either string or table", value->type());  
 }  
 static const char* form2string(HashStringValue& form) {  
         String string;  
         form.for_each(form_value2string, &string);  
         return string.cstr(String::L_UNSPECIFIED);  
 }  
 #ifndef DOXYGEN  
 struct File_read_http_result {  
         char *str; size_t length;  
         HashStringValue* headers;  
 };   
 #endif  
 /// @todo build .cookies field. use ^file.tables.SET-COOKIES.menu{ for now  
 static File_read_http_result file_read_http(Request_charsets& charsets,   
                                             const String& file_spec,   
                                             bool as_text,  
                                                 HashStringValue *options=0) {  
         File_read_http_result result;  
         char host[MAX_STRING];   
         const char* uri;   
         int port;  
         const char* method="GET"; bool method_is_get;  
         HashStringValue* form=0;  
         const char* body_cstr=0;  
         int timeout=2;  
         bool fail_on_status_ne_200=true;  
         Value* vheaders=0;  
         Charset *asked_remote_charset=0;  
         const char* user_cstr=0;  
         const char* password_cstr=0;  
   
         if(options) {  
                 int valid_options=0;  
                 if(Value* vmethod=options->get(HTTP_METHOD_NAME)) {  
                         valid_options++;  
                         method=vmethod->as_string().cstr();  
                 }  
                 if(Value* vform=options->get(HTTP_FORM_NAME)) {  
                         valid_options++;  
                         form=vform->get_hash();   
                 }   
                 if(Value* vbody=options->get(HTTP_BODY_NAME)) {  
                         valid_options++;  
                         body_cstr=vbody->as_string().cstr(String::L_UNSPECIFIED);  
                 }   
                 if(Value* vtimeout=options->get(HTTP_TIMEOUT_NAME)) {  
                         valid_options++;  
                         timeout=vtimeout->as_int();   
                 }   
                 if((vheaders=options->get(HTTP_HEADERS_NAME))) {  
                         valid_options++;  
                 }   
                 if(Value* vany_status=options->get(HTTP_ANY_STATUS_NAME)) {  
                         valid_options++;  
                         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(Value* vuser=options->get(HTTP_USER)) {  
                         valid_options++;  
                         user_cstr=vuser->as_string().cstr();  
                 }   
                 if(Value* vpassword=options->get(HTTP_PASSWORD)) {  
                         valid_options++;  
                         password_cstr=vpassword->as_string().cstr();  
                 }  
   
                 if(valid_options!=options->count())  
                         throw Exception("parser.runtime",  
                                 0,  
                                 "invalid option passed");  
         }  
         if(!asked_remote_charset) // defaulting to $request:charset  
                 asked_remote_charset=&charsets.source();  
   
         method_is_get=strcmp(method, "GET")==0;  
         if(method_is_get && body_cstr)  
                 throw Exception("parser.runtime",  
                         0,  
                         "you can not use $."HTTP_BODY_NAME" option with method GET");  
   
         //preparing request  
         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  
   
         String request_head_and_body;  
         {  
                 // influence URLencoding of tainted pieces to String::L_URI lang  
                 Temp_client_charset temp(charsets, *asked_remote_charset);  
   
                 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(strchr(uri, '?') && form)  
                         throw Exception("parser.runtime",  
                                 0,  
                                 "use either uri with ?params or $."HTTP_FORM_NAME" option");  
   
                 //making request head  
                 String head;  
                 head << method;  
                 head << " " << uri;  
                 if(form)  
                         if(method_is_get)  
                                 head << "?" << form2string(*form);  
                 head <<" HTTP/1.0" CRLF  
                         "host: "<< host << CRLF;   
                 if(form && !method_is_get) {  
                         head << "content-type: application/x-www-form-urlencoded" CRLF;  
                         body_cstr = form2string(*form);  
                 }  
   
                 // http://www.ietf.org/rfc/rfc2617.txt  
                 if(const String* authorization_field_value=basic_authorization_field(user_cstr, password_cstr))  
                         head<<"authorization: "<<*authorization_field_value<<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, &head, 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  
                         head << "user-agent: " DEFAULT_USER_AGENT CRLF;  
   
                 if(body_cstr) {  
                         // recode those pieces which are not in String::L_URI lang   
                         // [those violating HTTP standard, but widly used]  
                         body_cstr=Charset::transcode(  
                                 String::C(body_cstr, strlen(body_cstr)),  
                                 charsets.source(),  
                                 *asked_remote_charset);  
   
                         head << "content-length: " << format(strlen(body_cstr), "%u") << CRLF;  
                 }  
   
                 const char* head_cstr=head.cstr(String::L_UNSPECIFIED);  
   
                 // recode those pieces which are not in String::L_URI lang   
                 // [those violating HTTP standard, but widly used]  
                 head_cstr=Charset::transcode(  
                         String::C(head_cstr, strlen(head_cstr)),  
                         charsets.source(),  
                         *asked_remote_charset);  
   
                 // head + end of header  
                 request_head_and_body << head_cstr << CRLF;  
                 // body  
                 if(body_cstr)  
                         request_head_and_body << body_cstr;  
         }  
           
         //sending request  
         char* response;  
         size_t response_size;  
         int status_code=http_request(response, response_size,  
                 host, port, request_head_and_body.cstr(),   
                 timeout, fail_on_status_ne_200);   
           
         //processing results      
         char* raw_body; size_t raw_body_size;  
         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",   
                         &connect_string,  
                         "bad response from host - no headers found");   
           
         *headers_end_at=0;  
         const String header_block(response, headers_end_at-response, true);  
           
         ArrayString aheaders;  
         result.headers=new HashStringValue;  
         VHash* vtables=new VHash;  
         result.headers->put(HTTP_TABLES_NAME, vtables);  
         HashStringValue& tables=vtables->hash();  
   
         size_t pos_after=0;  
         header_block.split(aheaders, pos_after, CRLF);   
           
         //processing headers  
         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);  
                 size_t pos=line.pos(": ", 2);   
                 if(pos==STRING_NOT_FOUND || pos<1)  
                         throw Exception("http.response",   
                                 &connect_string,  
                                 "bad response from host - bad header \"%s\"", line.cstr());  
                 const String::Body HEADER_NAME=  
                         line.mid(0, pos).change_case(charsets.source(), String::CC_UPPER);  
                 const String& header_value=line.mid(pos+2, line.length());  
                 if(HEADER_NAME=="CONTENT-TYPE")  
                         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));  
         }  
         // defaulting to used-asked charset [it's never empty!]  
         if(!real_remote_charset)  
                 real_remote_charset=asked_remote_charset;  
   
         // output response  
         String::C real_body=String::C(raw_body, raw_body_size);  
         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_body=Charset::transcode(real_body, *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));  
         return result;          return result;
 }  }
   
 #endif  
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct File_read_action_info {  struct File_read_action_info {
         char **data; size_t *data_size;          char **data; size_t *data_size;
           char* buf; size_t offset; size_t count;
 };   }; 
 #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) {           size_t to_read_size=info.count;
                 *info.data=new(PointerFreeGC) char[to_read_size+(as_text?1:0)];           if(!to_read_size)
                   to_read_size=(size_t)finfo.st_size;
           assert( !(info.buf && as_text) );
           if(to_read_size) { 
                   if(info.offset)
                           lseek(f, info.offset, SEEK_SET);
                   *info.data=info.buf
                           ? info.buf
                           : new(PointerFreeGC) char[to_read_size+(as_text?1:0)]; 
                 *info.data_size=(size_t)read(f, *info.data, to_read_size);                   *info.data_size=(size_t)read(f, *info.data, to_read_size); 
   
                 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("file.read", 
                                 &file_spec,                                   &file_spec, 
                                 "read failed: actually read %u bytes count not in [0..%u] 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) {                  // for both, text and binary: for text we need that terminator, for binary we need nonzero pointer to be able to save such files
                         *info.data=new(PointerFreeGC) char[1];                   *info.data=new(PointerFreeGC) char[1]; 
                         *(char*)(*info.data)=0;                  *(char*)(*info.data)=0;
                 } else   
                         *info.data=0;  
                 *info.data_size=0;                  *info.data_size=0;
                 return;                  return;
         }          }
 }  }
   
 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,
                           char* buf, size_t offset, size_t count, bool transcode_text_result) {
         File_read_result result={false, 0, 0, 0};          File_read_result result={false, 0, 0, 0};
 #ifdef PA_HTTP          if(params){
         if(file_spec.starts_with("http://")) {                  int valid_options=pa_get_valid_file_options_count(*params);
                 // fail on read problem                  if(valid_options!=params->count())
                 File_read_http_result http=file_read_http(charsets, file_spec, as_text, params);                          throw Exception(PARSER_RUNTIME,
                 result.success=true;  
                 result.str=http.str;  
                 result.length=http.length;  
                 result.headers=http.headers;   
         } else {  
 #endif  
                 if(params && params->count())  
                         throw Exception("parser.runtime",  
                                 0,                                  0,
                                 "invalid option passed");                                  "invalid option passed");
   
                 File_read_action_info info={&result.str, &result.length};   
                 result.success=file_read_action_under_lock(file_spec,   
                         "read", file_read_action, &info,   
                         as_text, fail_on_read_problem);   
 #ifdef PA_HTTP  
         }          }
 #endif  
   
         if(result.success && as_text) {          File_read_action_info info={&result.str, &result.length, buf, offset, count}; 
                 // UTF-8 signature: EF BB BF  
                 if(result.length>=3) {          result.success=file_read_action_under_lock(file_spec, 
                         char *in=(char *)result.str;                  "read", file_read_action, &info, 
                         if(strncmp(in, "\xEF\xBB\xBF", 3)==0) {                  as_text, fail_on_read_problem); 
                                 result.str=in+3; result.length-=3;// skip prefix  
           if(as_text){
                   if(result.success){
                           if(result.length>=3 && strncmp(result.str, "\xEF\xBB\xBF", 3)==0){
                                   // skip UTF-8 signature (BOM code)
                                   result.str+=3;
                                   result.length-=3;
                           }
                           
                           if(result.length && transcode_text_result && params){ // must be checked because transcode returns CONST string in case length==0, which contradicts hacking few lines below
                                   if(Value* vcharset_name=params->get(PA_CHARSET_NAME)){
                                           Charset asked_charset=::charsets.get(vcharset_name->as_string().
                                                   change_case(charsets.source(), String::CC_UPPER));
   
                                           String::C body=String::C(result.str, result.length);
                                           body=Charset::transcode(body, asked_charset, charsets.source());
   
                                           result.str=const_cast<char*>(body.str); // hacking a little
                                           result.length=body.length;
                                   }
                         }                          }
                 }                  }
                   if(result.length)
                 fix_line_breaks((char *)(result.str), result.length);                           fix_line_breaks(result.str, result.length);
         }          }
   
         return result;          return result;
 }  }
   
   File_read_result file_load(Request& r, const String& file_spec, 
                           bool as_text, HashStringValue *params,
                           bool fail_on_read_problem,
                           char* buf, size_t offset, size_t count, bool transcode_text_result) {
   
           File_read_result result={false, 0, 0, 0};
           if(file_spec.starts_with("http://")) {
                   if(offset || count)
                           throw Exception(PARSER_RUNTIME,
                                   0,
                                   "offset and load options are not supported for HTTP:// file load");
   
                   // fail on read problem
                   File_read_http_result http=pa_internal_file_read_http(r, file_spec, as_text, params, transcode_text_result);
                   result.success=true;
                   result.str=http.str;
                   result.length=http.length;
                   result.headers=http.headers; 
           } else
                   result=
                           file_read(r.charsets, file_spec, as_text, params, fail_on_read_problem, buf, offset, count, transcode_text_result);
   
           return result;
   }
   
 #ifdef PA_SAFE_MODE   #ifdef PA_SAFE_MODE 
 void check_safe_mode(struct 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,  
                         &file_spec,                            &file_spec,  
                         "parser is in safe mode: "                           "parser is in safe mode: " 
                         "reading files of foreign group and user disabled "                           "reading files of foreign group and user disabled " 
Line 736  bool file_read_action_under_lock(const S Line 260  bool file_read_action_under_lock(const S
                                 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, 
                                 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_taint(String::L_FILE_SPEC); 
         int f;          int f;
   
         // first open, next stat:          // first open, next stat:
Line 748  bool file_read_action_under_lock(const S Line 272  bool file_read_action_under_lock(const S
         //   they delay update till open, so we would receive "!^test[" string          //   they delay update till open, so we would receive "!^test[" string
         //   if would do stat, next open.          //   if would do stat, next open.
         // 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(pa_lock_shared_blocking(f)!=0)                          if(pa_lock_shared_blocking(f)!=0)
                                 throw Exception("file.lock",                                   throw Exception("file.lock", 
Line 777  bool file_read_action_under_lock(const S Line 301  bool file_read_action_under_lock(const S
   
                 pa_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)
                         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, 
Line 787  bool file_read_action_under_lock(const S Line 311  bool file_read_action_under_lock(const S
         }          }
 }  }
   
 static void create_dir_for_file(const String& file_spec) {  void create_dir_for_file(const String& file_spec) {
         size_t pos_after=1;          size_t pos_after=1;
         size_t pos_before;          size_t pos_before;
         while((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND) {          while((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND) {
                 mkdir(file_spec.mid(0, pos_before).cstr(String::L_FILE_SPEC), 0775);                   mkdir(file_spec.mid(0, pos_before).cstr_taint(String::L_FILE_SPEC), 0775); 
                 pos_after=pos_before+1;                  pos_after=pos_before+1;
         }          }
 }  }
   
 bool file_write_action_under_lock(  bool file_write_action_under_lock(
                                 const String& file_spec,                                   const String& file_spec, 
                                 const char* action_name, File_write_action action, void *context,                                   const char* action_name,
                                   File_write_action action,
                                   void *context, 
                                 bool as_text,                                   bool as_text, 
                                 bool do_append,                                   bool do_append, 
                                 bool do_block,                                   bool do_block, 
                                 bool fail_on_lock_problem) {                                  bool fail_on_lock_problem) {
         const char* fname=file_spec.cstr(String::L_FILE_SPEC);           const char* fname=file_spec.cstr_taint(String::L_FILE_SPEC); 
         int f;          int f;
         if(access(fname, W_OK)!=0) // no          if(access(fname, W_OK)!=0) // no
                 create_dir_for_file(file_spec);                   create_dir_for_file(file_spec); 
Line 858  static void file_write_action(int f, voi Line 384  static void file_write_action(int f, voi
         if(info.length) {          if(info.length) {
                 int written=write(f, info.str, info.length);                   int written=write(f, info.str, info.length); 
                 if(written<0)                  if(written<0)
                         throw Exception(0,                           throw Exception("file.access", 
                                 0,                                   0, 
                                 "write failed: %s (%d)",  strerror(errno), errno);                                   "write failed: %s (%d)",  strerror(errno), errno); 
         }          }
Line 869  void file_write( Line 395  void file_write(
                                 bool as_text,                                   bool as_text, 
                                 bool do_append) {                                  bool do_append) {
         File_write_action_info info={data, size};           File_write_action_info info={data, size}; 
   
         file_write_action_under_lock(          file_write_action_under_lock(
                 file_spec,                   file_spec, 
                 "write", file_write_action, &info,                   "write",
                   file_write_action,
                   &info, 
                 as_text,                   as_text, 
                 do_append);                   do_append); 
 }  }
Line 882  static void rmdir(const String& file_spe Line 411  static void rmdir(const String& file_spe
         if((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND)          if((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND)
                 rmdir(file_spec, pos_before+1);                   rmdir(file_spec, pos_before+1); 
                   
         rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::L_FILE_SPEC));           rmdir(file_spec.mid(0, pos_after-1/* / */).cstr_taint(String::L_FILE_SPEC)); 
 }  }
   
 bool file_delete(const String& file_spec, bool fail_on_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_taint(String::L_FILE_SPEC); 
         if(unlink(fname)!=0)          if(unlink(fname)!=0)
                 if(fail_on_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, 
Line 898  bool file_delete(const String& file_spec Line 428  bool file_delete(const String& file_spec
         rmdir(file_spec, 1);           rmdir(file_spec, 1); 
         return true;          return true;
 }  }
   
 void file_move(const String& old_spec, const String& new_spec) {  void file_move(const String& old_spec, const String& new_spec) {
         const char* old_spec_cstr=old_spec.cstr(String::L_FILE_SPEC);           const char* old_spec_cstr=old_spec.cstr_taint(String::L_FILE_SPEC); 
         const char* new_spec_cstr=new_spec.cstr(String::L_FILE_SPEC);           const char* new_spec_cstr=new_spec.cstr_taint(String::L_FILE_SPEC); 
                   
         create_dir_for_file(new_spec);           create_dir_for_file(new_spec); 
   
Line 923  bool entry_exists(const char* fname, str Line 454  bool entry_exists(const char* fname, str
 }  }
   
 bool entry_exists(const String& file_spec) {  bool entry_exists(const String& file_spec) {
         const char* fname=file_spec.cstr(String::L_FILE_SPEC);           const char* fname=file_spec.cstr_taint(String::L_FILE_SPEC); 
         return entry_exists(fname, 0);           return entry_exists(fname, 0); 
 }  }
   
 static bool entry_readable(const String& file_spec, bool need_dir) {  static bool entry_readable(const String& file_spec, bool need_dir) {
         char* fname=file_spec.cstrm(String::L_FILE_SPEC);           char* fname=file_spec.cstrm_taint(String::L_FILE_SPEC); 
         if(need_dir) {          if(need_dir) {
                 size_t size=strlen(fname);                   size_t size=strlen(fname); 
                 while(size) {                  while(size) {
Line 946  static bool entry_readable(const String& Line 477  static bool entry_readable(const String&
         }          }
         return false;          return false;
 }  }
 bool file_readable(const String& file_spec) {  
   bool file_exist(const String& file_spec) {
         return entry_readable(file_spec, false);           return entry_readable(file_spec, false); 
 }  }
 bool dir_readable(const String& file_spec) {  
   bool dir_exists(const String& file_spec) {
         return entry_readable(file_spec, true);           return entry_readable(file_spec, true); 
 }  }
 const String* file_readable(const String& path, const String& name) {  
   const String* file_exist(const String& path, const String& name) {
         String& result=*new String(path);          String& result=*new String(path);
         result << "/";           result << "/"; 
         result << name;          result << name;
         return file_readable(result)?&result:0;          return file_exist(result)?&result:0;
 }  }
   
 bool file_executable(const String& file_spec) {  bool file_executable(const String& file_spec) {
     return access(file_spec.cstr(String::L_FILE_SPEC), X_OK)==0;          return access(file_spec.cstr_taint(String::L_FILE_SPEC), X_OK)==0;
 }  }
   
 bool file_stat(const String& file_spec,   bool file_stat(const String& file_spec, 
                            size_t& rsize,                           size_t& rsize,
                            time_t& ratime,                           time_t& ratime,
                            time_t& rmtime,                           time_t& rmtime,
                            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_taint(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)
Line 986  bool file_stat(const String& file_spec, Line 521  bool file_stat(const String& file_spec,
 }  }
   
 char* getrow(char* *row_ref, char delim) {  char* getrow(char* *row_ref, char delim) {
     char* result=*row_ref;          char* result=*row_ref;
     if(result) {          if(result) {
                 *row_ref=strchr(result, delim);                   *row_ref=strchr(result, delim); 
                 if(*row_ref)                   if(*row_ref) 
                         *((*row_ref)++)=0;                           *((*row_ref)++)=0; 
                 else if(!*result)                   else if(!*result) 
                         return 0;                          return 0;
     }          }
     return result;          return result;
 }  }
   
 char* lsplit(char* string, char delim) {  char* lsplit(char* string, char delim) {
     if(string) {          if(string) {
                 char* v=strchr(string, delim);                   char* v=strchr(string, delim); 
                 if(v) {                  if(v) {
                         *v=0;                          *v=0;
                         return v+1;                          return v+1;
                 }                  }
     }          }
     return 0;          return 0;
 }  }
   
 char* lsplit(char* *string_ref, char delim) {  char* lsplit(char* *string_ref, char delim) {
     char* result=*string_ref;          char* result=*string_ref;
         char* next=lsplit(*string_ref, delim);           char* next=lsplit(*string_ref, delim); 
     *string_ref=next;          *string_ref=next;
     return result;          return result;
 }  }
   
 char* rsplit(char* string, char delim) {  char* rsplit(char* string, char delim) {
     if(string) {          if(string) {
                 char* v=strrchr(string, delim);                   char* v=strrchr(string, delim); 
                 if(v) {                  if(v) {
                         *v=0;                          *v=0;
                         return v+1;                          return v+1;
                 }                  }
     }          }
     return NULL;                  return NULL;    
   }
   
   
   // format: %[flags][width][.precision]type      http://msdn.microsoft.com/ru-ru/library/56e442dc(en-us,VS.80).aspx
   //              flags: '-', '+', ' ', '#', '0'          http://msdn.microsoft.com/ru-ru/library/8aky45ct(en-us,VS.80).aspx
   //              width, precision: non negative decimal number
   enum FormatType {
           FormatInvalid,
           FormatInt,
           FormatUInt,
           FormatDouble
   };
   FormatType format_type(char* fmt){
           enum FormatState {
                   Percent, 
                   Flags, 
                   Width,
                   Precision,
                   Done
           } state=Percent;
   
           FormatType result=FormatInvalid;
   
           char* pos=fmt;
           while(char c=*(pos++)){
                   switch(state){
                           case Percent:
                                   if(c=='%'){
                                           state=Flags;
                                   } else {
                                           return FormatInvalid; // 1st char must be '%' only
                                   }
                                   break;
                           case Flags:
                                   if(strchr("-+ #0", c)!=0){
                                           break;
                                   }
                                   // go to the next step
                           case Width:
                                   if(c=='.'){
                                           state=Precision;
                                           break;
                                   }
                                   // go to the next step
                           case Precision:
                                   if(c>='0' && c<='9'){
                                           if(state == Flags) state=Width; // no more flags
                                           break;
                                   } else if(c=='d' || c=='i'){
                                           result=FormatInt;
                                   } else if(strchr("feEgG", c)!=0){
                                           result=FormatDouble;
                                   } else if(strchr("uoxX", c)!=0){
                                           result=FormatUInt;
                                   } else {
                                           return FormatInvalid; // invalid char
                                   }
                                   state=Done;
                                   break;
                           case Done:
                                   return FormatInvalid; // no chars allowed after 'type'
                   }
           }
           return result;
 }  }
   
 /// @todo less stupid type detection  
 const char* format(double value, char* fmt) {  const char* format(double value, char* fmt) {
         char local_buf[MAX_NUMBER];           char local_buf[MAX_NUMBER];
         size_t size;          int size=-1;
           
         if(fmt)          if(fmt && strlen(fmt)){
                 if(strpbrk(fmt, "diouxX"))                  switch(format_type(fmt)){
                         if(strpbrk(fmt, "ouxX"))                          case FormatDouble:
                                 size=snprintf(local_buf, sizeof(local_buf), fmt, (uint)value);                                   size=snprintf(local_buf, sizeof(local_buf), fmt, value); 
                         else                                  break;
                           case FormatInt:
                                 size=snprintf(local_buf, sizeof(local_buf), fmt, (int)value);                                   size=snprintf(local_buf, sizeof(local_buf), fmt, (int)value); 
                 else                                  break;
                         size=snprintf(local_buf, sizeof(local_buf), fmt, value);                           case FormatUInt:
         else                                  size=snprintf(local_buf, sizeof(local_buf), fmt, (uint)value); 
                 size=snprintf(local_buf, sizeof(local_buf), "%d", (int)value);                                   break;
                                   case FormatInvalid:
         return pa_strdup(local_buf, size);                                  throw Exception(PARSER_RUNTIME, 
                                           0, 
                                           "Incorrect format string '%s' was specified.", fmt);
                   }
           } else
                   size=snprintf(local_buf, sizeof(local_buf), "%d", (int)value);
   
           if(size < 0 || size >= MAX_NUMBER-1){ // on win32 we manually reduce max size while printing
                   throw Exception(PARSER_RUNTIME, 
                           0, 
                           "Error occure white executing snprintf with format string '%s'.", fmt);
           }
   
           return pa_strdup(local_buf, (size_t)size);
 }  }
   
 size_t stdout_write(const void *buf, size_t size) {  size_t stdout_write(const void *buf, size_t size) {
 #ifdef WIN32  #ifdef WIN32
           size_t to_write = size;
         do{          do{
                 int chunk_written=fwrite(buf, 1, min((size_t)8*0x400, size), stdout);                   int chunk_written=fwrite(buf, 1, min((size_t)8*0x400, size), stdout); 
                 if(chunk_written<=0)                  if(chunk_written<=0)
Line 1055  size_t stdout_write(const void *buf, siz Line 669  size_t stdout_write(const void *buf, siz
                 buf=((const char*)buf)+chunk_written;                  buf=((const char*)buf)+chunk_written;
         } while(size>0);           } while(size>0); 
   
         return size;          return to_write-size;
 #else  #else
         return fwrite(buf, 1, size, stdout);           return fwrite(buf, 1, size, stdout); 
 #endif  #endif
 }  }
   
 char* unescape_chars(const char* cp, int len) {  enum EscapeState {
         char* s=new(PointerFreeGC) char[len + 1];           EscapeRest, 
         enum EscapeState {          EscapeFirst, 
                 EscapeRest,           EscapeSecond,
                 EscapeFirst,           EscapeUnicode
                 EscapeSecond  };
         } escapeState=EscapeRest;  
         int escapedValue=0;  // @todo prescan for reduce required size (unescaped sting in 1 byte charset requires less memory usually)
   char* unescape_chars(const char* cp, int len, Charset* charset, bool ignore_plus){
           char* s=new(PointerFreeGC) char[len+1]; // must be enough (%uXXXX==6 bytes, max utf-8 char length==6 bytes)
           char* dst=s;
           EscapeState escapeState=EscapeRest;
           uint escapedValue=0;
         int srcPos=0;          int srcPos=0;
         int dstPos=0;          short int jsCnt=0;
         while(srcPos < len) {          while(srcPos<len){
                 int ch=cp[srcPos];                   uchar c=(uchar)cp[srcPos]; 
                 switch(escapeState) {                  if(c=='%'){
                         case EscapeRest:                          escapeState=EscapeFirst;
                         if(ch=='%') {                  } else {
                                 escapeState=EscapeFirst;                          switch(escapeState) {
                         } else if(ch=='+') {                                  case EscapeRest:
                                 s[dstPos++]=' ';                                           if(!ignore_plus && c=='+'){
                         } else {                                                  *dst++=' ';
                                 s[dstPos++]=ch;                                           } else {
                                                   *dst++=c;
                                           }
                                           break;
                                   case EscapeFirst:
                                           if(charset && c=='u'){
                                                   // escaped unicode value: %u0430
                                                   jsCnt=0;
                                                   escapedValue=0;
                                                   escapeState=EscapeUnicode;
                                           } else {
                                                   if(isxdigit(c)){
                                                           escapedValue=hex_value[c] << 4;
                                                           escapeState=EscapeSecond;
                                                   } else {
                                                           *dst++=c;
                                                           escapeState=EscapeRest;
                                                   }
                                           }
                                           break;
                                   case EscapeSecond:
                                           if(isxdigit(c)){
                                                   escapedValue+=hex_value[c]; 
                                                   *dst++=(char)escapedValue;
                                           }
                                           escapeState=EscapeRest;
                                           break;
                                   case EscapeUnicode:
                                           if(isxdigit(c)){
                                                   escapedValue=(escapedValue << 4) + hex_value[c];
                                                   if(++jsCnt==4){
                                                           // transcode utf8 char to client charset (we can lost some chars here)
                                                           charset->store_Char((XMLByte*&)dst, (XMLCh)escapedValue, '?');
                                                           escapeState=EscapeRest;
                                                   }
                                           } else {
                                                   // not full unicode value
                                                   escapeState=EscapeRest;
                                           }
                                           break;
                         }                          }
                         break;  
                         case EscapeFirst:  
                         escapedValue=hex_value[ch] << 4;          
                         escapeState=EscapeSecond;  
                         break;  
                         case EscapeSecond:  
                         escapedValue +=hex_value[ch];   
                         s[dstPos++]=escapedValue;  
                         escapeState=EscapeRest;  
                         break;  
                 }                  }
                 srcPos++;   
                   srcPos++;
         }          }
         s[dstPos]=0;  
           *dst=0; // zero-termination
         return s;          return s;
 }  }
   
Line 1116  void slashes_to_back_slashes(char* s) { Line 766  void slashes_to_back_slashes(char* s) {
 */  */
 #endif  #endif
   
 bool StrEqNc(const char* s1, const char* s2, bool strict) {  bool StrStartFromNC(const char* str, const char* substr, bool equal){
         while(true) {          while(true) {
                 if(!(*s1)) {                  if(!(*substr)){
                         if(!(*s2))                          if(!(*str))
                                 return true;                                  return true;
                         else                          else
                                 return !strict;                                  return !equal;
                 } else if(!(*s2))                  }
                         return !strict;                  if(!(*str))
                 if(isalpha(*s1)) {                          return false;
                         if(tolower(*s1) !=tolower(*s2))                  if(isalpha((unsigned char)*str)) {
                           if(tolower((unsigned char)*str)!=tolower((unsigned char)*substr))
                                 return false;                                  return false;
                 } else if((*s1) !=(*s2))                  } else if((*str) != (*substr))
                         return false;                          return false;
                 s1++;                   str++; 
                 s2++;                   substr++; 
           }
   }
   
   size_t strpos(const char *str, const char *substr) {
           const char *p = strstr(str, substr);
           return (p==0)?STRING_NOT_FOUND:p-str;
   }
   
   // content-type: xxx; charset=WE-NEED-THIS
   // content-type: xxx; charset="WE-NEED-THIS"
   // content-type: xxx; charset="WE-NEED-THIS";
   Charset* detect_charset(const char* content_type, bool already_uppercased){
           if(content_type){
                   char* CONTENT_TYPE=pa_strdup(content_type);
                   if(!already_uppercased)
                           for(char *p=CONTENT_TYPE; *p; p++)
                                   *p=(char)toupper((unsigned char)*p);
   
                   if(const char* begin=strstr(CONTENT_TYPE, "CHARSET=")){
                           begin+=8; // skip "CHARSET="
                           char* end=0;
                           if(*begin && (*begin=='"' || *begin =='\'')){
                                   char quote=*begin;
                                   begin++;
                                   end=(char*)strchr(begin, quote);
                           }
                           if(!end)
                                   end=(char*)strchr(begin, ';');
   
                           if(end)
                                   *end=0; // terminator
   
                           return *begin?&charsets.get(begin):0;
                   }
         }          }
           return 0;
 }  }
   
   
 static bool isLeap(int year) {  static bool isLeap(int year) {
     return !(          return !(
              (year % 4) || ((year % 400) && !(year % 100))                                  (year % 4) || ((year % 400) && !(year % 100))
             );                           ); 
 }  }
   
 int getMonthDays(int year, int month) {  int getMonthDays(int year, int month) {
     int monthDays[]={          static int monthDays[]={
         31,                   31, 
         isLeap(year) ? 29 : 28,                   28, 
         31,                   31, 
         30,                   30, 
         31,                   31, 
         30,                   30, 
         31,                   31, 
         31,                   31, 
         30,                   30, 
         31,                   31, 
         30,                   30, 
         31                  31
     };           }; 
     return monthDays[month];           return (month == 1 /* january -- 0 */ && isLeap(year)) ? 29 : monthDays[month]; 
 }  }
   
 void remove_crlf(char* start, char* end) {  int remove_crlf(char* start, char* end) {
         for(char* p=start; p<end; p++)          char* from=start;
                 switch(*p) {          char* to=start;
                         case '\n': *p='|';  break;          bool skip=false;
                         case '\r': *p=' ';  break;          while(from < end){
                   switch(*from){
                           case '\n':
                           case '\r':
                           case '\t':
                           case ' ':
                                   if(!skip){
                                           *to=' ';
                                           to++;
                                           skip=true;
                                   }
                                   break;
                           default:
                                   if(from != to)
                                           *to=*from;
                                   to++;
                                   skip=false;
                 }                  }
                   from++;
           }
           return to-start;
 }  }
   
   
Line 1217  int __vsnprintf(char* b, size_t s, const Line 923  int __vsnprintf(char* b, size_t s, const
   
 int __snprintf(char* b, size_t s, const char* f, ...) {  int __snprintf(char* b, size_t s, const char* f, ...) {
         va_list l;          va_list l;
     va_start(l, f);           va_start(l, f); 
     int r=__vsnprintf(b, s, f, l);           int r=__vsnprintf(b, s, f, l); 
     va_end(l);           va_end(l); 
         return r;          return r;
 }  }
   
Line 1266  static char *base64_alphabet = Line 972  static char *base64_alphabet =
 static size_t  static size_t
 g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)  g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
 {  {
         const register unsigned char *inptr;          register const unsigned char *inptr;
         register unsigned char *outptr;          register unsigned char *outptr;
                   
         if (inlen <= 0)          if (inlen <= 0)
Line 1324  g_mime_utils_base64_encode_step (const u Line 1030  g_mime_utils_base64_encode_step (const u
                 case 2: *saveout++ = *inptr++;                  case 2: *saveout++ = *inptr++;
                 case 1: *saveout++ = *inptr++;                  case 1: *saveout++ = *inptr++;
                 }                  }
                 ((char *)save)[0] += inlen;                  *(char *)save = *(char *)save+(char)inlen;
         }          }
                   
         /*d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",          /*d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
Line 1383  g_mime_utils_base64_encode_close (const Line 1089  g_mime_utils_base64_encode_close (const
         return (outptr - out);          return (outptr - out);
 }  }
   
 char* pa_base64(const char *in, size_t len)  static unsigned char gmime_base64_rank[256] = {
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
            52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
           255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
           255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
   };
   
   /**
    * g_mime_utils_base64_decode_step:
    * @in: input stream
    * @inlen: max length of data to decode
    * @out: output stream
    * @state: holds the number of bits that are stored in @save
    * @save: leftover bits that have not yet been decoded
    *
    * Decodes a chunk of base64 encoded data.
    *
    * Returns the number of bytes decoded (which have been dumped in @out).
    **/
   size_t
   g_mime_utils_base64_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
 {  {
           const unsigned char *inptr;
           unsigned char *outptr;
           const unsigned char *inend;
           int saved;
           unsigned char c;
           int i;
           
           inend = in + inlen;
           outptr = out;
           
           /* convert 4 base64 bytes to 3 normal bytes */
           saved = *save;
           i = *state;
           inptr = in;
           while (inptr < inend) {
                   c = gmime_base64_rank[*inptr++];
                   if (c != 0xff) {
                           saved = (saved << 6) | c;
                           i++;
                           if (i == 4) {
                                   *outptr++ = (unsigned char)(saved >> 16);
                                   *outptr++ = (unsigned char)(saved >> 8);
                                   *outptr++ = (unsigned char)(saved);
                                   i = 0;
                           }
                   }
           }
           
           *save = saved;
           *state = i;
           
           /* quick scan back for '=' on the end somewhere */
           /* fortunately we can drop 1 output char for each trailing = (upto 2) */
           i = 2;
           while (inptr > in && i) {
                   inptr--;
                   if (gmime_base64_rank[*inptr] != 0xff) {
                           if (*inptr == '=' && outptr > out)
                                   outptr--;
                           i--;
                   }
           }
           
           /* if i != 0 then there is a truncation error! */
           return (outptr - out);
   }
   
   
   char* pa_base64_encode(const char *in, size_t in_size){
         /* wont go to more than 2x size (overly conservative) */          /* wont go to more than 2x size (overly conservative) */
         char* result=new(PointerFreeGC) char[len * 2 + 6];          char* result=new(PointerFreeGC) char[in_size * 2 + 6];
         int state=0;          int state=0;
         int save=0;          int save=0;
 #ifndef NDEBUG  #ifndef NDEBUG
         size_t filled=          size_t filled=
 #endif  #endif
                 g_mime_utils_base64_encode_close ((const unsigned char*)in, len,                   g_mime_utils_base64_encode_close ((const unsigned char*)in, in_size, 
                 (unsigned char*)result, &state, &save);                  (unsigned char*)result, &state, &save);
         assert(filled <= len * 2 + 6);          assert(filled <= in_size * 2 + 6);
   
         return result;          return result;
 }  }
   
   
   char* pa_base64_encode(const String& file_spec){
           unsigned char* base64=0;
           File_base64_action_info info={&base64}; 
   
           file_read_action_under_lock(file_spec, 
                   "pa_base64_encode", file_base64_file_action, &info);
   
           return (char*)base64; 
   }
   
   
   static void file_base64_file_action(
                                   struct stat& finfo, 
                                   int f, 
                                   const String&, const char* /*fname*/, bool, 
                                   void *context) {
   
           if(finfo.st_size) { 
                   File_base64_action_info& info=*static_cast<File_base64_action_info *>(context);
                   *info.base64=new(PointerFreeGC) unsigned char[finfo.st_size * 2 + 6]; 
                   unsigned char* base64 = *info.base64;
                   int state=0;
                   int save=0;
                   int nCount;
                   do {
                           unsigned char buffer[FILE_BUFFER_SIZE];
                           nCount = file_block_read(f, buffer, sizeof(buffer));
                           if( nCount ){
                                   size_t filled=g_mime_utils_base64_encode_step ((const unsigned char*)buffer, nCount, base64, &state, &save);
                                   base64+=filled;
                           }
                   } while(nCount > 0);
                   g_mime_utils_base64_encode_close (0, 0, base64, &state, &save);
           }
   }
   
   void pa_base64_decode(const char *in, size_t in_size, char*& result, size_t& result_size) {
           /* wont go to more than had (overly conservative) */
           result=new(PointerFreeGC) char[in_size+1/*terminator*/];
           int state=0;
           int save=0;
           result_size=
                   g_mime_utils_base64_decode_step ((const unsigned char*)in, in_size, 
                   (unsigned char*)result, &state, &save);
           assert(result_size <= in_size);
           result[result_size]=0; // for text files
   }
   
   
   int file_block_read(const int f, unsigned char* buffer, const size_t size){
           int nCount = read(f, buffer, size);
           if (nCount < 0)
                   throw Exception("file.read", 
                           0, 
                           "read failed: %s (%d)",  strerror(errno), errno); 
           return nCount;
   }
   
   const unsigned long pa_crc32(const char *in, size_t in_size){
           unsigned long crc32=0xFFFFFFFF;
   
           InitCrc32Table();
           for(size_t i = 0; i<in_size; i++)
                   CalcCrc32(in[i], crc32);
   
           return ~crc32; 
   }
   
   const unsigned long pa_crc32(const String& file_spec){
           unsigned long crc32=0xFFFFFFFF;
           file_read_action_under_lock(file_spec, "crc32", file_crc32_file_action, &crc32);
           return ~crc32; 
   }
   
   static void file_crc32_file_action(
                                   struct stat& finfo, 
                                   int f, 
                                   const String&, const char* /*fname*/, bool, 
                                   void *context) {
           unsigned long& crc32=*static_cast<unsigned long *>(context);
           if(finfo.st_size) {
                   InitCrc32Table();
                   int nCount=0;
                   do {
                           unsigned char buffer[FILE_BUFFER_SIZE];
                           nCount = file_block_read(f, buffer, sizeof(buffer));
                           for(int i = 0; i < nCount; i++) CalcCrc32(buffer[i], crc32);
                   } while(nCount > 0);
           }
   }
   

Removed from v.1.185  
changed lines
  Added in v.1.246


E-mail: