/* !
* \file RyeolHttpClient.cpp
* \brief Implementations of Ryeol's HTTP client classes.
* \author Jo Hyeong-ryeol
* \since 2004.04.12
* \version $LastChangedRevision: 103 $
* $LastChangedDate: 2006-02-05 00:38:41 +0900 (일, 05 2 2006) $
*
*
* - Requirements:
* - Requires Internet Explorer 4.0 or later.
* - Unicode version class support on Windows Me/98/95 requires Microsoft Layer for Unicode.
* - UTF-8 encoding support on Windows 95 requires Microsoft Layer for Unicode.
*
* This file contains implementations of Ryeol's HTTP client classes.
* \n\n\n
* Copyright © 2006 by Jo Hyeong-ryeol\n
* Permission to copy, use, modify, sell and distribute this software is
* granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied warranty,
* and with no claim as to its suitability for any purpose.
*/
#include "stdafx.h"
#include "RyeolHttpClient.h"
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
#pragma warning (disable: 4290) // avoids 'C++ Exception Specification ignored' message
#pragma warning (disable: 4660)
#pragma warning (disable: 4996) // avoids 'This function or variable may be unsafe' message
#pragma comment (lib, "wininet.lib")
#pragma comment (lib, "urlmon.lib")
/*!
* \brief The namespace of the Ryeol's library
*
* This is the namespace for source codes written by Jo Hyeong-ryeol.
*/
namespace Ryeol {
/////////////////////////////// Global constant message table ////////////////////////////////////
#ifndef SAFEFREE
# define SAFEFREE(x) if(x){ ::free((void *)x); x = NULL; }
#endif
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
#define HTTPCLIENT_POSTCNTX_BUFF_SIZE (1024 * 56)
// An assertion macro for the CHttpToolA and the CHttpToolW class
#define HTTPTOOL_ASSERT(expr, msg) \
\
if ( !(expr) ) { \
_ASSERTE ( expr ) ; ThrowException (msg) ; \
}
// An assertion macro for the CHttpClient related classes
#define HTTPCLIENT_ASSERT(expr, msg) \
\
if ( !(expr) ) { \
_ASSERTE ( expr ) ; \
if ( HttpTool::IsAnsi () ) \
CHttpToolA::ThrowException (msg) ; \
else \
CHttpToolW::ThrowException (L##msg) ; \
}
// An assertion macro for the CHttpClient related classes
#define HTTPCLIENT_ASSERTA(expr, msg) \
\
if ( !(expr) ) { \
_ASSERTE ( expr ) ; \
CHttpToolA::ThrowException (msg) ; \
}
// An assertion macro for the CHttpClient related classes
#define HTTPCLIENT_ASSERTW(expr, msg) \
\
if ( !(expr) ) { \
_ASSERTE ( expr ) ; \
CHttpToolW::ThrowException (L##msg) ; \
}
// not specified
static LPCSTR g_NotSpecifiedA[] = {
"Not specified"
} ;
static LPCWSTR g_NotSpecifiedW[] = {
L"Not specified"
} ;
// error messages
static LPCSTR g_NormalMsgA[] = {
"Unexpected error occurred."
, "The index is out of range."
, "Out of memory."
, "The requested URL is not a valid URL."
, "The post context is not started yet."
, "Couldn't read expected bytes from a file."
, "The post context has not been finished yet."
, "The port number is not valid."
, "std::exception occurred."
, "The encoded URL is not valid."
, "The UTF8 string contains an invalid character."
, "An unexpected arithmetic error has been occurred."
, "An arithmetic overflow error has been occurred."
, "An interger divide by zero exception has been occurred."
, "The file (%s) aleady exists."
} ;
static LPCWSTR g_NormalMsgW[] = {
L"Unexpected error occurred."
, L"The index is out of range."
, L"Out of memory."
, L"The requested URL is not a valid URL."
, L"The post context is not started yet."
, L"Couldn't read expected bytes from a file."
, L"The post context has not been finished yet."
, L"The port number is not valid."
, L"std::exception occurred."
, L"The encoded URL is not valid."
, L"The UTF8 string contains an invalid character."
, L"An unexpected arithmetic error has been occurred."
, L"An arithmetic overflow error has been occurred."
, L"An interger divide by zero exception has been occurred."
, L"The file (%s) aleady exists."
} ;
// error messages (which has a win32 error code) - Reserved
static LPCSTR g_NormalMsgWin32A[] = {""} ;
static LPCWSTR g_NormalMsgWin32W[] = {L""} ;
// WinInet error messages (which has a win32 error code)
static LPCSTR g_WinInetMsgWin32A[] = {
"::HttpQueryInfo failed."
, "::InternetReadFile failed."
, "::InternetOpen failed."
, "::InternetConnect failed."
, "::HttpOpenRequest failed."
, "::HttpAddRequestHeaders failed."
, "::HttpSendRequest failed."
, "::HttpSendRequestEx failed."
, "::InternetWriteFile failed."
, "::HttpEndRequest failed."
, "::InternetSetOption failed."
} ;
static LPCWSTR g_WinInetMsgWin32W[] = {
L"::HttpQueryInfo failed."
, L"::InternetReadFile failed."
, L"::InternetOpen failed."
, L"::InternetConnect failed."
, L"::HttpOpenRequest failed."
, L"::HttpAddRequestHeaders failed."
, L"::HttpSendRequest failed."
, L"::HttpSendRequestEx failed."
, L"::InternetWriteFile failed."
, L"::HttpEndRequest failed."
, L"::InternetSetOption failed."
} ;
// Win32 API error messages except the WinInet API (They have a win32 error code)
static LPCSTR g_Win32MsgWin32A[] = {
"::WideCharToMultiByte failed."
, "::MultiByteToWideChar failed."
, "::ReadFile failed."
, "OpenFile (::CreateFile) failed (\"%s\")."
, "::SetFilePointer failed."
, "::GetFileSize failed (\"%s\")."
, "::WriteFile failed (\"%s\")."
} ;
static LPCWSTR g_Win32MsgWin32W[] = {
L"::WideCharToMultiByte failed."
, L"::MultiByteToWideChar failed."
, L"::ReadFile failed."
, L"OpenFile (::CreateFile) failed (\"%s\")."
, L"::SetFilePointer failed."
, L"::GetFileSize failed (\"%s\")."
, L"::WriteFile failed (\"%s\")."
} ;
// user-defined error message
static LPCSTR g_UsrErrMsgA[] = {
"user-defined error message"
} ;
static LPCWSTR g_UsrErrMsgW[] = {
L"user-defined error message"
} ;
static LPCSTR HTTPCLIENT_DEF_MIMETYPE = "application/octet-stream" ;
// Constants in the CHttpToolT
LPCSTR CHttpToolA::szDefUsrAgent = "Ryeol HTTP Client Class" ;
LPCSTR CHttpToolA::szGET = "GET" ;
LPCSTR CHttpToolA::szPost = "POST" ;
LPCSTR CHttpToolA::szHTTP = "HTTP://" ;
LPCSTR CHttpToolA::szHTTPS = "HTTPS://" ;
LPCSTR CHttpToolA::szSlash = "/" ;
LPCSTR CHttpToolA::szCacheControl = "Cache-Control" ;
LPCSTR CHttpToolA::szNoCache = "no-cache" ;
LPCSTR CHttpToolA::szContentType = "Content-Type" ;
LPCSTR CHttpToolA::szMultipartFormDataBoundary = "multipart/form-data; boundary=" ;
LPCSTR CHttpToolA::szFormUrlEncoded = "application/x-www-form-urlencoded" ;
LPCSTR CHttpToolA::szDefBoundary = "----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCSTR CHttpToolA::szDefUploadContType = "multipart/form-data; boundary=" "----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCSTR CHttpToolA::szNULL = "NULL" ;
LPCSTR CHttpToolA::szEmptyString = "" ;
LPCSTR CHttpToolA::szColonSlashSlash = "://" ;
LPCWSTR CHttpToolW::szDefUsrAgent = L"Ryeol HTTP Client Class" ;
LPCWSTR CHttpToolW::szGET = L"GET" ;
LPCWSTR CHttpToolW::szPost = L"POST" ;
LPCWSTR CHttpToolW::szHTTP = L"HTTP://" ;
LPCWSTR CHttpToolW::szHTTPS = L"HTTPS://" ;
LPCWSTR CHttpToolW::szSlash = L"/" ;
LPCWSTR CHttpToolW::szCacheControl = L"Cache-Control" ;
LPCWSTR CHttpToolW::szNoCache = L"no-cache" ;
LPCWSTR CHttpToolW::szContentType = L"Content-Type" ;
LPCWSTR CHttpToolW::szMultipartFormDataBoundary = L"multipart/form-data; boundary=" ;
LPCWSTR CHttpToolW::szFormUrlEncoded = L"application/x-www-form-urlencoded" ;
LPCWSTR CHttpToolW::szDefBoundary = L"----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCWSTR CHttpToolW::szDefUploadContType = L"multipart/form-data; boundary=" L"----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCWSTR CHttpToolW::szNULL = L"NULL" ;
LPCWSTR CHttpToolW::szEmptyString = L"" ;
LPCWSTR CHttpToolW::szColonSlashSlash = L"://" ;
/////////////////////////////// Global constant message table ////////////////////////////////////
///////////////////////////////////////// Explicit Instanciation /////////////////////////////////////////
template class CHttpClientMapT ;
template class CHttpClientMapT ;
template class CHttpResponseT ;
template class CHttpResponseT ;
template class CHttpPostStatT ;
template class CHttpPostStatT ;
template class CHttpUrlAnalyzerT ;
template class CHttpUrlAnalyzerT ;
template class CHttpClientT ;
template class CHttpClientT ;
///////////////////////////////////////// Explicit Instanciation /////////////////////////////////////////
///////////////////////////////////////// httpclientexception /////////////////////////////////////////
/*!
* This is a default constructor with no argument.
*/
httpclientexceptionA::httpclientexceptionA (void)
throw ()
{
m_dwLastError = HTTPCLIENT_ERR_NOT_SPECIFIED ;
m_dwWin32LastError = NO_ERROR ;
}
/*!
* This is a constructor with an initial error message and initial error codes.
* If memory allocation failed for the error message, the error message will not be copied
* and Internal error message will point to NULL.
*
* \param szErrMsg [in] An initial error message.
* \param dwLastError [in] An error code.
* \param dwWin32LastError [in] A win32 error code.
*/
httpclientexceptionA::httpclientexceptionA (LPCSTR szErrMsg, DWORD dwLastError, DWORD dwWin32LastError)
throw ()
: errmsg_exceptionA (szErrMsg)
{
m_dwLastError = dwLastError ;
m_dwWin32LastError = dwWin32LastError ;
}
/*!
* This is a default constructor with no argument.
*/
httpclientexceptionW::httpclientexceptionW (void)
throw ()
{
m_dwLastError = HTTPCLIENT_ERR_NOT_SPECIFIED ;
m_dwWin32LastError = NO_ERROR ;
}
/*!
* This is a constructor with an initial error message and initial error codes.
* If memory allocation failed for the error message, the error message will not be copied
* and Internal error message will point to NULL.
*
* \param szErrMsg [in] An initial error message.
* \param dwLastError [in] An error code.
* \param dwWin32LastError [in] A win32 error code.
*/
httpclientexceptionW::httpclientexceptionW (LPCWSTR szErrMsg, DWORD dwLastError, DWORD dwWin32LastError)
throw ()
: errmsg_exceptionW (szErrMsg)
{
m_dwLastError = dwLastError ;
m_dwWin32LastError = dwWin32LastError ;
}
///////////////////////////////////////// httpclientexception /////////////////////////////////////////
///////////////////////////////////////// CHttpToolA /////////////////////////////////////////
inline
LPCSTR CHttpToolA::GetConstMessage (DWORD nIdx)
throw ()
{
// user-defined error message
if ( nIdx >= 1000 )
return g_UsrErrMsgA[0] ;
// Win32 API error (which has a win32 error code)
if ( nIdx >= 600 )
return g_Win32MsgWin32A[nIdx % 100] ;
// WinInet error (which has a win32 error code)
if ( nIdx >= 400 )
return g_WinInetMsgWin32A[nIdx % 100] ;
// Normal error (which has a win32 error code)
if ( nIdx >= 200 )
return g_NormalMsgWin32A[nIdx % 100] ;
// Normal error
if ( nIdx >= 100 )
return g_NormalMsgA[nIdx % 100] ;
return g_NotSpecifiedA[nIdx] ;
}
void CHttpToolA::ThrowException (DWORD nErrMsgIdx)
throw (Exception &)
{
throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx) ;
}
void CHttpToolA::ThrowException (LPCSTR szErrMsg, DWORD nErrMsgIdx)
throw (Exception &)
{
throw Exception (szErrMsg, nErrMsgIdx) ;
}
void CHttpToolA::ThrowException (DWORD nErrMsgIdx, DWORD dwErrCode, LPCSTR szStrArg)
throw (Exception &)
{
if ( szStrArg == NULL )
throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx, dwErrCode) ;
CHAR szErrMsg[512] ;
LPCSTR szFormat = GetConstMessage (nErrMsgIdx) ;
int cchWritten = SNPrintf (szErrMsg, 511, szFormat, szStrArg ? szStrArg : "NULL") ;
// if an error occurs
if ( cchWritten < -1 )
throw Exception (szFormat, nErrMsgIdx, dwErrCode) ;
if ( cchWritten == -1 )
cchWritten = 511 ;
szErrMsg[cchWritten] = NULL ;
throw Exception (szErrMsg, nErrMsgIdx, dwErrCode) ;
}
void CHttpToolA::ThrowException (LPCWSTR szErrMsg, DWORD nErrMsgIdx, DWORD dwErrCode)
throw (Exception &)
{
LPSTR szErrMsgA = NULL ;
try {
szErrMsgA = CHttpToolA::Unicode2Ansi (szErrMsg) ;
} catch (Exception &) {
szErrMsgA = NULL ;
}
Exception objException (szErrMsgA, nErrMsgIdx, dwErrCode) ;
SAFEFREE (szErrMsgA) ;
throw objException ;
}
void CHttpToolA::ThrowException (CHttpToolW::Exception & e)
throw (Exception &)
{
ThrowException (e.errmsg (), e.LastError (), e.Win32LastError ()) ;
}
void CHttpToolA::ThrowException (::SafeIntException & e)
throw (Exception &)
{
switch ( e.m_code ) {
case ERROR_ARITHMETIC_OVERFLOW:
ThrowException (HTTPCLIENT_ERR_ARITHMETIC_OVERFLOW) ;
break ;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
ThrowException (HTTPCLIENT_ERR_INT_DIVIDE_BY_ZERO) ;
break ;
default:
ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ARITHMETIC_ERROR) ;
break ;
}
}
// Conversion methods
// This method returns a converted ansi string from a unicode string.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::Unicode2Ansi (LPCWSTR szStr, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolA::Unicode2Ansi: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
if ( szStr == NULL )
return NULL ;
int cchNeeded ;
if ( 0 == (cchNeeded = ::WideCharToMultiByte (CodePage, 0, szStr, -1, NULL, 0, NULL, NULL)) )
ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
PSTR szAnsi = (PSTR) ::malloc (sizeof (CHAR) * cchNeeded) ;
if ( szAnsi == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
if ( 0 == ::WideCharToMultiByte (CodePage, 0, szStr, -1, szAnsi, cchNeeded, NULL, NULL) ) {
SAFEFREE (szAnsi) ;
ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}
return szAnsi ;
}
// This method returns a converted unicode string from a ansi string.
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolA::Ansi2Unicode (LPCSTR szStr, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolA::Ansi2Unicode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
if ( szStr == NULL )
return NULL ;
int cchNeeded ;
if ( 0 == (cchNeeded = ::MultiByteToWideChar (CodePage, 0, szStr, -1, NULL, 0)) )
ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
PWSTR szUni = (PWSTR) ::malloc (sizeof (WCHAR) * cchNeeded) ;
if ( szUni == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
if ( 0 == ::MultiByteToWideChar (CodePage, 0, szStr, -1, szUni, cchNeeded) ) {
SAFEFREE (szUni) ;
ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
}
return szUni ;
}
// comparison function (used by STL multimap)
bool CHttpToolA::operator () (LPCSTR szKey1, LPCSTR szKey2) const
throw ()
{
// return true if the two strings are null
if ( szKey1 == NULL && szKey2 == NULL )
return true ;
if ( szKey1 == NULL )
return true ;
if ( szKey2 == NULL )
return false ;
// case insensitive
return ::stricmp (szKey1, szKey2) < 0 ;
}
// Initializes a internet handle.
HINTERNET CHttpToolA::OpenInternet (LPCSTR szUserAgent, DWORD dwAccessType
, LPCSTR szProxyName, LPCSTR szProxyBypass, DWORD dwFlags)
throw (Exception &)
{
HINTERNET hInternet ;
if ( NULL == (hInternet = ::InternetOpenA (
szUserAgent // user agent
, dwAccessType // use direct connection or proxy connection
, szProxyName
, szProxyBypass
, dwFlags)
) )
ThrowException (HTTPCLIENT_ERR_INTERNETOPEN_FAILED, ::GetLastError ()) ;
return hInternet ;
}
// Closes a internet handle
void CHttpToolA::CloseInternet (HINTERNET & hInternet)
throw ()
{
if ( hInternet == NULL )
return ;
// Ignore any errors while closing the handle
::InternetCloseHandle (hInternet) ;
hInternet = NULL ;
}
// Returns a connection handle
// The hInternet must be a valid internet handle.
HINTERNET CHttpToolA::OpenConnection (HINTERNET hInternet, LPCSTR szServerAddr, INTERNET_PORT nPort
, LPCSTR szUsrName, LPCSTR szUsrPwd)
throw (Exception &)
{
HTTPTOOL_ASSERT (hInternet != NULL, "CHttpToolA::OpenConnection: hInternet can not be NULL.") ;
HTTPTOOL_ASSERT (szServerAddr != NULL, "CHttpToolA::OpenConnection: szServerAddr can not be NULL.") ;
HTTPTOOL_ASSERT (::strlen (szServerAddr) != 0, "CHttpToolA::OpenConnection: szServerAddr can not be an empty string.") ;
HINTERNET hConnection ;
if ( NULL == (hConnection = ::InternetConnectA (
hInternet
, szServerAddr
, nPort
, szUsrName
, szUsrPwd
, INTERNET_SERVICE_HTTP
, 0
, NULL)
) )
ThrowException (HTTPCLIENT_ERR_INTERNETCONNECT_FAILED, ::GetLastError ()) ;
return hConnection ;
}
// Closes a connection handle
void CHttpToolA::CloseConnection (HINTERNET & hConnection)
throw ()
{
if ( hConnection == NULL )
return ;
// Ignore any errors while closing the handle
::InternetCloseHandle (hConnection) ;
hConnection = NULL ;
}
// Returns a HTTP request handle
HINTERNET CHttpToolA::OpenRequest (HINTERNET hConnection, LPCSTR szMethod, LPCSTR szObjectName
, DWORD dwFlags, LPCSTR szReferer, UINT /* CodePage */)
throw (Exception &)
{
HTTPTOOL_ASSERT (hConnection != NULL, "CHttpToolA::OpenRequest: hConnection can not be NULL.") ;
HTTPTOOL_ASSERT (szObjectName != NULL, "CHttpToolA::OpenRequest: szObjectName can not be NULL.") ;
HTTPTOOL_ASSERT (::strlen (szObjectName) != 0, "CHttpToolA::OpenRequest: szObjectName can not be an empty string.") ;
static LPCSTR szAcceptedType[] = {
"*/*"
, NULL
} ;
HINTERNET hRequest = NULL ;
// Opens a HTTP request handle
if ( NULL == (hRequest = ::HttpOpenRequestA (
hConnection
, szMethod // HTTP Method
, szObjectName // Target object
, "HTTP/1.1" // HTTP/1.1
, szReferer // referer
, szAcceptedType // Accepts any type.
, dwFlags // Flags
, NULL // Doesn't use any context value.
)
) )
ThrowException (HTTPCLIENT_ERR_HTTPOPENREQUEST_FAILED, ::GetLastError ()) ;
return hRequest ;
}
// Closes a request handle
void CHttpToolA::CloseRequest (HINTERNET & hRequest)
throw ()
{
if ( hRequest == NULL )
return ;
// Ignore any errors while closing the handle
::InternetCloseHandle (hRequest) ;
hRequest = NULL ;
}
void CHttpToolA::AddHeader (HINTERNET hRequest, LPCSTR szName, LPCSTR szValue, UINT /* CodePage */)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::AddHeader: hRequest can not be NULL.") ;
HTTPTOOL_ASSERT (szName != NULL, "CHttpToolA::AddHeader: szName can not be NULL.") ;
::SafeInt cbHeader ;
::SafeInt cchHeader ;
try {
cbHeader = ::strlen (szName) ;
cbHeader += szValue ? ::strlen (szValue) : 0 ;
cbHeader += (4 + 1) ; // for ": ", "\r\n", '\0'
cchHeader = cbHeader - 1 ;
} catch (::SafeIntException & e) {
ThrowException (e) ;
}
PSTR szHeader = (PSTR) ::malloc (sizeof (CHAR) * (cbHeader.Value ())) ;
if ( szHeader == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
::strcpy (szHeader, szName) ;
::strcat (szHeader, ": ") ;
::strcat (szHeader, szValue ? szValue : "") ;
::strcat (szHeader, "\r\n") ;
// Adds a header
if ( !::HttpAddRequestHeadersA (
hRequest
, szHeader // headers to append to the request.
, cchHeader.Value () // header length
, HTTP_ADDREQ_FLAG_ADD // flags
)
) {
SAFEFREE (szHeader) ;
ThrowException (HTTPCLIENT_ERR_HTTPADDREQUESTHEADERS_FAILED, ::GetLastError ()) ;
}
SAFEFREE (szHeader) ;
}
void CHttpToolA::SendRequest (HINTERNET hRequest, LPCSTR szPosted, UINT /* CodePage */)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::SendRequest: hRequest can not be NULL.") ;
::SafeInt cchPosted ;
try {
cchPosted = szPosted ? ::strlen (szPosted) : 0 ;
} catch (::SafeIntException & e) {
ThrowException (e) ;
}
if ( !::HttpSendRequestA (
hRequest
, NULL // Additional header
, 0 // The length of the additional header
, (void *) szPosted // A posted data
, cchPosted.Value () // The length of the posted data
) )
ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUEST_FAILED, ::GetLastError ()) ;
}
void CHttpToolA::SendRequestEx (HINTERNET hRequest, DWORD dwPostedSize)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::SendRequestEx: hRequest can not be NULL.") ;
INTERNET_BUFFERSA BufferIn;
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will occur
BufferIn.Next = NULL;
BufferIn.lpcszHeader = NULL;
BufferIn.dwHeadersLength = 0;
BufferIn.dwHeadersTotal = 0;
BufferIn.lpvBuffer = NULL;
BufferIn.dwBufferLength = 0;
BufferIn.dwBufferTotal = dwPostedSize; // This is the only member used other than dwStructSize
BufferIn.dwOffsetLow = 0;
BufferIn.dwOffsetHigh = 0;
if ( !::HttpSendRequestExA (
hRequest
, &BufferIn
, NULL
, 0
, 0
) )
ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUESTEX_FAILED, ::GetLastError ()) ;
}
void CHttpToolA::InternetWriteFile (HINTERNET hRequest, const BYTE * pbyBuff, DWORD cbyBuff)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::InternetWriteFile: hRequest can not be NULL.") ;
HTTPTOOL_ASSERT (pbyBuff != NULL, "CHttpToolA::InternetWriteFile: pbyBuff can not be NULL.") ;
HTTPTOOL_ASSERT (cbyBuff != 0, "CHttpToolA::InternetWriteFile: cbyBuff can not be zero.") ;
DWORD dwTotalWritten = 0, dwWritten ;
while ( dwTotalWritten < cbyBuff ) {
if ( !::InternetWriteFile (
hRequest
, pbyBuff + dwTotalWritten
, cbyBuff - dwTotalWritten
, &dwWritten
)
)
ThrowException (HTTPCLIENT_ERR_INTERNETWRITEFILE_FAILED, ::GetLastError ()) ;
dwTotalWritten += dwWritten ;
}
}
void CHttpToolA::EndRequest (HINTERNET hRequest)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::EndRequest: hRequest can not be NULL.") ;
if ( !::HttpEndRequest (
hRequest
, NULL
, 0
, 0
)
)
ThrowException (HTTPCLIENT_ERR_HTTPENDREQUEST_FAILED, ::GetLastError ()) ;
}
// Checks whether the file exists.
// (Does not throw an exception)
BOOL CHttpToolA::FileExists (LPCSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::FileExists: szFilePath can not be NULL.") ;
HANDLE hFile = ::CreateFileA (
szFilePath
, 0
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL) ;
if ( hFile != INVALID_HANDLE_VALUE )
{
::CloseHandle (hFile) ;
return TRUE ;
}
return FALSE ;
}
// It returns a INVALID_HANDLE_VALUE if the specified file is not valid.
// (Does not throw an exception)
HANDLE CHttpToolA::OpenFile (LPCSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::OpenFile: szFilePath can not be NULL.") ;
return ::CreateFileA (
szFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
, NULL) ;
}
// If it fails to create an file, it will return INVALID_HANDLE_VALUE.
// (Does not throw an exception)
HANDLE CHttpToolA::CreateFileAlwaysToWrite (LPCSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::CreateFileAlwaysToWrite: szFilePath can not be NULL.") ;
return ::CreateFileA (
szFilePath
, GENERIC_WRITE
, 0
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL) ;
}
DWORD CHttpToolA::GetFileSize (HANDLE hFile, LPCSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolA::GetFileSize: hFile can not be NULL.") ;
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::GetFileSize: szFilePath can not be NULL.") ;
DWORD dwFileSize = ::GetFileSize (hFile, NULL) ;
if ( dwFileSize == INVALID_FILE_SIZE )
ThrowException (HTTPCLIENT_ERR_GETFILESIZE_FAILED, ::GetLastError (), szFilePath) ;
return dwFileSize ;
}
// The file handle must point to BOF. If the file handle is INVALID_HANDLE_VALUE
// The default mime type is returned.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::GetMimeType (HANDLE hFile, UINT CodePage)
throw (Exception &)
{
HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolA::GetMimeType: hFile can not be NULL.") ;
LPSTR szMimeType = NULL ;
try {
szMimeType = CHttpToolW::GetMimeType (hFile, CodePage) ;
} catch (CHttpToolW::Exception & e) {
ThrowException (e) ;
}
return szMimeType ;
}
// Returns the HTTP status text from the HTTP request handle.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::GetStatusText (HINTERNET hRequest)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::GetStatusText: hRequest can not be NULL.") ;
DWORD cbBuff = 0 ;
LPSTR szStatusText = NULL ;
// Get required buffer size
if ( !::HttpQueryInfoA (
hRequest
, HTTP_QUERY_STATUS_TEXT // Get the status text
, static_cast (szStatusText)
, &cbBuff // Required buffer size (byte)
, NULL // Don't use a header index
)
)
if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER )
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
_ASSERTE ( cbBuff != 0 ) ;
szStatusText = (LPSTR) ::malloc (cbBuff) ;
if ( szStatusText == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
// Get the status text
if ( !::HttpQueryInfoA (
hRequest
, HTTP_QUERY_STATUS_TEXT // Get the status text
, static_cast (szStatusText)
, &cbBuff // Allocated buffer size (byte)
, NULL // Don't use a header index
)
) {
::free (szStatusText) ;
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
}
return szStatusText ;
}
// Get the HTTP header which has the name specified by szName from the HTTP request handle.
// If the header is not found, NULL is returned.
// The returned string must be freed by using the ::free () function.
// pnIdx exactly corresponds to the lpdwIndex parameter in the ::HttpQueryInfo function.
// For more information about this parameter, see microsoft's SDK documentation.
LPSTR CHttpToolA::GetHeader (HINTERNET hRequest, LPCSTR szName, DWORD * pnIdx)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::GetHeader: hRequest can not be NULL.") ;
HTTPTOOL_ASSERT (szName != NULL, "CHttpToolA::GetHeader: szName can not be NULL.") ;
DWORD nOrigIdx = 0 ;
if ( pnIdx ) nOrigIdx = *pnIdx ;
// Copy the header name
::SafeInt cbBuff ;
try {
cbBuff = ::strlen (szName) ;
cbBuff++ ;
cbBuff *= sizeof (CHAR) ;
} catch (::SafeIntException & e) {
ThrowException (e) ;
}
PSTR szHeader = (PSTR) ::malloc (cbBuff.Value ()) ;
if ( szHeader == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
::strcpy (szHeader, szName) ;
if ( ::HttpQueryInfoA (
hRequest
, HTTP_QUERY_CUSTOM // Get a custom header
, static_cast (szHeader)
, cbBuff.Ptr ()
, pnIdx
)
)
return szHeader ;
SAFEFREE (szHeader) ;
if ( pnIdx ) *pnIdx = nOrigIdx ;
// If the function failed
if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER ) {
// If the header does not exist
if ( ::GetLastError () == ERROR_HTTP_HEADER_NOT_FOUND )
return NULL ;
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
}
// Allocates required memory
szHeader = (LPSTR) ::malloc (cbBuff.Value ()) ;
if ( szHeader == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
::strcpy (szHeader, szName) ;
if ( !::HttpQueryInfoA (
hRequest
, HTTP_QUERY_CUSTOM // Get a custom header
, static_cast (szHeader)
, cbBuff.Ptr ()
, pnIdx
)
) {
SAFEFREE (szHeader) ;
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
}
return szHeader ;
}
void CHttpToolA::InternetSetOption (HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
throw (Exception &)
{
if ( ::InternetSetOptionA (hInternet, dwOption, lpBuffer, dwBufferLength) )
return ;
ThrowException (HTTPCLIENT_ERR_INTERNETSETOPTION_FAILED, ::GetLastError ()) ;
}
// Generates a new upload boundary. If an error occurs, NULL is returned
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::CreateUploadBoundary (void)
throw ()
{
GUID guid ;
if ( FAILED ( ::CoCreateGuid (&guid)) )
return NULL ;
PSTR szBoundary = (PSTR) ::malloc (sizeof (CHAR) * 44) ;
if ( szBoundary == NULL )
return NULL ;
::sprintf (szBoundary, "----LYOUL-%.08x%.04x%.04x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x-"
, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1]
, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return szBoundary ;
}
///////////////////////////////////////// CHttpToolA /////////////////////////////////////////
///////////////////////////////////////// CHttpToolW /////////////////////////////////////////
inline
LPCWSTR CHttpToolW::GetConstMessage (int nIdx)
throw ()
{
// user-defined error message
if ( nIdx >= 1000 )
return g_UsrErrMsgW[0] ;
// Win32 API error (which has a win32 error code)
if ( nIdx >= 600 )
return g_Win32MsgWin32W[nIdx % 100] ;
// WinInet error (which has a win32 error code)
if ( nIdx >= 400 )
return g_WinInetMsgWin32W[nIdx % 100] ;
// Normal error (which has a win32 error code)
if ( nIdx >= 200 )
return g_NormalMsgWin32W[nIdx % 100] ;
// Normal error
if ( nIdx >= 100 )
return g_NormalMsgW[nIdx % 100] ;
return g_NotSpecifiedW[nIdx] ;
}
void CHttpToolW::ThrowException (DWORD nErrMsgIdx)
throw (Exception &)
{
throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx) ;
}
void CHttpToolW::ThrowException (LPCWSTR szErrMsg, DWORD nErrMsgIdx)
throw (Exception &)
{
throw Exception (szErrMsg, nErrMsgIdx) ;
}
void CHttpToolW::ThrowException (DWORD nErrMsgIdx, DWORD dwErrCode, LPCWSTR szStrArg)
throw (Exception &)
{
if ( szStrArg == NULL )
throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx, dwErrCode) ;
WCHAR szErrMsg[512] ;
LPCWSTR szFormat = GetConstMessage (nErrMsgIdx) ;
int cchWritten = SNPrintf (szErrMsg, 511, szFormat, szStrArg ? szStrArg : L"NULL") ;
// if an error occurs
if ( cchWritten < -1 )
throw Exception (szFormat, nErrMsgIdx, dwErrCode) ;
if ( cchWritten == -1 )
cchWritten = 511 ;
szErrMsg[cchWritten] = NULL ;
throw Exception (szErrMsg, nErrMsgIdx, dwErrCode) ;
}
void CHttpToolW::ThrowException (LPCSTR szErrMsg, DWORD nErrMsgIdx, DWORD dwErrCode)
throw (Exception &)
{
LPWSTR szErrMsgW = NULL ;
try {
szErrMsgW = Ansi2Unicode (szErrMsg) ;
} catch (Exception &) {
;
}
Exception objException (szErrMsgW, nErrMsgIdx, dwErrCode) ;
SAFEFREE (szErrMsgW) ;
throw objException ;
}
void CHttpToolW::ThrowException (httpclientexceptionA & e)
throw (Exception &)
{
ThrowException (e.errmsg (), e.LastError (), e.Win32LastError ()) ;
}
void CHttpToolW::ThrowException (::SafeIntException & e)
throw (Exception &)
{
switch ( e.m_code ) {
case ERROR_ARITHMETIC_OVERFLOW:
ThrowException (HTTPCLIENT_ERR_ARITHMETIC_OVERFLOW) ;
break ;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
ThrowException (HTTPCLIENT_ERR_INT_DIVIDE_BY_ZERO) ;
break ;
default:
ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ARITHMETIC_ERROR) ;
break ;
}
}
// Conversion methods
// This function returns a converted ansi string from a unicode string.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolW::Unicode2Ansi (LPCWSTR szStr, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolW::Unicode2Ansi: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
if ( szStr == NULL )
return NULL ;
int cchNeeded ;
if ( 0 == (cchNeeded = ::WideCharToMultiByte (CodePage, 0, szStr, -1, NULL, 0, NULL, NULL)) )
ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
PSTR szAnsi = (PSTR) ::malloc (sizeof (CHAR) * cchNeeded) ;
if ( szAnsi == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
if ( 0 == ::WideCharToMultiByte (CodePage, 0, szStr, -1, szAnsi, cchNeeded, NULL, NULL) ) {
SAFEFREE (szAnsi) ;
ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}
return szAnsi ;
}
// This method returns a converted unicode string from a ansi string.
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolW::Ansi2Unicode (LPCSTR szStr, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolW::Ansi2Unicode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
if ( szStr == NULL )
return NULL ;
int cchNeeded ;
if ( 0 == (cchNeeded = ::MultiByteToWideChar (CodePage, 0, szStr, -1, NULL, 0)) )
ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
PWSTR szUni = (PWSTR) ::malloc (sizeof (WCHAR) * cchNeeded) ;
if ( szUni == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
if ( 0 == ::MultiByteToWideChar (CodePage, 0, szStr, -1, szUni, cchNeeded) ) {
SAFEFREE (szUni) ;
ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
}
return szUni ;
}
// comparison function (used by STL multimap)
bool CHttpToolW::operator () (LPCWSTR szKey1, LPCWSTR szKey2) const
throw ()
{
// return true if the two strings are null
if ( szKey1 == NULL && szKey2 == NULL )
return true ;
if ( szKey1 == NULL )
return true ;
if ( szKey2 == NULL )
return false ;
// case insensitive
return ::wcsicmp (szKey1, szKey2) < 0 ;
}
// Initializes a internet handle.
HINTERNET CHttpToolW::OpenInternet (LPCWSTR szUserAgent, DWORD dwAccessType
, LPCWSTR szProxyName, LPCWSTR szProxyBypass, DWORD dwFlags)
throw (Exception &)
{
HINTERNET hInternet ;
if ( NULL == (hInternet = InternetOpenW (
szUserAgent // user agent
, dwAccessType // use direct connection or proxy connection
, szProxyName
, szProxyBypass
, dwFlags)
) )
ThrowException (HTTPCLIENT_ERR_INTERNETOPEN_FAILED, ::GetLastError ()) ;
return hInternet ;
}
// Closes a internet handle
void CHttpToolW::CloseInternet (HINTERNET & hInternet)
throw ()
{
CHttpToolA::CloseInternet (hInternet) ;
}
// Returns a connection handle
// The hInternet must be a valid internet handle.
HINTERNET CHttpToolW::OpenConnection (HINTERNET hInternet, LPCWSTR szServerAddr, INTERNET_PORT nPort
, LPCWSTR szUsrName, LPCWSTR szUsrPwd)
throw (Exception &)
{
HTTPTOOL_ASSERT (hInternet != NULL, "CHttpToolW::OpenConnection: hInternet can not be NULL.") ;
HTTPTOOL_ASSERT (szServerAddr != NULL, "CHttpToolW::OpenConnection: szServerAddr can not be NULL.") ;
HTTPTOOL_ASSERT (::wcslen (szServerAddr) != 0, "CHttpToolW::OpenConnection: szServerAddr can not be an empty string.") ;
HINTERNET hConnection ;
if ( NULL == (hConnection = ::InternetConnectW (
hInternet
, szServerAddr
, nPort
, szUsrName
, szUsrPwd
, INTERNET_SERVICE_HTTP
, 0
, NULL)
) )
ThrowException (HTTPCLIENT_ERR_INTERNETCONNECT_FAILED, ::GetLastError ()) ;
return hConnection ;
}
// Closes a connection handle
void CHttpToolW::CloseConnection (HINTERNET & hConnection)
throw ()
{
CHttpToolA::CloseConnection (hConnection) ;
}
// Returns a HTTP request handle
HINTERNET CHttpToolW::OpenRequest (HINTERNET hConnection, LPCWSTR szMethod, LPCWSTR szObjectName
, DWORD dwFlags, LPCWSTR szReferer, UINT CodePage)
throw (Exception &)
{
HTTPTOOL_ASSERT (hConnection != NULL, "CHttpToolW::OpenRequest: hConnection can not be NULL.") ;
HTTPTOOL_ASSERT (szObjectName != NULL, "CHttpToolW::OpenRequest: szObjectName can not be NULL.") ;
HTTPTOOL_ASSERT (::wcslen (szObjectName) != 0, "CHttpToolW::OpenRequest: szObjectName can not be an empty string.") ;
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolW::OpenRequest: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
LPSTR szMethodA = NULL ;
LPSTR szObjectNameA = NULL ;
LPSTR szRefererA = NULL ;
HINTERNET hRequest = NULL ;
try {
szMethodA = Unicode2Ansi (szMethod, CodePage) ;
szObjectNameA = Unicode2Ansi (szObjectName, CodePage) ;
szRefererA = Unicode2Ansi (szReferer, CodePage) ;
hRequest = CHttpToolA::OpenRequest (hConnection, szMethodA, szObjectNameA
, dwFlags, szRefererA) ;
} catch (Exception &) {
SAFEFREE (szMethodA) ;
SAFEFREE (szObjectNameA) ;
SAFEFREE (szRefererA) ;
throw ;
} catch (CHttpToolA::Exception & e) {
SAFEFREE (szMethodA) ;
SAFEFREE (szObjectNameA) ;
SAFEFREE (szRefererA) ;
ThrowException (e) ;
}
SAFEFREE (szMethodA) ;
SAFEFREE (szObjectNameA) ;
SAFEFREE (szRefererA) ;
return hRequest ;
}
// Closes a request handle
void CHttpToolW::CloseRequest (HINTERNET & hRequest)
throw ()
{
CHttpToolA::CloseRequest (hRequest) ;
}
void CHttpToolW::AddHeader (HINTERNET hRequest, LPCWSTR szName, LPCWSTR szValue, UINT CodePage)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::AddHeader: hRequest can not be NULL.") ;
HTTPTOOL_ASSERT (szName != NULL, "CHttpToolW::AddHeader: szName can not be NULL.") ;
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolW::AddHeader: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
LPSTR szNameA = NULL ;
LPSTR szValueA = NULL ;
try {
szNameA = Unicode2Ansi (szName, CodePage) ;
szValueA = Unicode2Ansi (szValue, CodePage) ;
CHttpToolA::AddHeader (hRequest, szNameA, szValueA) ;
} catch (Exception &) {
SAFEFREE (szNameA) ;
SAFEFREE (szValueA) ;
throw ;
} catch (CHttpToolA::Exception & e) {
SAFEFREE (szNameA) ;
SAFEFREE (szValueA) ;
ThrowException (e) ;
}
SAFEFREE (szNameA) ;
SAFEFREE (szValueA) ;
}
void CHttpToolW::SendRequest (HINTERNET hRequest, LPCWSTR szPosted, UINT CodePage)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::SendRequest: hRequest can not be NULL.") ;
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolW::SendRequest: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
LPSTR szPostedA = Unicode2Ansi (szPosted, CodePage) ;
::SafeInt cchPosted ;
try {
cchPosted = szPostedA ? ::strlen (szPostedA) : 0 ;
} catch (::SafeIntException & e) {
SAFEFREE (szPostedA) ;
ThrowException (e) ;
}
if ( !::HttpSendRequestW (
hRequest
, NULL // Additional header
, 0 // The length of the additional header
, (void *) szPostedA // A posted data
, cchPosted.Value () // The length of the posted data
) ) {
SAFEFREE (szPostedA) ;
ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUEST_FAILED, ::GetLastError ()) ;
}
SAFEFREE (szPostedA) ;
}
void CHttpToolW::SendRequestEx (HINTERNET hRequest, DWORD dwPostedSize)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::SendRequestEx: hRequest can not be NULL.") ;
INTERNET_BUFFERSW BufferIn;
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will occur
BufferIn.Next = NULL;
BufferIn.lpcszHeader = NULL;
BufferIn.dwHeadersLength = 0;
BufferIn.dwHeadersTotal = 0;
BufferIn.lpvBuffer = NULL;
BufferIn.dwBufferLength = 0;
BufferIn.dwBufferTotal = dwPostedSize; // This is the only member used other than dwStructSize
BufferIn.dwOffsetLow = 0;
BufferIn.dwOffsetHigh = 0;
if ( !::HttpSendRequestExW (
hRequest
, &BufferIn
, NULL
, 0
, 0
) )
ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUESTEX_FAILED, ::GetLastError ()) ;
}
void CHttpToolW::InternetWriteFile (HINTERNET hRequest, const BYTE * pbyBuff, DWORD cbyBuff)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::InternetWriteFile: hRequest can not be NULL.") ;
HTTPTOOL_ASSERT (pbyBuff != NULL, "CHttpToolW::InternetWriteFile: pbyBuff can not be NULL.") ;
HTTPTOOL_ASSERT (cbyBuff != 0, "CHttpToolW::InternetWriteFile: cbyBuff can not be zero.") ;
DWORD dwTotalWritten = 0, dwWritten ;
while ( dwTotalWritten < cbyBuff ) {
if ( !::InternetWriteFile (
hRequest
, pbyBuff + dwTotalWritten
, cbyBuff - dwTotalWritten
, &dwWritten
)
)
ThrowException (HTTPCLIENT_ERR_INTERNETWRITEFILE_FAILED, ::GetLastError ()) ;
dwTotalWritten += dwWritten ;
}
}
void CHttpToolW::EndRequest (HINTERNET hRequest)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::EndRequest: hRequest can not be NULL.") ;
if ( !::HttpEndRequest (
hRequest
, NULL
, 0
, 0
)
)
ThrowException (HTTPCLIENT_ERR_HTTPENDREQUEST_FAILED, ::GetLastError ()) ;
}
// Checks whether the file exists.
// (Does not throw an exception)
BOOL CHttpToolW::FileExists (LPCWSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::FileExists: szFilePath can not be NULL.") ;
HANDLE hFile = ::CreateFileW (
szFilePath
, 0
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL) ;
if ( hFile != INVALID_HANDLE_VALUE )
{
::CloseHandle (hFile) ;
return TRUE ;
}
return FALSE ;
}
// It returns a INVALID_HANDLE_VALUE if the specified file is not valid.
// (Does not throw an exception)
HANDLE CHttpToolW::OpenFile (LPCWSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::OpenFile: szFilePath can not be NULL.") ;
return ::CreateFileW (
szFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
, NULL) ;
}
// If it fails to create an file, it will return INVALID_HANDLE_VALUE.
// (Does not throw an exception)
HANDLE CHttpToolW::CreateFileAlwaysToWrite (LPCWSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::CreateFileAlwaysToWrite: szFilePath can not be NULL.") ;
return ::CreateFileW (
szFilePath
, GENERIC_WRITE
, 0
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL) ;
}
DWORD CHttpToolW::GetFileSize (HANDLE hFile, LPCWSTR szFilePath)
throw (Exception &)
{
HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolW::GetFileSize: hFile can not be NULL.") ;
HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::GetFileSize: szFilePath can not be NULL.") ;
DWORD dwFileSize = ::GetFileSize (hFile, NULL) ;
if ( dwFileSize == INVALID_FILE_SIZE )
ThrowException (HTTPCLIENT_ERR_GETFILESIZE_FAILED, ::GetLastError (), szFilePath) ;
return dwFileSize ;
}
// The file handle must point to BOF. If the file handle is INVALID_HANDLE_VALUE
// The default mime type is returned.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolW::GetMimeType (HANDLE hFile, UINT CodePage)
throw (Exception &)
{
HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolW::GetMimeType: hFile can not be NULL.") ;
// The unicode encodings are not allowed
HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpToolW::GetMimeType: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
// If the file handle is not valid, just returns the default MimeType.
if ( hFile == INVALID_HANDLE_VALUE ) {
PSTR szMimeA = NULL ;
szMimeA = (PSTR) ::malloc (sizeof (CHAR) * (::strlen (HTTPCLIENT_DEF_MIMETYPE) + 1)) ;
if ( szMimeA == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
::strcpy (szMimeA, HTTPCLIENT_DEF_MIMETYPE) ;
return szMimeA ;
}
BYTE byBuff[256] ;
DWORD dwRead ;
if ( 0 == ::ReadFile (hFile
, byBuff
, 256
, &dwRead
, NULL) )
ThrowException (HTTPCLIENT_ERR_READFILE_FAILED, ::GetLastError ()) ;
// Moves the file pointer to the beginning of the file.
if ( INVALID_SET_FILE_POINTER == ::SetFilePointer (hFile, 0, NULL, FILE_BEGIN) )
ThrowException (HTTPCLIENT_ERR_SETFILEPOINTER_FAILED, ::GetLastError ()) ;
PWSTR szMimeW = NULL ;
PSTR szMimeA = NULL ;
HRESULT hResult ;
// If the ::FindMimeFromData function failed to get a appropriate MimeType,
// just returns the default MimeType.
if ( NOERROR != (hResult = ::FindMimeFromData (
NULL
, NULL
, byBuff
, dwRead
, NULL
, 0
, &szMimeW
, 0)) ) {
szMimeA = (PSTR) ::malloc (sizeof (CHAR) * (::strlen (HTTPCLIENT_DEF_MIMETYPE) + 1)) ;
if ( szMimeA == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
::strcpy (szMimeA, HTTPCLIENT_DEF_MIMETYPE) ;
return szMimeA ;
}
try {
szMimeA = Unicode2Ansi (szMimeW, CodePage) ;
} catch (Exception &) {
::CoTaskMemFree (szMimeW) ;
throw ;
}
::CoTaskMemFree (szMimeW) ;
return szMimeA ;
}
// Returns the HTTP status text from the HTTP request handle.
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolW::GetStatusText (HINTERNET hRequest)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::GetStatusText: hRequest can not be NULL.") ;
DWORD cbBuff = 0 ;
PWSTR szStatusText = NULL ;
// Get required buffer size
if ( !::HttpQueryInfoW (
hRequest
, HTTP_QUERY_STATUS_TEXT // Get the status text
, static_cast (szStatusText)
, &cbBuff // Required buffer size (byte)
, NULL // Don't use a header index
)
)
if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER )
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
_ASSERTE ( cbBuff != 0 ) ;
szStatusText = (PWSTR) ::malloc (cbBuff) ;
if ( szStatusText == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
// Get the status text
if ( !::HttpQueryInfoW (
hRequest
, HTTP_QUERY_STATUS_TEXT // Get the status text
, static_cast (szStatusText)
, &cbBuff // Required buffer size (byte)
, NULL // Don't use a header index
)
) {
::free (szStatusText) ;
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
}
return szStatusText ;
}
// Get the HTTP header which has the name specified by szName from the HTTP request handle.
// If the header is not found, NULL is returned.
// The returned string must be freed by using the ::free () function.
// pnIdx exactly corresponds to the lpdwIndex parameter in the ::HttpQueryInfo function.
// For more information about this parameter, see microsoft's SDK documentation.
LPWSTR CHttpToolW::GetHeader (HINTERNET hRequest, LPCWSTR szName, DWORD * pnIdx)
throw (Exception &)
{
HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::GetHeader: hRequest can not be NULL.") ;
HTTPTOOL_ASSERT (szName != NULL, "CHttpToolW::GetHeader: szName can not be NULL.") ;
DWORD nOrigIdx = 0 ;
if ( pnIdx ) nOrigIdx = *pnIdx ;
// Copy the header name
::SafeInt cbBuff ;
try {
cbBuff = ::wcslen (szName) ;
cbBuff++ ; // for '\0' character
cbBuff *= sizeof (WCHAR) ;
} catch (::SafeIntException & e) {
ThrowException (e) ;
}
PWSTR szHeader = (PWSTR) ::malloc (cbBuff.Value ()) ;
if ( szHeader == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
::wcscpy (szHeader, szName) ;
if ( ::HttpQueryInfoW (
hRequest
, HTTP_QUERY_CUSTOM // Get a custom header
, static_cast (szHeader)
, cbBuff.Ptr ()
, pnIdx
)
)
return szHeader ;
SAFEFREE (szHeader) ;
if ( pnIdx ) *pnIdx = nOrigIdx ;
// If the function failed
if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER ) {
// If the header does not exist
if ( ::GetLastError () == ERROR_HTTP_HEADER_NOT_FOUND )
return NULL ;
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
}
// Allocates required memory
szHeader = (PWSTR) ::malloc (cbBuff.Value ()) ;
if ( szHeader == NULL )
ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
::wcscpy (szHeader, szName) ;
if ( !::HttpQueryInfoW (
hRequest
, HTTP_QUERY_CUSTOM // Get a custom header
, static_cast (szHeader)
, cbBuff.Ptr ()
, pnIdx
)
) {
SAFEFREE (szHeader) ;
ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
}
return szHeader ;
}
void CHttpToolW::InternetSetOption (HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
throw (Exception &)
{
if ( ::InternetSetOptionW (hInternet, dwOption, lpBuffer, dwBufferLength) )
return ;
ThrowException (HTTPCLIENT_ERR_INTERNETSETOPTION_FAILED, ::GetLastError ()) ;
}
// Generates a new upload boundary. If an error occurs, NULL is returned
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolW::CreateUploadBoundary (void)
throw ()
{
GUID guid ;
if ( FAILED ( ::CoCreateGuid (&guid)) )
return NULL ;
PWSTR szBoundary = NULL ;
szBoundary = (PWSTR) ::malloc (sizeof (WCHAR) * 44) ;
if ( szBoundary == NULL )
return NULL ;
::swprintf (szBoundary, L"----LYOUL-%.08x%.04x%.04x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x-"
, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1]
, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return szBoundary ;
}
///////////////////////////////////////// CHttpToolW /////////////////////////////////////////
///////////////////////////////////////// CHttpClientMapT /////////////////////////////////////////
template
CHttpClientMapT::CHttpClientMapT (void)
throw ()
{
; // do nothing yet
}
template
CHttpClientMapT::~CHttpClientMapT (void)
throw ()
{
Clear () ;
}
// If some data cleared, it will return TRUE, otherwise return FALSE.
// If memory is exhausted, it can leak memory.
template
BOOL CHttpClientMapT::Clear (void)
throw ()
{
if ( m_map.empty () )
return FALSE ;
// Allocates memory to save pointers for key name.
// If an overflow exception or a memory allocation failure is occurs,
// The memory pointed by the key name pointer are leaked.
::SafeInt cKeys = m_map.size () ;
::SafeInt cbRequired = 0 ;
PCSZ * arrKeys = NULL ;
try {
cbRequired = cKeys ;
cbRequired *= sizeof (PCSZ) ;
arrKeys = (PCSZ *) ::malloc (cbRequired.Value ()) ;
} catch (::SafeIntException &) {
arrKeys = NULL ;
}
MapSizeType nIdx = 0 ;
for (MapIter iter = m_map.begin (); iter != m_map.end (); ++iter) {
(iter->second).Delete () ;
// Saves the key name pointer
if ( arrKeys )
arrKeys[nIdx++] = iter->first ;
}
m_map.clear () ;
if ( arrKeys == NULL )
return TRUE ;
for (nIdx = 0; nIdx < cKeys; nIdx++)
SAFEFREE ( arrKeys[nIdx] ) ;
SAFEFREE ( arrKeys ) ;
return TRUE ;
}
// This function deletes an element in position nIdx from the map.
// If the element deleted, it will return TRUE, otherwise return FALSE.
template
BOOL CHttpClientMapT::Remove (DWORD nIdx)
throw ()
{
if ( m_map.empty () )
return FALSE ;
// Find the item to delete
MapIter iter = m_map.begin () ;
for (DWORD i = 0; i < nIdx; i++) {
++iter ;
// If the nIdx is out of range.
if ( iter == m_map.end () )
return FALSE ;
}
(iter->second).Delete () ;
PCSZ szName = iter->first ;
m_map.erase (iter) ;
SAFEFREE (szName) ;
return TRUE ;
}
// This function erases all elements of which key name equals to szName.
// If some data cleared, it will return TRUE, otherwise return FALSE.
// If memory is exhausted, memory will be leaked.
template
BOOL CHttpClientMapT::RemoveAll (PCSZ szName)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::RemoveAll: szName can not be NULL.") ;
::SafeInt cKeys = m_map.count (szName) ;
// If the specified key name does not exist in the map
if ( cKeys.Value () == 0 )
return FALSE ;
// Allocates memory to save pointers to key name.
// If the allocation failed, the memory pointed by the key name pointer are leaked.
::SafeInt cbRequired ;
PCSZ * arrKeys = NULL ;
try {
cbRequired = cKeys ;
cbRequired *= sizeof (PCSZ) ;
arrKeys = (PCSZ *) ::malloc (cbRequired.Value ()) ;
} catch (::SafeIntException &) {
arrKeys = NULL ;
}
std::pair pairIter = m_map.equal_range (szName) ;
MapSizeType nIdx = 0 ;
for (MapIter iter = pairIter.first; iter != pairIter.second; ++iter) {
(iter->second).Delete () ;
// Saves the key name pointer
if ( arrKeys )
arrKeys[nIdx++] = iter->first ;
}
m_map.erase (pairIter.first, pairIter.second) ;
if ( arrKeys == NULL )
return TRUE ;
for (nIdx = 0; nIdx < cKeys; nIdx++)
SAFEFREE ( arrKeys[nIdx] ) ;
SAFEFREE ( arrKeys ) ;
return TRUE ;
}
// This function deletes an element of which the key name equals to szName and the index is nIdx.
// If some data cleared, it will return TRUE, otherwise return FALSE.
template
BOOL CHttpClientMapT::Remove (PCSZ szName, DWORD nIdx)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Remove: szName can not be NULL.") ;
std::pair pairIter = m_map.equal_range (szName) ;
// If the specified key name does not exist in the map
if ( pairIter.first == pairIter.second )
return FALSE ;
MapIter iter = pairIter.first ;
for (DWORD i = 0; i < nIdx; i++) {
++iter ;
// If the nIdx is out of range.
if ( iter == pairIter.second )
return FALSE ;
}
// Deletes the element
(iter->second).Delete () ;
PCSZ szKeyName = iter->first ;
m_map.erase (iter) ;
SAFEFREE (szKeyName) ;
return TRUE ;
}
template
BOOL CHttpClientMapT::Exists (PCSZ szName, DWORD nIdx)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Exists: szName can not be NULL.") ;
::SafeInt cKeys = m_map.count (szName) ;
return (cKeys > nIdx) ;
}
// If the szName is NULL, it will return the count of elements.
template
DWORD CHttpClientMapT::Count (PCSZ szName)
throw ()
{
::SafeInt cKeys ;
try {
cKeys = szName ? m_map.count (szName) : m_map.size () ;
} catch (::SafeIntException &) {
cKeys = cKeys.MaxInt () ; // This statement is never executed.
}
return cKeys.Value () ;
}
template
BOOL CHttpClientMapT::Empty (void) const
throw ()
{
return m_map.empty () ? TRUE : FALSE ;
}
// If the nIdx is not valid, it will return NULL.
template
typename CHttpClientMapT::PCSZ
CHttpClientMapT::GetKey (DWORD nIdx)
throw ()
{
if ( m_map.empty () )
return NULL ;
MapIter iter = m_map.begin () ;
for (DWORD i = 0; i < nIdx; i++) {
++iter ;
// If the nIdx is not valid.
if ( iter == m_map.end () )
return NULL ;
}
return iter->first ;
}
// If the specified element is not found in position nIdx,
// returns MapValue of which szValue is NULL.
template
typename CHttpClientMapT::MapValue
CHttpClientMapT::Get (DWORD nIdx)
throw ()
{
MapValue mapValue = { NULL, 0 } ;
if ( m_map.empty () )
return mapValue ;
MapIter iter = m_map.begin () ;
for (DWORD i = 0; i < nIdx; i++) {
++iter ;
// If the nIdx is not valid.
if ( iter == m_map.end () )
return mapValue ;
}
mapValue = iter->second ;
if ( mapValue.szValue == NULL )
mapValue.szValue = HttpTool::szEmptyString ;
return mapValue ;
}
// If the specified element is not found, it will return NULL.
template
typename CHttpClientMapT::PCSZ
CHttpClientMapT::GetValue (DWORD nIdx)
throw ()
{
return Get (nIdx).szValue ;
}
// Returns 0 if the specified element is not found.
// (If the dwFlag in MapValue is 0, it also returns 0)
template
DWORD CHttpClientMapT::GetFlag (DWORD nIdx)
throw ()
{
return Get (nIdx).dwFlag ;
}
// If the specified element is not found, returns MapValue of which szValue is NULL.
template
typename CHttpClientMapT::MapValue
CHttpClientMapT::Get (PCSZ szName, DWORD nIdx)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Get: szName can not be NULL.") ;
MapValue mapValue = { NULL, 0 } ;
std::pair pairIter ;
pairIter = m_map.equal_range (szName) ;
// If the key is not found.
if ( pairIter.first == pairIter.second )
return mapValue ;
MapIter iter = pairIter.first ;
for (DWORD i = 0; i < nIdx; i++) {
++iter ;
// If the nIdx is not valid
if ( iter == pairIter.second )
return mapValue ;
}
mapValue = iter->second ;
if ( mapValue.szValue == NULL )
mapValue.szValue = HttpTool::szEmptyString ;
return mapValue ;
}
// Returns NULL if the specified element is not found.
template
typename CHttpClientMapT::PCSZ
CHttpClientMapT::GetValue (PCSZ szName, DWORD nIdx)
throw ()
{
return Get (szName, nIdx).szValue ;
}
// Returns 0 if the specified element is not found.
// (If the dwFlag in MapValue is 0, it also returns 0)
template
DWORD CHttpClientMapT::GetFlag (PCSZ szName, DWORD nIdx)
throw ()
{
return Get (szName, nIdx).dwFlag ;
}
// Adds a new MapValue. It also receives the ownership of memory pointed by szName and szValue.
// The szName and szValue must be allocated by using ::malloc.
// The szValue could be NULL.
template
void CHttpClientMapT::AddPointerDirectly (PSZ szName, PSZ szValue, BOOL dwFlag)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::AddPointerDirectly: szName can not be NULL.") ;
try {
// Checks the arithmetic overflow exception
::SafeInt cKeys = Count () ;
::SafeInt cdwKeys = cKeys ;
cKeys++ ;
cdwKeys++ ;
MapValue newValue = { (PCSZ) szValue, dwFlag } ;
m_map.insert (MapItem ((PCSZ) szName, newValue)) ;
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
} catch (std::exception & e) {
HttpTool::ThrowException (e.what (), HTTPCLIENT_ERR_STD_EXCEPTION) ;
} catch (...) {
HttpTool::ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ERROR) ;
}
}
// The szValue could be NULL.
template
void CHttpClientMapT::Add (PCSZ szName, PCSZ szValue, BOOL dwFlag)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Add: szName can not be NULL.") ;
PSZ szNewName = NULL ;
PSZ szNewValue = NULL ;
::SafeInt cbName, cbValue ;
try {
cbName = HttpTool::StringLen (szName) ;
cbName++ ;
cbName *= sizeof (CharType) ;
szNewName = (PSZ) ::malloc (cbName.Value ()) ;
if ( szNewName == NULL )
HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
HttpTool::StringCopy (szNewName, szName) ;
if ( szValue != NULL ) {
cbValue = HttpTool::StringLen (szValue) ;
cbValue++ ;
cbValue *= sizeof (CharType) ;
szNewValue = (PSZ) ::malloc (cbValue.Value ()) ;
if ( szNewValue == NULL )
HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
HttpTool::StringCopy (szNewValue, szValue) ;
}
AddPointerDirectly (szNewName, szNewValue, dwFlag) ;
} catch (::SafeIntException & e) {
SAFEFREE (szNewName) ;
SAFEFREE (szNewValue) ;
HttpTool::ThrowException (e) ;
} catch (Exception &) {
SAFEFREE (szNewName) ;
SAFEFREE (szNewValue) ;
throw ;
}
}
// If the specified element is not found, it will add a new value.
// otherwise it will modifiy the existing value.
// The szValue could be NULL.
template
void CHttpClientMapT::Set (PCSZ szName, PCSZ szValue, BOOL dwFlag, DWORD nIdx)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Set: szName can not be NULL.") ;
std::pair pairIter ;
pairIter = m_map.equal_range (szName) ;
// Adds a new value if the specified element is not found.
if ( pairIter.first == pairIter.second ) {
// The nIdx must be 0
_ASSERTE ( nIdx == 0 ) ;
Add (szName, szValue, dwFlag) ;
return ;
}
MapIter iter = pairIter.first ;
for (DWORD i = 0; i < nIdx; i++) {
++iter ;
// The nIdx must be valid
_ASSERTE ( iter != pairIter.second ) ;
}
MapValue newValue = { NULL, dwFlag } ;
if ( szValue ) {
::SafeInt cbValue ;
try {
cbValue = HttpTool::StringLen (szValue) ;
cbValue++ ;
cbValue *= sizeof (CharType) ;
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
newValue.szValue = (PCSZ) ::malloc (cbValue.Value ()) ;
if ( newValue.szValue == NULL )
HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
HttpTool::StringCopy ((PSZ) newValue.szValue, szValue) ;
}
MapValue oldValue = iter->second ;
iter->second = newValue ;
oldValue.Delete () ;
}
template
typename CHttpClientMapT::ConstMapIter
CHttpClientMapT::Begin (void) const
throw ()
{
return m_map.begin () ;
}
template
typename CHttpClientMapT::ConstMapIter
CHttpClientMapT::End (void) const
throw ()
{
return m_map.end () ;
}
///////////////////////////////////////// CHttpClientMapT /////////////////////////////////////////
///////////////////////////////////////// CHttpEncoder /////////////////////////////////////////
/*!
* This method has no meaning because the input string is an Ansi string.
* It just returns the length of the input string.
*
* \param szStr [in] A string which is encoded.
* \param CodePage [in] Ignored.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderA::AnsiEncodeLen (PCSTR szStr, UINT /* CodePage */)
throw (Exception &)
{
if ( szStr == NULL || szStr[0] == '\0' )
return 0 ;
::SafeInt dwLen ;
try {
dwLen = ::strlen (szStr) ;
} catch (::SafeIntException & e) {
CHttpToolA::ThrowException (e) ;
}
return dwLen.Value () ;
}
/*!
* This method has no meaning because the input string is an Ansi string.
* It just returns the copy of the input string.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param CodePage [in] Ignored.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderA::AnsiEncode (PSTR szBuff, PCSTR szStr, UINT /* CodePage */)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::AnsiEncode: szBuff can not be NULL.") ;
if ( (szStr == NULL) || (szStr[0] == '\0') ) {
szBuff[0] = '\0' ;
return szBuff ;
}
return ::strcpy (szBuff, szStr) ;
}
/*!
* This method has no meaning because the decoded string is an Ansi string.
* It just returns the length of the input string.
*
* \param szEncoded [in] A string to decode.
* \param CodePage [in] Ignored.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderA::AnsiDecodeLen (PCSTR szEncoded, UINT /* CodePage */)
throw (Exception &)
{
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt dwLen ;
try {
dwLen = ::strlen (szEncoded) ;
} catch (::SafeIntException & e) {
CHttpToolA::ThrowException (e) ;
}
return dwLen.Value () ;
}
/*!
* This method has no meaning because the decoded string is an Ansi string.
* It just returns the copy of the input string.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param CodePage [in] Ignored.
* \return A decoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderA::AnsiDecode (PSTR szBuff, PCSTR szEncoded, UINT /* CodePage */)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::AnsiDecode: szBuff can not be NULL.") ;
if ( (szEncoded == NULL) || (szEncoded[0] == '\0') ) {
szBuff[0] = '\0' ;
return szBuff ;
}
return ::strcpy (szBuff, szEncoded) ;
}
// Checks whether the character is a valid hexadecimal.
template
static BOOL _HexIsValid(CharType chHex)
{
if ( chHex >= '0' && chHex <= '9' )
return TRUE ;
if ( chHex >= 'A' && chHex <= 'F' )
return TRUE ;
if ( chHex >= 'a' && chHex <= 'f' )
return TRUE ;
return FALSE ;
}
template
static CharType _HexToNum(CharType chHex)
{
if ( chHex >= '0' && chHex <= '9' )
return chHex - '0' ;
if ( chHex >= 'A' && chHex <= 'F' )
return chHex - 'A' + 10 ;
if ( chHex >= 'a' && chHex <= 'f' )
return chHex - 'a' + 10 ;
return 0 ;
}
template
static BOOL _IsAlNum (CharType chChr)
{
if (chChr >= 'A' && chChr <= 'Z')
return TRUE ;
if (chChr >= 'a' && chChr <= 'z')
return TRUE ;
if (chChr >= '0' && chChr <= '9')
return TRUE ;
return FALSE ;
}
/*!
* \internal
* This method returns the number of characters required to make a URL-encoded string.
*
* \param szEncoded [in] An Ansi string or a UTF-8 string. The string can not be NULL.
* \return The number of characters required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
template
static DWORD _UrlEncodeLen (PCSTR szStr)
throw (typename HttpTool::Exception &)
{
HTTPCLIENT_ASSERT (szStr != NULL, "_UrlEncodeLen: szStr can not be NULL.") ;
::SafeInt cchEncoded = 0 ;
PCSTR pchStr = szStr ;
try {
while ( *pchStr ) {
cchEncoded++ ;
if ( !_IsAlNum (*pchStr) && *pchStr != ' ' )
cchEncoded += 2 ;
pchStr++ ;
}
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
return cchEncoded.Value () ;
}
template
static CharType _ToHex (CharType chChr)
{
_ASSERTE ( chChr < 16 ) ;
return chChr > 9 ? 'A' + (chChr - 10) : '0' + chChr ;
}
/*!
* \internal
* This method makes a URL-encoded string.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szEncoded [in] An Ansi string or a UTF-8 string. The string can not be NULL.
* \return An URL-encoded string.
*/
template
static StringType _UrlEncode (StringType szBuff, PCSTR szStr)
{
_ASSERTE ( szBuff != NULL && szStr != NULL ) ;
const CHAR * pchStr = szStr ;
StringType pchOut = szBuff ;
// do encoding
for (; *pchStr != NULL; pchStr++) {
if ( _IsAlNum (*pchStr) ) {
*pchOut++ = *pchStr ;
continue ;
}
if ( *pchStr == ' ' ) {
*pchOut++ = '+' ;
continue ;
}
*pchOut++ = '%';
*pchOut++ = _ToHex (((*pchStr) & 0xF0) >> 4);
*pchOut++ = _ToHex ((*pchStr) & 0x0F);
}
*pchOut = '\0' ;
return szBuff ;
}
/*!
* \internal
* This method converts an Ansi character into an UTF-8 character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szUtf8Char [out] A buffer to save the converted UTF-8 character. The buffer can not be NULL.
* The buffer must be allocated at least 7 characters.
* \param szAnsiChar [in] A string which contains an Ansi character. The string can not be NULL.
* \param CodePage [in] A code page of the Ansi character.
* \throw Throws a httpclientexception if an error occurs.
*/
void CHttpEncoderA::_AnsiCharToUtf8Char (PSTR szUtf8Char, PCSTR szAnsiChar, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szUtf8Char != NULL && szAnsiChar != NULL
, "CHttpEncoderA::_AnsiCharToUtf8Char: szUtf8Char and szAnsiChar can not be NULL.") ;
WCHAR wszUnicode[2] ;
// Get a unicode character
if ( 0 == ::MultiByteToWideChar (CodePage, 0, szAnsiChar, -1, wszUnicode, 2) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
// Get a UTF-8 character sequence
if ( 0 == ::WideCharToMultiByte (CP_UTF8, 0, wszUnicode, 2, szUtf8Char, 7, NULL, NULL) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}
/*!
* \internal
* This method converts an UTF-8 character into an Ansi character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szAnsiChar [out] A buffer to save the converted Ansi character. The buffer can not be NULL.
* The buffer must be allocated at least 3 characters.
* \param szUtf8Char [in] A string which contains an UTF-8 character. The string can not be NULL.
* \param CodePage [in] A code page of the converted Ansi character.
* \throw Throws a httpclientexception if an error occurs.
*/
void CHttpEncoderA::_Utf8CharToAnsiChar (PSTR szAnsiChar, PCSTR szUtf8Char, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szAnsiChar != NULL && szUtf8Char != NULL
, "CHttpEncoderA::_Utf8CharToAnsiChar: szAnsiChar and szUtf8Char can not be NULL.") ;
WCHAR wszUnicode[2] ;
// Get a unicode character
if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8Char, -1, wszUnicode, 2) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
// Get a Ansi character sequence
if ( 0 == ::WideCharToMultiByte (CodePage, 0, wszUnicode, 2, szAnsiChar, 3, NULL, NULL) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}
/*!
* This method returns the number of bytes required to encode an Ansi string using UTF-8 encoding.
* The returned value does not include the terminating NULL character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szStr [in] A string which is encoded.
* \param CodePage [in] A code page of the szStr parameter.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderA::Utf8EncodeLen (PCSTR szStr, UINT CodePage)
throw (Exception &)
{
if ( szStr == NULL || szStr[0] == '\0' )
return 0 ;
::SafeInt cchEncoded = 0 ;
PCSTR pchStr = szStr ;
CHAR szAnsi[3], szUtf8[7] ;
PSTR pchUtf8 ;
try {
while ( *pchStr ) {
szAnsi[0] = *pchStr ;
if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
szAnsi[1] = *(++pchStr) ;
szAnsi[2] = '\0' ;
} else
szAnsi[1] = '\0' ;
_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
for (pchUtf8 = szUtf8; *pchUtf8 != '\0'; pchUtf8++, cchEncoded++) ;
pchStr++ ;
}
} catch (::SafeIntException & e) {
CHttpToolA::ThrowException (e) ;
}
return cchEncoded.Value () ;
}
/*!
* This method encodes an Ansi string using UTF-8 encoding.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param CodePage [in] A code page of the szStr parameter.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderA::Utf8Encode (PSTR szBuff, PCSTR szStr, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::Utf8Encode: szBuff can not be NULL.") ;
if ( szStr == NULL || szStr[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
CHAR szAnsi[3], szUtf8[7] ;
PSTR pchBuff = szBuff ;
PCSTR pchStr = szStr ;
while ( *pchStr ) {
szAnsi[0] = *pchStr ;
if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
szAnsi[1] = *(++pchStr) ;
szAnsi[2] = '\0' ;
} else
szAnsi[1] = '\0' ;
_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
::strcpy (pchBuff, szUtf8) ;
for (; *pchBuff != NULL; pchBuff++) ;
pchStr++ ;
}
return szBuff ;
}
/*!
* \internal
* This method saves an UTF-8 character into the buffer.
* The pszSrc parameter is modified to point to the next UTF-8 character.
*
* \param szUtf8Char [out] A buffer to save an UTF-8 character. The buffer can not be NULL.
* The buffer must be allocated at least 7 characters.
* \param pszSrc [in] A pointer to a UTF-8 string. The pointer can not be NULL.
* \return Returns FALSE if the UTF-8 string contains an invalid byte.
* \throw Throws a httpclientexception if an error occurs.
*/
static BOOL _GetNextUtf8Char (PSTR szUtf8Char, PCSTR * pszSrc)
{
_ASSERTE ( szUtf8Char != NULL ) ;
_ASSERTE ( pszSrc != NULL && *pszSrc != NULL ) ;
if ( '\0' == (szUtf8Char[0] = **pszSrc) )
return TRUE ;
(*pszSrc)++ ;
// An ASCII character
if ( szUtf8Char[0] >= 0 && szUtf8Char[0] <= 0x7F ) {
szUtf8Char[1] = '\0' ;
return TRUE ;
}
BYTE cchUtf8, byChar ;
byChar = static_cast (szUtf8Char[0]) ;
// Following values are not allowed in UTF-8 encoding
if ( byChar == 0xFE || byChar == 0xFF )
return FALSE ;
// It must be the first byte of the UTF-8 character sequence
if ( !(byChar >= 0xC0 && byChar <= 0xFD) )
return FALSE ;
// Counts the number of bytes of the UTF-8 character sequence
if ( (byChar & 0xFE) == 0xFC ) cchUtf8 = 6 ;
else if ( (byChar & 0xFC) == 0xF8 ) cchUtf8 = 5 ;
else if ( (byChar & 0xF8) == 0xF0 ) cchUtf8 = 4 ;
else if ( (byChar & 0xF0) == 0xE0 ) cchUtf8 = 3 ;
else if ( (byChar & 0xE0) == 0xC0 ) cchUtf8 = 2 ;
else return FALSE ;
for (BYTE i = 1; i < cchUtf8; i++) {
szUtf8Char[i] = *((*pszSrc)++) ;
byChar = static_cast (szUtf8Char[i]) ;
// It must be a valid byte of the UTF-8 multibyte sequence.
if ( !(byChar >=0x80 && byChar <= 0xBF) )
return FALSE ;
}
szUtf8Char[cchUtf8] = '\0' ;
return TRUE ;
}
/*!
* This method returns the number of bytes required to decode an UTF-8 string in ANSI.
* The returned value does not include the terminating NULL character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szEncoded [in] A string to decode.
* \param CodePage [in] A code page of the returned Ansi string.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderA::Utf8DecodeLen (PCSTR szEncoded, UINT CodePage)
throw (Exception &)
{
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt cchDecoded = 0 ;
PCSTR pchEncoded = szEncoded ;
CHAR szAnsi[3], szUtf8[7] ;
PSTR pchAnsi ;
try {
while ( *pchEncoded ) {
if ( !_GetNextUtf8Char (szUtf8, &pchEncoded) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_INVALID_UTF8_CHARACTER) ;
_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
for (pchAnsi = szAnsi; *pchAnsi != '\0'; pchAnsi++, cchDecoded++) ;
}
} catch (::SafeIntException & e) {
CHttpToolA::ThrowException (e) ;
}
return cchDecoded.Value () ;
}
/*!
* This method decodes an UTF-8 string using Ansi encoding.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param CodePage [in] A code page of the returned Ansi string.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderA::Utf8Decode (PSTR szBuff, PCSTR szEncoded, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::Utf8Decode: szBuff can not be NULL.") ;
if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
PCSTR pchEncoded = szEncoded ;
CHAR szUtf8[7] ;
PSTR pchBuff = szBuff ;
while ( *pchEncoded ) {
if ( !_GetNextUtf8Char (szUtf8, &pchEncoded) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_INVALID_UTF8_CHARACTER) ;
_Utf8CharToAnsiChar (pchBuff, szUtf8, CodePage) ;
for (; *pchBuff != '\0'; pchBuff++) ;
}
return szBuff ;
}
/*!
* This method returns the number of bytes required to make a URL-encoded string
* (in Ansi character set) from an Ansi string.
* The URL-encoded string is a string that is safe to transmit from the Web server to a client.
* The returned value does not include the terminating NULL character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szStr [in] A string which is encoded.
* \param bUtf8Encoding [in] If this is TRUE, the string is encoded using UTF-8 encoding
* and that string is used to make a URL-encoded string.
* \param CodePage [in] A code page of the szStr parameter.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderA::UrlEncodeLenA (PCSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
if ( szStr == NULL || szStr[0] == '\0' )
return 0 ;
if ( !bUtf8Encoding )
return _UrlEncodeLen (szStr) ;
::SafeInt cchEncoded = 0 ;
PCSTR pchStr = szStr ;
CHAR szAnsi[3], szUtf8[7] ;
try {
while ( *pchStr ) {
szAnsi[0] = *pchStr ;
if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
szAnsi[1] = *(++pchStr) ;
szAnsi[2] = '\0' ;
} else
szAnsi[1] = '\0' ;
_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
cchEncoded += _UrlEncodeLen (szUtf8) ;
pchStr++ ;
}
} catch (::SafeIntException & e) {
CHttpToolA::ThrowException (e) ;
}
return cchEncoded.Value () ;
}
/*!
* This method encodes a Ansi string to make a URL-encoded string (in Ansi character set).
* The URL-encoded string is a string that is safe to transmit from the Web server to a client.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param bUtf8Encoding [in] If this is TRUE, the string is encoded using UTF-8 encoding
* and that string is used to make a URL-encoded string.
* \param CodePage [in] A code page of the szStr parameter.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderA::UrlEncodeA (PSTR szBuff, PCSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlEncodeA: szBuff can not be NULL.") ;
if ( szStr == NULL || szStr[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
if ( !bUtf8Encoding )
return _UrlEncode (szBuff, szStr) ;
CHAR szAnsi[3], szUtf8[7] ;
PSTR pchBuff = szBuff ;
PCSTR pchStr = szStr ;
while ( *pchStr ) {
szAnsi[0] = *pchStr ;
if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
szAnsi[1] = *(++pchStr) ;
szAnsi[2] = '\0' ;
} else
szAnsi[1] = '\0' ;
_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
_UrlEncode (pchBuff, szUtf8) ;
for (; *pchBuff != NULL; pchBuff++) ;
pchStr++ ;
}
return szBuff ;
}
/*!
* This method encodes a Ansi string to make a URL-encoded string (in Unicode character set).
* The URL-encoded string is a string that is safe to transmit from the Web server to a client.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param bUtf8Encoding [in] If this is TRUE, the string is encoded using UTF-8 encoding
* and that string is used to make a URL-encoded string.
* \param CodePage [in] A code page of the szStr parameter.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PWSTR CHttpEncoderA::UrlEncodeW (PWSTR szBuff, PCSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlEncodeW: szBuff can not be NULL.") ;
if ( szStr == NULL || szStr[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
if ( !bUtf8Encoding )
return _UrlEncode (szBuff, szStr) ;
CHAR szAnsi[3], szUtf8[7] ;
PWSTR pchBuff = szBuff ;
PCSTR pchStr = szStr ;
while ( *pchStr ) {
szAnsi[0] = *pchStr ;
if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
szAnsi[1] = *(++pchStr) ;
szAnsi[2] = '\0' ;
} else
szAnsi[1] = '\0' ;
_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
_UrlEncode (pchBuff, szUtf8) ;
for (; *pchBuff != NULL; pchBuff++) ;
pchStr++ ;
}
return szBuff ;
}
// [out] pchDecoded A pointer to a character to save the next URL decoded character.
// [in] pszSrc A pointer to a URL Encoded string.
// Returns TRUE if the operation success
template
static BOOL _GetNextUrlDecodedChar (CHAR * pchDecoded, StringType * pszSrc)
{
_ASSERTE ( pchDecoded != NULL && pszSrc != NULL && *pszSrc != NULL ) ;
if ( **pszSrc == NULL ) {
*pchDecoded = '\0' ;
return TRUE ;
}
if ( **pszSrc == '+' ) {
*pchDecoded = ' ' ;
(*pszSrc)++ ;
return TRUE ;
}
if ( **pszSrc != '%' ) {
*pchDecoded = static_cast (*((*pszSrc)++)) ;
return FALSE ;
}
(*pszSrc)++ ;
if ( !_HexIsValid (**pszSrc) )
return FALSE ;
*pchDecoded = _HexToNum (**pszSrc) * 16 ;
(*pszSrc)++ ;
if ( !_HexIsValid (**pszSrc) )
return FALSE ;
*pchDecoded += _HexToNum (**pszSrc) ;
(*pszSrc)++ ;
return TRUE ;
}
// [out] szAnsiChar a buffer to save the next URL decoded Ansi character sequence.
// The buffer must be allocated at least 3 characters.
// [in] pszSrc A pointer to a URL Encoded string.
// [in] CodePage codepage
// Returns TRUE if the operation success
template
static BOOL _GetNextUrlDecodedAnsiChar (PSTR szAnsiChar, StringType * pszSrc, UINT CodePage = CP_ACP)
{
_ASSERTE ( szAnsiChar != NULL ) ;
_ASSERTE ( pszSrc != NULL && *pszSrc != NULL ) ;
if ( !_GetNextUrlDecodedChar (szAnsiChar, pszSrc) )
return FALSE ;
if ( szAnsiChar[0] == '\0' )
return TRUE ;
if ( !::IsDBCSLeadByteEx (CodePage, szAnsiChar[0]) ) {
szAnsiChar[1] = '\0' ;
return TRUE ;
}
if ( !_GetNextUrlDecodedChar (szAnsiChar + 1, pszSrc) )
return FALSE ;
// It can not be '\0'
if ( szAnsiChar[1] == '\0' )
return FALSE ;
szAnsiChar[2] = '\0' ;
return TRUE ;
}
// [out] szUtf8Char a buffer to save a returned UTF-8 character sequence.
// The buffer must be allocated at least 7 characters.
// [in] pszSrc A pointer to a URL Encoded string.
// Returns TRUE if the operation success
template
static BOOL _GetNextUrlDecodedUtf8Char (PSTR szUtf8Char, StringType * pszSrc)
{
_ASSERTE ( szUtf8Char != NULL ) ;
_ASSERTE ( pszSrc != NULL && *pszSrc != NULL ) ;
if ( !_GetNextUrlDecodedChar (szUtf8Char, pszSrc) )
return FALSE ;
if ( szUtf8Char[0] == '\0' )
return TRUE ;
// An ASCII character
if ( szUtf8Char[0] >= 0 && szUtf8Char[0] <= 0x7F ) {
szUtf8Char[1] = '\0' ;
return TRUE ;
}
BYTE cchUtf8, byChar ;
byChar = static_cast (szUtf8Char[0]) ;
// following values are not allowed in UTF-8 encoding
if ( byChar == 0xFE || byChar == 0xFF )
return FALSE ; // Invalid UTF-8 byte
// It must be the first character of a UTF-8 character sequence
if ( !(byChar >= 0xC0 && byChar <= 0xFD) )
return FALSE ;
// Counts the number of bytes of the UTF-8 character sequence
if ( (byChar & 0xFE) == 0xFC ) cchUtf8 = 6 ;
else if ( (byChar & 0xFC) == 0xF8 ) cchUtf8 = 5 ;
else if ( (byChar & 0xF8) == 0xF0 ) cchUtf8 = 4 ;
else if ( (byChar & 0xF0) == 0xE0 ) cchUtf8 = 3 ;
else if ( (byChar & 0xE0) == 0xC0 ) cchUtf8 = 2 ;
else return FALSE ;
for (BYTE i = 1; i < cchUtf8; i++) {
if ( !_GetNextUrlDecodedChar (szUtf8Char + i, pszSrc) )
return FALSE ;
byChar = static_cast (szUtf8Char[i]) ;
// It must be a byte of the UTF-8 multibyte sequence.
if ( !(byChar >=0x80 && byChar <= 0xBF) )
return FALSE ;
}
szUtf8Char[cchUtf8] = '\0' ;
return TRUE ;
}
/*!
* This method returns the number of bytes required to decode an URL-encoded string. (Ansi version)
* The returned value does not include the terminating NULL character.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Ansi string.
* \param CodePage [in] A code page of the decoded string.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderA::UrlDecodeLenA (PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt cchDecoded = 0 ;
PCSTR pchEncoded = szEncoded ;
CHAR szAnsi[3], szUtf8[7] ;
CHAR * pchAnsi ;
try {
do {
if ( bUtf8Encoding ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
// Converts into Ansi
_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
} else {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
}
for (pchAnsi = szAnsi; *pchAnsi != NULL; pchAnsi++, cchDecoded++) ;
} while ( szAnsi[0] != '\0' ) ;
} catch (::SafeIntException & e) {
CHttpToolA::ThrowException (e) ;
}
return cchDecoded.Value () ;
}
/*!
* This method returns the number of unicode characters required to decode an URL-encoded string.
* The returned value does not include the terminating NULL character.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Unicode string.
* \param CodePage [in] A code page of the decoded string.
* \return The number of unicode characters required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderA::UrlDecodeLenW (PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt cchDecoded = 0 ;
PCSTR pchEncoded = szEncoded ;
try {
if ( bUtf8Encoding ) {
CHAR szUtf8[7] ;
while ( true ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szUtf8[0] == '\0' )
break ;
cchDecoded++ ;
}
return cchDecoded.Value () ;
}
CHAR szAnsi[3] ;
while ( true ) {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szAnsi[0] == '\0' )
break ;
cchDecoded++ ;
}
} catch (::SafeIntException & e) {
CHttpToolA::ThrowException (e) ;
}
return cchDecoded.Value () ;
}
/*!
* This method decodes an URL-encoded string.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Ansi string.
* \param CodePage [in] A code page of the decoded string.
* \return A decoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderA::UrlDecodeA (PSTR szBuff, PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlDecodeA: szBuff can not be NULL.") ;
if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
CHAR szAnsi[3], szUtf8[7] ;
PCSTR pchEncoded = szEncoded ;
PSTR pchBuff = szBuff ;
PSTR pchAnsi ;
do {
if ( bUtf8Encoding ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
// Converts into a Ansi character
_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
} else {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
}
for (pchAnsi = szAnsi; *pchAnsi != '\0'; *(pchBuff++) = *pchAnsi, pchAnsi++) ;
} while ( szAnsi[0] != '\0' ) ;
*pchBuff = '\0' ;
return szBuff ;
}
/*!
* This method decodes an URL-encoded string.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Unicode string.
* \param CodePage [in] A code page of the decoded string.
* \return A decoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PWSTR CHttpEncoderA::UrlDecodeW (PWSTR szBuff, PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlDecodeW: szBuff can not be NULL.") ;
if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
PCSTR pchEncoded = szEncoded ;
PWSTR pchBuff = szBuff ;
if ( bUtf8Encoding ) {
CHAR szUtf8[7] ;
while ( true ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szUtf8[0] == '\0' )
break ;
// Converts into a unicode character
if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8, -1, pchBuff, 2) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
pchBuff++ ;
}
} else {
CHAR szAnsi[3] ;
while ( true ) {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szAnsi[0] == '\0' )
break ;
// Converts into a unicode character
if ( 0 == ::MultiByteToWideChar (CodePage, 0, szAnsi, -1, pchBuff, 2) )
CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
pchBuff++ ;
}
}
*pchBuff = '\0' ;
return szBuff ;
}
/*!
* This method returns the number of bytes required to convert an Unicode string into an Ansi string.
* The returned value does not include the terminating NULL character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szStr [in] A string which is encoded.
* \param CodePage [in] A code page of the encoded string.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderW::AnsiEncodeLen (PCWSTR szStr, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpEncoderW::AnsiEncodeLen: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
if ( szStr == NULL || szStr[0] == '\0' )
return 0 ;
::SafeInt cchAnsiLen ;
try {
if ( 0 == (cchAnsiLen = ::WideCharToMultiByte (CodePage, 0, szStr, -1, NULL, 0, NULL, NULL)) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
cchAnsiLen-- ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
return cchAnsiLen.Value () ;
}
/*!
* This method converts an Unicode string into an Ansi string.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param CodePage [in] A code page of the encoded string.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderW::AnsiEncode (PSTR szBuff, PCWSTR szStr, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpEncoderW::AnsiEncode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::AnsiEncode: szBuff can not be NULL.") ;
if ( (szStr == NULL) || (szStr[0] == '\0') ) {
szBuff[0] = '\0' ;
return szBuff ;
}
::SafeInt cbAnsiLen ;
try {
cbAnsiLen = AnsiEncodeLen (szStr, CodePage) ;
cbAnsiLen++ ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
if ( 0 == ::WideCharToMultiByte (CodePage, 0, szStr, -1, szBuff, cbAnsiLen.Value (), NULL, NULL) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
return szBuff ;
}
/*!
* This method returns the number of unicode characters required to convert an Ansi string into an Unicode string.
* The returned value does not include the terminating NULL character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szEncoded [in] A string to decode.
* \param CodePage [in] A code page of the szEncoded parameter.
* \return The number of unicode characters required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderW::AnsiDecodeLen (PCSTR szEncoded, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpEncoderW::AnsiDecodeLen: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt cchUnicodeLen ;
try {
if ( 0 == (cchUnicodeLen = ::MultiByteToWideChar (CodePage, 0, szEncoded, -1, NULL, 0)) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
cchUnicodeLen-- ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
return cchUnicodeLen.Value () ;
}
/*!
* This method converts an Ansi string into an Unicode string.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param CodePage [in] A code page of the szEncoded parameter.
* \return A decoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PWSTR CHttpEncoderW::AnsiDecode (PWSTR szBuff, PCSTR szEncoded, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpEncoderW::AnsiDecode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::AnsiDecode: szBuff can not be NULL.") ;
if ( (szEncoded == NULL) || (szEncoded[0] == '\0') ) {
szBuff[0] = '\0' ;
return szBuff ;
}
::SafeInt cchUnicode ;
try {
cchUnicode = AnsiDecodeLen (szEncoded, CodePage) ;
cchUnicode++ ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
if ( 0 == ::MultiByteToWideChar (CodePage, 0, szEncoded, -1, szBuff, cchUnicode.Value ()) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
return szBuff ;
}
/*!
* This method returns the number of characters required to encode an Unicode string using UTF-8 encoding.
* The returned value does not include the terminating NULL character.
*
* \param szStr [in] A string which is encoded.
* \param CodePage [in] Ignored.
* \return The number of characters required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderW::Utf8EncodeLen (PCWSTR szStr, UINT /* CodePage */)
throw (Exception &)
{
if ( szStr == NULL || szStr[0] == '\0' )
return 0 ;
::SafeInt cchRequired ;
try {
if ( 0 == (cchRequired = ::WideCharToMultiByte (CP_UTF8, 0, szStr, -1, NULL, 0, NULL, NULL)) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
cchRequired-- ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
return cchRequired.Value () ;
}
/*!
* This method encodes an Unicode string using UTF-8 encoding.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param CodePage [in] Ignored.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderW::Utf8Encode (PSTR szBuff, PCWSTR szStr, UINT /* CodePage */)
throw (Exception &)
{
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::Utf8Encode: szBuff can not be NULL.") ;
if ( szStr == NULL || szStr[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
::SafeInt cchRequired ;
try {
cchRequired = Utf8EncodeLen (szStr) ;
cchRequired++ ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
if ( 0 == ::WideCharToMultiByte (CP_UTF8, 0, szStr, -1, szBuff, cchRequired.Value (), NULL, NULL) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
return szBuff ;
}
/*!
* This method returns the number of unicode characters required to decode an UTF-8 string in unicode.
* The returned value does not include the terminating NULL character.
*
* \param szEncoded [in] A string to decode.
* \param CodePage [in] Ignored.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderW::Utf8DecodeLen (PCSTR szEncoded, UINT /* CodePage */)
throw (Exception &)
{
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt cchRequired ;
try {
if ( 0 == (cchRequired = ::MultiByteToWideChar (CP_UTF8, 0, szEncoded, -1, NULL, 0)) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
cchRequired-- ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
return cchRequired.Value () ;
}
/*!
* This method decodes an UTF-8 string in unicode.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param CodePage [in] Ignored.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PWSTR CHttpEncoderW::Utf8Decode (PWSTR szBuff, PCSTR szEncoded, UINT /* CodePage */)
throw (Exception &)
{
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::Utf8Decode: szBuff can not be NULL.") ;
if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
::SafeInt cchRequired ;
try {
cchRequired = Utf8DecodeLen (szEncoded) ;
cchRequired++ ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szEncoded, -1, szBuff, cchRequired.Value ()) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
return szBuff ;
}
/*!
* This method returns the number of bytes required to make a URL-encoded string
* (in Ansi character set) from an Unicode string.
* The URL-encoded string is a string that is safe to transmit from the Web server to a client.
* The returned value does not include the terminating NULL character.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szStr [in] A string which is encoded.
* \param bUtf8Encoding [in] If this is TRUE, the string is encoded using UTF-8 encoding
* and that string is used to make a URL-encoded string.
* \param CodePage [in] A code page of the encoded string.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderW::UrlEncodeLenA (PCWSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
if ( szStr == NULL || szStr[0] == '\0' )
return 0 ;
::SafeInt cchEncoded = 0 ;
PCWSTR pchStr = szStr ;
WCHAR szUnicode[2] = {'\0', '\0'} ;
CHAR szAnsiOrUtf8[7] ;
UINT nActualCodePage = bUtf8Encoding ? CP_UTF8 : CodePage ;
try {
while ( *pchStr ) {
szUnicode[0] = *(pchStr++) ;
if ( 0 == ::WideCharToMultiByte (nActualCodePage, 0, szUnicode, 2, szAnsiOrUtf8, 7, NULL, NULL) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
cchEncoded += _UrlEncodeLen (szAnsiOrUtf8) ;
}
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
return cchEncoded.Value () ;
}
/*!
* This method encodes a Unicode string to make a URL-encoded string (in Ansi character set).
* The URL-encoded string is a string that is safe to transmit from the Web server to a client.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param bUtf8Encoding [in] If this is TRUE, the string is encoded using UTF-8 encoding
* and that string is used to make a URL-encoded string.
* \param CodePage [in] A code page of the encoded string.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderW::UrlEncodeA (PSTR szBuff, PCWSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlEncodeA: szBuff can not be NULL.") ;
if ( szStr == NULL || szStr[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
PCWSTR pchStr = szStr ;
PSTR pchBuff = szBuff ;
WCHAR szUnicode[2] = {'\0', '\0'} ;
CHAR szAnsiOrUtf8[7] ;
UINT nActualCodePage = bUtf8Encoding ? CP_UTF8 : CodePage ;
while ( *pchStr ) {
szUnicode[0] = *(pchStr++) ;
if ( 0 == ::WideCharToMultiByte (nActualCodePage, 0, szUnicode, 2, szAnsiOrUtf8, 7, NULL, NULL) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
_UrlEncode (pchBuff, szAnsiOrUtf8) ;
for (; *pchBuff != NULL; pchBuff++) ;
}
return szBuff ;
}
/*!
* This method encodes a Unicode string to make a URL-encoded string (in Unicode character set).
* The URL-encoded string is a string that is safe to transmit from the Web server to a client.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the encoded string. The buffer can not be NULL.
* \param szStr [in] A string which is encoded.
* \param bUtf8Encoding [in] If this is TRUE, the string is encoded using UTF-8 encoding
* and that string is used to make a URL-encoded string.
* \param CodePage [in] A code page of the encoded string.
* \return An encoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PWSTR CHttpEncoderW::UrlEncodeW (PWSTR szBuff, PCWSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlEncodeW: szBuff can not be NULL.") ;
if ( szStr == NULL || szStr[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
PCWSTR pchStr = szStr ;
PWSTR pchBuff = szBuff ;
WCHAR szUnicode[2] = {'\0', '\0'} ;
CHAR szAnsiOrUtf8[7] ;
UINT nActualCodePage = bUtf8Encoding ? CP_UTF8 : CodePage ;
while ( *pchStr ) {
szUnicode[0] = *(pchStr++) ;
if ( 0 == ::WideCharToMultiByte (nActualCodePage, 0, szUnicode, 2, szAnsiOrUtf8, 7, NULL, NULL) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
_UrlEncode (pchBuff, szAnsiOrUtf8) ;
for (; *pchBuff != NULL; pchBuff++) ;
}
return szBuff ;
}
// [out] szAnsiChar a buffer to save a returned Ansi character sequence.
// The buffer must be allocated at least 3 characters.
// [in] szUtf8Char An Utf8 character
void CHttpEncoderW::_Utf8CharToAnsiChar (PSTR szAnsiChar, PCSTR szUtf8Char, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTW (szAnsiChar != NULL && szUtf8Char != NULL
, "CHttpEncoderW::_Utf8CharToAnsiChar: szAnsiChar and szUtf8Char can not be NULL.") ;
WCHAR wszUnicode[2] ;
// Get a unicode character
if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8Char, -1, wszUnicode, 2) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
// Get a Ansi character sequence
if ( 0 == ::WideCharToMultiByte (CodePage, 0, wszUnicode, 2, szAnsiChar, 3, NULL, NULL) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}
/*!
* This method returns the number of bytes required to decode an URL-encoded string. (Ansi version)
* The returned value does not include the terminating NULL character.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Ansi string.
* \param CodePage [in] A code page of the decoded string.
* \return The number of bytes required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderW::UrlDecodeLenA (PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt cchDecoded = 0 ;
PCWSTR pchEncoded = szEncoded ;
CHAR szAnsi[3], szUtf8[7] ;
CHAR * pchAnsi ;
try {
do {
if ( bUtf8Encoding ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
// Converts into Ansi
_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
} else {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
}
for (pchAnsi = szAnsi; *pchAnsi != NULL; pchAnsi++, cchDecoded++) ;
} while ( szAnsi[0] != '\0' ) ;
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
return cchDecoded.Value () ;
}
/*!
* This method returns the number of unicode characters required to decode an URL-encoded string.
* The returned value does not include the terminating NULL character.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Unicode string.
* \param CodePage [in] A code page of the decoded string.
* \return The number of unicode characters required. (Not including the terminating NULL character)
* \throw Throws a httpclientexception if an error occurs.
*/
DWORD CHttpEncoderW::UrlDecodeLenW (PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
if ( szEncoded == NULL || szEncoded[0] == '\0' )
return 0 ;
::SafeInt cchDecoded = 0 ;
PCWSTR pchEncoded = szEncoded ;
try {
if ( bUtf8Encoding ) {
CHAR szUtf8[7] ;
while ( true ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szUtf8[0] == '\0' )
break ;
cchDecoded++ ;
}
return cchDecoded.Value () ;
}
CHAR szAnsi[3] ;
while ( true ) {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szAnsi[0] == '\0' )
break ;
cchDecoded++ ;
}
} catch (::SafeIntException & e) {
CHttpToolW::ThrowException (e) ;
}
return cchDecoded.Value () ;
}
/*!
* This method decodes an URL-encoded string.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Ansi string.
* \param CodePage [in] A code page of the decoded string.
* \return A decoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PSTR CHttpEncoderW::UrlDecodeA (PSTR szBuff, PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlDecodeA: szBuff can not be NULL.") ;
if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
CHAR szAnsi[3], szUtf8[7] ;
PCWSTR pchEncoded = szEncoded ;
PSTR pchBuff = szBuff ;
PSTR pchAnsi ;
do {
if ( bUtf8Encoding ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
// Converts into a Ansi character
_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
} else {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
}
for (pchAnsi = szAnsi; *pchAnsi != '\0'; *(pchBuff++) = *pchAnsi, pchAnsi++) ;
} while ( szAnsi[0] != '\0' ) ;
*pchBuff = '\0' ;
return szBuff ;
}
/*!
* This method decodes an URL-encoded string.
* This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
* The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
*
* \param szBuff [out] A buffer to save the decoded string. The buffer can not be NULL.
* \param szEncoded [in] A string to decode.
* \param bUtf8Encoding [in] If this is TRUE, the decoded string is assumed an UTF-8 string.
* So the decoded string is converted into an Unicode string.
* \param CodePage [in] A code page of the decoded string.
* \return A decoded string.
* \throw Throws a httpclientexception if an error occurs.
*/
PWSTR CHttpEncoderW::UrlDecodeW (PWSTR szBuff, PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
throw (Exception &)
{
HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlDecodeW: szBuff can not be NULL.") ;
if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
szBuff[0] = '\0' ;
return szBuff ;
}
PCWSTR pchEncoded = szEncoded ;
PWSTR pchBuff = szBuff ;
if ( bUtf8Encoding ) {
CHAR szUtf8[7] ;
while ( true ) {
if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szUtf8[0] == '\0' )
break ;
// Converts into a unicode character
if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8, -1, pchBuff, 2) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
pchBuff++ ;
}
} else {
CHAR szAnsi[3] ;
while ( true ) {
if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
if ( szAnsi[0] == '\0' )
break ;
// Converts into a unicode character
if ( 0 == ::MultiByteToWideChar (CodePage, 0, szAnsi, -1, pchBuff, 2) )
CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
pchBuff++ ;
}
}
*pchBuff = '\0' ;
return szBuff ;
}
///////////////////////////////////////// CHttpEncoder /////////////////////////////////////////
///////////////////////////////////////// CHttpResponseT /////////////////////////////////////////
/*!
* This is the constructor which has three parameters which are the WinInet internet handles.
* The handles are owned by the CHttpResponseT and the lifetime of the handles are managed by the CHttpResponseT.
* If you pass NULL for hInternet or hConnection parameter, user has to manage the lifetime of the handles
* which are not passed to the CHttpResponseT until the CHttpResponseT is destoried.
*
* \param hInternet [in] An internet handle returned by ::InternetOpen.
* \param hConnection [in] A connection handle returned by ::InternetConnect.
* \param hRequest [in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
* The hRequest parameter can not be NULL.
*/
template
CHttpResponseT::CHttpResponseT (HINTERNET hInternet, HINTERNET hConnection, HINTERNET hRequest)
throw ()
{
_Initialize (hInternet, hConnection, hRequest) ;
}
/*!
* This is the constructor which has two parameters which are the WinInet internet handles.
* The handles are owned by the CHttpResponseT and the lifetime of the handles are managed by the CHttpResponseT.
* If you pass NULL for hConnection parameter, user has to manage the lifetime of the hConnection handle
* which is not passed to the CHttpResponseT until the CHttpResponseT is destoried.
* The user also has to manage the lifetime of the internet handle returned by ::InternetOpen
* until the CHttpResponseT is destoried.
*
* \param hConnection [in] A connection handle returned by ::InternetConnect.
* \param hRequest [in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
* The hRequest parameter can not be NULL.
*/
template
CHttpResponseT::CHttpResponseT (HINTERNET hConnection, HINTERNET hRequest)
throw ()
{
_Initialize (NULL, hConnection, hRequest) ;
}
/*!
* This is the constructor which has a parameter which is the WinInet internet handle.
* The handle is owned by the CHttpResponseT and the lifetime of the handle is managed by the CHttpResponseT.
* user has to manage the lifetime of the internet handles returned by ::InternetOpen and ::InternetConnect
* until the CHttpResponseT is destoried.
*
* \param hRequest [in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
* The hRequest parameter can not be NULL.
*/
template
CHttpResponseT::CHttpResponseT (HINTERNET hRequest)
throw ()
{
_Initialize (NULL, NULL, hRequest) ;
}
/*!
* This is a default destructor. It closes all internet handles saved in this object.
*/
template
CHttpResponseT::~CHttpResponseT (void)
throw ()
{
HttpTool::CloseRequest (m_hRequest) ;
HttpTool::CloseConnection (m_hConnection) ;
HttpTool::CloseInternet (m_hInternet) ;
SAFEFREE (m_szStatusText) ;
}
/*!
* This method initializes the internal member variables.
*
* \param hInternet [in] An internet handle returned by ::InternetOpen.
* \param hConnection [in] A connection handle returned by ::InternetConnect.
* \param hRequest [in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
* The hRequest parameter can not be NULL.
*/
template
void CHttpResponseT::_Initialize (HINTERNET hInternet, HINTERNET hConnection, HINTERNET hRequest)
throw ()
{
_ASSERTE ( hRequest != NULL ) ;
m_hInternet = hInternet ;
m_hConnection = hConnection ;
m_hRequest = hRequest ;
m_dwStatus = 0 ;
m_szStatusText = NULL ;
m_cbContLen = 0 ;
}
/*!
* \internal
* This method caches all HTTP headers of which name is the szName.
*
* \param szName [in] The case-insensitive name of the header. NULL is not allowed.
* \return The number of cached headers.
* \throw Throws a httpclientexception if an error occurs.
*/
template
DWORD CHttpResponseT::_LoadHeader (PCSZ szName)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::_LoadHeader: m_hRequest can not be NULL.") ;
HTTPCLIENT_ASSERT (szName != NULL, "CHttpResponseT::_LoadHeader: szName can not be NULL.") ;
PSZ szHeader = NULL ;
PSZ szCopiedName = NULL ;
::SafeInt cHeader = 0 ;
try {
try {
DWORD nIdx = 0 ;
::SafeInt cbName = 1 ;
cbName += HttpTool::StringLen (szName) ;
cbName *= sizeof (CharType) ;
while ( true ) {
if ( NULL == (szHeader = HttpTool::GetHeader (m_hRequest, szName, &nIdx)) )
break ;
cHeader++ ;
// Copy the header name
if ( NULL == (szCopiedName = (PSZ) ::malloc (cbName.Value ())) )
HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
HttpTool::StringCopy (szCopiedName, szName) ;
m_mapHeader.AddPointerDirectly (szCopiedName, szHeader) ;
szCopiedName = szHeader = NULL ;
}
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
} catch (Exception &) {
m_mapHeader.RemoveAll (szName) ; // All or nothing
SAFEFREE (szHeader) ;
SAFEFREE (szCopiedName) ;
throw ;
}
return cHeader.Value () ;
}
/*!
* This method returns the number of headers of which name is szName.
*
* \param szName [in] A case-insensitive header name. NULL is not allowed.
* \return The number of headers of which name is bstrName.
* \throw Throws a httpclientexception if an error occurs.
*/
template
DWORD CHttpResponseT::GetHeaderCount (PCSZ szName)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpResponseT::GetHeaderCount: szName can not be NULL.") ;
DWORD cHeader = m_mapHeader.Count (szName) ;
// Tries to load headers if the header does not exist
if ( cHeader == 0 )
cHeader = _LoadHeader (szName) ;
return cHeader ;
}
/*!
* This method returns the header of which name is the szName.
* If a header name has multiple values, you can specify a zero-based index for a specific value.
* If a header specified by szName is not found or the index is out of range, it will return NULL.
* Otherwise it always returns a null-terminated string. The returned string is owned by
* the CHttpResponseT. So you can not free it.
*
* \param szName [in] A case-insensitive header name. NULL is not allowed.
* \param nIdx [in] A zero-based index for a header which has multiple values.
* \return The requested header.
* \throw Throws a httpclientexception if an error occurs.
*/
template
typename CHttpResponseT::PCSZ
CHttpResponseT::GetHeader (PCSZ szName, DWORD nIdx = 0)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szName != NULL, "CHttpResponseT::GetHeader: szName can not be NULL.") ;
// Tries to load headers if the header does not exist
if ( !m_mapHeader.Count (szName) && !_LoadHeader (szName) )
return NULL ;
return m_mapHeader.GetValue (szName, nIdx) ;
}
/*!
* This method returns the HTTP status code of a HTTP response.
*
* \return The HTTP status code.
* \throw Throws a httpclientexception if an error occurs.
*/
template
DWORD CHttpResponseT::GetStatus (void)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::GetStatus: m_hRequest can not be NULL.") ;
// If the cached one exists
if ( m_dwStatus != 0 )
return m_dwStatus ;
DWORD dwBuffSize = sizeof (DWORD) ;
if ( !::HttpQueryInfo (
m_hRequest
, HTTP_QUERY_STATUS_CODE // Get the HTTP status code
| HTTP_QUERY_FLAG_NUMBER
, static_cast (&m_dwStatus)
, &dwBuffSize // Buffer size (byte)
, NULL // Does not use a header index
)
)
HttpTool::ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
return m_dwStatus ;
}
/*!
* This method returns the HTTP status text of a HTTP response.
* It always returns a null-terminated string. The returned string is owned by
* the CHttpResponse. So you can not free it.
*
* \return The HTTP status text returned by a HTTP server.
* \throw Throws a httpclientexception if an error occurs.
*/
template
typename CHttpResponseT::PCSZ
CHttpResponseT::GetStatusText (void)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::GetStatusText: m_hRequest can not be NULL.") ;
// If the cached one exists
if ( m_szStatusText != NULL )
return m_szStatusText ;
return (m_szStatusText = HttpTool::GetStatusText (m_hRequest)) ;
}
/*!
* This method retrieves the content length of a returned HTTP response.
* The content is the data stream except the HTTP headers. If the length is retrieved,
* it will return TRUE, otherwise return FALSE.
*
* \param cbContLen [out] A reference variable to save the content length.
* \return TRUE if the content length is available, otherwise FALSE.
* \throw Throws a httpclientexception if an error occurs.
*/
template
BOOL CHttpResponseT::GetContentLength (DWORD & cbContLen)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::GetContentLength: m_hRequest can not be NULL.") ;
DWORD dwBuffSize = sizeof (DWORD) ;
if ( !::HttpQueryInfo (
m_hRequest
, HTTP_QUERY_CONTENT_LENGTH // Reads the 'content-length' header
| HTTP_QUERY_FLAG_NUMBER
, static_cast (&cbContLen)
, &dwBuffSize // Buffer size (byte)
, NULL // Does not use a header index
)
) {
if ( ::GetLastError () == ERROR_HTTP_HEADER_NOT_FOUND )
return FALSE ;
HttpTool::ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
}
return TRUE ;
}
/*!
* This method reads the content (the data stream except headers) of a returned HTTP response.
*
* \param pbyBuff [out] A buffer to save the read content.
* \param cbBuff [in] A buffer size in byte.
* \return Returns the number of bytes read. Returns zero if all content is read.
* \throw Throws a httpclientexception if an error occurs.
*/
template
DWORD CHttpResponseT::ReadContent (BYTE * pbyBuff, DWORD cbBuff)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::ReadContent: m_hRequest can not be NULL.") ;
HTTPCLIENT_ASSERT (pbyBuff != NULL, "CHttpResponseT::ReadContent: pbyBuff can not be NULL.") ;
HTTPCLIENT_ASSERT (cbBuff != 0, "CHttpResponseT::ReadContent: cbBuff can not be zero.") ;
DWORD cbRead = 0 ;
if ( !::InternetReadFile (
m_hRequest
, pbyBuff
, cbBuff
, &cbRead
)
)
HttpTool::ThrowException (HTTPCLIENT_ERR_INTERNETREADFILE_FAILED, ::GetLastError ()) ;
return cbRead ;
}
/*!
* This method saves the content (the data stream except headers) of a returned HTTP response
* to a file which is specified by szFilePath.
*
* \param szFilePath [in] A file path to save the return content.
* \param bOverwrite [in] If this is TRUE, a file is overwritten if aleady exists. otherwise an exception is thrown.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpResponseT::SaveContent (PCSZ szFilePath, BOOL bOverwrite)
throw (Exception &)
{
HTTPCLIENT_ASSERT (szFilePath != NULL, "CHttpResponseT::SaveContent: szFilePath can not be NULL.") ;
// Checks whether the target file aleady exists.
if ( !bOverwrite )
{
if ( HttpTool::FileExists (szFilePath) )
HttpTool::ThrowException (HTTPCLIENT_ERR_FILE_ALEADY_EXISTS, NO_ERROR, szFilePath) ;
}
// Create a file to save content stream
HANDLE hFile = HttpTool::CreateFileAlwaysToWrite (szFilePath) ;
if ( hFile == INVALID_HANDLE_VALUE )
HttpTool::ThrowException (HTTPCLIENT_ERR_OPENFILE_FAILED, ::GetLastError (), szFilePath) ;
const DWORD cbBuff = 1024 * 10 ; // use 10k buffer
BYTE byBuff[cbBuff] ;
DWORD cbRead, cbWritten, cbRemain ;
// Reads the data stream returned by the HTTP server.
while ( cbRead = ReadContent (byBuff, cbBuff) ) {
cbRemain = cbRead ;
do {
if ( !::WriteFile (hFile, byBuff + (cbRead - cbRemain) , cbRemain, &cbWritten, NULL) ) {
DWORD dwLastError = ::GetLastError () ;
::CloseHandle (hFile) ;
HttpTool::ThrowException (HTTPCLIENT_ERR_WRITEFILE_FAILED, dwLastError, szFilePath) ;
}
cbRemain -= cbWritten ;
} while ( cbRemain > 0 ) ;
}
::CloseHandle (hFile) ;
}
///////////////////////////////////////// CHttpResponseT /////////////////////////////////////////
///////////////////////////////////////// CHttpPostStatT /////////////////////////////////////////
/*!
* This is a default constructor with no argument.
*/
template
CHttpPostStatT::CHttpPostStatT (void)
throw ()
{
// Initialize member variables
_InitMemberVariables () ;
}
/*!
* This is a copy constructor.
* If memory allocation failed, the messages contained in the objPostStat object will
* not be copied and the internal string pointers will point to NULL.
*
* \param objPostStat [in] An CHttpPostStat object.
*/
template
CHttpPostStatT::CHttpPostStatT (const CHttpPostStatT & objPostStat)
throw ()
{
operator= (objPostStat) ;
}
/*!
* This is a default destructor.
*/
template
CHttpPostStatT::~CHttpPostStatT (void)
throw ()
{
_MakeUnActive () ;
}
// Initialize member variables
template
void CHttpPostStatT::_InitMemberVariables (void)
throw ()
{
m_bIsActive = FALSE ;
m_cbActualTotal = 0 ;
m_cbActualPosted = 0 ;
m_cbTotal = 0 ;
m_cbPosted = 0 ;
m_cParam = 0 ;
m_cParamPosted = 0 ;
m_cFile = 0 ;
m_cFilePosted = 0 ;
m_szCurrParam = NULL ;
m_szCurrFile = NULL ;
m_cbCurrParam = 0 ;
m_cbCurrParamPosted = 0 ;
}
// Copy a string.
// The destination pointer will point to NULL if memory allocation failed.
template
void CHttpPostStatT::_SetString (PSZ * pszDest, PCSZ szSrc)
throw ()
{
_ASSERTE ( pszDest != NULL ) ;
if ( szSrc == NULL ) {
SAFEFREE ( (*pszDest) ) ;
return ;
}
// If the source and destination string are the same, just return.
if ( *pszDest != NULL ) {
if ( 0 == HttpTool::StringCmp (*pszDest, szSrc) )
return ;
SAFEFREE ( (*pszDest) ) ;
}
*pszDest = (PSZ) ::malloc (sizeof (CharType) * (HttpTool::StringLen (szSrc) + 1)) ;
// If memory allocation failed, just return.
if ( *pszDest == NULL )
return ;
HttpTool::StringCopy (*pszDest, szSrc) ;
}
/*!
* This method returns a boolean which indicates whether the POST is in progress or not.
*
* \return TRUE if the POST is in progress, otherwise FALSE
*/
template
BOOL CHttpPostStatT::IsActive (void) const
throw ()
{
return m_bIsActive ;
}
/*!
* This method returns the actual number of bytes to send to a HTTP web server.
* The returned size includes the size of boundaries, parameter names, and other elements.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The actual number of bytes which is going to be sent to a HTTP web server
*/
template
size_t CHttpPostStatT::ActualTotalByte (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::ActualTotalByte: The post context is not active.") ;
return m_cbActualTotal ;
}
/*!
* This method returns the actual number of bytes posted to a HTTP web server.
* The returned size includes the size of boundaries, parameter names, and other elements.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The actual number of bytes posted to a HTTP web server
*/
template
size_t CHttpPostStatT::ActualPostedByte (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::ActualPostedByte: The post context is not active.") ;
return m_cbActualPosted ;
}
/*!
* This method returns the number of bytes to send to a HTTP web server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of bytes to send to a HTTP web server
*/
template
size_t CHttpPostStatT::TotalByte (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::TotalByte: The post context is not active.") ;
return m_cbTotal ;
}
/*!
* This method returns the number of bytes posted to a HTTP web server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of bytes posted to a HTTP web server
*/
template
size_t CHttpPostStatT::PostedByte (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::PostedByte: The post context is not active.") ;
return m_cbPosted ;
}
/*!
* This method returns the total number of parameters to send to a HTTP web server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The total number of parameters
*/
template
DWORD CHttpPostStatT::TotalCount (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::TotalCount: The post context is not active.") ;
return m_cParam ;
}
/*!
* This method returns the number of posted parameters.
* The count includes the current parameter which is being sent to the server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of posted parameters
*/
template
DWORD CHttpPostStatT::PostedCount (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::PostedCount: The post context is not active.") ;
return m_cParamPosted ;
}
/*!
* This method returns the number of file parameters.
* The file parameter is a parameter which contains a file path to upload.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of file parameters
*/
template
DWORD CHttpPostStatT::FileCount (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::FileCount: The post context is not active.") ;
return m_cFile ;
}
/*!
* This method returns the number of posted file parameters.
* The file parameter is a parameter which contains a file path to upload.
* If the current parameter is a file parameter, then the count includes the current parameter.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of posted file parameters
*/
template
DWORD CHttpPostStatT::PostedFileCount (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::PostedFileCount: The post context is not active.") ;
return m_cFilePosted ;
}
/*!
* This method returns the current parameter name.
* The current parameter is a parameter which is being sent to the server.
* It always returns a null-terminated string. The returned string is owned by
* the CHttpPostStat. So you can not free it.
* If the system fails to allocate memory for the current parameter name,
* it will return "NULL" instead of NULL.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The current parameter name
*/
template
typename CHttpPostStatT::PCSZ
CHttpPostStatT::CurrParam (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParam: The post context is not active.") ;
// If the string of the current parameter is NULL,
// returns "NULL" instead of NULL
if ( m_szCurrParam == NULL )
return HttpTool::szNULL ;
return m_szCurrParam ;
}
/*!
* This method returns the file path which is currently being uploaded.
* It always returns a null-terminated string. The returned string is owned by
* the CHttpPostStat. So you can not free it.
* If the current parameter is not a file parameter or the system fails to allocate
* memory for the current file path, it will return "NULL".
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The current file path
*/
template
typename CHttpPostStatT::PCSZ
CHttpPostStatT::CurrFile (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrFile: The post context is not active.") ;
// If the string of the current file is NULL,
// returns "NULL" instead of NULL
if ( m_szCurrFile == NULL )
return HttpTool::szNULL ;
return m_szCurrFile ;
}
/*!
* This method returns the number of bytes of the current parameter.
* The current parameter is a parameter which is being sent to the server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of bytes of the current parameter
*/
template
size_t CHttpPostStatT::CurrParamTotalByte (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamTotalByte: The post context is not active.") ;
return m_cbCurrParam ;
}
/*!
* This method returns the number of posted bytes of the current parameter.
* The current parameter is a parameter which is being sent to the server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of posted bytes of the current parameter
*/
template
size_t CHttpPostStatT::CurrParamPostedByte (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamPostedByte: The post context is not active.") ;
return m_cbCurrParamPosted ;
}
/*!
* This method returns the number of remained bytes of the current parameter.
* The current parameter is a parameter which is being sent to the server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return The number of remained bytes of the current parameter
*/
template
size_t CHttpPostStatT::CurrParamRemainByte (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamRemainByte: The post context is not active.") ;
return m_cbCurrParam - m_cbCurrParamPosted ;
}
/*!
* This method returns TRUE if the current parameter is a file parameter,
* otherwise it will return FALSE.
* The current parameter is a parameter which is being sent to the server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return TRUE if the current parameter is a file parameter, otherwise FALSE
*/
template
BOOL CHttpPostStatT::CurrParamIsFile (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamIsFile: The post context is not active.") ;
return (m_szCurrFile != NULL) ;
}
/*!
* This method returns TRUE if the current parameter is completely posted,
* otherwise it will return FALSE.
* The current parameter is a parameter which is being sent to the server.
* If the IsActive is FALSE, you shouldn't call this method.
*
* \return TRUE if the current parameter is completely posted, otherwise FALSE
*/
template
BOOL CHttpPostStatT::CurrParamIsComplete (void) const
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamIsComplete: The post context is not active.") ;
return (CurrParamTotalByte () == CurrParamPostedByte ()) ;
}
/*!
* \internal
* This method returns TRUE if all bytes are sent.
*
* \return Returns TRUE if all bytes are sent
*/
template
BOOL CHttpPostStatT::_IsComplete (void) const
throw ()
{
_ASSERTE ( m_bIsActive ) ;
return (ActualTotalByte () == ActualPostedByte ()) ;
}
/*!
* \internal
* This method tests whether the m_cbActualPosted is safe to increase.
*
* \param nBytes [in] The number of bytes to add to the m_cbActualPosted.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpPostStatT::_TestAddActualPostedBytes (size_t nBytes)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::_TestAddActualPostedBytes: The post context is not active.") ;
try {
::SafeInt cbActualPosted = m_cbActualPosted ;
_ASSERTE ( (cbActualPosted + nBytes) <= m_cbActualTotal ) ;
cbActualPosted += nBytes ;
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
}
/*!
* \internal
* This method increases the m_cbActualPosted member variable.
*
* \param nBytes [in] The number of bytes to add to the m_cbActualPosted.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpPostStatT::_AddActualPostedBytes (size_t nBytes)
throw (Exception &)
{
_TestAddActualPostedBytes (nBytes) ;
m_cbActualPosted += nBytes ;
}
/*!
* \internal
* This method cleans all internal states and closes the POST state.
*/
template
void CHttpPostStatT::_MakeUnActive (void)
throw ()
{
SAFEFREE (m_szCurrParam) ;
SAFEFREE (m_szCurrFile) ;
_InitMemberVariables () ;
}
/*!
* \internal
* This method starts a new state of the HTTP POST.
*
* \param cbActualTotal [in] The actual total number of bytes of the request.
* \param cbTotal [in] The total number of bytes of the request.
* \param cParam [in] The number of parameters.
* \param cFile [in] The number of file parameters.
*/
template
void CHttpPostStatT::_MakeActive (size_t cbActualTotal, size_t cbTotal, DWORD cParam
, DWORD cFile)
throw ()
{
_MakeUnActive () ;
m_bIsActive = TRUE ;
m_cbActualTotal = cbActualTotal ;
m_cbTotal = cbTotal ;
m_cParam = cParam ;
m_cFile = cFile ;
}
/*!
* \internal
* This method tests whether a new current parameter is safely set.
*
* \param szCurrParam [in] The name of the current parameter.
* \param cbCurrParam [in] The number of bytes of the current parameter.
* \param bIsFile [in] TRUE if the current parameter is a file parameter.
* \param szCurrFile [in] The file path of the current parameter.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpPostStatT::_TestStartNewEntry (PCSZ /* szCurrParam */, size_t /* cbCurrParam */
, BOOL bIsFile, PCSZ /* szCurrFile */)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::_TestStartNewEntry: The post context is not active.") ;
try {
::SafeInt cParamPosted = m_cParamPosted ;
_ASSERTE ( (cParamPosted + 1) <= m_cParam ) ;
cParamPosted++ ;
if ( bIsFile ) {
::SafeInt cFilePosted = m_cFilePosted ;
_ASSERTE ( (cFilePosted + 1) <= m_cFile ) ;
cFilePosted++ ;
}
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
}
/*!
* \internal
* This method sets a new current parameter.
* If memory allocation is failed for strings (szCurrParam, szCurrFile),
* NULL is saved. (and "NULL" is returned when the user requests these values)
*
* \param szCurrParam [in] The name of the current parameter.
* \param cbCurrParam [in] The number of bytes of the current parameter.
* \param bIsFile [in] TRUE if the current parameter is a file parameter.
* \param szCurrFile [in] The file path of the current parameter.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpPostStatT::_StartNewEntry (PCSZ szCurrParam, size_t cbCurrParam
, BOOL bIsFile, PCSZ szCurrFile)
throw (Exception &)
{
_TestStartNewEntry (szCurrParam, cbCurrParam, bIsFile, szCurrFile) ;
if ( bIsFile ) {
m_cFilePosted++ ;
_SetString (&m_szCurrFile, szCurrFile) ;
} else {
_SetString (&m_szCurrFile, NULL) ;
}
m_cParamPosted++ ;
_SetString (&m_szCurrParam, szCurrParam) ;
m_cbCurrParam = cbCurrParam ;
m_cbCurrParamPosted = 0 ;
}
/*!
* \internal
* This method tests whether the counters related to the number of posted bytes is safe to increase.
* (m_cbPosted, m_cbCurrParamPosted, m_cbActualPosted)
*
* \param nBytes [in] The number of bytes to add.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpPostStatT::_TestAddPostedBytes (size_t nBytes)
throw (Exception &)
{
HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::_TestAddPostedBytes: The post context is not active.") ;
try {
_TestAddActualPostedBytes (nBytes) ;
::SafeInt cbPosted = m_cbPosted ;
::SafeInt cbCurrParamPosted = m_cbCurrParamPosted ;
_ASSERTE ( (cbPosted + nBytes) <= m_cbTotal ) ;
_ASSERTE ( (cbCurrParamPosted + nBytes) <= m_cbCurrParam ) ;
cbPosted += nBytes ;
cbCurrParamPosted += nBytes ;
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
}
/*!
* \internal
* This method increases all counters related to the number of posted bytes.
* (m_cbPosted, m_cbCurrParamPosted, m_cbActualPosted)
*
* \param nBytes [in] The number of bytes to add.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpPostStatT::_AddPostedBytes (size_t nBytes)
throw (Exception &)
{
_TestAddPostedBytes (nBytes) ;
m_cbPosted += nBytes ;
m_cbCurrParamPosted += nBytes ;
_AddActualPostedBytes (nBytes) ;
}
/*!
* This operator copies the state information from the source object.
* All strings contained in the targeted object can be NULL if memory allocation failed.
* (and "NULL" is returned when the user requests these values)
*
* \param objPostStat [in] A CHttpPostStat object.
* \return The targeted object.
*/
template
CHttpPostStatT &
CHttpPostStatT::operator= (const CHttpPostStatT & objPostStat)
throw ()
{
if ( this == &objPostStat )
return *this ;
_MakeUnActive () ;
m_bIsActive = objPostStat.m_bIsActive ;
m_cbActualTotal = objPostStat.m_cbActualTotal ;
m_cbActualPosted = objPostStat.m_cbActualPosted ;
m_cbTotal = objPostStat.m_cbTotal ;
m_cbPosted = objPostStat.m_cbPosted ;
m_cParam = objPostStat.m_cParam ;
m_cParamPosted = objPostStat.m_cParamPosted ;
m_cFile = objPostStat.m_cFile ;
m_cFilePosted = objPostStat.m_cFilePosted ;
_SetString (&m_szCurrParam, objPostStat.m_szCurrParam) ;
_SetString (&m_szCurrFile, objPostStat.m_szCurrFile) ;
m_cbCurrParam = objPostStat.m_cbCurrParam ;
m_cbCurrParamPosted = objPostStat.m_cbCurrParamPosted ;
return *this ;
}
///////////////////////////////////////// CHttpPostStatT /////////////////////////////////////////
///////////////////////////////////////// CHttpUrlAnalyzer /////////////////////////////////////////
/*!
* This is the default constructor. If you pass a URL to the constructor,
* It analyzes a URL using Analyze method.
*
* \param szUrl [in] An URL to analyze.
* \param CodePage [in] A code page of the URL. If the URL is a unicode string,
* this parameter is ignored.
* CP_UTF8, CP_UTF7 are not allowed.
* \throw Throws a httpclientexception if an error occurs.
*
* \sa Analyze
*/
template
CHttpUrlAnalyzerT::CHttpUrlAnalyzerT (PCSZ szUrl, UINT CodePage)
throw (Exception &)
{
Analyze (szUrl, CodePage) ;
}
/*!
* This method clears all saved information and sets to 0.
*/
template
void CHttpUrlAnalyzerT::Reset (void)
throw ()
{
m_nProtocolIdx = m_cchProtocol = m_nServerAddrIdx = m_cchServerAddr
= m_nServerPortIdx = m_cchServerPort = m_nUrlPathIdx = m_cchUrlPath
= m_nSearchIdx = m_cchSearch = m_nBookmarkIdx = m_cchBookmark = 0 ;
}
/*!
* This method analyzes a URL and saves information about its component parts.
* It does not check whether the URL is valid. The URL itself is not saved.
*
* \param szUrl [in] An URL to analyze.
* \param CodePage [in] A code page of the URL. If the URL is a unicode string,
* this parameter is ignored.
* CP_UTF8, CP_UTF7 are not allowed.
* \throw Throws a httpclientexception if an error occurs.
*/
template
void CHttpUrlAnalyzerT::Analyze (PCSZ szUrl, UINT CodePage)
throw (Exception &)
{
// The unicode encodings are not allowed
HTTPCLIENT_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
, "CHttpUrlAnalyzerT::Analyze: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
Reset () ;
if ( szUrl == NULL || szUrl[0] == '\0' )
return ;
// Checks overflow
try {
::SafeInt dw = HttpTool::StringLen (szUrl) ;
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
PCSZ pchNextUrl = szUrl ;
DWORD nNextUrlCharIdx = 0 ;
{
// Get the protocol
PCSZ pchColonSlashSlash = NULL ;
{
// Finds "://" in the URL
for (pchColonSlashSlash = pchNextUrl; *pchColonSlashSlash != '\0'; pchColonSlashSlash++) {
// If the character is a leadbyte of the double byte character set
if ( HttpTool::IsDBCSLeadByteEx (CodePage, *pchColonSlashSlash) ) {
// Ignores a trailing byte
if ( *(pchColonSlashSlash + 1) != '\0' )
pchColonSlashSlash++ ;
continue ;
}
if ( (*pchColonSlashSlash == ':')
&& (*(pchColonSlashSlash + 1) != '\0') && (*(pchColonSlashSlash + 1) == '/')
&& (*(pchColonSlashSlash + 2) != '\0') && (*(pchColonSlashSlash + 2) == '/') )
break ;
}
}
if ( *pchColonSlashSlash != '\0' ) {
for (; pchNextUrl != pchColonSlashSlash; pchNextUrl++, m_cchProtocol++, nNextUrlCharIdx++) ;
pchNextUrl += 3/* == strlen (szColonSlashSlash)*/ ;
nNextUrlCharIdx += 3 ;
}
}
{
// Get server address and server port
BOOL bFoundColon = FALSE ;
m_nServerAddrIdx = nNextUrlCharIdx ;
for (; *pchNextUrl != '/' && *pchNextUrl != '?' && *pchNextUrl != '#' && *pchNextUrl != '\0';
pchNextUrl++, nNextUrlCharIdx++) {
// If the character is a leadbyte of the double byte character set
if ( HttpTool::IsDBCSLeadByteEx (CodePage, *pchNextUrl) ) {
m_cchServerPort += (bFoundColon ? 1 : 0) ;
m_cchServerAddr += (bFoundColon ? 0 : 1) ;
// Ignores a trailing byte
if ( *(pchNextUrl + 1) != '\0' ) {
pchNextUrl++ ;
nNextUrlCharIdx++ ;
m_cchServerPort += (bFoundColon ? 1 : 0) ;
m_cchServerAddr += (bFoundColon ? 0 : 1) ;
}
continue ;
}
if ( bFoundColon ) {
m_cchServerPort++ ;
continue ;
}
if ( *pchNextUrl == ':' ) {
m_nServerPortIdx = nNextUrlCharIdx + 1 ;
bFoundColon = TRUE ;
continue ;
}
m_cchServerAddr++ ;
}
if ( !bFoundColon )
m_nServerPortIdx = nNextUrlCharIdx ;
}
{
// Get path and search string
BOOL bFoundQuestionMark = FALSE ;
m_nUrlPathIdx = nNextUrlCharIdx ;
for (; *pchNextUrl != '#' && *pchNextUrl != '\0'; pchNextUrl++, nNextUrlCharIdx++) {
// If the character is a leadbyte of the double byte character set
if ( HttpTool::IsDBCSLeadByteEx (CodePage, *pchNextUrl) ) {
m_cchSearch += (bFoundQuestionMark ? 1 : 0) ;
m_cchUrlPath += (bFoundQuestionMark ? 0 : 1) ;
// Ignores a trailing byte
if ( *(pchNextUrl + 1) != '\0' ) {
pchNextUrl++ ;
nNextUrlCharIdx++ ;
m_cchSearch += (bFoundQuestionMark ? 1 : 0) ;
m_cchUrlPath += (bFoundQuestionMark ? 0 : 1) ;
}
continue ;
}
if ( bFoundQuestionMark ) {
m_cchSearch++ ;
continue ;
}
if ( *pchNextUrl == '?' ) {
m_nSearchIdx = nNextUrlCharIdx ;
m_cchSearch++ ;
bFoundQuestionMark = TRUE ;
continue ;
}
m_cchUrlPath++ ;
}
if ( !bFoundQuestionMark )
m_nSearchIdx = nNextUrlCharIdx ;
}
{
// Get bookmark
m_nBookmarkIdx = nNextUrlCharIdx ;
for (; *pchNextUrl != '\0'; pchNextUrl++, nNextUrlCharIdx++, m_cchBookmark++) ;
}
}
///////////////////////////////////////// CHttpUrlAnalyzer /////////////////////////////////////////
///////////////////////////////////////// CHttpClientT /////////////////////////////////////////
/*!
* This is a default constructor with no argument.
*/
template
CHttpClientT::CHttpClientT (void)
throw ()
{
// Initializes member variables
m_bStrictFileCheck = FALSE ;
m_bUseUtf8 = FALSE ;
m_nAnsiCodePage = CP_ACP ;
m_RefCount = 0;
// variables related to the WinInet API
m_szInternetUserAgent = NULL ;
m_dwInternetAccessType = INTERNET_OPEN_TYPE_PRECONFIG ;
m_szInternetProxyName = NULL ;
m_szInternetProxyBypass = NULL ;
m_dwInternetFlags = 0 ;
m_szProxyUserName = NULL ;
m_szProxyPassword = NULL ;
// Initializes a boundary
m_szBoundary = NULL ;
m_szUploadContType = NULL ;
m_szBoundaryA = NULL ;
m_bNeedToFreeBoundaryA = FALSE ;
try {
m_szBoundary = HttpTool::CreateUploadBoundary () ;
if ( m_szBoundary ) {
m_szUploadContType =
(PSZ) ::malloc (sizeof (CharType)
* (30 /* ::strlen (szMultipartFormDataBoundary) */
+ HttpTool::StringLen (m_szBoundary)
+ 1)
) ;
if ( m_szUploadContType == NULL )
throw "Out of memory" ;
HttpTool::StringCopy (m_szUploadContType, HttpTool::szMultipartFormDataBoundary) ;
HttpTool::StringCat (m_szUploadContType, m_szBoundary) ;
m_szBoundaryA = _String2Ansi (m_szBoundary, m_bNeedToFreeBoundaryA) ;
}
} catch (...) {
SAFEFREE (m_szBoundary) ;
SAFEFREE (m_szUploadContType) ;
}
// Initializes the context related to the POST context
_InitPostContext () ;
}
/*!
* This is a default destructor.
*/
template
CHttpClientT::~CHttpClientT (void)
throw ()
{
// Releases the POST context
_EndPostContext () ;
// Releases the Boundary
SAFEFREE (m_szBoundary) ;
SAFEFREE (m_szUploadContType) ;
if ( m_bNeedToFreeBoundaryA )
SAFEFREE (m_szBoundaryA) ;
// Releases the variables related to the WinInet API
SAFEFREE (m_szInternetUserAgent) ;
SAFEFREE (m_szInternetProxyName) ;
SAFEFREE (m_szInternetProxyBypass) ;
SAFEFREE (m_szProxyUserName) ;
SAFEFREE (m_szProxyPassword) ;
}
template
DWORD CHttpClientT::_String2AnsiLen (PCSZ szStr)
throw (Exception &)
{
return HttpEncoder::AnsiEncodeLen (szStr, m_nAnsiCodePage) ;
}
template
PCSTR CHttpClientT::_String2Ansi (PCSZ szStr, BOOL & bNeedToFree)
throw (Exception &)
{
if ( HttpTool::IsAnsi () ) {
bNeedToFree = FALSE ;
return reinterpret_cast (szStr) ;
}
DWORD cchAnsi = HttpEncoder::AnsiEncodeLen (szStr, m_nAnsiCodePage) ;
::SafeInt cbRequired = 0 ;
try {
cbRequired += cchAnsi ;
cbRequired += 1 ;
cbRequired *= sizeof (CHAR) ;
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
PSTR szBuff = (PSTR) ::malloc (cbRequired.Value ()) ;
if ( szBuff == NULL )
HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
try {
HttpEncoder::AnsiEncode (szBuff, szStr, m_nAnsiCodePage) ;
} catch (Exception &) {
SAFEFREE (szBuff) ;
throw ;
}
bNeedToFree = TRUE ;
return szBuff ;
}
template
DWORD CHttpClientT::_UrlEncodeLenA (PCSZ szStr)
throw (Exception &)
{
return HttpEncoder::UrlEncodeLenA (szStr, m_bUseUtf8, m_nAnsiCodePage) ;
}
template
PCSTR CHttpClientT::_UrlEncodeA (PSTR szBuff, PCSZ szStr)
throw (Exception &)
{
return HttpEncoder::UrlEncodeA (szBuff, szStr, m_bUseUtf8, m_nAnsiCodePage) ;
}
template
PCSTR CHttpClientT::_UrlEncodeA (PCSZ szStr)
throw (Exception &)
{
DWORD cchEncoded = _UrlEncodeLenA (szStr) ;
::SafeInt cbRequired = 0 ;
try {
cbRequired += cchEncoded ;
cbRequired += 1 ;
cbRequired *= sizeof (CHAR) ;
} catch (::SafeIntException & e) {
HttpTool::ThrowException (e) ;
}
PSTR szBuff = (PSTR) ::malloc (cbRequired.Value ()) ;
if ( szBuff == NULL )
HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
try {
_UrlEncodeA (szBuff, szStr) ;
} catch (Exception &) {
SAFEFREE (szBuff) ;
throw ;
}
return szBuff ;
}
template
DWORD CHttpClientT::_UrlEncodeLen (PCSZ szStr)
throw (Exception &)
{
return HttpEncoder::UrlEncodeLen (szStr, m_bUseUtf8, m_nAnsiCodePage) ;
}
template
typename CHttpClientT