mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-12-18 13:35:53 +01:00
518 lines
11 KiB
C++
518 lines
11 KiB
C++
#include "main.h"
|
|
#include "./addressEncoder.h"
|
|
|
|
|
|
#include <wininet.h>
|
|
#include <strsafe.h>
|
|
|
|
|
|
typedef struct __ENCODEBUFFER
|
|
{
|
|
LPWSTR buffer;
|
|
size_t bufferMax;
|
|
LPWSTR cursor;
|
|
size_t remaining;
|
|
} ENCODEBUFFER;
|
|
|
|
HRESULT AddressEncoder_ReAllocBuffer(ENCODEBUFFER *decoder, size_t cchBufferSize)
|
|
{
|
|
if (NULL == decoder)
|
|
return E_INVALIDARG;
|
|
|
|
if (cchBufferSize == decoder->bufferMax)
|
|
return S_FALSE;
|
|
|
|
if (cchBufferSize < decoder->bufferMax)
|
|
return E_FAIL;
|
|
|
|
LPWSTR test = Plugin_ReAllocString(decoder->buffer, cchBufferSize);
|
|
if (NULL == test)
|
|
return E_OUTOFMEMORY;
|
|
|
|
decoder->cursor = test + (decoder->cursor - decoder->buffer);
|
|
decoder->remaining += (cchBufferSize - decoder->bufferMax);
|
|
decoder->buffer = test;
|
|
decoder->bufferMax = cchBufferSize;
|
|
|
|
return S_OK;
|
|
}
|
|
HRESULT AddressEncoder_AppendAnsiString(ENCODEBUFFER *decoder, LPCSTR pszString, size_t cchString)
|
|
{
|
|
if (NULL == decoder)
|
|
return E_INVALIDARG;
|
|
|
|
INT cchConverted;
|
|
while(0 ==(cchConverted = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, pszString, (int)cchString, decoder->cursor, (int)decoder->remaining)))
|
|
{
|
|
DWORD errorCode = GetLastError();
|
|
if (ERROR_INSUFFICIENT_BUFFER == errorCode)
|
|
{
|
|
INT cchNeed = MultiByteToWideChar(CP_UTF8, 0, pszString, (int)cchString, NULL, 0) - (INT)decoder->remaining;
|
|
if (cchNeed < 32) cchNeed = 32;
|
|
HRESULT hr = AddressEncoder_ReAllocBuffer(decoder, decoder->bufferMax + cchNeed);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
return HRESULT_FROM_WIN32(errorCode);
|
|
}
|
|
}
|
|
|
|
if (0 != cchConverted)
|
|
{
|
|
decoder->cursor += cchConverted;
|
|
decoder->remaining -= cchConverted;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT AddressEncoder_AppendString(ENCODEBUFFER *decoder, LPCWSTR pszString, size_t cchString)
|
|
{
|
|
if (NULL == decoder)
|
|
return E_INVALIDARG;
|
|
|
|
LPWSTR cursor;
|
|
size_t remaining;
|
|
|
|
HRESULT hr;
|
|
if (cchString >= decoder->remaining)
|
|
{
|
|
hr = AddressEncoder_ReAllocBuffer(decoder, decoder->bufferMax + (cchString - decoder->remaining) + 1);
|
|
if (FAILED(hr)) return hr;
|
|
}
|
|
|
|
hr = StringCchCopyNEx(decoder->cursor, decoder->remaining, pszString, cchString, &cursor, &remaining, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
decoder->cursor = cursor;
|
|
decoder->remaining = remaining;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT AddressEncoder_GetEscapeBlock(LPCWSTR pszEscape, LPCWSTR *ppszEnd, size_t *pcchEscapeLen, LPSTR pszBuffer, UINT *pcbBufferMax)
|
|
{
|
|
if (NULL == pszEscape || (NULL != pszBuffer && NULL == pcbBufferMax))
|
|
return E_INVALIDARG;
|
|
|
|
UINT cbBinary = 0;
|
|
WORD charInfo;
|
|
WCHAR szDigit[3] = {0};
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
LPCWSTR cursor = pszEscape;
|
|
while (L'%' == *cursor)
|
|
{
|
|
LPCWSTR testChar = CharNext(cursor);
|
|
if (L'\0' == *testChar ||
|
|
FALSE == GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, testChar, 1, &charInfo) ||
|
|
0 == (C1_XDIGIT & charInfo))
|
|
{
|
|
break;
|
|
}
|
|
szDigit[0] = *testChar;
|
|
|
|
testChar = CharNext(testChar);
|
|
if (L'\0' == *testChar ||
|
|
FALSE == GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, testChar, 1, &charInfo) ||
|
|
0 == (C1_XDIGIT & charInfo))
|
|
{
|
|
break;
|
|
}
|
|
|
|
szDigit[1] = *testChar;
|
|
CharUpperBuff(szDigit, 2);
|
|
|
|
BYTE binaryData = ((szDigit[0] - ((szDigit[0] <= L'9' ? L'0' : L'A' - 10))) << 4) & 0xf0;
|
|
binaryData += (szDigit[1] - ((szDigit[1] <= L'9' ? L'0' : L'A' - 10))) & 0x0f;
|
|
if (NULL != pszBuffer)
|
|
{
|
|
if (cbBinary < *pcbBufferMax)
|
|
pszBuffer[cbBinary] = binaryData;
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
cbBinary++;
|
|
|
|
cursor = CharNext(testChar);
|
|
}
|
|
|
|
if (cursor == pszEscape)
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_BLOCK_LENGTH);
|
|
|
|
if (NULL != ppszEnd)
|
|
*ppszEnd = cursor;
|
|
|
|
if (NULL != pcchEscapeLen)
|
|
*pcchEscapeLen = (size_t)(cursor - pszEscape);
|
|
|
|
if (NULL != pcbBufferMax)
|
|
*pcbBufferMax = cbBinary;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT AddressEncoder_DecodeString(LPCWSTR pszUrl, LPWSTR *ppResult)
|
|
{
|
|
if (NULL == pszUrl)
|
|
{
|
|
*ppResult = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
UINT cchUrl = 0;
|
|
UINT escapeSize = 0;
|
|
UINT escapeBlockSize,escapeBlockMaxSize = 0;
|
|
LPCWSTR escapeBlockEnd;
|
|
for (LPCWSTR cursor = pszUrl; L'\0' != *cursor;)
|
|
{
|
|
if (L'%' == *cursor && SUCCEEDED(AddressEncoder_GetEscapeBlock(cursor, &escapeBlockEnd, NULL, NULL, &escapeBlockSize)))
|
|
{
|
|
escapeSize += escapeBlockSize;
|
|
if (escapeBlockSize > escapeBlockMaxSize)
|
|
escapeBlockMaxSize = escapeBlockSize;
|
|
|
|
cursor = escapeBlockEnd;
|
|
}
|
|
else
|
|
{
|
|
cchUrl++;
|
|
cursor = CharNext(cursor);
|
|
}
|
|
}
|
|
|
|
if (0 == escapeSize)
|
|
{
|
|
*ppResult = Plugin_CopyString(pszUrl);
|
|
if (NULL == *ppResult) return E_OUTOFMEMORY;
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ENCODEBUFFER decoder;
|
|
ZeroMemory(&decoder, sizeof(decoder));
|
|
|
|
LPSTR escapeBuffer = Plugin_MallocAnsiString(escapeBlockMaxSize);
|
|
if (NULL == escapeBuffer)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
hr = AddressEncoder_ReAllocBuffer(&decoder, cchUrl + (escapeSize + 1)* sizeof(WCHAR));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPCWSTR cursor = pszUrl;
|
|
LPCWSTR copyBlock = cursor;
|
|
|
|
for (;;)
|
|
{
|
|
escapeBlockSize = escapeBlockMaxSize;
|
|
if (L'%' == *cursor && SUCCEEDED(AddressEncoder_GetEscapeBlock(cursor, &escapeBlockEnd, NULL, escapeBuffer, &escapeBlockSize)))
|
|
{
|
|
if (copyBlock != cursor)
|
|
{
|
|
hr = AddressEncoder_AppendString(&decoder, copyBlock, cursor - copyBlock);
|
|
if (FAILED(hr))
|
|
break;
|
|
copyBlock = cursor;
|
|
}
|
|
|
|
HRESULT convertResult = AddressEncoder_AppendAnsiString(&decoder, escapeBuffer, escapeBlockSize);
|
|
if (L'\0' == *cursor)
|
|
break;
|
|
|
|
cursor = escapeBlockEnd;
|
|
if (SUCCEEDED(convertResult))
|
|
{
|
|
copyBlock = cursor;
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
}
|
|
|
|
if (L'\0' == *cursor)
|
|
{
|
|
if (copyBlock != cursor)
|
|
hr = AddressEncoder_AppendString(&decoder, copyBlock, cursor - copyBlock);
|
|
break;
|
|
}
|
|
else
|
|
cursor = CharNext(cursor);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (NULL != escapeBuffer)
|
|
Plugin_FreeAnsiString(escapeBuffer);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Plugin_FreeString(decoder.buffer);
|
|
decoder.buffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
*decoder.cursor = L'\0';
|
|
}
|
|
|
|
*ppResult = decoder.buffer;
|
|
|
|
return hr;
|
|
}
|
|
HRESULT AddressEncoder_GetWideBlock(LPCWSTR pszWide, LPCWSTR *pszEnd, LPWSTR pszBuffer, size_t *pcchBufferMax)
|
|
{
|
|
LPCWSTR cursor = pszWide;
|
|
if (NULL == pszWide)
|
|
return E_INVALIDARG;
|
|
|
|
if (NULL != pszBuffer && NULL == pcchBufferMax)
|
|
return E_INVALIDARG;
|
|
|
|
while (L'\0' == *cursor || *cursor > 0xFF)
|
|
{
|
|
if (L'\0' == *cursor)
|
|
break;
|
|
cursor = CharNext(cursor);
|
|
}
|
|
|
|
if (NULL != pszEnd)
|
|
*pszEnd = cursor;
|
|
|
|
HRESULT hr = S_OK;
|
|
size_t cchBuffer = 0;
|
|
|
|
if (cursor == pszWide)
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
size_t bytesCount = WideCharToMultiByte(CP_UTF8, 0, pszWide, (int)(cursor - pszWide), NULL, 0, NULL, NULL);
|
|
if (0 == bytesCount)
|
|
{
|
|
DWORD errorCode = GetLastError();
|
|
if (ERROR_SUCCESS != errorCode)
|
|
hr = HRESULT_FROM_WIN32(errorCode);
|
|
}
|
|
else
|
|
{
|
|
cchBuffer = 3 * bytesCount;
|
|
if (NULL != pszBuffer)
|
|
{
|
|
if (*pcchBufferMax >= cchBuffer)
|
|
{
|
|
LPWSTR p = pszBuffer;
|
|
BYTE *bytes = ((BYTE*)(pszBuffer + *pcchBufferMax)) - bytesCount;
|
|
WideCharToMultiByte(CP_UTF8, 0, pszWide, (int)(cursor - pszWide), (LPSTR)bytes, (int)bytesCount, NULL, NULL);
|
|
for (size_t i = 0; i < bytesCount; i++)
|
|
{
|
|
BYTE b = bytes[i];
|
|
*p++ = L'%';
|
|
BYTE c = (b >> 4) & 0x0F;
|
|
*p++ = (c < 10) ? (L'0' + c) : (L'A' + (c -10));
|
|
c = b & 0x0F;
|
|
*p++ = (c < 10) ? (L'0' + c) : (L'A' + (c -10));
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL != pcchBufferMax)
|
|
{
|
|
*pcchBufferMax = cchBuffer;
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
HRESULT AddressEncoder_EncodeWideChars(LPCWSTR pszAddress, size_t cchAddress, LPWSTR *ppResult)
|
|
{
|
|
if (NULL == ppResult)
|
|
return E_POINTER;
|
|
|
|
if (NULL == pszAddress)
|
|
{
|
|
*ppResult = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
LPCWSTR blockEnd;
|
|
size_t blockSize;
|
|
size_t cchResultMax = 0;
|
|
BOOL needEncode = FALSE;
|
|
for (LPCWSTR cursor = pszAddress; L'\0' != *cursor;)
|
|
{
|
|
if (*cursor > 0xFF && SUCCEEDED(AddressEncoder_GetWideBlock(cursor, &blockEnd, NULL, &blockSize)))
|
|
{
|
|
cursor = blockEnd;
|
|
cchResultMax += blockSize;
|
|
needEncode = TRUE;
|
|
}
|
|
else
|
|
{
|
|
cursor = CharNext(cursor);
|
|
cchResultMax++;
|
|
}
|
|
}
|
|
|
|
if (FALSE == needEncode)
|
|
{
|
|
*ppResult = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT hr;
|
|
cchResultMax++;
|
|
LPWSTR result = Plugin_MallocString(cchResultMax);
|
|
if (NULL == result)
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
{
|
|
LPWSTR cursor = result;
|
|
size_t remaining = cchResultMax;
|
|
LPCWSTR address = pszAddress;
|
|
LPCWSTR addressBlock = address;
|
|
|
|
for (;;)
|
|
{
|
|
if (*address > 0xFF)
|
|
{
|
|
if (addressBlock != address)
|
|
{
|
|
hr = StringCchCopyNEx(cursor, remaining, addressBlock, (size_t)(address - addressBlock), &cursor, &remaining, 0);
|
|
if (FAILED(hr)) break;
|
|
}
|
|
|
|
blockSize = remaining;
|
|
hr = AddressEncoder_GetWideBlock(address, &address, cursor, &blockSize);
|
|
if (FAILED(hr)) break;
|
|
|
|
cursor += blockSize;
|
|
remaining -= blockSize;
|
|
|
|
addressBlock = address;
|
|
continue;
|
|
}
|
|
|
|
if (L'\0' == *address)
|
|
{
|
|
if (addressBlock != address)
|
|
{
|
|
hr = StringCchCopyNEx(cursor, remaining, addressBlock, (size_t)(address - addressBlock), &cursor, &remaining, 0);
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
address = CharNext(address);
|
|
}
|
|
|
|
*cursor = L'\0';
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Plugin_FreeString(result);
|
|
result = NULL;
|
|
}
|
|
|
|
*ppResult = result;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT AddressEncoder_EncodeString(LPCWSTR pszAddress, LPWSTR pszBuffer, size_t *pcchBufferMax, UINT flags)
|
|
{
|
|
if (NULL == pszBuffer || NULL == pcchBufferMax)
|
|
return E_INVALIDARG;
|
|
|
|
if (NULL == pszAddress || L'\0' == *pszAddress)
|
|
{
|
|
*pszBuffer = L'\0';
|
|
*pcchBufferMax = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
INT cchAddress = lstrlen(pszAddress);
|
|
LPCWSTR begin, end;
|
|
begin = pszAddress;
|
|
end = pszAddress + cchAddress;
|
|
WORD charType;
|
|
while (L'\0' != *begin &&
|
|
FALSE != GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, begin, 1, &charType) && 0 != (C1_SPACE & charType))
|
|
{
|
|
begin = CharNext(begin);
|
|
}
|
|
while (begin != end &&
|
|
FALSE != GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, begin, 1, &charType) && 0 != (C1_SPACE & charType))
|
|
{
|
|
end = CharPrev(begin, end);
|
|
}
|
|
|
|
if (end <= begin)
|
|
{
|
|
*pszBuffer = L'\0';
|
|
*pcchBufferMax = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
LPWSTR encoded;
|
|
HRESULT hr = AddressEncoder_EncodeWideChars(begin, (end - begin), &encoded);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
begin = encoded;
|
|
end = begin + lstrlen(begin);
|
|
}
|
|
|
|
DWORD bufferLen = (DWORD)(*pcchBufferMax);
|
|
if (FALSE == InternetCanonicalizeUrl(begin, pszBuffer, &bufferLen, flags))
|
|
{
|
|
DWORD errorCode = GetLastError();
|
|
if (ERROR_INSUFFICIENT_BUFFER == errorCode)
|
|
{
|
|
*pcchBufferMax = bufferLen;
|
|
hr = ENC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
else
|
|
{
|
|
size_t cchNeeded = (end - begin);
|
|
if (cchNeeded < *pcchBufferMax)
|
|
{
|
|
hr = StringCchCopyN(pszBuffer, *pcchBufferMax, begin, cchNeeded);
|
|
if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
|
|
{
|
|
hr = ENC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = ENC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
*pcchBufferMax = cchNeeded + 1;
|
|
}
|
|
}
|
|
|
|
else
|
|
hr = S_OK;
|
|
|
|
|
|
Plugin_FreeString(encoded);
|
|
return hr;
|
|
} |