--- parser3/src/main/pa_common.C 2015/04/21 22:12:27 1.283 +++ parser3/src/main/pa_common.C 2016/07/29 20:24:16 1.291 @@ -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.283 2015/04/21 22:12:27 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.291 2016/07/29 20:24:16 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=&::charsets.get(vcharset_name->as_string()); + + asked_charset=::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); @@ -534,10 +503,7 @@ bool file_stat(const String& 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 +543,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 +712,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); @@ -787,7 +763,7 @@ char* unescape_chars(const char* cp, int } else { switch(escapeState) { case EscapeRest: - if(!js && c=='+'){ + if(c=='+' && !js){ *dst++=' '; } else { *dst++=c; @@ -926,8 +902,9 @@ int __vsnprintf(char* b, size_t s, const return 0; int r; - // note: on win32& maybe somewhere else + // 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 @@ -935,22 +912,16 @@ int __vsnprintf(char* b, size_t s, const return 0; #ifdef _MSC_VER - /* - win32: - mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm - - if the number of bytes to write exceeds buffer, then count bytes are written and Ö1 is returned - */ + // 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 + solaris: man vsnprintf - The snprintf() function returns the number of characters + 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 @@ -1247,11 +1218,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); @@ -1276,8 +1243,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; } @@ -1290,9 +1256,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 @@ -1304,9 +1268,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; } @@ -1349,11 +1311,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(); @@ -1377,10 +1335,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=" @@ -1396,7 +1351,7 @@ Charset* detect_charset(const char* cont if(end) *end=0; // terminator - return *begin?&charsets.get(begin):0; + return *begin?&charsets.get_direct(begin):0; } } return 0; @@ -1468,43 +1423,3 @@ const char *pa_idna_decode(const char *i 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)); -}