/* * Copyright: JessMA Open Source (ldcsaa@gmail.com) * * Version : 4.2.1 * Author : Bruce Liang * Website : http://www.jessma.org * Project : https://github.com/ldcsaa * Blog : http://www.cnblogs.com/ldcsaa * Wiki : http://www.oschina.net/p/hp-socket * QQ Group : 75375912 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "stdafx.h" #include "HttpHelper.h" /////////////////////////////////////////////////////////////////////////////////////////////////////// CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult) { strResult.Format("HTTP/%d.%d", LOBYTE(enVersion), HIBYTE(enVersion)); return strResult; } CStringA& AdjustRequestPath(LPCSTR lpszPath, CStringA& strPath) { strPath = lpszPath; if(strPath.IsEmpty() || strPath.GetAt(0) != HTTP_PATH_SEPARATOR_CHAR) strPath.Insert(0, HTTP_PATH_SEPARATOR_CHAR); return strPath; } LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode) { switch(enCode) { case HSC_CONTINUE : return "Continue"; case HSC_SWITCHING_PROTOCOLS : return "Switching Protocols"; case HSC_PROCESSING : return "Processing"; case HSC_OK : return "OK"; case HSC_CREATED : return "Created"; case HSC_ACCEPTED : return "Accepted"; case HSC_NON_AUTHORITATIVE_INFORMATION : return "Non-Authoritative Information"; case HSC_NO_CONTENT : return "No Content"; case HSC_RESET_CONTENT : return "Reset Content"; case HSC_PARTIAL_CONTENT : return "Partial Content"; case HSC_MULTI_STATUS : return "Multi-Status"; case HSC_ALREADY_REPORTED : return "Already Reported"; case HSC_IM_USED : return "IM Used"; case HSC_MULTIPLE_CHOICES : return "Multiple Choices"; case HSC_MOVED_PERMANENTLY : return "Moved Permanently"; case HSC_MOVED_TEMPORARILY : return "Move temporarily"; case HSC_SEE_OTHER : return "See Other"; case HSC_NOT_MODIFIED : return "Not Modified"; case HSC_USE_PROXY : return "Use Proxy"; case HSC_SWITCH_PROXY : return "Switch Proxy"; case HSC_TEMPORARY_REDIRECT : return "Temporary Redirect"; case HSC_PERMANENT_REDIRECT : return "Permanent Redirect"; case HSC_BAD_REQUEST : return "Bad Request"; case HSC_UNAUTHORIZED : return "Unauthorized"; case HSC_PAYMENT_REQUIRED : return "Payment Required"; case HSC_FORBIDDEN : return "Forbidden"; case HSC_NOT_FOUND : return "Not Found"; case HSC_METHOD_NOT_ALLOWED : return "Method Not Allowed"; case HSC_NOT_ACCEPTABLE : return "Not Acceptable"; case HSC_PROXY_AUTHENTICATION_REQUIRED : return "Proxy Authentication Required"; case HSC_REQUEST_TIMEOUT : return "Request Timeout"; case HSC_CONFLICT : return "Conflict"; case HSC_GONE : return "Gone"; case HSC_LENGTH_REQUIRED : return "Length Required"; case HSC_PRECONDITION_FAILED : return "Precondition Failed"; case HSC_REQUEST_ENTITY_TOO_LARGE : return "Request Entity Too Large"; case HSC_REQUEST_URI_TOO_LONG : return "Request-URI Too Long"; case HSC_UNSUPPORTED_MEDIA_TYPE : return "Unsupported Media Type"; case HSC_REQUESTED_RANGE_NOT_SATISFIABLE: return "Requested Range Not Satisfiable"; case HSC_EXPECTATION_FAILED : return "Expectation Failed"; case HSC_MISDIRECTED_REQUEST : return "Misdirected Request"; case HSC_UNPROCESSABLE_ENTITY : return "Unprocessable Entity"; case HSC_LOCKED : return "Locked"; case HSC_FAILED_DEPENDENCY : return "Failed Dependency"; case HSC_UNORDERED_COLLECTION : return "Unordered Collection"; case HSC_UPGRADE_REQUIRED : return "Upgrade Required"; case HSC_PRECONDITION_REQUIRED : return "Precondition Required"; case HSC_TOO_MANY_REQUESTS : return "Too Many Requests"; case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE: return "Request Header Fields Too Large"; case HSC_UNAVAILABLE_FOR_LEGAL_REASONS : return "Unavailable For Legal Reasons"; case HSC_RETRY_WITH : return "Retry With"; case HSC_INTERNAL_SERVER_ERROR : return "Internal Server Error"; case HSC_NOT_IMPLEMENTED : return "Not Implemented"; case HSC_BAD_GATEWAY : return "Bad Gateway"; case HSC_SERVICE_UNAVAILABLE : return "Service Unavailable"; case HSC_GATEWAY_TIMEOUT : return "Gateway Timeout"; case HSC_HTTP_VERSION_NOT_SUPPORTED : return "HTTP Version Not Supported"; case HSC_VARIANT_ALSO_NEGOTIATES : return "Variant Also Negotiates"; case HSC_INSUFFICIENT_STORAGE : return "Insufficient Storage"; case HSC_LOOP_DETECTED : return "Loop Detected"; case HSC_BANDWIDTH_LIMIT_EXCEEDED : return "Bandwidth Limit Exceeded"; case HSC_NOT_EXTENDED : return "Not Extended"; case HSC_NETWORK_AUTHENTICATION_REQUIRED: return "Network Authentication Required"; case HSC_UNPARSEABLE_RESPONSE_HEADERS : return "Unparseable Response Headers"; default : return "***"; } } static inline CStringA& AppendHeader(LPCSTR lpszName, LPCSTR lpszValue, CStringA& strValue) { strValue.Append(lpszName); strValue.Append(HTTP_HEADER_SEPARATOR); strValue.Append(lpszValue); strValue.Append(HTTP_CRLF); return strValue; } void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue) { ASSERT(lpszMethod); strValue.Format("%s %s HTTP/%d.%d%s", CStringA(lpszMethod).MakeUpper(), lpszPath, LOBYTE(enVersion), HIBYTE(enVersion), HTTP_CRLF); } void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue) { if(!lpszDesc) lpszDesc = ::GetHttpDefaultStatusCodeDesc((EnHttpStatusCode)usStatusCode); strValue.Format("HTTP/%d.%d %d %s%s", LOBYTE(enVersion), HIBYTE(enVersion), usStatusCode, lpszDesc, HTTP_CRLF); } void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue) { unordered_set szHeaderNames; if(iHeaderCount > 0) { ASSERT(lpHeaders); for(int i = 0; i < iHeaderCount; i++) { const THeader& header = lpHeaders[i]; if(header.name != nullptr) { ASSERT(strlen(header.name) > 0); szHeaderNames.emplace(header.name); AppendHeader(header.name, header.value, strValue); } } } if( (!bRequest || iBodyLength > 0) && (szHeaderNames.empty() || (szHeaderNames.find(HTTP_HEADER_CONTENT_LENGTH) == szHeaderNames.end() && szHeaderNames.find(HTTP_HEADER_TRANSFER_ENCODING) == szHeaderNames.end()))) { char szBodyLength[16]; _itoa(iBodyLength, szBodyLength, 10); AppendHeader(HTTP_HEADER_CONTENT_LENGTH, szBodyLength, strValue); } if( (iConnFlag == 0 || iConnFlag == 1) && (szHeaderNames.empty() || szHeaderNames.find(HTTP_HEADER_CONNECTION) == szHeaderNames.end() )) { LPCSTR lpszValue = iConnFlag == 0 ? HTTP_CONNECTION_CLOSE_VALUE : HTTP_CONNECTION_KEEPALIVE_VALUE; AppendHeader(HTTP_HEADER_CONNECTION, lpszValue, strValue); } if( bRequest && lpszDefaultHost && lpszDefaultHost[0] != 0 && (szHeaderNames.empty() || (szHeaderNames.find(HTTP_HEADER_HOST) == szHeaderNames.end()) )) { CStringA strHost(lpszDefaultHost); if(usPort != 0) strHost.AppendFormat(":%u", usPort); AppendHeader(HTTP_HEADER_HOST, strHost, strValue); } szHeaderNames.clear(); if(pCookies != nullptr) { DWORD dwSize = (DWORD)pCookies->size(); if(dwSize > 0) { strValue.Append(HTTP_HEADER_COOKIE); strValue.Append(HTTP_HEADER_SEPARATOR); DWORD dwIndex = 0; for(TCookieMapCI it = pCookies->begin(), end = pCookies->end(); it != end; ++it, ++dwIndex) { strValue.Append(it->first); strValue.AppendChar(COOKIE_KV_SEP_CHAR); strValue.Append(it->second); if(dwIndex < dwSize - 1) strValue.Append(HTTP_COOKIE_SEPARATOR); } strValue.Append(HTTP_CRLF); } } strValue.Append(HTTP_CRLF); } void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2]) { ASSERT(pBody != nullptr || iLength == 0); szBuffer[0].buf = (LPSTR)(LPCSTR)strHeader; szBuffer[0].len = strHeader.GetLength(); szBuffer[1].buf = (LPSTR)(LPCSTR)pBody; szBuffer[1].len = iLength; } BOOL MakeWSPacket(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], BYTE* pData, int iLength, ULONGLONG ullBodyLen, BYTE szHeader[HTTP_MAX_WS_HEADER_LEN], WSABUF szBuffer[2]) { ULONGLONG ullLength = (ULONGLONG)iLength; ASSERT(pData != nullptr || iLength == 0); ASSERT(ullBodyLen == 0 || ullBodyLen >= ullLength); if(ullBodyLen == 0) ullBodyLen = ullLength; else if(ullBodyLen < ullLength) { ::SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } TBaseWSHeader bh(szHeader, TRUE); int iHeaderLen = HTTP_MIN_WS_HEADER_LEN; bh.set_fin(bFinal); bh.set_rsv(iReserved); bh.set_code(iOperationCode); bh.set_mask(lpszMask ? TRUE : FALSE); if(ullBodyLen < 126) bh.set_len((BYTE)ullBodyLen); else if(ullBodyLen <= 0xFFFF) { bh.set_len(126); bh.set_extlen((USHORT)ullBodyLen); iHeaderLen += 2; } else { bh.set_len(127); *(ULONGLONG*)(szHeader + HTTP_MIN_WS_HEADER_LEN) = ::HToN64(ullBodyLen); iHeaderLen += 8; } if(lpszMask) { memcpy(szHeader + iHeaderLen, lpszMask, 4); for(int i = 0; i < iLength; i++) pData[i] = pData[i] ^ lpszMask[i & 0x03]; iHeaderLen += 4; } szBuffer[0].buf = (LPSTR)(LPCSTR)szHeader; szBuffer[0].len = iHeaderLen; szBuffer[1].buf = (LPSTR)(LPCSTR)pData; szBuffer[1].len = iLength; return TRUE; } BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath) { int iSchemaLength = (int)strlen(HTTP_SCHEMA); CStringA strSchema = strUrl.Left(iSchemaLength); strSchema.MakeLower(); if(strSchema.Compare(HTTP_SCHEMA) == 0) bHttps = FALSE; else { iSchemaLength = (int)strlen(HTTPS_SCHEMA); strSchema = strUrl.Left(iSchemaLength); strSchema.MakeLower(); if(strSchema.Compare(HTTPS_SCHEMA) == 0) bHttps = TRUE; else return FALSE; } int i = strUrl.Find(HTTP_PATH_SEPARATOR_CHAR, iSchemaLength); if(i > 0) { strHost = strUrl.Mid(iSchemaLength, i - iSchemaLength); strPath = strUrl.Mid(i); } else { strHost = strUrl.Mid(iSchemaLength); strPath = HTTP_PATH_SEPARATOR; } if(strHost.IsEmpty() || !::isalnum(strHost.GetAt(0))) return FALSE; i = strHost.Find(HTTP_PORT_SEPARATOR_CHAR); if(i > 0) { CStringA strPort = strHost.Mid(i + 1); int iLength = strPort.GetLength(); if(iLength == 0) return FALSE; for(int j = 0; j < iLength; j++) { if(!::isdigit(strPort.GetAt(j))) return FALSE; } usPort = (USHORT)::atoi(strPort); strHost = strHost.Mid(0, i); } else { usPort = bHttps ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT; } return TRUE; } BOOL CodePageToUnicode(int iCodePage, const char szSrc[], WCHAR szDest[], int& iDestLength) { ASSERT(szSrc); int iSize = ::MultiByteToWideChar(iCodePage, 0, szSrc, -1, nullptr, 0); if(iSize == 0 || iSize > iDestLength || !szDest || iDestLength == 0) { iDestLength = iSize; return FALSE; } if(::MultiByteToWideChar(iCodePage, 0, szSrc, -1, szDest, iSize) != 0) iDestLength = iSize; else iDestLength = 0; return iDestLength != 0; } BOOL UnicodeToCodePage(int iCodePage, const WCHAR szSrc[], char szDest[], int& iDestLength) { ASSERT(szSrc); int iSize = ::WideCharToMultiByte(iCodePage, 0, szSrc, -1, nullptr, 0, nullptr, nullptr); if(iSize == 0 || iSize > iDestLength || !szDest || iDestLength == 0) { iDestLength = iSize; return FALSE; } if(::WideCharToMultiByte(iCodePage, 0, szSrc, -1, szDest, iSize, nullptr, nullptr) != 0) iDestLength = iSize; else iDestLength = 0; return iDestLength != 0; } BOOL GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength) { return CodePageToUnicode(CP_ACP, szSrc, szDest, iDestLength); } BOOL UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength) { return UnicodeToCodePage(CP_ACP, szSrc, szDest, iDestLength); } BOOL Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength) { return CodePageToUnicode(CP_UTF8, szSrc, szDest, iDestLength); } BOOL UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength) { return UnicodeToCodePage(CP_UTF8, szSrc, szDest, iDestLength); } BOOL GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength) { int iMiddleLength = 0; GbkToUnicode(szSrc, nullptr, iMiddleLength); if(iMiddleLength == 0) { iDestLength = 0; return FALSE; } unique_ptr p(new WCHAR[iMiddleLength]); VERIFY(GbkToUnicode(szSrc, p.get(), iMiddleLength)); return UnicodeToUtf8(p.get(), szDest, iDestLength); } BOOL Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength) { int iMiddleLength = 0; Utf8ToUnicode(szSrc, nullptr, iMiddleLength); if(iMiddleLength == 0) { iDestLength = 0; return FALSE; } unique_ptr p(new WCHAR[iMiddleLength]); VERIFY(Utf8ToUnicode(szSrc, p.get(), iMiddleLength)); return UnicodeToGbk(p.get(), szDest, iDestLength); } int Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen); } int CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy) { z_stream stream; stream.next_in = (z_const Bytef*)lpszSrc; stream.avail_in = dwSrcLen; stream.next_out = lpszDest; stream.avail_out = dwDestLen; stream.zalloc = nullptr; stream.zfree = nullptr; stream.opaque = nullptr; int err = ::deflateInit2(&stream, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy); if(err != Z_OK) return err; err = ::deflate(&stream, Z_FINISH); if(err != Z_STREAM_END) { ::deflateEnd(&stream); return err == Z_OK ? Z_BUF_ERROR : err; } if(dwDestLen > stream.total_out) { lpszDest[stream.total_out] = 0; dwDestLen = stream.total_out; } return ::deflateEnd(&stream); } int Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen); } int UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits) { z_stream stream; stream.next_in = (z_const Bytef*)lpszSrc; stream.avail_in = (uInt)dwSrcLen; stream.next_out = lpszDest; stream.avail_out = dwDestLen; stream.zalloc = nullptr; stream.zfree = nullptr; int err = ::inflateInit2(&stream, iWindowBits); if(err != Z_OK) return err; err = ::inflate(&stream, Z_FINISH); if(err != Z_STREAM_END) { ::inflateEnd(&stream); return (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) ? Z_DATA_ERROR : err; } if(dwDestLen > stream.total_out) { lpszDest[stream.total_out] = 0; dwDestLen = stream.total_out; } return inflateEnd(&stream); } DWORD GuessCompressBound(DWORD dwSrcLen, BOOL bGZip) { DWORD dwBound = ::compressBound(dwSrcLen); if(bGZip) dwBound += 11; return dwBound; } int GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16); } int GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, MAX_WBITS + 32); } DWORD GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen) { if(dwSrcLen < 20 || *(USHORT*)lpszSrc != 0x8B1F) return 0; return *(DWORD*)(lpszSrc + dwSrcLen - 4); } DWORD GuessBase64EncodeBound(DWORD dwSrcLen) { return 4 * ((dwSrcLen + 2) / 3); } DWORD GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) { if(dwSrcLen < 2) return 0; if(lpszSrc[dwSrcLen - 2] == '=') dwSrcLen -= 2; else if(lpszSrc[dwSrcLen - 1] == '=') --dwSrcLen; DWORD dwMod = dwSrcLen % 4; DWORD dwAdd = dwMod == 2 ? 1 : (dwMod == 3 ? 2 : 0); return 3 * (dwSrcLen / 4) + dwAdd; } int Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { static const BYTE CODES[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; DWORD dwRealLen = GuessBase64EncodeBound(dwSrcLen); if(lpszDest == nullptr || dwDestLen < dwRealLen) { dwDestLen = dwRealLen; return -5; } BYTE* p = lpszDest; DWORD leven = 3 * (dwSrcLen / 3); DWORD i = 0; for (; i < leven; i += 3) { *p++ = CODES[lpszSrc[0] >> 2]; *p++ = CODES[((lpszSrc[0] & 3) << 4) + (lpszSrc[1] >> 4)]; *p++ = CODES[((lpszSrc[1] & 0xf) << 2) + (lpszSrc[2] >> 6)]; *p++ = CODES[lpszSrc[2] & 0x3f]; lpszSrc += 3; } if(i < dwSrcLen) { BYTE a = lpszSrc[0]; BYTE b = (i + 1 < dwSrcLen) ? lpszSrc[1] : 0; *p++ = CODES[a >> 2]; *p++ = CODES[((a & 3) << 4) + (b >> 4)]; *p++ = (i + 1 < dwSrcLen) ? CODES[((b & 0xf) << 2)] : '='; *p++ = '='; } ASSERT(dwRealLen == p - lpszDest); if(dwDestLen > dwRealLen) { *p = 0; dwDestLen = dwRealLen; } return 0; } int Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { static const BYTE MAP[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 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, 254, 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 }; DWORD dwRealLen = GuessBase64DecodeBound(lpszSrc, dwSrcLen); if(lpszDest == nullptr || dwDestLen < dwRealLen) { dwDestLen = dwRealLen; return -5; } BYTE c; int g = 3; DWORD i, x, y, z; for(i = x = y = z = 0; i < dwSrcLen || x != 0;) { c = i < dwSrcLen ? MAP[lpszSrc[i++]] : 254; if(c == 255) {dwDestLen = 0; return -3;} else if(c == 254) {c = 0; g--;} else if(c == 253) continue; z = (z << 6) | c; if(++x == 4) { lpszDest[y++] = (BYTE)((z >> 16) & 255); if (g > 1) lpszDest[y++] = (BYTE)((z >> 8) & 255); if (g > 2) lpszDest[y++] = (BYTE)(z & 255); x = z = 0; } } BOOL isOK = (y == dwRealLen); if(!isOK) dwDestLen = 0; else { if(dwDestLen > dwRealLen) { lpszDest[dwRealLen] = 0; dwDestLen = dwRealLen; } } return isOK ? 0 : -3; } DWORD GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) { DWORD dwAdd = 0; for(DWORD i = 0; i < dwSrcLen; i++) { BYTE c = lpszSrc[i]; if(!(isalnum(c) || c == ' ' || c == '.' || c == '-' || c == '_' || c == '*')) dwAdd += 2; } return dwSrcLen + dwAdd; } DWORD GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) { DWORD dwPercent = 0; for(DWORD i = 0; i < dwSrcLen; i++) { if(lpszSrc[i] == '%') { ++dwPercent; i += 2; } } DWORD dwSub = dwPercent * 2; if(dwSrcLen < dwSub) return 0; return dwSrcLen - dwSub; } #define HEX_CHAR_TO_VALUE(c) (c <= '9' ? c - '0' : (c <= 'F' ? c - 'A' + 0x0A : c - 'a' + 0X0A)) #define HEX_DOUBLE_CHAR_TO_VALUE(pc) (((HEX_CHAR_TO_VALUE(*(pc))) << 4) | (HEX_CHAR_TO_VALUE(*(pc + 1)))) #define HEX_VALUE_TO_CHAR(n) (n <= 9 ? n + '0' : (n <= 'F' ? n + 'A' - 0X0A : n + 'a' - 0X0A)) #define HEX_VALUE_TO_DOUBLE_CHAR(pc, n) {*(pc) = HEX_VALUE_TO_CHAR((n >> 4)); *((pc) + 1) = HEX_VALUE_TO_CHAR((n & 0X0F));} int UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { if(lpszDest == nullptr || dwDestLen == 0) goto ERROR_DEST_LEN; BYTE c; DWORD j = 0; for(DWORD i = 0; i < dwSrcLen; i++) { if(j >= dwDestLen) goto ERROR_DEST_LEN; c = lpszSrc[i]; if (isalnum(c) || c == '.' || c == '-' || c == '_' || c == '*') lpszDest[j++] = c; else if(c == ' ') lpszDest[j++] = '+'; else { if(j + 3 >= dwDestLen) goto ERROR_DEST_LEN; lpszDest[j++] = '%'; HEX_VALUE_TO_DOUBLE_CHAR(lpszDest + j, c); j += 2; } } if(dwDestLen > j) { lpszDest[j] = 0; dwDestLen = j; } return 0; ERROR_DEST_LEN: dwDestLen = GuessUrlEncodeBound(lpszSrc, dwSrcLen); return -5; } int UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) { if(lpszDest == nullptr || dwDestLen == 0) goto ERROR_DEST_LEN; char c; DWORD j = 0; for(DWORD i = 0; i < dwSrcLen; i++) { if(j >= dwDestLen) goto ERROR_DEST_LEN; c = lpszSrc[i]; if(c == '+') lpszDest[j++] = ' '; else if(c != '%') lpszDest[j++] = c; else { if(i + 2 >= dwSrcLen) goto ERROR_SRC_DATA; lpszDest[j++] = HEX_DOUBLE_CHAR_TO_VALUE(lpszSrc + i + 1); i += 2; } } if(dwDestLen > j) { lpszDest[j] = 0; dwDestLen = j; } return 0; ERROR_SRC_DATA: dwDestLen = 0; return -3; ERROR_DEST_LEN: dwDestLen = GuessUrlDecodeBound(lpszSrc, dwSrcLen); return -5; }