--- parser3/src/main/pa_common.C 2015/10/07 21:24:40 1.286 +++ parser3/src/main/pa_common.C 2016/12/24 23:02:13 1.297 @@ -1,7 +1,7 @@ /** @file Parser: commonly functions. - Copyright (c) 2000-2012 Art. Lebedev Studio (http://www.artlebedev.com) + Copyright (c) 2000-2015 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) * BASE64 part @@ -50,7 +50,7 @@ #define pa_mkdir(path, mode) mkdir(path, mode) #endif -volatile const char * IDENT_PA_COMMON_C="$Id: pa_common.C,v 1.286 2015/10/07 21:24:40 moko Exp $" IDENT_PA_COMMON_H IDENT_PA_HASH_H IDENT_PA_ARRAY_H IDENT_PA_STACK_H; +volatile const char * IDENT_PA_COMMON_C="$Id: pa_common.C,v 1.297 2016/12/24 23:02:13 moko Exp $" IDENT_PA_COMMON_H IDENT_PA_HASH_H IDENT_PA_ARRAY_H IDENT_PA_STACK_H; // some maybe-undefined constants @@ -114,12 +114,11 @@ struct File_read_action_info { }; #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, 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(info.offset) lseek(f, info.offset, SEEK_SET); @@ -150,23 +149,16 @@ File_read_result file_read(Request_chars File_read_action_info info={&result.str, &result.length, buf, offset, count}; - result.success=file_read_action_under_lock(file_spec, - "read", file_read_action, &info, - as_text, fail_on_read_problem); + result.success=file_read_action_under_lock(file_spec, "read", file_read_action, &info, as_text, fail_on_read_problem); 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(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)); + asked_charset=&pa_charsets.get(vcharset_name->as_string()); + + asked_charset=pa_charsets.checkBOM(result.str, result.length, asked_charset); 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); @@ -191,9 +183,7 @@ File_read_result file_load(Request& r, c 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"); + 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); @@ -213,16 +203,13 @@ File_read_result file_load(Request& r, c void check_safe_mode(struct stat finfo, const String& file_spec, const char* fname) { if(finfo.st_uid/*foreign?*/!=geteuid() && finfo.st_gid/*foreign?*/!=getegid()) - throw Exception(PARSER_RUNTIME, - &file_spec, - "parser is in safe mode: " - "reading files of foreign group and user disabled " - "[recompile parser with --disable-safe-mode configure option], " - "actual filename '%s', " - "fuid(%d)!=euid(%d) or fgid(%d)!=egid(%d)", - fname, - finfo.st_uid, geteuid(), - finfo.st_gid, getegid()); + throw Exception(PARSER_RUNTIME, + &file_spec, + "parser is in safe mode: reading files of foreign group and user disabled " + "[recompile parser with --disable-safe-mode configure option], " + "actual filename '%s', fuid(%d)!=euid(%d) or fgid(%d)!=egid(%d)", + fname, finfo.st_uid, geteuid(), finfo.st_gid, getegid() + ); } #else void check_safe_mode(struct stat, const String&, const char*) { @@ -250,37 +237,29 @@ bool file_read_action_under_lock(const S if((f=open(fname, O_RDONLY|(as_text?_O_TEXT:_O_BINARY)))>=0) { try { if(pa_lock_shared_blocking(f)!=0) - throw Exception("file.lock", - &file_spec, - "shared lock failed: %s (%d), actual filename '%s'", - strerror(errno), errno, fname); + throw Exception("file.lock", &file_spec, "shared lock failed: %s (%d), actual filename '%s'", strerror(errno), errno, fname); struct stat finfo; if(fstat(f, &finfo)!=0) throw Exception("file.missing", // hardly possible: we just opened it OK - &file_spec, - "stat failed: %s (%d), actual filename '%s'", - strerror(errno), errno, fname); + &file_spec, "stat failed: %s (%d), actual filename '%s'", strerror(errno), errno, fname); check_safe_mode(finfo, file_spec, fname); - action(finfo, f, file_spec, fname, as_text, context); + action(finfo, f, file_spec, context); } catch(...) { pa_unlock(f);close(f); if(fail_on_read_problem) rethrow; - return false; + return false; } pa_unlock(f);close(f); return true; } else { if(fail_on_read_problem) - throw Exception(errno==EACCES?"file.access" - :(errno==ENOENT || errno==ENOTDIR || errno==ENODEV)?"file.missing":0, - &file_spec, - "%s failed: %s (%d), actual filename '%s'", - action_name, strerror(errno), errno, fname); + throw Exception(errno==EACCES ? "file.access" : (errno==ENOENT || errno==ENOTDIR || errno==ENODEV) ? "file.missing" : 0, + &file_spec, "%s failed: %s (%d), actual filename '%s'", action_name, strerror(errno), errno, fname); return false; } } @@ -313,10 +292,7 @@ bool file_write_action_under_lock( |(as_text?_O_TEXT:_O_BINARY) |(do_append?O_APPEND:PA_O_TRUNC), 0664))>=0) { if((do_block?pa_lock_exclusive_blocking(f):pa_lock_exclusive_nonblocking(f))!=0) { - Exception e("file.lock", - &file_spec, - "shared lock failed: %s (%d), actual filename '%s'", - strerror(errno), errno, fname); + Exception e("file.lock", &file_spec, "shared lock failed: %s (%d), actual filename '%s'", strerror(errno), errno, fname); close(f); if(fail_on_lock_problem) throw e; @@ -346,10 +322,7 @@ bool file_write_action_under_lock( pa_unlock(f);close(f); return true; } else - throw Exception(errno==EACCES?"file.access":0, - &file_spec, - "%s failed: %s (%d), actual filename '%s'", - action_name, strerror(errno), errno, fname); + throw Exception(errno==EACCES ? "file.access" : 0, &file_spec, "%s failed: %s (%d), actual filename '%s'", action_name, strerror(errno), errno, fname); // here should be nothing, see rethrow above } @@ -460,10 +433,8 @@ bool file_delete(const String& file_spec const char* fname=file_spec.taint_cstr(String::L_FILE_SPEC); if(unlink(fname)!=0) { if(fail_on_problem) - throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, - &file_spec, - "unlink failed: %s (%d), actual filename '%s'", - strerror(errno), errno, fname); + throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, + &file_spec, "unlink failed: %s (%d), actual filename '%s'", strerror(errno), errno, fname); else return false; } @@ -481,10 +452,8 @@ void file_move(const String& old_spec, c create_dir_for_file(new_spec); if(rename(old_spec_cstr, new_spec_cstr)!=0) - throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, - &old_spec, - "rename failed: %s (%d), actual filename '%s' to '%s'", - strerror(errno), errno, old_spec_cstr, new_spec_cstr); + throw Exception(errno==EACCES ? "file.access" : errno==ENOENT ? "file.missing" : 0, + &old_spec, "rename failed: %s (%d), actual filename '%s' to '%s'", strerror(errno), errno, old_spec_cstr, new_spec_cstr); if(!keep_empty_dirs) rmdir(old_spec, 1); @@ -524,20 +493,12 @@ bool file_executable(const String& file_ return access(file_spec.taint_cstr(String::L_FILE_SPEC), X_OK)==0; } -bool file_stat(const String& file_spec, - size_t& rsize, - time_t& ratime, - time_t& rmtime, - time_t& rctime, - bool fail_on_read_problem) { +bool file_stat(const String& file_spec, uint64_t& rsize, time_t& ratime, time_t& rmtime, time_t& rctime, bool fail_on_read_problem) { const char* fname=file_spec.taint_cstr(String::L_FILE_SPEC); struct stat finfo; if(stat(fname, &finfo)!=0) { if(fail_on_read_problem) - throw Exception("file.missing", - &file_spec, - "getting file size failed: %s (%d), real filename '%s'", - strerror(errno), errno, fname); + throw Exception("file.missing", &file_spec, "getting file size failed: %s (%d), real filename '%s'", strerror(errno), errno, fname); else return false; } @@ -577,6 +538,20 @@ const char* capitalize(const char* s){ return (const char*)result; } +char *str_lower(const char *s, size_t helper_length){ + char *result=pa_strdup(s, helper_length); + for(char* c=result; *c; c++) + *c=(char)tolower((unsigned char)*c); + return result; +} + +char *str_upper(const char *s, size_t helper_length){ + char *result=pa_strdup(s, helper_length); + for(char* c=result; *c; c++) + *c=(char)toupper((unsigned char)*c); + return result; +} + void fix_line_breaks(char *str, size_t& length) { //_asm int 3; const char* const eob=str+length; @@ -732,17 +707,13 @@ const char* format(double value, const c size=snprintf(local_buf, sizeof(local_buf), fmt, (uint)value); break; case FormatInvalid: - throw Exception(PARSER_RUNTIME, - 0, - "Incorrect format string '%s' was specified.", fmt); + throw Exception(PARSER_RUNTIME, 0, "Incorrect format string '%s' was specified.", fmt); } } else size=snprintf(local_buf, sizeof(local_buf), "%d", (int)value); if(size < 0 || size >= MAX_NUMBER-1){ // on win32 we manually reduce max size while printing - throw Exception(PARSER_RUNTIME, - 0, - "Error occure white executing snprintf with format string '%s'.", fmt); + throw Exception(PARSER_RUNTIME, 0, "Error occure white executing snprintf with format string '%s'.", fmt); } return pa_strdup(local_buf, (size_t)size); @@ -919,56 +890,6 @@ const char* hex_string(unsigned char* by return bytes_hex; } -/// must be last in this file -#undef vsnprintf -int __vsnprintf(char* b, size_t s, const char* f, va_list l) { - if(!s) - return 0; - - int r; - // note: on win32 & maybe somewhere else - // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing - // http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 - --s; - - // clients do not check for negative 's', feature: ignore such prints - if((ssize_t)s<0) - return 0; - -#ifdef _MSC_VER - // win32: if the number of bytes to write exceeds buffer, then count bytes are written and -1 is returned - r=_vsnprintf(b, s, f, l); - if(r<0) - r=s; -#else - r=vsnprintf(b, s, f, l); - /* - solaris: man vsnprintf - - The snprintf() function returns the number of characters - formatted, that is, the number of characters that would have - been written to the buffer if it were large enough. If the - value of n is 0 on a call to snprintf(), an unspecified - value less than 1 is returned. - */ - - if(r<0) - r=0; - else if((size_t)r>s) - r=s; -#endif - b[r]=0; - return r; -} - -int __snprintf(char* b, size_t s, const char* f, ...) { - va_list l; - va_start(l, f); - int r=__vsnprintf(b, s, f, l); - va_end(l); - return r; -} - /* mime64 functions are from libgmime[http://spruce.sourceforge.net/gmime/] lib */ /* * Authors: Michael Zucchi @@ -1242,11 +1163,7 @@ struct File_base64_action_info { unsigned char** base64; }; -static void file_base64_file_action( - struct stat& finfo, - int f, - const String&, const char* /*fname*/, bool, - void *context) { +static void file_base64_file_action(struct stat& finfo, int f, const String&, void *context) { if(finfo.st_size) { File_base64_action_info& info=*static_cast(context); @@ -1271,8 +1188,7 @@ char* pa_base64_encode(const String& fil unsigned char* base64=0; File_base64_action_info info={&base64}; - file_read_action_under_lock(file_spec, - "pa_base64_encode", file_base64_file_action, &info); + file_read_action_under_lock(file_spec, "pa_base64_encode", file_base64_file_action, &info); return (char*)base64; } @@ -1285,9 +1201,7 @@ void pa_base64_decode(const char *in, si 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, strict); + result_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 @@ -1299,9 +1213,7 @@ void pa_base64_decode(const char *in, si int file_block_read(const int f, unsigned char* buffer, const size_t size){ int nCount = read(f, buffer, size); if (nCount < 0) - throw Exception("file.read", - 0, - "read failed: %s (%d)", strerror(errno), errno); + throw Exception("file.read", 0, "read failed: %s (%d)", strerror(errno), errno); return nCount; } @@ -1334,7 +1246,7 @@ inline void CalcCrc32(const unsigned cha } -const unsigned long pa_crc32(const char *in, size_t in_size){ +unsigned long pa_crc32(const char *in, size_t in_size){ unsigned long crc32=0xFFFFFFFF; InitCrc32Table(); @@ -1344,11 +1256,7 @@ const unsigned long pa_crc32(const char return ~crc32; } -static void file_crc32_file_action( - struct stat& finfo, - int f, - const String&, const char* /*fname*/, bool, - void *context) { +static void file_crc32_file_action(struct stat& finfo, int f, const String&, void *context) { unsigned long& crc32=*static_cast(context); if(finfo.st_size) { InitCrc32Table(); @@ -1361,7 +1269,7 @@ static void file_crc32_file_action( } } -const unsigned long pa_crc32(const String& file_spec){ +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; @@ -1372,10 +1280,7 @@ const unsigned long pa_crc32(const Strin // 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); + char* CONTENT_TYPE=str_upper(content_type); if(const char* begin=strstr(CONTENT_TYPE, "CHARSET=")){ begin+=8; // skip "CHARSET=" @@ -1391,7 +1296,7 @@ Charset* detect_charset(const char* cont if(end) *end=0; // terminator - return *begin?&charsets.get(begin):0; + return *begin ? &pa_charsets.get_direct(begin) : 0; } } return 0; @@ -1418,7 +1323,7 @@ const char *pa_idna_encode(const char *i String::C sIn(in,strlen(in)); if(!source_charset.isUTF8()) - sIn=Charset::transcode(sIn, source_charset, UTF8_charset); + sIn=Charset::transcode(sIn, source_charset, pa_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) @@ -1459,7 +1364,57 @@ const char *pa_idna_decode(const char *i *result_end='\0'; if(!asked_charset.isUTF8()) - result = (char *)Charset::transcode(result, UTF8_charset, asked_charset).cstr(); + result = (char *)Charset::transcode(result, pa_UTF8_charset, asked_charset).cstr(); return result; } +/// must be last in this file +#undef vsnprintf +int pa_vsnprintf(char* b, size_t s, const char* f, va_list l) { + if(!s) + return 0; + + int r; + // note: on win32 & maybe somewhere else + // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing + // http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 + --s; + + // clients do not check for negative 's', feature: ignore such prints + if((ssize_t)s<0) + return 0; + +#ifdef _MSC_VER + // win32: if the number of bytes to write exceeds buffer, then count bytes are written and -1 is returned + r=_vsnprintf(b, s, f, l); + if(r<0) + r=s; +#else + r=vsnprintf(b, s, f, l); + /* + solaris: man vsnprintf + + The snprintf() function returns the number of characters + formatted, that is, the number of characters that would have + been written to the buffer if it were large enough. If the + value of n is 0 on a call to snprintf(), an unspecified + value less than 1 is returned. + */ + + if(r<0) + r=0; + else if((size_t)r>s) + r=s; +#endif + b[r]=0; + return r; +} + +int pa_snprintf(char* b, size_t s, const char* f, ...) { + va_list l; + va_start(l, f); + int r=pa_vsnprintf(b, s, f, l); + va_end(l); + return r; +} +