/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2008-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #ifndef headerfile_winix_core_misc #define headerfile_winix_core_misc #include #include #include #include #include "requesttypes.h" #include "date/date.h" #include "textstream/textstream.h" #include "utf8/utf8.h" #include "winix_const.h" #include "convert/convert.h" namespace Winix { /* conversions between text and numbers */ int Toi(const std::string & str, int base = 10); int Toi(const std::wstring & str, int base = 10); int Toi(const char * str, int base = 10); int Toi(const wchar_t * str, int base = 10); long Tol(const std::string & str, int base = 10); long Tol(const std::wstring & str, int base = 10); long Tol(const char * str, int base = 10); long Tol(const wchar_t * str, int base = 10); double Tod(const std::string & str); double Tod(const std::wstring & str); double Tod(const char * str); double Tod(const wchar_t * str); // if the buffer is too small it will be terminated at the beginning (empty string) // and the function returns false // IMPROVEME now we can use Toa from Pikotools template bool Toa(unsigned long value, CharType * buffer, size_t buf_len, int base = 10) { size_t i1, i2; long rest; if( buf_len == 0 ) return false; i1 = i2 = 0; if( base < 2 ) base = 2; if( base > 16 ) base = 16; do { rest = value % base; value = value / base; buffer[i2++] = (rest < 10) ? char(rest) + '0' : char(rest) - 10 + 'A'; } while(value != 0 && i2 < buf_len); if( i2 >= buf_len ) { buffer[0] = 0; // ops, the buffer was too small return false; } buffer[i2--] = 0; for( ; i1 < i2 ; ++i1, --i2) { CharType temp = buffer[i1]; buffer[i1] = buffer[i2]; buffer[i2] = temp; } return true; } // if the buffer is too small it will be terminated at the beginning (empty string) // and the function returns false template bool Toa(long value, CharType * buffer, size_t buf_len, int base = 10) { if( buf_len == 0 ) return false; CharType * buf = buffer; if( value < 0 ) { buffer[0] = '-'; buf += 1; buf_len -= 1; value = -value; } bool res = Toa(static_cast(value), buf, buf_len, base); if( !res ) buffer[0] = 0; return res; } template bool Toa(unsigned int value, CharType * buffer, size_t buf_len, int base = 10) { return Toa(static_cast(value), buffer, buf_len, base); } template bool Toa(int value, CharType * buffer, size_t buf_len, int base = 10) { return Toa(static_cast(value), buffer, buf_len, base); } /* these methos don't take the buffer size make sure the buffer size is sufficient big 2^64 - 1 = 18446744073709551615 = 20 characters (plus minus sign and plus terminating zero) so the buffer should have at least 22 characters !! CHECK ME check the size whether is correct */ template bool Toa(unsigned long value, CharType * buffer) { size_t sufficient_space = 25; return Toa(value, buffer, sufficient_space); } template bool Toa(long value, CharType * buffer) { size_t sufficient_space = 25; return Toa(value, buffer, sufficient_space); } template bool Toa(unsigned int value, CharType * buffer) { size_t sufficient_space = 25; return Toa(value, buffer, sufficient_space); } template bool Toa(int value, CharType * buffer) { size_t sufficient_space = 25; return Toa(value, buffer, sufficient_space); } // warning: it uses its own static buffer // one buffer for both these functions // !! REMOVE ME they are deprecated (don't use it) const wchar_t * Toa(unsigned int value, int base = 10); const wchar_t * Toa(unsigned long value, int base = 10); const wchar_t * Toa(int value, int base = 10); const wchar_t * Toa(long value, int base = 10); void Toa(int value, std::string & res, int base = 10, bool clear = true); void Toa(long value, std::string & res, int base = 10, bool clear = true); void Toa(int value, std::wstring & res, int base = 10, bool clear = true); void Toa(long value, std::wstring & res, int base = 10, bool clear = true); /* * this method make sure that there is no more than 'digits' non zero digits * in the given string * * * samples (if parameter 'digits' is equal to two): * "0.0001234" -> "0.00012" * "10" -> "10" * "123.345" -> "123.34" * "55.1003" -> "55.10" * */ void SetNonZeroDigitsAfterComma(char * str, size_t digits); bool CorrectUrlChar(wchar_t c); void CorrectUrlDots(std::wstring & url); void CorrectUrlChars(std::wstring & url); void CorrectUrlOnlyAllowedChar(std::wstring & url); const wchar_t * DateToStr(int year, int month, int day); const wchar_t * DateToStr(int year, int month, int day, int hour, int min, int sec); const wchar_t * DateToStr(const pt::Date & d); const wchar_t * DateToStr(time_t t); const wchar_t * DateToStrWithoutHours(const pt::Date & d); const wchar_t * DateToStrWithoutHours(time_t t); const char * DateToStrCookie(int year, int month, int day, int hour, int min, int sec); const char * DateToStrCookie(const pt::Date & d); const char * DateToStrCookie(time_t t); // depracated // not thread safe const wchar_t * IpToStr(unsigned int ip_); // in a new code we can use WTextStream in such a way // DateToStr() etc can use it too pt::WTextStream IPToStr(unsigned int ip); pt::WTextStream IPToStr(int ip); bool IsWhite(wchar_t s, bool treat_new_line_as_white = false); bool IsWhite(const wchar_t * str, bool treat_new_line_as_white = false); bool IsWhite(const std::wstring & str, bool treat_new_line_as_white = false); void TrimWhite(std::wstring & s, bool trim_new_line_too = false); const wchar_t * SkipWhite(const wchar_t * s, bool treat_new_line_as_white = false); void TrimFirst(std::wstring & s, wchar_t c); void TrimLast(std::wstring & s, wchar_t c); void Trim(std::wstring & s, wchar_t c); bool IsLastSlash(const std::wstring & path); /* * returns true if str is an integer number * white strings at the beginning and at the end are ignored * e.g. returns true for: * " 10 " * " -20 " * e.g. returns false for: * "" * "- 20" * "z" */ bool IsInt(const wchar_t * str, bool treat_new_line_as_white = false); bool IsInt(const std::wstring & str, bool treat_new_line_as_white = false); /* * returns true if str is a non-negative integer number * white strings at the beginning and at the end are ignored * e.g. returns true for: * " 0 " * " 10 " * " 20 " * e.g. returns false for: * "" * " -20 " * "z" */ bool IsSize(const wchar_t * str, bool treat_new_line_as_white = false); bool IsSize(const std::wstring & str, bool treat_new_line_as_white = false); /* * returns true if str is a floating point number * white strings at the beginning and at the end are ignored * as a decimal comma can be a dot or a comma * e.g. returns true for: * " 10 " * " -20.3 " * " 30,5 " * e.g. returns false for: * "" * "- 20.1" * "20.1.2" * "z" */ bool IsFloat(const wchar_t * str, bool treat_new_line_as_white = false); bool IsFloat(const std::wstring & str, bool treat_new_line_as_white = false); void Overwrite(std::string & str); void Overwrite(std::wstring & str); template void MaxSize(StringType & str, size_t max_size) { if( str.size() > max_size ) str.erase(max_size); } /* this method removing all characters from given string only digits are allowed and if allow_comma then one comma (or dot) character is allowed if change_to_dot is true then if a comma exists then it is changed to a dot */ template void OnlyDigit(StringType & s, bool allow_comma = true, bool change_to_dot = true) { typename StringType::size_type i; bool was_comma = false; if( s.empty() ) { s = '0'; return; } for(i=0 ; i='0' && s[i]<='9') || (allow_comma && !was_comma && (s[i]=='.' || s[i]==',')) ) { if( change_to_dot && s[i] == ',' ) s[i] = '.'; if( s[i]=='.' || s[i]==',' ) was_comma = true; i += 1; } else { s.erase(i, 1); } } } template bool Equal(const StringType1 * str1, const StringType2 * str2) { while( *str1 && *str2 && wchar_t(*str1) == wchar_t(*str2) ) { ++str1; ++str2; } if( *str1 == 0 && *str2 == 0 ) return true; return false; } template bool Equal(const StringType1 & str1, const StringType2 & str2) { return Equal(str1.c_str(), str2.c_str()); } /* looking for 'look_for' string in 'buf' and replacing it with 'replace' 'replace' can be empty (so only 'look_for' will be deleted) */ template void ReplaceString(StringType & buf, const StringType & look_for, const StringType & replace) { size_t i = 0; if( look_for.empty() ) return; while( i < buf.size() ) { if( pt::is_substr(look_for.c_str(), buf.c_str() + i) ) { buf.erase(i, look_for.size()); buf.insert(i, replace); i += replace.size(); } else { i += 1; } } } template void NoLastSlash(StringType & s) { if( s.empty() ) return; size_t i = s.size(); for( ; i>0 && s[i-1]=='/' ; --i); if( i < s.size() ) s.erase(i); } template void NoFirstHttp(StringType & s) { if( s.empty() ) return; const char http[] = "http://"; const char https[] = "https://"; if( IsSubStringNoCase(http, s.c_str()) ) { s.erase(0, sizeof(http)/sizeof(char)); } else if( IsSubStringNoCase(https, s.c_str()) ) { s.erase(0, sizeof(https)/sizeof(char)); } } /* this method returns true if there are two dots meaning 'go up' somewhere in the path for example such paths return true: ".." "test/../path" "test/where/../" "test/where/.." "../abc" */ template bool PathHasUpDir(const StringType * str) { size_t i = 0; while( str[i] ) { if( str[i]=='.' && str[i+1]=='.' ) { i += 2; if( str[i]=='/' || str[i]==0 ) return true; } // skipping until to next slash while( str[i] && str[i]!='/' ) i += 1; // skipping the slash (or slashes) while( str[i]=='/' ) i += 1; } return false; } template bool PathHasUpDir(const StringType & str) { return PathHasUpDir(str.c_str()); } /* this method calculates how many directories there are in the given path input: str - path last_is_dir - true if the last part of the path should be treated as a directory too samples: HowManyDirs("", false) -> 0 HowManyDirs("", true) -> 0 HowManyDirs("abc", false) -> 0 HowManyDirs("abc", true) -> 1 HowManyDirs("/abc/", true) -> 1 HowManyDirs("////", false) -> 0 HowManyDirs("////", true) -> 0 HowManyDirs("////abc", false) -> 0 HowManyDirs("////abc", true) -> 1 HowManyDirs("/var/static", false) -> 1 HowManyDirs("/var/static", true) -> 2 HowManyDirs("/var/static/", false) -> 2 HowManyDirs("/var/static/", true) -> 2 */ template size_t HowManyDirs(const StringType * str, bool is_last_dir = false) { size_t res = 0; size_t i = 0; // first slash (root dir) is not calculated while( str[i]=='/' ) i += 1; while( str[i] ) { if( str[i]=='/' ) { res += 1; while( str[i]=='/' ) i += 1; } else { i += 1; } } if( is_last_dir && i>0 && str[i-1]!='/' ) res += 1; return res; } template size_t HowManyDirs(const StringType & str, bool is_last_dir = false) { return HowManyDirs(str.c_str(), is_last_dir); } /* this method skips some first directories from given path samples: SkipDirs("/var/test", 1) -> "test" SkipDirs("/var/test/somewhere", 1) -> "test/somewhere" SkipDirs("/var/test/somewhere", 2) -> "somewhere" SkipDirs("/var/test/somewhere", 10) -> "" */ template const StringType * SkipDirs(const StringType * str, size_t how_many_skip) { size_t i = 0; size_t skipped = 0; if( how_many_skip == 0 ) return str; // first slash (root dir) is not calculated while( str[i]=='/' ) i += 1; while( str[i] ) { if( str[i]=='/' ) { skipped += 1; while( str[i]=='/' ) i += 1; if( skipped == how_many_skip ) return str+i; } else { i += 1; } } return str + i; } bool IsEmailCorrectChar(wchar_t c); bool ValidateEmail(const wchar_t * email); bool ValidateEmail(const std::wstring & email); bool IsFile(const wchar_t * file); bool IsFile(const std::wstring & file); bool CreateDir(const wchar_t * dir, int priv, int group = -1); bool CreateDir(const std::wstring & dir, int priv, int group = -1); // creating directories (dirs) under base_dir (base_dir must exist) // if skip_last == true then last part from dir is treated as a file (the last directory is not created) bool CreateDirs(const wchar_t * base_dir, const wchar_t * dirs, int priv = 0755, int group = -1, bool skip_last = false); bool CreateDirs(const std::wstring & base_dir, const std::wstring & dirs, int priv = 0755, int group = -1, bool skip_last = false); int GetGroupId(const wchar_t * name); int GetGroupId(const std::wstring & name); bool SetPriv(const wchar_t * name, int priv, int group = -1); bool SetPriv(const std::wstring & name, int priv, int group = -1); bool CopyFile(FILE * in, FILE * out); bool CopyFile(const wchar_t * src, const wchar_t * dst); bool CopyFile(const std::wstring & src, const std::wstring & dst); bool RemoveFile(const wchar_t * file); bool RemoveFile(const std::wstring & file); bool RenameFile(const wchar_t * from, const wchar_t * to); bool RenameFile(const std::wstring & from, const std::wstring & to); bool GetUTF8File(const wchar_t * file_path, std::wstring & content, bool clear_content = true); bool GetUTF8File(const std::wstring & file_path, std::wstring & content, bool clear_content = true); bool GetBinaryFile(const wchar_t * file_path, BinaryPage & content, bool clear_content = true); bool GetBinaryFile(const std::wstring & file_path, BinaryPage & content, bool clear_content = true); const wchar_t * GetFileExt(const wchar_t * name); void PrepareNewFileName(const wchar_t * src, const wchar_t * postfix, std::wstring & res, bool clear_res = true); void PrepareNewFileName(const std::wstring & src, const std::wstring & postfix, std::wstring & res, bool clear_res = true); int SelectFileType(const wchar_t * file_name); int SelectFileType(const std::wstring & file_name); template void UrlEncode(char c, StreamType & out, bool clear_out = true) { char buffer[10]; size_t buflen = sizeof(buffer)/sizeof(char); if( clear_out ) out.clear(); if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '.' || c == ',' || c == '-' || c == '_' || c == '(' || c == ')' ) { out << c; } else { Toa(static_cast(c), buffer, buflen, 16); out << '%'; if( buffer[1] == 0 ) out << '0'; // there is only one character in the buffer out << buffer; } } template void UrlEncode(wchar_t c, StreamType & out, bool clear_out = true) { char buffer[10]; size_t buflen = sizeof(buffer)/sizeof(char); if( clear_out ) out.clear(); // this conversion is not using surrogate pairs if wchar_t is 2 bytes long size_t utf8_len = pt::int_to_utf8(static_cast(c), buffer, buflen); if( utf8_len > 0 ) { for(size_t i=0 ; i < utf8_len ; ++i) { UrlEncode(buffer[i], out, false); } } } template void UrlEncode(const char * in, StreamType & out, bool clear_out = true) { if( clear_out ) out.clear(); for(size_t i=0 ; in[i] != 0 ; ++i) UrlEncode(in[i], out, false); } template void UrlEncode(const std::string & in, StreamType & out, bool clear_out = true) { UrlEncode(in.c_str(), out, clear_out); } template void UrlEncode(const wchar_t * in, StreamType & out, bool clear_out = true) { if( clear_out ) out.clear(); for(size_t i=0 ; in[i] != 0 ; ++i) UrlEncode(in[i], out, false); } template void UrlEncode(const std::wstring & in, StreamType & out, bool clear_out = true) { UrlEncode(in.c_str(), out, clear_out); } void UrlEncode(const char * in, std::string & out, bool clear_out = true); void UrlEncode(const std::string & in, std::string & out, bool clear_out = true); //void UrlEncode(const wchar_t * in, std::string & out, bool clear_out = true); //void UrlEncode(const std::wstring & in, std::string & out, bool clear_out = true); void UrlEncode(const wchar_t * in, std::wstring & out, bool clear_out = true); void UrlEncode(const std::wstring & in, std::wstring & out, bool clear_out = true); void UrlEncode(const pt::TextStream & in, pt::TextStream & out, bool clear_out = true); void UrlEncode(const pt::WTextStream & in, pt::WTextStream & out, bool clear_out = true); /* * decoding a URL * * return false is the url is too long (longer than WINIX_URL_MAX_SIZE) * or it cannot be correctly converted from UTF8 to wide characters * */ bool UrlDecode(const char * url, std::wstring & out, bool clear_out = true); bool UrlDecode(const std::string & url, std::wstring & out, bool clear_out = true); template void QEncodeAddChar(char_type c, pt::TextStreamBase & out) { if( (c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') ) { out << c; } else { char buf1[10]; char buf2[10]; size_t len1 = sizeof(buf1) / sizeof(char); size_t len2 = sizeof(buf2) / sizeof(char); size_t len = pt::int_to_utf8(int(c), buf1, len1); for(size_t i=0 ; i void QEncode(const wchar_t * in, pt::TextStreamBase & out, bool clear = true) { if( clear ) out.clear(); out << "=?UTF-8?Q?"; for( ; *in ; ++in) QEncodeAddChar(*in, out); out << "?="; } template void QEncode(const std::wstring & in, pt::TextStreamBase & out, bool clear = true) { if( clear ) out.clear(); out << "=?UTF-8?Q?"; // do not use QEncode(in.c_str()) as 'in' can have a zero byte for(size_t i=0 ; i void CreateSubdomain(const StringType1 * short_str, const StringType2 * long_str, StringType3 & out) { size_t i1, i2; out.clear(); for(i1=0 ; short_str[i1] != 0 ; ++i1); for(i2=0 ; long_str[i2] != 0 ; ++i2); if( i1 >= i2 ) return; // i1 is < i2 while( i1-- > 0 ) { i2 -= 1; if( short_str[i1] != long_str[i2] ) return; // short_str is not a last substring of long_str } while( i2>0 && long_str[i2-1] == '.' ) i2 -= 1; for(i1=0 ; i1 < i2 ; ++i1) out += long_str[i1]; } template void SetMinMax(IntType & val, IntType min_val, IntType max_val) { if( val < min_val ) val = min_val; if( val > max_val ) val = max_val; } template void JSONescape(wchar_t c, Stream & out) { switch( c ) { case 0: out << '\\'; out << '0'; break; case '\r': out << '\\'; out << 'r'; break; case '\n': out << '\\'; out << 'n'; break; case '\t': out << '\\'; out << 't'; break; case 0x08: out << '\\'; out << 'b'; break; case 0x0c: out << '\\'; out << 'f'; break; case '\\': out << '\\'; out << '\\'; break; //case '/': out << '\\'; out << '/'; break; // slash doesn't have to be escaped case '"': out << '\\'; out << '\"'; break; default: out << c; } } template void JSONescape(const StringType & str, Stream & out) { for(size_t i=0 ; i < str.size() ; ++i) { if constexpr(sizeof(char) == sizeof(typename StringType::value_type)) { JSONescape(static_cast(static_cast(str[i])), out); } else { JSONescape(str[i], out); } } } void JSONescapeStream(const pt::WTextStream & in, pt::WTextStream & out); void JSONescapeStream(const std::wstring & in, pt::WTextStream & out); /* * converting from a wide string to an UTF-8 string * and puts a log if the conversion fails * * it uses pt::wide_to_utf8() */ bool wide_to_utf8(const wchar_t * wide_string, char * utf8, size_t utf8_size); bool wide_to_utf8(const std::wstring & wide_string, char * utf8, size_t utf8_size); /* * calculate diff between start and stop timespec * * if start is greater than stop then the result is negative: * - result.tv_sec is less than zero * - result.tv_nsec is greater than or equal to zero (only if start.tv_nsec and stop.tv_nsec are correct values from zero to 1000000000L) * * 'double' value can be calculated in this way: * double res = result.tv_sec + result.tv_nsec / 1000000000.0; * */ void calculate_timespec_diff(timespec & start, timespec & stop, timespec & result); /* * converts timespec to double in the following way: * double val_double = val.tv_sec + val.tv_nsec / 1000000000.0; * * val.tv_sec can be negative (less than zero) but tv_nsec should be in the range [0, 1000000000.0) * */ double timespec_to_double(timespec & val); void timespec_to_stream(timespec & val, pt::Stream & stream); void timespec_to_stream_with_unit(timespec & val, pt::Stream & stream); void slice_by(const std::wstring & str, wchar_t c, std::vector & out); } // namespace Winix #endif