--- parser3/src/main/pa_common.C 2010/06/03 02:03:21 1.260 +++ parser3/src/main/pa_common.C 2013/07/22 15:32:23 1.273 @@ -1,7 +1,7 @@ /** @file Parser: commonly functions. - Copyright(c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2000-2012 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) * BASE64 part @@ -26,8 +26,6 @@ * */ -static const char * const IDENT_COMMON_C="$Date: 2010/06/03 02:03:21 $"; - #include "pa_common.h" #include "pa_exception.h" #include "pa_hash.h" @@ -38,6 +36,12 @@ static const char * const IDENT_COMMON_C #include "pcre.h" #include "pa_request.h" +#ifdef _MSC_VER +#include +#endif + +volatile const char * IDENT_PA_COMMON_C="$Id: pa_common.C,v 1.273 2013/07/22 15:32:23 moko Exp $" IDENT_PA_COMMON_H IDENT_PA_HASH_H IDENT_PA_ARRAY_H IDENT_PA_STACK_H; + // some maybe-undefined constants #ifndef _O_TEXT @@ -118,23 +122,13 @@ void fix_line_breaks(char *str, size_t& str[length]=0; // terminating } -char* file_read_text(Request_charsets& charsets, - const String& file_spec, - 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); +char* file_read_text(Request_charsets& charsets, const String& file_spec, 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; } -char* file_load_text(Request& r, - const String& file_spec, - bool fail_on_read_problem, - HashStringValue* params, - bool transcode_result) { - File_read_result file= - file_load(r, file_spec, true, params, fail_on_read_problem, 0, 0, 0, transcode_result); +char* file_load_text(Request& r, const String& file_spec, bool fail_on_read_problem, HashStringValue* params, bool transcode_result) { + File_read_result file=file_load(r, file_spec, true, params, fail_on_read_problem, 0, 0, 0, transcode_result); return file.success?file.str:0; } @@ -160,29 +154,21 @@ struct File_read_action_info { char* buf; size_t offset; size_t count; }; #endif -static void file_read_action( - struct stat& finfo, - int f, - const String& file_spec, const char* /*fname*/, bool as_text, - void *context) { + +static void file_read_action(struct stat& finfo, int f, const String& file_spec, const char* /*fname*/, bool as_text, void *context) { File_read_action_info& info=*static_cast(context); size_t to_read_size=info.count; if(!to_read_size) to_read_size=(size_t)finfo.st_size; assert( !(info.buf && as_text) ); - if(to_read_size) { + if(to_read_size) { if(info.offset) lseek(f, info.offset, SEEK_SET); - *info.data=info.buf - ? info.buf - : (char *)pa_malloc_atomic(to_read_size+1); - *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) - throw Exception("file.read", - &file_spec, - "read failed: actually read %u bytes count not in [0..%u] valid range", - *info.data_size, to_read_size); + *info.data=info.buf ? info.buf : (char *)pa_malloc_atomic(to_read_size+1); + ssize_t result=read(f, *info.data, to_read_size); + if(result<0) + throw Exception("file.read", &file_spec, "read failed: %s (%d)", strerror(errno), errno); + *info.data_size=result; } 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 *info.data=(char *)pa_malloc_atomic(1); @@ -200,9 +186,7 @@ File_read_result file_read(Request_chars 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); + throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); } File_read_action_info info={&result.str, &result.length, buf, offset, count}; @@ -213,23 +197,24 @@ File_read_result file_read(Request_chars if(as_text){ if(result.success){ + Charset* asked_charset=0; if(result.length>=3 && strncmp(result.str, "\xEF\xBB\xBF", 3)==0){ // skip UTF-8 signature (BOM code) result.str+=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(Value* vcharset_name=params->get(PA_CHARSET_NAME)){ - Charset asked_charset=::charsets.get(vcharset_name->as_string(). - change_case(charsets.source(), String::CC_UPPER)); - - String::C body=String::C(result.str, result.length); - body=Charset::transcode(body, asked_charset, charsets.source()); + if(params) + if(Value* vcharset_name=params->get(PA_CHARSET_NAME)) + asked_charset=&::charsets.get(vcharset_name->as_string().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); + body=Charset::transcode(body, *asked_charset, charsets.source()); - result.str=const_cast(body.str); // hacking a little - result.length=body.length; - } + result.str=const_cast(body.str); // hacking a little + result.length=body.length; } } if(result.length) @@ -415,16 +400,18 @@ struct File_write_action_info { size_t length; }; #endif + static void file_write_action(int f, void *context) { File_write_action_info& info=*static_cast(context); if(info.length) { - int written=write(f, info.str, info.length); + ssize_t written=write(f, info.str, info.length); if(written<0) - throw Exception("file.access", - 0, - "write failed: %s (%d)", strerror(errno), errno); + throw Exception("file.write", 0, "write failed: %s (%d)", strerror(errno), errno); + if(written!=info.length) + throw Exception("file.write", 0, "write failed: %u of %u bytes written", written, info.length); } } + void file_write( Request_charsets& charsets, const String& file_spec, @@ -452,16 +439,65 @@ void file_write( 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] -static void rmdir(const String& file_spec, size_t pos_after) { - size_t pos_before; - if((pos_before=file_spec.pos('/', pos_after))!=STRING_NOT_FOUND) - rmdir(file_spec, pos_before+1); - - rmdir(file_spec.mid(0, pos_after-1/* / */).taint_cstr(String::L_FILE_SPEC)); +static void rmdir(const String& file_spec, size_t pos_after=0) { + char* dir_spec=file_spec.taint_cstrm(String::L_FILE_SPEC); + size_t length=strlen(dir_spec); + while( (length=get_dir(dir_spec, length)) && (length > pos_after) ){ +#ifdef WIN32 + 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) { +bool file_delete(const String& file_spec, bool fail_on_problem, bool keep_empty_dirs) { const char* fname=file_spec.taint_cstr(String::L_FILE_SPEC); if(unlink(fname)!=0) if(fail_on_problem) @@ -472,11 +508,13 @@ bool file_delete(const String& file_spec else return false; - rmdir(file_spec, 1); + if(!keep_empty_dirs) + rmdir(file_spec, 1); + return true; } -void file_move(const String& old_spec, const String& new_spec) { +void file_move(const String& old_spec, const String& new_spec, bool keep_empty_dirs) { 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); @@ -488,7 +526,8 @@ void file_move(const String& old_spec, c "rename failed: %s (%d), actual filename '%s' to '%s'", strerror(errno), errno, old_spec_cstr, new_spec_cstr); - rmdir(old_spec, 1); + if(!keep_empty_dirs) + rmdir(old_spec, 1); } @@ -505,26 +544,6 @@ bool entry_exists(const String& file_spe return entry_exists(fname, 0); } -static bool entry_readable(const String& file_spec, bool need_dir) { - char* fname=file_spec.taint_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) { return entry_readable(file_spec, false); } @@ -535,7 +554,8 @@ bool dir_exists(const String& file_spec) const String* file_exist(const String& path, const String& name) { String& result=*new String(path); - result << "/"; + if(path.last_char() != '/') + result << "/"; result << name; return file_exist(result)?&result:0; } @@ -618,7 +638,7 @@ enum FormatType { FormatUInt, FormatDouble }; -FormatType format_type(char* fmt){ +FormatType format_type(const char* fmt){ enum FormatState { Percent, Flags, @@ -629,7 +649,7 @@ FormatType format_type(char* fmt){ FormatType result=FormatInvalid; - char* pos=fmt; + const char* pos=fmt; while(char c=*(pos++)){ switch(state){ case Percent: @@ -673,7 +693,7 @@ 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]; int size=-1; @@ -796,6 +816,26 @@ char* unescape_chars(const char* cp, int 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 void back_slashes_to_slashes(char* s) { if(s) @@ -936,7 +976,7 @@ int __vsnprintf(char* b, size_t s, const if((ssize_t)s<0) return 0; -#if _MSC_VER +#ifdef _MSC_VER /* win32: mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm @@ -998,7 +1038,7 @@ int __snprintf(char* b, size_t s, const * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. * */ -static char *base64_alphabet = +static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** @@ -1140,9 +1180,9 @@ g_mime_utils_base64_encode_close (const } 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, 62,255,255,255, 63, + 254,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, @@ -1165,13 +1205,14 @@ static unsigned char gmime_base64_rank[2 * @out: output stream * @state: holds the number of bits that are stored in @save * @save: leftover bits that have not yet been decoded + * @strict: only base64 and whitespace chars are allowed * * 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) +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; unsigned char *outptr; @@ -1189,15 +1230,21 @@ g_mime_utils_base64_decode_step (const u 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; - } + switch(c) { + case 0xff: // non-base64 and non-whitespace chars. not allowed in strict mode + if(strict) + throw Exception(BASE64_FORMAT, 0, "Invalid base64 char on position %d is detected", inptr-in-1); + case 0xfe: // whitespace chars 0x09, 0x0A, 0x0D, 0x20 are allowed in any mode + break; + default: + saved = (saved << 6) | c; + i++; + if (i == 4) { + *outptr++ = (unsigned char)(saved >> 16); + *outptr++ = (unsigned char)(saved >> 8); + *outptr++ = (unsigned char)(saved); + i = 0; + } } } @@ -1209,7 +1256,7 @@ g_mime_utils_base64_decode_step (const u i = 2; while (inptr > in && i) { inptr--; - if (gmime_base64_rank[*inptr] != 0xff) { + if (gmime_base64_rank[*inptr] <= 0xfe) { if (*inptr == '=' && outptr > out) outptr--; i--; @@ -1275,16 +1322,22 @@ static void file_base64_file_action( } } -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*/]; +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 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); + g_mime_utils_base64_decode_step ((const unsigned char*)in, in_size, + (unsigned char*)result, &state, &save, strict); + assert(result_size <= new_size); result[result_size]=0; // for text files + + if(strict && state!=0) + throw Exception(BASE64_FORMAT, 0, "Unexpected end of chars"); }