Diff for /parser3/src/main/pa_common.C between versions 1.236 and 1.283

version 1.236, 2008/07/15 12:50:11 version 1.283, 2015/04/21 22:12:27
Line 1 Line 1
 /** @file  /** @file
         Parser: commonly functions.          Parser: commonly functions.
   
         Copyright(c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2000-2012 Art. Lebedev Studio (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   * BASE64 part
Line 26 Line 26
  *   *
  */   */
   
 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"
Line 35  static const char * const IDENT_COMMON_C Line 33  static const char * const IDENT_COMMON_C
 #include "pa_charsets.h"  #include "pa_charsets.h"
 #include "pa_http.h"  #include "pa_http.h"
 #include "pa_request_charsets.h"  #include "pa_request_charsets.h"
   #include "pcre.h"
   #include "pa_request.h"
   
   #include "pa_idna.h"
   #include "pa_convert_utf.h"
   
   #ifdef _MSC_VER
   #include <windows.h>
   #include <direct.h>
   #endif
   
   #ifdef _MSC_VER
   #define pa_mkdir(path, mode) _mkdir(path)
   #else
   #define pa_mkdir(path, mode) mkdir(path, mode)
   #endif
   
   volatile const char * IDENT_PA_COMMON_C="$Id$" IDENT_PA_COMMON_H IDENT_PA_HASH_H IDENT_PA_ARRAY_H IDENT_PA_STACK_H; 
   
 // some maybe-undefined constants  // some maybe-undefined constants
   
Line 65  const String file_status_name(FILE_STATU Line 81  const String file_status_name(FILE_STATU
   
 // functions  // functions
   
 void fix_line_breaks(char *str, size_t& length) {  char* file_read_text(Request_charsets& charsets, const String& file_spec, bool fail_on_read_problem, HashStringValue* params, bool transcode_result) {
         //_asm int 3;          File_read_result file=file_read(charsets, file_spec, true, params, fail_on_read_problem, 0, 0, 0, transcode_result);
         const char* const eob=str+length;          return file.success?file.str:0;
         char* dest=str;  
         // fix DOS: \r\n -> \n  
         // fix Macintosh: \r -> \n  
         char* bol=str;  
         while(char* eol=(char*)memchr(bol, '\r', eob -bol)) {  
                 size_t len=eol-bol;  
                 if(dest!=bol)  
                         memcpy(dest, bol, len);   
                 dest+=len;  
                 *dest++='\n';   
   
                 if(&eol[1]<eob && eol[1]=='\n') { // \r, \n = DOS  
                         bol=eol+2;  
                         length--;   
                 } else // \r, not \n = Macintosh  
                         bol=eol+1;  
         }  
         // last piece without \r  
         if(dest!=bol)  
                 memcpy(dest, bol, eob-bol);   
         str[length]=0; // terminating  
 }  }
   
 char* file_read_text(Request_charsets& charsets,   char* file_load_text(Request& r, const String& file_spec, bool fail_on_read_problem, HashStringValue* params, bool transcode_result) {
                         const String& file_spec,           File_read_result file=file_load(r, file_spec, true, params, fail_on_read_problem, 0, 0, 0, transcode_result);
                         bool fail_on_read_problem,  
                         HashStringValue* params,  
                         bool transcode_result) {  
         File_read_result file=  
                 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;
 }  }
   
 /// these options were handled but not checked elsewhere, now check them  /// these options were handled but not checked elsewhere, now check them
 int pa_get_valid_file_options_count(HashStringValue& options)  int pa_get_valid_file_options_count(HashStringValue& options) {
 {  
         int result=0;          int result=0;
         if(options.get(PA_SQL_LIMIT_NAME))          if(options.get(PA_SQL_LIMIT_NAME))
                 result++;                  result++;
Line 124  struct File_read_action_info { Line 113  struct File_read_action_info {
         char* buf; size_t offset; size_t count;          char* buf; size_t offset; size_t count;
 };   }; 
 #endif  #endif
 static void file_read_action(  
                                 struct stat& finfo,   static void file_read_action(struct stat& finfo, int f, const String& file_spec, const char* /*fname*/, bool as_text, void *context) {
                                 int f,   
                                 const String& file_spec, const char* /*fname*/, bool as_text,   
                                 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); 
         size_t to_read_size=info.count;          size_t to_read_size=info.count;
         if(!to_read_size)          if(!to_read_size)
                 to_read_size=(size_t)finfo.st_size;                  to_read_size=(size_t)finfo.st_size;
         assert( !(info.buf && as_text) );          assert( !(info.buf && as_text) );
         if(to_read_size) {           if(to_read_size) {
                 if(info.offset)                  if(info.offset)
                         lseek(f, info.offset, SEEK_SET);                          lseek(f, info.offset, SEEK_SET);
                 *info.data=info.buf                  *info.data=info.buf ? info.buf : (char *)pa_malloc_atomic(to_read_size+1);
                         ? info.buf                  ssize_t result=read(f, *info.data, to_read_size);
                         : new(PointerFreeGC) char[to_read_size+(as_text?1:0)];                   if(result<0)
                 *info.data_size=(size_t)read(f, *info.data, to_read_size);                           throw Exception("file.read", &file_spec, "read failed: %s (%d)", strerror(errno), errno);
                   *info.data_size=result;
                 if(ssize_t(*info.data_size)<0 || *info.data_size>to_read_size)  
                         throw Exception(0,   
                                 &file_spec,   
                                 "read failed: actually read %u bytes count not in [0..%u] valid range",   
                                         *info.data_size, to_read_size);   
         } else { // empty file          } else { // empty file
                 // for both, text and binary: for text we need that terminator, for binary we need nonzero pointer to be able to save such files                  // 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=(char *)pa_malloc_atomic(1);
                 *(char*)(*info.data)=0;                  *(char*)(*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) {                          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};
         if(file_spec.starts_with("http://")) {          if(params){
                 if(offset || count)                  int valid_options=pa_get_valid_file_options_count(*params);
                         throw Exception(PARSER_RUNTIME,                  if(valid_options!=params->count())
                                 0,                          throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                                 "offset and load options are not supported for HTTP:// file load");          }
   
                 // fail on read problem          File_read_action_info info={&result.str, &result.length, buf, offset, count}; 
                 File_read_http_result http=pa_internal_file_read_http(charsets, file_spec, as_text, params, transcode_text_result);  
                 result.success=true;  
                 result.str=http.str;  
                 result.length=http.length;  
                 result.headers=http.headers;   
         } else {  
                 if(params){  
                         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,          result.success=file_read_action_under_lock(file_spec, 
                         buf, offset, count};                   "read", file_read_action, &info, 
                 result.success=file_read_action_under_lock(file_spec,                   as_text, fail_on_read_problem); 
                         "read", file_read_action, &info,   
                         as_text, fail_on_read_problem);   
   
                 if(as_text && result.success){          if(as_text){
                   if(result.success){
                           Charset* asked_charset=0;
                         if(result.length>=3 && strncmp(result.str, "\xEF\xBB\xBF", 3)==0){                          if(result.length>=3 && strncmp(result.str, "\xEF\xBB\xBF", 3)==0){
                                 // skip UTF-8 signature: EF BB BF  (BOM code)                                  // skip UTF-8 signature (BOM code)
                                 result.str+=3;                                  result.str+=3;
                                 result.length-=3;                                  result.length-=3;
                                   asked_charset=&UTF8_charset;
                         }                          }
                                                   
                         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(params)
                                 if( Value* vcharset_name=params->get(PA_CHARSET_NAME) ){                                  if(Value* vcharset_name=params->get(PA_CHARSET_NAME))
                                         Charset asked_charset=::charsets.get(vcharset_name->as_string().                                          asked_charset=&::charsets.get(vcharset_name->as_string().change_case(charsets.source(), String::CC_UPPER));
                                                 change_case(charsets.source(), String::CC_UPPER));  
                           if(result.length && transcode_text_result && asked_charset){ // length must be checked because transcode returns CONST string in case length==0, which contradicts hacking few lines below
                                         String::C body=String::C(result.str, result.length);                                  String::C body=String::C(result.str, result.length);
                                         body=Charset::transcode(body, asked_charset, charsets.source());                                  body=Charset::transcode(body, *asked_charset, charsets.source());
   
                                         result.str=const_cast<char*>(body.str); // hacking a little                                  result.str=const_cast<char*>(body.str); // hacking a little
                                         result.length=body.length;                                  result.length=body.length;
                                 }  
                         }                          }
                 }                  }
                   if(result.length)
                           fix_line_breaks(result.str, result.length);
         }          }
           
         if(as_text && 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,  
Line 228  void check_safe_mode(struct stat finfo, Line 222  void check_safe_mode(struct stat finfo,
                         "fuid(%d)!=euid(%d) or fgid(%d)!=egid(%d)",                            "fuid(%d)!=euid(%d) or fgid(%d)!=egid(%d)",  
                                 fname,                                   fname, 
                                 finfo.st_uid, geteuid(),                                   finfo.st_uid, geteuid(), 
                                 finfo.st_gid, getegid());                                   finfo.st_gid, getegid());
 }   }
 #endif   #else
   void check_safe_mode(struct stat, const String&, const char*) {
   }
   #endif
   
   
   
 bool file_read_action_under_lock(const String& file_spec,   bool file_read_action_under_lock(const String& file_spec, 
                                 const char* action_name, File_read_action action, void *context,                                   const char* action_name, File_read_action action, void *context, 
                                 bool as_text,                                   bool as_text, 
                                 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.taint_cstr(String::L_FILE_SPEC); 
         int f;          int f;
   
         // first open, next stat:          // first open, next stat:
Line 257  bool file_read_action_under_lock(const S Line 256  bool file_read_action_under_lock(const S
                                                         strerror(errno), errno, fname);                                                          strerror(errno), errno, fname);
   
                         struct stat finfo;                          struct stat finfo;
                         if(stat(fname, &finfo)!=0)                          if(fstat(f, &finfo)!=0)
                                 throw Exception("file.missing", // hardly possible: we just opened it OK                                  throw Exception("file.missing", // hardly possible: we just opened it OK
                                         &file_spec,                                           &file_spec, 
                                         "stat failed: %s (%d), actual filename '%s'",                                           "stat failed: %s (%d), actual filename '%s'", 
                                                 strerror(errno), errno, fname);                                                  strerror(errno), errno, fname);
   
 #ifdef PA_SAFE_MODE  
                         check_safe_mode(finfo, file_spec, fname);                          check_safe_mode(finfo, file_spec, fname);
 #endif  
   
                         action(finfo, f, file_spec, fname, as_text, context);                           action(finfo, f, file_spec, fname, as_text, context); 
                 } catch(...) {                  } catch(...) {
Line 279  bool file_read_action_under_lock(const S Line 276  bool file_read_action_under_lock(const S
                 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 || errno==ENOTDIR || errno==ENODEV)?"file.missing":0, 
                                 &file_spec,                                   &file_spec, 
                                 "%s failed: %s (%d), actual filename '%s'",                                   "%s failed: %s (%d), actual filename '%s'", 
                                         action_name, strerror(errno), errno, fname);                                          action_name, strerror(errno), errno, fname);
Line 291  void create_dir_for_file(const String& f Line 289  void create_dir_for_file(const String& f
         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);                   pa_mkdir(file_spec.mid(0, pos_before).taint_cstr(String::L_FILE_SPEC), 0775); 
                 pos_after=pos_before+1;                  pos_after=pos_before+1;
         }          }
 }  }
Line 305  bool file_write_action_under_lock( Line 303  bool file_write_action_under_lock(
                                 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.taint_cstr(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 326  bool file_write_action_under_lock( Line 324  bool file_write_action_under_lock(
                 }                  }
   
                 try {                  try {
                         action(f, context);   #if (defined(HAVE_FCHMOD) && defined(PA_SAFE_MODE))
                           struct stat finfo;
                           if(fstat(f, &finfo)==0 && finfo.st_mode & 0111)
                                   fchmod(f, finfo.st_mode & 0666/*clear executable bits*/); // backward: ignore errors if any
   #endif
                           action(f, context);
                 } catch(...) {                  } catch(...) {
 #ifdef HAVE_FTRUNCATE  #ifdef HAVE_FTRUNCATE
                         if(!do_append)                          if(!do_append)
Line 352  bool file_write_action_under_lock( Line 355  bool file_write_action_under_lock(
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct File_write_action_info {  struct File_write_action_info {
         const char* str; size_t length;          const char* str;
           size_t length;
 };   }; 
 #endif  #endif
   
 static void file_write_action(int f, void *context) {  static void file_write_action(int f, void *context) {
         File_write_action_info& info=*static_cast<File_write_action_info *>(context);           File_write_action_info& info=*static_cast<File_write_action_info *>(context); 
         if(info.length) {          if(info.length) {
                 int written=write(f, info.str, info.length);                   ssize_t written=write(f, info.str, info.length); 
                 if(written<0)                  if(written<0)
                         throw Exception(0,                           throw Exception("file.write", 0, "write failed: %s (%d)",  strerror(errno), errno); 
                                 0,                   if((size_t)written!=info.length)
                                 "write failed: %s (%d)",  strerror(errno), errno);                           throw Exception("file.write", 0, "write failed: %u of %u bytes written", written, info.length);
         }          }
 }  }
   
 void file_write(  void file_write(
                                 const String& file_spec,                                   Request_charsets& charsets,
                                 const char* data, size_t size,                                   const String& file_spec,
                                   const char* data,
                                   size_t size, 
                                 bool as_text,                                   bool as_text, 
                                 bool do_append) {                                  bool do_append,
                                   Charset* asked_charset) {
   
           if(as_text && asked_charset){
                   String::C body=String::C(data, size);
                   body=Charset::transcode(body, charsets.source(), *asked_charset);
                   data=body.str;
                   size=body.length;
           };
   
         File_write_action_info info={data, size};           File_write_action_info info={data, size}; 
   
         file_write_action_under_lock(          file_write_action_under_lock(
Line 381  void file_write( Line 398  void file_write(
                 do_append);                   do_append); 
 }  }
   
   static size_t get_dir(char* fname, size_t helper_length){
           bool dir=false;
           size_t pos=0;
           for(pos=helper_length; pos; pos--){
                   char c=fname[pos-1];
                   if(c=='/' || c=='\\'){
                           fname[pos-1]=0;
                           dir=true;
                   } else if(dir) break;
           }
           return pos;
   }
   
   static bool entry_readable(char* fname, bool need_dir) {
           if(need_dir){
                   size_t size=strlen(fname);
                   while(size) {
                           char c=fname[size-1];
                           if(c=='/' || c=='\\')
                                   fname[--size]=0;
                           else
                                   break;
                   }
           }
   
           struct stat finfo;
           if(access(fname, R_OK)==0 && entry_exists(fname, &finfo)) {
                   bool is_dir=(finfo.st_mode&S_IFDIR) != 0;
                   return is_dir==need_dir;
           }
           return false;
   }
   
   static bool entry_readable(const String& file_spec, bool need_dir) {
           return entry_readable(file_spec.taint_cstrm(String::L_FILE_SPEC), need_dir);
   }
   
 // throws nothing! [this is required in file_move & file_delete]  // throws nothing! [this is required in file_move & file_delete]
 static void rmdir(const String& file_spec, size_t pos_after) {  static void rmdir(const String& file_spec, size_t pos_after) {
         size_t pos_before;          char* dir_spec=file_spec.taint_cstrm(String::L_FILE_SPEC);
         if((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND)          size_t length=strlen(dir_spec);
                 rmdir(file_spec, pos_before+1);           while( (length=get_dir(dir_spec, length)) && (length > pos_after) ){
           #ifdef _MSC_VER
         rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::L_FILE_SPEC));                   if(!entry_readable(dir_spec, true))
                           break;
                   DWORD attrs=GetFileAttributes(dir_spec);
                   if(
                           (attrs==INVALID_FILE_ATTRIBUTES)
                           || !(attrs & FILE_ATTRIBUTE_DIRECTORY)
                           || (attrs & FILE_ATTRIBUTE_REPARSE_POINT)
                   )
                           break;
   #endif
                   if( rmdir(dir_spec) )
                           break;
           };
 }  }
 bool file_delete(const String& file_spec, bool fail_on_problem) {  
         const char* fname=file_spec.cstr(String::L_FILE_SPEC);   bool file_delete(const String& file_spec, bool fail_on_problem, bool keep_empty_dirs) {
         if(unlink(fname)!=0)          const char* fname=file_spec.taint_cstr(String::L_FILE_SPEC); 
           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, 
                                 &file_spec,                                   &file_spec, 
Line 399  bool file_delete(const String& file_spec Line 466  bool file_delete(const String& file_spec
                                         strerror(errno), errno, fname);                                          strerror(errno), errno, fname);
                 else                  else
                         return false;                          return false;
           }
   
           if(!keep_empty_dirs)
                   rmdir(file_spec, 1); 
   
         rmdir(file_spec, 1);   
         return true;          return true;
 }  }
 void file_move(const String& old_spec, const String& new_spec) {  
         const char* old_spec_cstr=old_spec.cstr(String::L_FILE_SPEC);   void file_move(const String& old_spec, const String& new_spec, bool keep_empty_dirs) {
         const char* new_spec_cstr=new_spec.cstr(String::L_FILE_SPEC);           const char* old_spec_cstr=old_spec.taint_cstr(String::L_FILE_SPEC); 
           const char* new_spec_cstr=new_spec.taint_cstr(String::L_FILE_SPEC); 
                   
         create_dir_for_file(new_spec);           create_dir_for_file(new_spec); 
   
Line 415  void file_move(const String& old_spec, c Line 486  void file_move(const String& old_spec, c
                         "rename failed: %s (%d), actual filename '%s' to '%s'",                           "rename failed: %s (%d), actual filename '%s' to '%s'", 
                                 strerror(errno), errno, old_spec_cstr, new_spec_cstr);                                  strerror(errno), errno, old_spec_cstr, new_spec_cstr);
   
         rmdir(old_spec, 1);           if(!keep_empty_dirs)
                   rmdir(old_spec, 1); 
 }  }
   
   
Line 428  bool entry_exists(const char* fname, str Line 500  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.taint_cstr(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) {  
         char* fname=file_spec.cstrm(String::L_FILE_SPEC);   
         if(need_dir) {  
                 size_t size=strlen(fname);   
                 while(size) {  
                         char c=fname[size-1];   
                         if(c=='/' || c=='\\')  
                                 fname[--size]=0;  
                         else  
                                 break;  
                 }  
         }  
         struct stat finfo;  
         if(access(fname, R_OK)==0 && entry_exists(fname, &finfo)) {  
                 bool is_dir=(finfo.st_mode&S_IFDIR) != 0;  
                 return is_dir==need_dir;  
         }  
         return false;  
 }  
 bool file_exist(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_exists(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_exist(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 << "/";           if(path.last_char() != '/')
                   result << "/"; 
         result << name;          result << name;
         return file_exist(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.taint_cstr(String::L_FILE_SPEC), X_OK)==0;
 }  }
   
 bool file_stat(const String& file_spec,   bool file_stat(const String& file_spec, 
Line 473  bool file_stat(const String& file_spec, Line 530  bool file_stat(const String& file_spec,
                         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.taint_cstr(String::L_FILE_SPEC); 
         struct stat finfo;          struct stat finfo;
         if(stat(fname, &finfo)!=0)          if(stat(fname, &finfo)!=0) {
                 if(fail_on_read_problem)                  if(fail_on_read_problem)
                         throw Exception("file.missing",                           throw Exception("file.missing", 
                                 &file_spec,                                   &file_spec, 
Line 483  bool file_stat(const String& file_spec, Line 540  bool file_stat(const String& file_spec,
                                         strerror(errno), errno, fname);                                          strerror(errno), errno, fname);
                 else                  else
                         return false;                          return false;
           }
         rsize=finfo.st_size;          rsize=finfo.st_size;
         ratime=finfo.st_atime;          ratime=finfo.st_atime;
         rmtime=finfo.st_mtime;          rmtime=finfo.st_mtime;
Line 490  bool file_stat(const String& file_spec, Line 548  bool file_stat(const String& file_spec,
         return true;          return true;
 }  }
   
   /** 
           String related functions
   */
   
   bool capitalized(const char* s){
           bool upper=true;
           for(const char* c=s; *c; c++){
                   if(*c != (upper ? toupper((unsigned char)*c) : tolower((unsigned char)*c)))
                           return false;
                   upper=strchr("-_ ", *c) != 0;
           }
           return true;
   }
   
   const char* capitalize(const char* s){
           if(!s || capitalized(s))
                   return s;
   
           char* result=pa_strdup(s);
           if(result){
                   bool upper=true;
                   for(char* c=result; *c; c++){
                           *c=upper ? (char)toupper((unsigned char)*c) : (char)tolower((unsigned char)*c);
                           upper=strchr("-_ ", *c) != 0;
                   }
           }
           return (const char*)result;
   }
   
   void fix_line_breaks(char *str, size_t& length) {
           //_asm int 3;
           const char* const eob=str+length;
           char* dest=str;
           // fix DOS: \r\n -> \n
           // fix Macintosh: \r -> \n
           char* bol=str;
           while(char* eol=(char*)memchr(bol, '\r', eob -bol)) {
                   size_t len=eol-bol;
                   if(dest!=bol)
                           memmove(dest, bol, len); 
                   dest+=len;
                   *dest++='\n'; 
   
                   if(&eol[1]<eob && eol[1]=='\n') { // \r, \n = DOS
                           bol=eol+2;
                           length--; 
                   } else // \r, not \n = Macintosh
                           bol=eol+1;
           }
           // last piece without \r
           if(dest!=bol)
                   memmove(dest, bol, eob-bol); 
           str[length]=0; // terminating
   }
   
   /**
           scans for @a delim[default \n] in @a *row_ref, 
           @return piece of line before it or end of string, if no @a delim found
           assigns @a *row_ref to point right after delimiter if there were one
           or to zero if no @a delim were found.
   */
   
 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) {
Line 541  enum FormatType { Line 661  enum FormatType {
         FormatUInt,          FormatUInt,
         FormatDouble          FormatDouble
 };  };
 FormatType format_type(char* fmt){  FormatType format_type(const char* fmt){
         enum FormatState {          enum FormatState {
                 Percent,                   Percent, 
                 Flags,                   Flags, 
Line 552  FormatType format_type(char* fmt){ Line 672  FormatType format_type(char* fmt){
   
         FormatType result=FormatInvalid;          FormatType result=FormatInvalid;
   
         char* pos=fmt;          const char* pos=fmt;
         while(char c=*(pos++)){          while(char c=*(pos++)){
                 switch(state){                  switch(state){
                         case Percent:                          case Percent:
Line 596  FormatType format_type(char* fmt){ Line 716  FormatType format_type(char* fmt){
 }  }
   
   
 const char* format(double value, char* fmt) {  const char* format(double value, const char* fmt) {
         char local_buf[MAX_NUMBER];          char local_buf[MAX_NUMBER];
         int size=-1;          int size=-1;
   
Line 653  enum EscapeState { Line 773  enum EscapeState {
 };  };
   
 // @todo prescan for reduce required size (unescaped sting in 1 byte charset requires less memory usually)  // @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* unescape_chars(const char* cp, int len, Charset* charset, bool js){
         char* s=new(PointerFreeGC) char[len+1]; // must be enough (%uXXXX==6 bytes, max utf-8 char length==6 bytes)          char* s=new(PointerFreeGC) char[len+1]; // must be enough (%uXXXX==6 bytes, max utf-8 char length==6 bytes)
         char* dst=s;          char* dst=s;
         EscapeState escapeState=EscapeRest;          EscapeState escapeState=EscapeRest;
Line 662  char* unescape_chars(const char* cp, int Line 782  char* unescape_chars(const char* cp, int
         short int jsCnt=0;          short int jsCnt=0;
         while(srcPos<len){          while(srcPos<len){
                 uchar c=(uchar)cp[srcPos];                   uchar c=(uchar)cp[srcPos]; 
                 if(c=='%'){                  if(c=='%' || (c=='\\' && js)){
                         escapeState=EscapeFirst;                          escapeState=EscapeFirst;
                 } else {                  } else {
                         switch(escapeState) {                          switch(escapeState) {
                                 case EscapeRest:                                  case EscapeRest:
                                         if(!ignore_plus && c=='+'){                                          if(!js && c=='+'){
                                                 *dst++=' ';                                                  *dst++=' ';
                                         } else {                                          } else {
                                                 *dst++=c;                                                  *dst++=c;
Line 719  char* unescape_chars(const char* cp, int Line 839  char* unescape_chars(const char* cp, int
         return s;          return s;
 }  }
   
   char *search_stop(char*& current, char cstop_at) {
           // sanity check
           if(!current)
                   return 0;
   
           // skip leading WS
           while(*current==' ' || *current=='\t')
                   current++;
           if(!*current)
                   return current=0;
   
           char *result=current;
           if(char *pstop_at=strchr(current, cstop_at)) {
                   *pstop_at=0;
                   current=pstop_at+1;
           } else
                   current=0;
           return result;
   }
   
 #ifdef WIN32  #ifdef WIN32
 void back_slashes_to_slashes(char* s) {  void back_slashes_to_slashes(char* s) {
         if(s)          if(s)
Line 726  void back_slashes_to_slashes(char* s) { Line 866  void back_slashes_to_slashes(char* s) {
                         if(*s=='\\')                          if(*s=='\\')
                                 *s='/';                                   *s='/'; 
 }  }
 /*  
 void slashes_to_back_slashes(char* s) {  
         if(s)  
                 for(; *s; s++)  
                         if(*s=='/')  
                                 *s='\\';   
 }  
 */  
 #endif  #endif
   
 bool StrStartFromNC(const char* str, const char* substr, bool equal){  
         while(true) {  
                 if(!(*substr)){  
                         if(!(*str))  
                                 return true;  
                         else  
                                 return !equal;  
                 }  
                 if(!(*str))  
                         return false;  
                 if(isalpha((unsigned char)*str)) {  
                         if(tolower((unsigned char)*str)!=tolower((unsigned char)*substr))  
                                 return false;  
                 } else if((*str) != (*substr))  
                         return false;  
                 str++;   
                 substr++;   
         }  
 }  
   
 size_t strpos(const char *str, const char *substr) {  size_t strpos(const char *str, const char *substr) {
         const char *p = strstr(str, substr);          const char *p = strstr(str, substr);
         return (p==0)?STRING_NOT_FOUND:p-str;          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){  
         if(content_type){  
                 size_t len=strlen(content_type);  
                 char* CONTENT_TYPE=new(PointerFreeGC) char[len+1];  
                 memcpy(CONTENT_TYPE, content_type, len);  
                 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, ';');  
   
                         size_t len;  
                         if(end){  
                                 *end=0; // terminator  
                                 len=end-begin;  
                         } else {  
                                 len=strlen(begin);  
                         }  
   
                         String::Body NAME=String::Body(begin, len);  
                         return &charsets.get(NAME);  
                 }  
         }  
         return 0;  
 }  
   
 Charset* detect_charset(Charset& source_charset, const String& content_type){  
         const String& CONTENT_TYPE=content_type.change_case(source_charset, String::CC_UPPER);  
         size_t begin=CONTENT_TYPE.pos("CHARSET=");  
         if(begin!=STRING_NOT_FOUND) {  
                 begin+=8; // skip "CHARSET="  
                 size_t end=STRING_NOT_FOUND;  
   
                 if(CONTENT_TYPE.pos('"', begin)==begin){  
                         begin++;  
                         end=CONTENT_TYPE.pos('"', begin);  
                 } else if(CONTENT_TYPE.pos('\'', begin)==begin){  
                         begin++;  
                         end=CONTENT_TYPE.pos('\'', begin);  
                 }  
   
                 if(end==STRING_NOT_FOUND)  
                         end=CONTENT_TYPE.pos(';', begin);  
   
                 if(end==STRING_NOT_FOUND)  
                         end=CONTENT_TYPE.length();  
   
                 const String::Body NAME=CONTENT_TYPE.mid(begin, end);  
                 return &charsets.get(NAME);  
         }  
         return 0;  
 }  
   
   
 static bool isLeap(int year) {  
         return !(  
                                 (year % 4) || ((year % 400) && !(year % 100))  
                         );   
 }  
   
 int getMonthDays(int year, int month) {  
         static int monthDays[]={  
                 31,   
                 28,   
                 31,   
                 30,   
                 31,   
                 30,   
                 31,   
                 31,   
                 30,   
                 31,   
                 30,   
                 31  
         };   
         return (month == 1 /* january -- 0 */ && isLeap(year)) ? 29 : monthDays[month];   
 }  
   
 int remove_crlf(char* start, char* end) {  int remove_crlf(char* start, char* end) {
         char* from=start;          char* from=start;
         char* to=start;          char* to=start;
Line 877  int remove_crlf(char* start, char* end) Line 900  int remove_crlf(char* start, char* end)
         return to-start;          return to-start;
 }  }
   
   const char* hex_digits="0123456789ABCDEF";
   
   const char* hex_string(unsigned char* bytes, size_t size, bool upcase) {
           char *bytes_hex=new(PointerFreeGC) char [size*2/*byte->hh*/+1/*for zero-teminator*/];
           unsigned char *src=bytes;
           unsigned char *end=bytes+size;
           char *dest=bytes_hex;
   
           const char *hex=upcase? hex_digits : "0123456789abcdef";
   
           for(; src<end; src++) {
                   *dest++=hex[*src/0x10];
                   *dest++=hex[*src%0x10];
           }
           *dest=0;
   
           return bytes_hex;
   }
   
 /// must be last in this file  /// must be last in this file
 #undef vsnprintf  #undef vsnprintf
Line 893  int __vsnprintf(char* b, size_t s, const Line 934  int __vsnprintf(char* b, size_t s, const
         if((ssize_t)s<0)          if((ssize_t)s<0)
                 return 0;                  return 0;
   
 #if _MSC_VER  #ifdef _MSC_VER
         /*          /*
         win32:           win32: 
         mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm          mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm
Line 927  int __vsnprintf(char* b, size_t s, const Line 968  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 955  int __snprintf(char* b, size_t s, const Line 996  int __snprintf(char* b, size_t s, const
  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.   *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
  *   *
  */   */
 static char *base64_alphabet =  static const char *base64_alphabet =
         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";          "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
   
 /**  /**
Line 973  static char *base64_alphabet = Line 1014  static char *base64_alphabet =
  *   *
  * Returns the number of bytes encoded.   * Returns the number of bytes encoded.
  **/   **/
   
   #define BASE64_GROUPS_IN_LINE 19
   
 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)
 {  {
Line 1010  g_mime_utils_base64_encode_step (const u Line 1054  g_mime_utils_base64_encode_step (const u
                         *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];                          *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
                         *outptr++ = base64_alphabet [c3 & 0x3f];                          *outptr++ = base64_alphabet [c3 & 0x3f];
                         /* this is a bit ugly ... */                          /* this is a bit ugly ... */
                         if ((++already) >= 19) {                          if ((++already) >= BASE64_GROUPS_IN_LINE) {
                                 *outptr++ = '\n';                                  *outptr++ = '\n';
                                 already = 0;                                  already = 0;
                         }                          }
Line 1094  g_mime_utils_base64_encode_close (const Line 1138  g_mime_utils_base64_encode_close (const
 }  }
   
 static unsigned char gmime_base64_rank[256] = {  static unsigned char gmime_base64_rank[256] = {
           255,255,255,255,255,255,255,255,255,254,254,255,255,254,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,          254,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
         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,           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,          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,           15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
Line 1119  static unsigned char gmime_base64_rank[2 Line 1163  static unsigned char gmime_base64_rank[2
  * @out: output stream   * @out: output stream
  * @state: holds the number of bits that are stored in @save   * @state: holds the number of bits that are stored in @save
  * @save: leftover bits that have not yet been decoded   * @save: leftover bits that have not yet been decoded
    * @strict: only base64 and whitespace chars are allowed
  *   *
  * Decodes a chunk of base64 encoded data.   * Decodes a chunk of base64 encoded data.
  *   *
  * Returns the number of bytes decoded (which have been dumped in @out).   * Returns the number of bytes decoded (which have been dumped in @out).
  **/   **/
 size_t  size_t
 g_mime_utils_base64_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)  g_mime_utils_base64_decode_step(const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save, bool strict=false)
 {  {
         const unsigned char *inptr;          const unsigned char *inptr;
         unsigned char *outptr;          unsigned char *outptr;
Line 1143  g_mime_utils_base64_decode_step (const u Line 1188  g_mime_utils_base64_decode_step (const u
         inptr = in;          inptr = in;
         while (inptr < inend) {          while (inptr < inend) {
                 c = gmime_base64_rank[*inptr++];                  c = gmime_base64_rank[*inptr++];
                 if (c != 0xff) {                  switch(c) {
                         saved = (saved << 6) | c;                          case 0xff: // non-base64 and non-whitespace chars. not allowed in strict mode
                         i++;                                  if(strict)
                         if (i == 4) {                                          throw Exception(BASE64_FORMAT, 0, "Invalid base64 char on position %d is detected", inptr-in-1);
                                 *outptr++ = (unsigned char)(saved >> 16);                          case 0xfe: // whitespace chars 0x09, 0x0A, 0x0D, 0x20 are allowed in any mode
                                 *outptr++ = (unsigned char)(saved >> 8);                                  break;
                                 *outptr++ = (unsigned char)(saved);                          default:
                                 i = 0;                                  saved = (saved << 6) | c;
                         }                                  i++;
                                   if (i == 4) {
                                           *outptr++ = (unsigned char)(saved >> 16);
                                           *outptr++ = (unsigned char)(saved >> 8);
                                           *outptr++ = (unsigned char)(saved);
                                           i = 0;
                                   }
                 }                  }
         }          }
                   
Line 1163  g_mime_utils_base64_decode_step (const u Line 1214  g_mime_utils_base64_decode_step (const u
         i = 2;          i = 2;
         while (inptr > in && i) {          while (inptr > in && i) {
                 inptr--;                  inptr--;
                 if (gmime_base64_rank[*inptr] != 0xff) {                  if (gmime_base64_rank[*inptr] <= 0xfe) {
                         if (*inptr == '=' && outptr > out)                          if (*inptr == '=' && outptr > out)
                                 outptr--;                                  outptr--;
                         i--;                          i--;
Line 1175  g_mime_utils_base64_decode_step (const u Line 1226  g_mime_utils_base64_decode_step (const u
 }  }
   
   
 char* pa_base64_encode(const char *in, size_t in_size)  char* pa_base64_encode(const char *in, size_t in_size){
 {          size_t new_size = ((in_size / 3 + 1) * 4);
         /* wont go to more than 2x size (overly conservative) */          new_size += new_size / (BASE64_GROUPS_IN_LINE * 4)/*new lines*/ + 1/*zero terminator*/;
         char* result=new(PointerFreeGC) char[in_size * 2 + 6];          char* result = new(PointerFreeGC) char[new_size];
         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, in_size,                   g_mime_utils_base64_encode_close ((const unsigned char*)in, in_size, (unsigned char*)result, &state, &save);
                 (unsigned char*)result, &state, &save);  
         assert(filled <= in_size * 2 + 6);  
   
         return result;  
 }  
   
   
 char* pa_base64_encode(const String& file_spec)          //throw Exception(PARSER_RUNTIME, 0, "%d %d %d", in_size, new_size, filled);
 {          assert(filled <= new_size);
         unsigned char* base64=0;  
         File_base64_action_info info={&base64};   
   
         file_read_action_under_lock(file_spec,           return result;
                 "pa_base64_encode", file_base64_file_action, &info);  
   
         return (char*)base64;   
 }  }
   
   struct File_base64_action_info {
           unsigned char** base64;
   }; 
   
 static void file_base64_file_action(  static void file_base64_file_action(
                                 struct stat& finfo,                                   struct stat& finfo, 
Line 1229  static void file_base64_file_action( Line 1272  static void file_base64_file_action(
         }          }
 }  }
   
 void pa_base64_decode(const char *in, size_t in_size, char*& result, size_t& result_size)  char* pa_base64_encode(const String& file_spec){
 {          unsigned char* base64=0;
         /* wont go to more than had (overly conservative) */          File_base64_action_info info={&base64}; 
         result=new(PointerFreeGC) char[in_size+1/*terminator*/];  
           file_read_action_under_lock(file_spec, 
                   "pa_base64_encode", file_base64_file_action, &info);
   
           return (char*)base64; 
   }
   
   void pa_base64_decode(const char *in, size_t in_size, char*& result, size_t& result_size, bool strict) {
           // every 4 base64 bytes are converted into 3 normal bytes
           // not full set (tail) of 4-bytes set is ignored
           size_t new_size=in_size/4*3;
           result=new(PointerFreeGC) char[new_size+1/*terminator*/];
   
         int state=0;          int state=0;
         int save=0;          int save=0;
         result_size=          result_size=
                 g_mime_utils_base64_decode_step ((const unsigned char*)in, in_size,                   g_mime_utils_base64_decode_step ((const unsigned char*)in, in_size,
                 (unsigned char*)result, &state, &save);                  (unsigned char*)result, &state, &save, strict);
         assert(result_size <= in_size);          assert(result_size <= new_size);
         result[result_size]=0; // for text files          result[result_size]=0; // for text files
   
           if(strict && state!=0)
                   throw Exception(BASE64_FORMAT, 0, "Unexpected end of chars");
 }  }
   
   
 int file_block_read(const int f, unsigned char* buffer, const size_t size){  int file_block_read(const int f, unsigned char* buffer, const size_t size){
         int nCount = read(f, buffer, size);          int nCount = read(f, buffer, size);
         if (nCount < 0)          if (nCount < 0)
                 throw Exception(0,                   throw Exception("file.read", 
                         0,                           0, 
                         "read failed: %s (%d)",  strerror(errno), errno);                           "read failed: %s (%d)",  strerror(errno), errno); 
         return nCount;          return nCount;
 }  }
   
 const unsigned long pa_crc32(const char *in, size_t in_size)  static unsigned long crc32Table[256];
   static void InitCrc32Table()
 {  {
         unsigned long crc32=0xFFFFFFFF;          if(crc32Table[1] == 0){
                   // This is the official polynomial used by CRC32 in PKZip.
                 InitCrc32Table();                  // Often times the polynomial shown reversed as 0x04C11DB7.
                 for(size_t i = 0; i < in_size; i++) CalcCrc32(in[i], crc32);                  static const unsigned long dwPolynomial = 0xEDB88320;
   
         return ~crc32;                   for(int i = 0; i < 256; i++)
                   {
                           unsigned long dwCrc = i;
                           for(int j = 8; j > 0; j--)
                           {
                                   if(dwCrc & 1)
                                           dwCrc = (dwCrc >> 1) ^ dwPolynomial;
                                   else
                                           dwCrc >>= 1;
                           }
                           crc32Table[i] = dwCrc;
                   }
           }
 }  }
   
 const unsigned long pa_crc32(const String& file_spec)  inline void CalcCrc32(const unsigned char byte, unsigned long &crc32)
 {  {
           crc32 = ((crc32) >> 8) ^ crc32Table[(byte) ^ ((crc32) & 0x000000FF)];
   }
   
   
   const unsigned long pa_crc32(const char *in, size_t in_size){
         unsigned long crc32=0xFFFFFFFF;          unsigned long crc32=0xFFFFFFFF;
         file_read_action_under_lock(file_spec, "crc32", file_crc32_file_action, &crc32);  
           InitCrc32Table();
           for(size_t i = 0; i<in_size; i++)
                   CalcCrc32(in[i], crc32);
   
         return ~crc32;           return ~crc32; 
 }  }
   
Line 1273  static void file_crc32_file_action( Line 1353  static void file_crc32_file_action(
                                 struct stat& finfo,                                   struct stat& finfo, 
                                 int f,                                   int f, 
                                 const String&, const char* /*fname*/, bool,                                   const String&, const char* /*fname*/, bool, 
                                 void *context)                                  void *context) {
 {  
         unsigned long& crc32=*static_cast<unsigned long *>(context);          unsigned long& crc32=*static_cast<unsigned long *>(context);
         if(finfo.st_size) {          if(finfo.st_size) {
                 InitCrc32Table();                  InitCrc32Table();
Line 1287  static void file_crc32_file_action( Line 1366  static void file_crc32_file_action(
         }          }
 }  }
   
   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; 
   }
   
   // 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){
           if(content_type){
                   char* CONTENT_TYPE=pa_strdup(content_type);
   
                   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 is_latin(const char *in){
           for(; *in; in++){
                   if ((unsigned char)(*in) > 0x7F)
                           return false;
           }
           return true;
   }
   
   #define MAX_IDNA_LENGTH 256
   
   const char *pa_idna_encode(const char *in, Charset &source_charset){
           if(!in || is_latin(in))
                   return in;
   
           uint32_t utf32[MAX_IDNA_LENGTH];
           uint32_t *utf32_end=utf32;
   
           String::C sIn(in,strlen(in));
   
           if(!source_charset.isUTF8())
                   sIn=Charset::transcode(sIn, source_charset, UTF8_charset);
   
           int status=pa_convertUTF8toUTF32((const UTF8**)&sIn.str, (const UTF8*)(sIn.str+sIn.length), &utf32_end, utf32+MAX_IDNA_LENGTH-1, strictConversion);
           if(status != conversionOK)
                   throw Exception("idna encode", new String(in), "utf conversion failed (%d)", status);
   
           *utf32_end=0;
   
           char *result = (char *)pa_malloc(MAX_IDNA_LENGTH);
           status=pa_idna_to_ascii_4z(utf32, result, MAX_IDNA_LENGTH, 0);
           if(status != IDNA_SUCCESS)
                   throw Exception("idna encode", new String(in), "encode failed: %s", pa_idna_strerror(status));
   
           return result;
   }
   
   const char *pa_idna_decode(const char *in, Charset &asked_charset){
           if(!in || !(*in))
                   return in;
   
           uint32_t utf32[MAX_IDNA_LENGTH];
           const uint32_t *utf32_start=utf32;
           uint32_t *utf32_end;
   
           int status=pa_idna_to_unicode_4z(in, utf32, MAX_IDNA_LENGTH, 0);
           if(status != IDNA_SUCCESS)
                   throw Exception("idna decode", new String(in), "decode failed: %s", pa_idna_strerror(status));
   
           for(utf32_end=utf32; *utf32_end; utf32_end++);
   
           char *result = (char *)pa_malloc(MAX_IDNA_LENGTH);
           char *result_end = result;
   
           status=pa_convertUTF32toUTF8(&utf32_start, utf32_end, (UTF8**)&result_end, (UTF8*)(result+MAX_IDNA_LENGTH-1), strictConversion);
   
           if(status != conversionOK)
                   throw Exception("idna decode", new String(in), "utf conversion failed (%d)", status);
   
           *result_end='\0';
   
           if(!asked_charset.isUTF8())
                   result = (char *)Charset::transcode(result, UTF8_charset, asked_charset).cstr();
   
           return result;
   }
   
   
   static bool isLeap(int year) {
           return !(
                                   (year % 4) || ((year % 400) && !(year % 100))
                           ); 
   }
   
   int getMonthDays(int year, int month) {
           static int monthDays[]={
                   31, 
                   28, 
                   31, 
                   30, 
                   31, 
                   30, 
                   31, 
                   31, 
                   30, 
                   31, 
                   30, 
                   31
           }; 
           return (month == 1 /* january -- 0 */ && isLeap(year)) ? 29 : monthDays[month]; 
   }
   
   String::C date_gmt_string(tm* tms) {
           /// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
           static const char month_names[12][4]={
                   "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
           static const char days[7][4]={
                   "Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
   
           char *buf=new(PointerFreeGC) char[MAX_STRING];
           return String::C(buf, 
                   snprintf(buf, MAX_STRING, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", 
                   days[tms->tm_wday],
                   tms->tm_mday,month_names[tms->tm_mon],tms->tm_year+1900,
                   tms->tm_hour,tms->tm_min,tms->tm_sec));
   }

Removed from v.1.236  
changed lines
  Added in v.1.283


E-mail: