Diff for /parser3/src/main/pa_common.C between versions 1.199 and 1.210

version 1.199, 2004/12/10 07:55:22 version 1.210, 2005/11/22 11:12:40
Line 1 Line 1
 /** @file  /** @file
         Parser: commonly functions.          Parser: commonly functions.
   
         Copyright(c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com)          Copyright(c) 2001-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)
 */  
    * BASE64 part
    *  Authors: Michael Zucchi <notzed@ximian.com>
    *           Jeffrey Stedfast <fejj@ximian.com>
    *
    *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
    *
    *  This program is free software; you can redistribute it and/or modify
    *  it under the terms of the GNU General Public License as published by
    *  the Free Software Foundation; either version 2 of the License, or
    *  (at your option) any later version.
    *
    *  This program is distributed in the hope that it will be useful,
    *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    *  GNU General Public License for more details.
    *
    *  You should have received a copy of the GNU General Public License
    *  along with this program; if not, write to the Free Software
    *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
    *
    */
   
 static const char * const IDENT_COMMON_C="$Date$";   static const char * const IDENT_COMMON_C="$Date$"; 
   
Line 131  char* file_read_text(Request_charsets& c Line 152  char* file_read_text(Request_charsets& c
         return file.success?file.str:0;          return file.success?file.str:0;
 }  }
   
   /// these options were handled but not checked elsewhere, now check them
   static int get_valid_file_options_count(HashStringValue& options)
   {
           int result=0;
           if(options.get(PA_SQL_LIMIT_NAME))
                   result++;
           if(options.get(PA_SQL_OFFSET_NAME))
                   result++;
           if(options.get(PA_COLUMN_SEPARATOR_NAME))
                   result++;
           if(options.get(PA_COLUMN_ENCLOSER_NAME))
                   result++;
           return result;
   }
   
 //http request stuff  //http request stuff
 #ifdef PA_HTTP  #ifdef PA_HTTP
   
Line 157  static bool set_addr(struct sockaddr_in Line 193  static bool set_addr(struct sockaddr_in
     return true;      return true;
 }  }
   
 static int http_read_response(char*& response, size_t& response_size, int sock, bool fail_on_status_ne_200){  size_t guess_content_length(char* buf) {
         response=(char*)pa_malloc_atomic(1/*terminator*/); // setting memory block type          char* ptr;
         response[(response_size=0)]=0;          if((ptr=strstr(buf, "Content-Length:"))) // Apache
                   goto found;
           if((ptr=strstr(buf, "content-length:"))) // Parser 3
                   goto found;
           if((ptr=strstr(buf, "Content-length:"))) // maybe 1
                   goto found;
           if((ptr=strstr(buf, "CONTENT-LENGTH:"))) // maybe 2
                   goto found;
           return 0;
   found:
           char *error_pos;
           size_t result=(size_t)strtol(ptr+15/*strlen("CONTENT-LENGTH:")*/, &error_pos, 0);
           
           const size_t reasonable_initial_max=0x400*0x400*10 /*10M*/;
           if(result>reasonable_initial_max) // sanity check
                   return reasonable_initial_max;
           return 0;//result;
   }
   
   static int http_read_response(char*& response, size_t& response_size, int sock, bool fail_on_status_ne_200) {
         int result=0;          int result=0;
         char* EOLat=0;          // fetching some to local buffer, guessing on possible content-length   
         while(true) {          response_size=0x400*20; // initial size if content-length could not be determined       
                 char buf[MAX_STRING*10];           const size_t preview_size=0x400*20;
                 ssize_t received_size=recv(sock, buf, sizeof(buf), 0);           char preview_buf[preview_size+1/*terminator*/];  // 20K buffer to preview headers
                 if(received_size<=0) {          ssize_t received_size=recv(sock, preview_buf, preview_size, 0); 
                         if(int no=pa_socks_errno())          if(received_size==0)
                                 throw Exception("http.timeout",                   goto done;
                                         0,           if(received_size<0) {
                                         "error receiving response: %s (%d)", pa_socks_strerr(no), no);                   if(int no=pa_socks_errno())
                         break;                          throw Exception("http.timeout", 
                 }                                  0, 
                 response=(char*)pa_realloc(response, response_size+received_size+1/*terminator*/);                                  "error receiving response header: %s (%d)", pa_socks_strerr(no), no); 
                 memcpy(response+response_size, buf, received_size);                  goto done;
                 response_size+=received_size;          }
                 response[response_size]=0;          // detecting response_size
           {
                   preview_buf[received_size]=0; // terminator
                   if(size_t content_length=guess_content_length(preview_buf))
                           response_size=preview_size+content_length; // a little more than needed, will adjust response_size by actual received size later
           }
   
                 if(!result && (EOLat=strstr(response, "\n"))) { // checking status in first response          // [gcc is happier this way, see goto above]
                         const String status_line(pa_strdup(response, EOLat-response));          {
                         ArrayString astatus;                   // allocating initial buf
                         size_t pos_after=0;                  response=(char*)pa_malloc_atomic(response_size+1/*terminator*/); // just setting memory block type
                         status_line.split(astatus, pos_after, " ");                   char* ptr=response;
                         const String& status_code=*astatus.get(astatus.count()>1?1:0);                  size_t todo_size=response_size;
                         result=status_code.as_int();                   // coping part of already received body
                   memcpy(ptr, preview_buf, received_size);
                         if(fail_on_status_ne_200 && result!=200)                  ptr+=received_size;
                                 throw Exception("http.status",                  todo_size-=received_size;               
                                         &status_code,  
                                         "invalid HTTP response status");                  // we use terminator byte for two purposes here:
                   // 1. we return there zero always, not knowing: maybe they would want to create String form $file.body?
                   //     invariant: all Strings should have zero-terminated buffers
                   // 2. we use that out-of-size byte to detect if our content-length guess was wrong
                   //    when recv gets more than we expected
                   //    a) we know that the content-length guess was wrong
                   //    b) we have space to put the first byte of extra data
                   //    c) we use less code to detect normal situation: on last while-cycle recv expected to just return 0
                   while(true) {
                           received_size=recv(sock, ptr, todo_size+1/*there is always a place for terminator*/, 0); 
                           if(received_size==0) {
                                   response_size-=todo_size; // in case we received less than expected, cut down the reported size
                                   break;
                           }
                           if(received_size<0) {
                                   if(int no=pa_socks_errno())
                                           throw Exception("http.timeout", 
                                                   0, 
                                                   "error receiving response body: %s (%d)", pa_socks_strerr(no), no); 
                                   break;
                           }
                           // they've touched the terminator?
                           if((size_t)received_size>todo_size)
                           {
                                   // that means that our guessed response_size was not big enough
                                   const size_t grow_chunk_size=0x400*0x400; // 1M
                                   response_size+=grow_chunk_size;
                                   size_t ptr_offset=ptr-response;
                                   response=(char*)pa_realloc(response, response_size+1/*terminator*/);
                                   ptr=response+ptr_offset;
                                   todo_size+=grow_chunk_size;
                           }
                           // can't do this before realloc: we need <todo_size check
                           ptr+=received_size;
                           todo_size-=received_size;
   
                           char* EOLat=0;
                           if(!result && (EOLat=strstr(response, "\n"))) { // checking status in first response
                                   const String status_line(pa_strdup(response, EOLat-response));
                                   ArrayString astatus; 
                                   size_t pos_after=0;
                                   status_line.split(astatus, pos_after, " "); 
                                   const String& status_code=*astatus.get(astatus.count()>1?1:0);
                                   result=status_code.as_int(); 
   
                                   if(fail_on_status_ne_200 && result!=200)
                                           throw Exception("http.status",
                                                   &status_code,
                                                   "invalid HTTP response status");
                           }
                 }                  }
         }          }
   done:
         if(result)          if(result)
           {
                   response[response_size]=0;
                 return result;                  return result;
           }
         else          else
                 throw Exception("http.response",                  throw Exception("http.response",
                         0,                          0,
Line 222  static int http_request(char*& response, Line 335  static int http_request(char*& response,
                         0,                           0, 
                         "zero hostname");  //never                          "zero hostname");  //never
   
         volatile int sock=-1;          volatile // to prevent makeing it register variable, because it will be clobbered by longjmp [thanks gcc warning]
                   int sock=-1;
 #ifdef PA_USE_ALARM  #ifdef PA_USE_ALARM
         signal(SIGALRM, timeout_handler);           signal(SIGALRM, timeout_handler); 
 #endif  #endif
Line 256  static int http_request(char*& response, Line 370  static int http_request(char*& response,
                                         "can not make socket: %s (%d)", pa_socks_strerr(no), no);                                           "can not make socket: %s (%d)", pa_socks_strerr(no), no); 
                         }                          }
   
 #ifdef SO_DONTLINGER  
                         int dont_linger = 0;  
                         setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (const char *)&dont_linger, sizeof(dont_linger));  
 #else  
                         // To enable SO_DONTLINGER (that is, disable SO_LINGER)                           // To enable SO_DONTLINGER (that is, disable SO_LINGER) 
                         // l_onoff should be set to zero and setsockopt should be called                          // l_onoff should be set to zero and setsockopt should be called
                         linger dont_linger={0,0};                          linger dont_linger={0,0};
                         setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char *)&dont_linger, sizeof(dont_linger));                          setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char *)&dont_linger, sizeof(dont_linger));
 #endif  
   
 #if defined(SO_SNDTIMEO) || defined(SO_RCVTIMEO)  #ifdef WIN32
   // SO_*TIMEO can be defined in .h but not implemlemented in protocol,
   // failing subsequently with Option not supported by protocol (99) message
   // could not suppress that, so leaving this only for win32
                         int timeout_ms=timeout_secs*1000;                          int timeout_ms=timeout_secs*1000;
 #endif  
 #ifdef SO_SNDTIMEO  
                         setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));                          setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
 #endif  
 #ifdef SO_RCVTIMEO  
                         setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));                          setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
 #endif  #endif
   
Line 371  static const String* basic_authorization Line 479  static const String* basic_authorization
         if(pass)          if(pass)
                 combined<<pass;                  combined<<pass;
                   
         String* result=new String("Basic "); *result<<pa_base64(combined.cstr(), combined.length());          String* result=new String("Basic "); *result<<pa_base64_encode(combined.cstr(), combined.length());
         return result;          return result;
 }  }
   
Line 461  static File_read_http_result file_read_h Line 569  static File_read_http_result file_read_h
         const char* password_cstr=0;          const char* password_cstr=0;
   
         if(options) {          if(options) {
                 int valid_options=0;                  int valid_options=get_valid_file_options_count(*options);
   
                 if(Value* vmethod=options->get(HTTP_METHOD_NAME)) {                  if(Value* vmethod=options->get(HTTP_METHOD_NAME)) {
                         valid_options++;                          valid_options++;
                         method=vmethod->as_string().cstr();                          method=vmethod->as_string().cstr();
Line 722  static void file_read_action( Line 831  static void file_read_action(
                                 "read failed: actually read %u bytes count not in [0..%u] valid range",                                   "read failed: actually read %u bytes count not in [0..%u] valid range", 
                                         *info.data_size, to_read_size);                                           *info.data_size, to_read_size); 
         } else { // empty file          } else { // empty file
                 if(as_text) {                  // for both, text and binary: for text we need that terminator, for binary we need nonzero pointer to be able to save such files
                         *info.data=new(PointerFreeGC) char[1];                   *info.data=new(PointerFreeGC) char[1]; 
                         *(char*)(*info.data)=0;                  *(char*)(*info.data)=0;
                 } else   
                         *info.data=0;  
                 *info.data_size=0;                  *info.data_size=0;
                 return;                  return;
         }          }
Line 738  File_read_result file_read(Request_chars Line 845  File_read_result file_read(Request_chars
         File_read_result result={false, 0, 0, 0};          File_read_result result={false, 0, 0, 0};
 #ifdef PA_HTTP  #ifdef PA_HTTP
         if(file_spec.starts_with("http://")) {          if(file_spec.starts_with("http://")) {
                   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, as_text, params);                  File_read_http_result http=file_read_http(charsets, file_spec, as_text, params);
                 result.success=true;                  result.success=true;
Line 746  File_read_result file_read(Request_chars Line 858  File_read_result file_read(Request_chars
                 result.headers=http.headers;                   result.headers=http.headers; 
         } else {          } else {
 #endif  #endif
                 if(params && params->count())                  if(params) {
                         throw Exception("parser.runtime",                          int valid_options=get_valid_file_options_count(*params);
                                 0,                          if(valid_options!=params->count())
                                 "invalid option passed");                                  throw Exception("parser.runtime",
                                           0,
                                           "invalid option passed");
                   }
   
                 File_read_action_info info={&result.str, &result.length,                  File_read_action_info info={&result.str, &result.length,
                         buf, offset, count};                           buf, offset, count}; 
Line 847  bool file_read_action_under_lock(const S Line 962  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 1444  g_mime_utils_base64_encode_close (const Line 1559  g_mime_utils_base64_encode_close (const
         return (outptr - out);          return (outptr - out);
 }  }
   
 char* pa_base64(const char *in, size_t len)  static unsigned char gmime_base64_rank[256] = {
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
            52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
           255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
           255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
   };
   
   /**
    * g_mime_utils_base64_decode_step:
    * @in: input stream
    * @inlen: max length of data to decode
    * @out: output stream
    * @state: holds the number of bits that are stored in @save
    * @save: leftover bits that have not yet been decoded
    *
    * Decodes a chunk of base64 encoded data.
    *
    * Returns the number of bytes decoded (which have been dumped in @out).
    **/
   size_t
   g_mime_utils_base64_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
   {
           const register unsigned char *inptr;
           register unsigned char *outptr;
           const unsigned char *inend;
           register guint32 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++ = saved >> 16;
                                   *outptr++ = saved >> 8;
                                   *outptr++ = saved;
                                   i = 0;
                           }
                   }
           }
           
           *save = saved;
           *state = i;
           
           /* quick scan back for '=' on the end somewhere */
           /* fortunately we can drop 1 output char for each trailing = (upto 2) */
           i = 2;
           while (inptr > in && i) {
                   inptr--;
                   if (gmime_base64_rank[*inptr] != 0xff) {
                           if (*inptr == '=' && outptr > out)
                                   outptr--;
                           i--;
                   }
           }
           
           /* if i != 0 then there is a truncation error! */
           return (outptr - out);
   }
   
   
   char* pa_base64_encode(const char *in, size_t in_size)
 {  {
         /* wont go to more than 2x size (overly conservative) */          /* wont go to more than 2x size (overly conservative) */
         char* result=new(PointerFreeGC) char[len * 2 + 6];          char* result=new(PointerFreeGC) char[in_size * 2 + 6];
         int state=0;          int state=0;
         int save=0;          int save=0;
 #ifndef NDEBUG  #ifndef NDEBUG
         size_t filled=          size_t filled=
 #endif  #endif
                 g_mime_utils_base64_encode_close ((const unsigned char*)in, len,                   g_mime_utils_base64_encode_close ((const unsigned char*)in, in_size, 
                 (unsigned char*)result, &state, &save);                  (unsigned char*)result, &state, &save);
         assert(filled <= len * 2 + 6);          assert(filled <= in_size * 2 + 6);
   
         return result;          return result;
 }  }
   
   void pa_base64_decode(const char *in, size_t in_size, void*& result, size_t& result_size)
   {
           /* wont go to more than had (overly conservative) */
           result=static_cast<void*>(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);
           static_cast<char*>(result)[result_size]=0; // for text files
   }

Removed from v.1.199  
changed lines
  Added in v.1.210


E-mail: