Diff for /parser3/src/main/pa_common.C between versions 1.154 and 1.217

version 1.154, 2003/07/24 11:31:23 version 1.217, 2006/02/03 15:08:04
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-2005 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$";    * 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$"; 
   
 #include "pa_common.h"  #include "pa_common.h"
 #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  
   
 #ifdef PA_HTTP  
 #include "pa_vstring.h"  
 #include "pa_vint.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 62  int PASCAL closesocket(SOCKET); Line 54  int PASCAL closesocket(SOCKET);
 #       endif  #       endif
 #endif  #endif
   
 // locking constants  
 //#define PA_DEBUG_NO_LOCKING  
   
 #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
   
 #define FILE_STATUS_NAME  "status"  #define FILE_STATUS_NAME  "status"
Line 126  static int unlock(int fd) { return 0; } Line 62  static int unlock(int fd) { return 0; }
   
 const String file_status_name(FILE_STATUS_NAME);  const String file_status_name(FILE_STATUS_NAME);
   
 // defines for statics  
   
 #define HTTP_METHOD_NAME  "method"  
 #define HTTP_TIMEOUT_NAME    "timeout"  
 #define HTTP_HEADERS_NAME "headers"  
 #define HTTP_ANY_STATUS_NAME "any-status"  
 #define HTTP_CHARSET_NAME "charset"  
   
 // 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  
   
 #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 183  char* file_read_text(Request_charsets& c Line 99  char* file_read_text(Request_charsets& c
         return file.success?file.str:0;          return file.success?file.str:0;
 }  }
   
 #ifdef PA_HTTP  /// these options were handled but not checked elsewhere, now check them
 //http request stuff  int pa_get_valid_file_options_count(HashStringValue& options)
   {
 #undef CRLF  
 #define CRLF "\r\n"  
   
 static bool set_addr(struct sockaddr_in *addr, const char* host, const short port){  
     memset(addr, 0, sizeof(*addr));   
     addr->sin_family=AF_INET;  
     addr->sin_port=htons(port);   
     if(host) {  
                 if(struct hostent *hostIP=gethostbyname(host))   
                         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(String& response, int sock, bool fail_on_status_ne_200){  
         int result=0;          int result=0;
         size_t EOLat=0;          if(options.get(PA_SQL_LIMIT_NAME))
         while(true) {                  result++;
                 char *buf=new(PointerFreeGC) char[MAX_STRING];           if(options.get(PA_SQL_OFFSET_NAME))
                 ssize_t size=recv(sock, buf, MAX_STRING, 0);                   result++;
                 if(size<=0)          if(options.get(PA_COLUMN_SEPARATOR_NAME))
                         break;                  result++;
                 response.append_strdup(buf, size, String::L_TAINTED);           if(options.get(PA_COLUMN_ENCLOSER_NAME))
                 if(!result && (EOLat=response.pos(CRLF, 2))!=STRING_NOT_FOUND) { // checking status in first response                  result++;
                         const String& status_line=response.mid(0, (size_t)EOLat);  
                         ArrayString astatus;   
                         size_t pos_after=0;  
                         status_line.split(astatus, pos_after, " ");   
                         const String& status_code=*astatus.get(1);  
                         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=%lu)", response.length());   
 }  
   
 /* ********************** 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(String& response,  
                         const char* host, int port,   
                         const char* request,   
                         int timeout,  
                         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, 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 StringBody CONTENT_TYPE_VALUE=  
                 content_type_value.change_case(source_charset, String::CC_UPPER);  
         // content-type: xxx/xxx; source_charset=WE-NEED-THIS  
         // content-type: xxx/xxx; source_charset="WE-NEED-THIS"  
         // content-type: xxx/xxx; source_charset="WE-NEED-THIS";  
         size_t before_charseteq_pos=CONTENT_TYPE_VALUE.pos("CHARSET=");  
         if(before_charseteq_pos!=STRING_NOT_FOUND) {  
                 size_t charset_begin=before_charseteq_pos+8/*CHARSET="*/;  
                 size_t open_quote_pos=CONTENT_TYPE_VALUE.pos('"', charset_begin);  
                 bool quoted=open_quote_pos==charset_begin;  
                 if(quoted)  
                         charset_begin++; // skip opening '"'  
                 size_t charset_end=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 StringBody CHARSET_NAME_BODY=  
                         CONTENT_TYPE_VALUE.mid(charset_begin, charset_end);  
   
                 return &charsets.get(CHARSET_NAME_BODY);  
         }  
   
         return 0;  
 }  
   
   
 #ifndef DOXYGEN  
 struct File_read_http_result {  
         char *str; size_t length;  
         HashStringValue* headers;  
 };   
 #endif  
 static File_read_http_result file_read_http(Request_charsets& charsets,   
                                             const String& file_spec,   
                                             HashStringValue *options=0) {  
         File_read_http_result result;  
         char host[MAX_STRING];   
         const char* uri;   
         int port;  
         const char* method=0;  
         int timeout=2;  
         bool fail_on_status_ne_200=true;  
         Value* vheaders=0;  
         Charset *asked_remote_charset=0;  
   
         if(options) {  
                 int valid_options=0;  
                 if(Value* vmethod=options->get(http_method_name)) {  
                         valid_options++;  
                         method=vmethod->as_string().cstr();  
                 }  
                 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(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();  
   
         //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  
   
         const char* request_cstr;  
         {  
                 // 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;  
   
   
                 //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};  
                                 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);  
         }  
         // 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  
         String response;  
         int status_code=http_request(response,  
                 host, port, request_cstr,   
                 timeout, fail_on_status_ne_200);   
           
         //processing results      
         size_t pos=response.pos(CRLF CRLF, 4);   
         if(pos==STRING_NOT_FOUND || pos<1) {  
                 throw Exception("http.response",   
                         &connect_string,  
                         "bad response from host - no headers found");   
         }  
         const String& header_block=response.mid(0, pos);   
         const String& raw_body=response.mid(pos+4, response.length());   
           
         ArrayString aheaders;  
         result.headers=new HashStringValue;  
         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);  
                 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 StringBody 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);  
                 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=Charset::transcode(  
                 String::C(raw_body.cstrm()/*must be modifiable*/, raw_body.length()),  
                 *real_remote_charset,  
                 charsets.source());  
   
         result.str=const_cast<char *>(real_body.str); // hacking a little  
         result.length=real_body.length;  
         result.headers->put(file_status_name, new VInt(status_code));  
         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(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) {                  // 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,
         File_read_result result={false};                             char* buf, size_t offset, size_t count) {
 #ifdef PA_HTTP          File_read_result result={false, 0, 0, 0};
         if(file_spec.starts_with("http://")) {          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                  // fail on read problem
                 File_read_http_result http=file_read_http(charsets, file_spec, params);                  File_read_http_result http=pa_internal_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                  if(params) {
                 File_read_action_info info={&result.str, &result.length};                           int valid_options=pa_get_valid_file_options_count(*params);
                           if(valid_options!=params->count())
                                   throw Exception("parser.runtime",
                                           0,
                                           "invalid option passed");
                   }
   
                   File_read_action_info info={&result.str, &result.length,
                           buf, offset, count}; 
                 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, 
                         as_text, fail_on_read_problem);                           as_text, fail_on_read_problem); 
 #ifdef PA_HTTP  
         }          }
 #endif  
   
         if(result.success && as_text) {          if(result.success && as_text) {
                 // 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;
Line 638  bool file_read_action_under_lock(const S Line 234  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 657  bool file_read_action_under_lock(const S Line 253  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 675  bool file_read_action_under_lock(const S Line 271  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) {
Line 700  bool file_write_action_under_lock( Line 296  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 718  bool file_write_action_under_lock( Line 314  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 726  bool file_write_action_under_lock( Line 322  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 772  static void rmdir(const String& file_spe Line 368  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 834  static bool entry_readable(const String& Line 430  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(String::L_FILE_SPEC), X_OK)==0;
Line 935  const char* format(double value, char* f Line 531  const char* format(double value, char* f
   
 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 943  size_t stdout_write(const void *buf, siz Line 540  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
Line 956  char* unescape_chars(const char* cp, int Line 553  char* unescape_chars(const char* cp, int
                 EscapeFirst,                   EscapeFirst, 
                 EscapeSecond                  EscapeSecond
         } escapeState=EscapeRest;          } escapeState=EscapeRest;
         int escapedValue=0;          uint escapedValue=0;
         int srcPos=0;          int srcPos=0;
         int dstPos=0;          int dstPos=0;
         while(srcPos < len) {          while(srcPos < len) {
                 int ch=cp[srcPos];                   uchar ch=(uchar)cp[srcPos]; 
                 switch(escapeState) {                  switch(escapeState) {
                         case EscapeRest:                          case EscapeRest:
                         if(ch=='%') {                          if(ch=='%') {
Line 972  char* unescape_chars(const char* cp, int Line 569  char* unescape_chars(const char* cp, int
                         }                          }
                         break;                          break;
                         case EscapeFirst:                          case EscapeFirst:
                         escapedValue=hex_value[ch] << 4;                                  escapedValue=hex_value[ch] << 4;
                         escapeState=EscapeSecond;                          escapeState=EscapeSecond;
                         break;                          break;
                         case EscapeSecond:                          case EscapeSecond:
                         escapedValue +=hex_value[ch];                           escapedValue +=hex_value[ch]; 
                         s[dstPos++]=escapedValue;                          s[dstPos++]=(char)escapedValue;
                         escapeState=EscapeRest;                          escapeState=EscapeRest;
                         break;                          break;
                 }                  }
Line 1013  bool StrEqNc(const char* s1, const char* Line 610  bool StrEqNc(const char* s1, const char*
                                 return !strict;                                  return !strict;
                 } else if(!(*s2))                  } else if(!(*s2))
                         return !strict;                          return !strict;
                 if(isalpha(*s1)) {                  if(isalpha((unsigned char)*s1)) {
                         if(tolower(*s1) !=tolower(*s2))                          if(tolower((unsigned char)*s1) !=tolower((unsigned char)*s2))
                                 return false;                                  return false;
                 } else if((*s1) !=(*s2))                  } else if((*s1) !=(*s2))
                         return false;                          return false;
Line 1066  int __vsnprintf(char* b, size_t s, const Line 663  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 1091  int __vsnprintf(char* b, size_t s, const Line 693  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 1106  int __snprintf(char* b, size_t s, const Line 708  int __snprintf(char* b, size_t s, const
         return r;          return r;
 }  }
   
 int pa_sleep(unsigned long secs, unsigned long usecs) {  /* mime64 functions are from libgmime[http://spruce.sourceforge.net/gmime/] lib */
         for (;  usecs >= 1000000; ++secs, usecs -= 1000000);   /*
    *  Authors: Michael Zucchi <notzed@helixcode.com>
    *           Jeffrey Stedfast <fejj@helixcode.com>
    *
    *  Copyright 2000 Helix Code, Inc. (www.helixcode.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 char *base64_alphabet =
           "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
   
   /**
    * g_mime_utils_base64_encode_step:
    * @in: input stream
    * @inlen: length of the input
    * @out: output string
    * @state: holds the number of bits that are stored in @save
    * @save: leftover bits that have not yet been encoded
    *
    * Base64 encodes a chunk of data. Performs an 'encode step', only
    * encodes blocks of 3 characters to the output at a time, saves
    * left-over state in state and save (initialise to 0 on first
    * invocation).
    *
    * Returns the number of bytes encoded.
    **/
   static size_t
   g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
   {
           register const unsigned char *inptr;
           register unsigned char *outptr;
           
           if (inlen <= 0)
                   return 0;
           
           inptr = in;
           outptr = out;
           
           if (inlen + ((unsigned char *)save)[0] > 2) {
                   const unsigned char *inend = in + inlen - 2;
                   register int c1 = 0, c2 = 0, c3 = 0;
                   register int already;
                   
                   already = *state;
                   
                   switch (((char *)save)[0]) {
                   case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
                   case 2: c1 = ((unsigned char *)save)[1];
                           c2 = ((unsigned char *)save)[2]; goto skip2;
                   }
                   
                   /* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
                   while (inptr < inend) {
                           c1 = *inptr++;
                   skip1:
                           c2 = *inptr++;
                   skip2:
                           c3 = *inptr++;
                           *outptr++ = base64_alphabet [c1 >> 2];
                           *outptr++ = base64_alphabet [(c2 >> 4) | ((c1 & 0x3) << 4)];
                           *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
                           *outptr++ = base64_alphabet [c3 & 0x3f];
                           /* this is a bit ugly ... */
                           if ((++already) >= 19) {
                                   *outptr++ = '\n';
                                   already = 0;
                           }
                   }
                   
                   ((unsigned char *)save)[0] = 0;
                   inlen = 2 - (inptr - inend);
                   *state = already;
           }
           
           //d(printf ("state = %d, inlen = %d\n", (int)((char *)save)[0], inlen));
           
           if (inlen > 0) {
                   register char *saveout;
                   
                   /* points to the slot for the next char to save */
                   saveout = & (((char *)save)[1]) + ((char *)save)[0];
                   
                   /* inlen can only be 0 1 or 2 */
                   switch (inlen) {
                   case 2: *saveout++ = *inptr++;
                   case 1: *saveout++ = *inptr++;
                   }
                   *(char *)save = *(char *)save+(char)inlen;
           }
           
           /*d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
                     (int)((char *)save)[0],
                     (int)((char *)save)[1],
                     (int)((char *)save)[2]));*/
           
           return (outptr - out);
   }
   
 #ifdef WIN32  /**
         Sleep(secs * 1000 + usecs / 1000);    * g_mime_utils_base64_encode_close:
         return 0;   * @in: input stream
 #else   * @inlen: length of the input
         struct timeval t;   * @out: output string
         t.tv_sec = secs;   * @state: holds the number of bits that are stored in @save
         t.tv_usec = usecs;   * @save: leftover bits that have not yet been encoded
         return (select(0, NULL, NULL, NULL, &t) == -1 ? errno : 0);    *
 #endif   * Base64 encodes the input stream to the output stream. Call this
    * when finished encoding data with g_mime_utils_base64_encode_step to
    * flush off the last little bit.
    *
    * Returns the number of bytes encoded.
    **/
   static size_t
   g_mime_utils_base64_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
   {
           unsigned char *outptr = out;
           int c1, c2;
           
           if (inlen > 0)
                   outptr += g_mime_utils_base64_encode_step (in, inlen, outptr, state, save);
           
           c1 = ((unsigned char *)save)[1];
           c2 = ((unsigned char *)save)[2];
           
           switch (((unsigned char *)save)[0]) {
           case 2:
                   outptr[2] = base64_alphabet [(c2 & 0x0f) << 2];
                   goto skip;
           case 1:
                   outptr[2] = '=';
           skip:
                   outptr[0] = base64_alphabet [c1 >> 2];
                   outptr[1] = base64_alphabet [c2 >> 4 | ((c1 & 0x3) << 4)];
                   outptr[3] = '=';
                   outptr += 4;
                   break;
           }
           
           *outptr++ = 0;
           
           *save = 0;
           *state = 0;
           
           return (outptr - out);
   }
   
   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) */
           char* result=new(PointerFreeGC) char[in_size * 2 + 6];
           int state=0;
           int save=0;
   #ifndef NDEBUG
           size_t filled=
   #endif
                   g_mime_utils_base64_encode_close ((const unsigned char*)in, in_size, 
                   (unsigned char*)result, &state, &save);
           assert(filled <= in_size * 2 + 6);
   
           return result;
   }
   
   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
   }

Removed from v.1.154  
changed lines
  Added in v.1.217


E-mail: