/* ! * \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::PCSZ CHttpClientT::_UrlEncode (PSZ szBuff, PCSZ szStr) throw (Exception &) { return HttpEncoder::UrlEncode (szBuff, szStr, m_bUseUtf8, m_nAnsiCodePage) ; } template typename CHttpClientT::PCSZ CHttpClientT::_UrlEncode (PCSZ szStr) throw (Exception &) { DWORD cchEncoded = _UrlEncodeLen (szStr) ; ::SafeInt cbRequired = 0 ; try { cbRequired += cchEncoded ; cbRequired += 1 ; cbRequired *= sizeof (CharType) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } PSZ szBuff = (PSZ) ::malloc (cbRequired.Value ()) ; if ( szBuff == NULL ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; try { _UrlEncode (szBuff, szStr) ; } catch (Exception &) { SAFEFREE (szBuff) ; throw ; } return szBuff ; } template DWORD CHttpClientT::_Utf8EncodeLen (PCSZ szStr) throw (Exception &) { return HttpEncoder::Utf8EncodeLen (szStr, m_nAnsiCodePage) ; } template PCSTR CHttpClientT::_Utf8Encode (PSTR szBuff, PCSZ szStr) throw (Exception &) { return HttpEncoder::Utf8Encode (szBuff, szStr, m_nAnsiCodePage) ; } template PCSTR CHttpClientT::_Utf8Encode (PCSZ szStr) throw (Exception &) { DWORD cchEncoded = _Utf8EncodeLen (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 { _Utf8Encode (szBuff, szStr) ; } catch (Exception &) { SAFEFREE (szBuff) ; throw ; } return szBuff ; } /*! * This method allocates memory and copies the target string if the szTargetString and the szOldString * are not the same. If the szTargetString and the szOldString are the same, it returns szOldString. * * \param szTargetString [in] A target string to compare. * \param szOldString [in] An original string to compare. * \return A copied target string or szOldString. * \throw Throws a httpclientexception if an error occurs. */ template typename CHttpClientT::PSZ CHttpClientT::_AllocNCopyIfNewString (PCSZ szTargetString, PSZ szOldString) throw (Exception &) { if ( szTargetString == szOldString ) return szOldString ; if ( szTargetString == NULL ) return NULL ; if ( szOldString && !HttpTool::StringCmp (szTargetString, szOldString) ) return szOldString ; ::SafeInt cbRequired = 0 ; try { cbRequired = HttpTool::StringLen (szTargetString) ; cbRequired++ ; cbRequired *= sizeof (CharType) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } PSZ szNewString = (PSZ) ::malloc (cbRequired.Value ()) ; if ( szNewString == NULL ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; HttpTool::StringCopy (szNewString, szTargetString) ; return szNewString ; } /*! * This method sets the current StrictFileCheck property. * If the StrictFileCheck is TRUE, an exception will be thrown * when it fails to open a file which is going to be uploaded. * If FALSE, it does nothing. The default is FALSE. * * \param bStrict [in] A boolean value which specifies StrictFileCheck. * \return Previously set value. * * The following code snippet demonstrates the usage of this property. * \code ... CHttpClient objHttpReq ; // CHttpClient will throw a httpclientexception if it fails to open a file. objHttpReq.SetStrictFileCheck (TRUE) ; try { // Adds HTTP parameters objHttpReq.AddParam (_T ("title"), _T ("The memory of K-NET")) ; objHttpReq.AddParam (_T ("photo"), _T ("C:\\myphoto\\k-net.jpg"), ParamFile) ; // Starts a new HTTP UPLOAD objHttpReq.RequestUpload (...) or objHttpReq.BeginUpload (...) ; ... } catch (httpclientexception & e) { // If it failed to open the file (C:\myphoto\k-net.jpg) if ( e.LastError () == HTTPCLIENT_ERR_OPENFILE_FAILED ) { ... // Place error handling codes here. } ... } * \endcode */ template BOOL CHttpClientT::SetStrictFileCheck (BOOL bStrict) throw () { BOOL bOldStrictFileCheck = m_bStrictFileCheck ; m_bStrictFileCheck = bStrict ; return bOldStrictFileCheck ; } /*! * This method returns the current StrictFileCheck property. * If the StrictFileCheck is TRUE, an exception will be thrown * when it fails to open a file which is going to be uploaded. * If FALSE, it does nothing. The default is FALSE. * * \return StrictFileCheck property. */ template BOOL CHttpClientT::GetStrictFileCheck (void) const throw () { return m_bStrictFileCheck ; } /*! * This method returns the current StrictFileCheck property. * If the StrictFileCheck is TRUE, an exception will be thrown * when it fails to open a file which is going to be uploaded. * If FALSE, it does nothing. The default is FALSE. * * \return StrictFileCheck property. */ template BOOL CHttpClientT::StrictFileCheck (void) const throw () { return GetStrictFileCheck () ; } /*! * This method sets the current UseUtf8 property. * If the UseUtf8 is TRUE, all request will be sent in UTF-8 encoding, * otherwise, all request will be sent in ANSI encoding. * If a web server uses UTF-8 encoding, you should set TRUE. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * The default is FALSE. * * \param bUseUtf8 [in] A boolean value which specifies UseUtf8. * \return previously set value. */ template BOOL CHttpClientT::SetUseUtf8 (BOOL bUseUtf8) throw (Exception &) { // It is not allowed to call this method if the POST context is active. if( _PostContextIsActive () ) { OutputDebugString (L"CHttpClientT::SetUseUtf8: It is not allowed to call this method if the POST context is active.") ; } BOOL bOldUseUtf8 = m_bUseUtf8 ; m_bUseUtf8 = bUseUtf8 ; return bOldUseUtf8 ; } /*! * This method returns the current UseUtf8 property. * If the UseUtf8 is TRUE, all request will be sent in UTF-8 encoding, * otherwise, all request will be sent in ANSI encoding. * If a web server uses UTF-8 encoding, you should set TRUE. * The default is FALSE. * * \return UseUtf8 property. */ template BOOL CHttpClientT::GetUseUtf8 (void) const throw () { return m_bUseUtf8 ; } /*! * This method returns the current UseUtf8 property. * If the UseUtf8 is TRUE, all request will be sent in UTF-8 encoding, * otherwise, all request will be sent in ANSI encoding. * If a web server uses UTF-8 encoding, you should set TRUE. * The default is FALSE. * * \return UseUtf8 property. */ template BOOL CHttpClientT::UseUtf8 (void) const throw () { return GetUseUtf8 () ; } /*! * This method sets the current ANSI code page which is used for all non-unicode strings. * If a web server uses an ANSI character set, you should set an appropriate ANSI code page. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * For more infomation about the Code-Page Identifiers, see the MSDN documentation. * The default is CP_ACP (which is the system's ANSI code page). * * \param nAnsiCodePage [in] An ANSI code page to set. * \return previously set value. * * \sa ::WideCharToMultiByte, ::MultiByteToWideChar */ template UINT CHttpClientT::SetAnsiCodePage (UINT nAnsiCodePage) throw (Exception &) { // It is not allowed to call this method if the POST context is active. HTTPCLIENT_ASSERT (!_PostContextIsActive () , "CHttpClientT::SetAnsiCodePage: It is not allowed to call this method if the POST context is active.") ; // It is not allowed to set the unicode encodings HTTPCLIENT_ASSERT ((nAnsiCodePage != CP_UTF8) && (nAnsiCodePage != CP_UTF7) , "CHttpClientT::SetAnsiCodePage: CP_UTF8 and CP_UTF7 can not be used for the nAnsiCodePage parameter.") ; UINT nOldAnsiCodePage = m_nAnsiCodePage ; m_nAnsiCodePage = nAnsiCodePage ; return nOldAnsiCodePage ; } /*! * This method returns the current ANSI code page which is used for all non-unicode strings. * If a web server uses an ANSI character set, you should set an appropriate ANSI code page. * For more infomation about the Code-Page Identifiers, see the MSDN documentation. * The default is CP_ACP (which is the system's ANSI code page). * * \return The current ANSI code page. */ template UINT CHttpClientT::GetAnsiCodePage (void) const throw () { return m_nAnsiCodePage ; } /*! * This method returns the current ANSI code page which is used for all non-unicode strings. * If a web server uses an ANSI character set, you should set an appropriate ANSI code page. * For more infomation about the Code-Page Identifiers, see the MSDN documentation. * The default is CP_ACP (which is the system's ANSI code page). * * \return The current ANSI code page. */ template UINT CHttpClientT::AnsiCodePage (void) const throw () { return GetAnsiCodePage () ; } /*! * This method erases all HTTP headers which is sent to a web server. * * \return FALSE if all header is aleady erased, otherwise TRUE. */ template BOOL CHttpClientT::ClearHeader (void) throw () { return m_mapHeader.Clear () ; } /*! * This method erases a HTTP header at the given index specified by nIdx. * * \param nIdx [in] A header index to remove. * \return TRUE if the header is found and removed, otherwise FALSE. */ template BOOL CHttpClientT::RemoveHeader (DWORD nIdx) throw () { return m_mapHeader.Remove (nIdx) ; } /*! * This method erases a HTTP header of which name is szName. * If the header has multiple values, you can specify a zero-based index for a specific value. * * \param szName [in] A case-insensitive header name to remove. NULL is not allowed. * \param nIdx [in] A zero-based value index if the header has multiple values. The default is zero. * \return TRUE if the header is found and removed, otherwise FALSE. */ template BOOL CHttpClientT::RemoveHeader (PCSZ szName, DWORD nIdx) throw () { return m_mapHeader.Remove (szName, nIdx) ; } /*! * This method erases all HTTP headers of which name is szName. * * \param szName [in] A case-insensitive header name to remove. NULL is not allowed. * \return TRUE if the header is found and removed, otherwise FALSE. */ template BOOL CHttpClientT::RemoveAllHeader (PCSZ szName) throw () { return m_mapHeader.RemoveAll (szName) ; } /*! * This method adds a new HTTP header. * The header's name and the header's value can not be NULL or an empty string. * * \param szName [in] A case-insensitive header name. NULL or an empty string is not allowed. * \param szValue [in] A header value. NULL or an empty string is not allowed. * \throw Throws a httpclientexception if an error occurs. */ template void CHttpClientT::AddHeader (PCSZ szName, PCSZ szValue) throw (Exception &) { HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientT::AddHeader: szName can not be NULL.") ; HTTPCLIENT_ASSERT (szName[0] != '\0', "CHttpClientT::AddHeader: szName can not be an empty string.") ; HTTPCLIENT_ASSERT (szValue != NULL, "CHttpClientT::AddHeader: szValue can not be NULL.") ; HTTPCLIENT_ASSERT (szValue[0] != '\0', "CHttpClientT::AddHeader: szValue can not be an empty string.") ; m_mapHeader.Add (szName, szValue) ; } /*! * This method modifies the HTTP header value of which name is szName. * If the header has multiple values, you can specify a zero-based index for a specific value. * If the header is not found and the nIdx is zero, it will add a new header. * The header's name and the header's value can not be NULL or an empty string. * * \param szName [in] A case-insensitive header name. NULL or an empty string is not allowed. * \param szValue [in] A header value. NULL or an empty string is not allowed. * \param nIdx [in] A zero-based value index if the header has multiple values. The default is zero. * \throw Throws a httpclientexception if an error occurs. */ template void CHttpClientT::SetHeader (PCSZ szName, PCSZ szValue, DWORD nIdx) throw (Exception &) { HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientT::SetHeader: szName can not be NULL.") ; HTTPCLIENT_ASSERT (szName[0] != '\0', "CHttpClientT::SetHeader: szName can not be an empty string.") ; HTTPCLIENT_ASSERT (szValue != NULL, "CHttpClientT::SetHeader: szValue can not be NULL.") ; HTTPCLIENT_ASSERT (szValue[0] != '\0', "CHttpClientT::SetHeader: szValue can not be an empty string.") ; m_mapHeader.Set (szName, szValue, 0, nIdx) ; } /*! * This method returns a HTTP header name at the given index specified by nIdx. * If 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 CHttpClientT. So you can not free it. * * \param nIdx [in] A zero-based header index. * \return A header name. */ template typename CHttpClientT::PCSZ CHttpClientT::GetHeaderName (DWORD nIdx) throw () { return m_mapHeader.GetKey (nIdx) ; } /*! * This method returns a HTTP header at the given index specified by nIdx. * If 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 CHttpClient. So you can not free it. * * \param nIdx [in] A zero-based header index. * \return A header. */ template typename CHttpClientT::PCSZ CHttpClientT::GetHeader (DWORD nIdx) throw () { return m_mapHeader.GetValue (nIdx) ; } /*! * This method returns a HTTP header of which name is szName. * If the header has multiple values, you can specify a zero-based index for a specific value. * If the header is not found, it will return NULL. Otherwise it always returns a null-terminated string. * The returned string is owned by the CHttpClient. So you can not free it. * * \param szName [in] A case-insensitive header name to find. NULL is not allowed. * \param nIdx [in] A zero-based value index if the header has multiple values. The default is zero. * \return A header. */ template typename CHttpClientT::PCSZ CHttpClientT::GetHeader (PCSZ szName, DWORD nIdx) throw () { return m_mapHeader.GetValue (szName, nIdx) ; } /*! * This method returns whether the HTTP header of which name is szName exists. * If the header has multiple values, you can specify a zero-based index for a specific value. * * \param szName [in] A case-insensitive header name to find. NULL is not allowed. * \param nIdx [in] A zero-based value index if the header has multiple values. The default is zero. * \return TRUE if the header is found, otherwise FALSE. */ template BOOL CHttpClientT::HeaderExists (PCSZ szName, DWORD nIdx) throw () { return m_mapHeader.Exists (szName, nIdx) ; } /*! * This method returns the number of HTTP headers of which name is szName. * If the szName is NULL, it will return the number of all headers. * * \param szName [in] A case-insensitive header name to find. * \return The number of headers. */ template DWORD CHttpClientT::GetHeaderCount (PCSZ szName) throw () { return m_mapHeader.Count (szName) ; } /*! * This method erases all HTTP parameters. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * * \return FALSE if all parameter is aleady erased, otherwise TRUE. */ template BOOL CHttpClientT::ClearParam (void) throw (Exception &) { // It is not allowed to call this method if the POST context is active. HTTPCLIENT_ASSERT (!_PostContextIsActive () , "CHttpClientT::ClearParam: It is not allowed to call this method if the POST context is active.") ; return m_mapParam.Clear () ; } /*! * This method erases a HTTP parameter at the given index specified by nIdx. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * * \param nIdx [in] A zero-based parameter index to remove. * \return TRUE if the parameter is found and removed, otherwise FALSE. */ template BOOL CHttpClientT::RemoveParam (DWORD nIdx) throw (Exception &) { // It is not allowed to call this method if the POST context is active. HTTPCLIENT_ASSERT (!_PostContextIsActive () , "CHttpClientT::RemoveParam: It is not allowed to call this method if the POST context is active.") ; return m_mapParam.Remove (nIdx) ; } /*! * This method erases a HTTP parameter of which name is szName. * If the parameter has multiple values, you can specify a zero-based index for a specific value. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * * \param szName [in] A case-insensitive parameter name to remove. NULL is not allowed. * \param nIdx [in] A zero-based value index if the parameter has multiple values. The default is zero. * \return TRUE if the parameter is found and removed, otherwise FALSE. */ template BOOL CHttpClientT::RemoveParam (PCSZ szName, DWORD nIdx) throw (Exception &) { // It is not allowed to call this method if the POST context is active. HTTPCLIENT_ASSERT (!_PostContextIsActive () , "CHttpClientT::RemoveParam: It is not allowed to call this method if the POST context is active.") ; return m_mapParam.Remove (szName, nIdx) ; } /*! * This method erases all HTTP parameters of which name is szName. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * * \param szName [in] A case-insensitive parameter name to remove. NULL is not allowed. * \return TRUE if the parameter is found and removed, otherwise FALSE. */ template BOOL CHttpClientT::RemoveAllParam (PCSZ szName) throw (Exception &) { // It is not allowed to call this method if the POST context is active. HTTPCLIENT_ASSERT (!_PostContextIsActive () , "CHttpClientT::RemoveAllParam: It is not allowed to call this method if the POST context is active.") ; return m_mapParam.RemoveAll (szName) ; } /*! * This method adds a new HTTP parameter. The dwParamAttr specifies parameter attributes. * For more information about parameter attributes, see the ParamAttr enumerator. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * * \param szName [in] A case-insensitive parameter name. NULL is not allowed. * \param szValue [in] A parameter value. The default is NULL. * \param dwParamAttr [in] A parameter attribute. * This is a bitwise Ored value of the ParamAttr enumerator. * The default is ParamNormal. * \throw Throws a httpclientexception if an error occurs. * * \sa ParamAttr */ template void CHttpClientT::AddParam (PCSZ szName, PCSZ szValue, DWORD dwParamAttr) throw (Exception &) { // It is not allowed to call this method if the POST context is active. /*HTTPCLIENT_ASSERT (!_PostContextIsActive () , "CHttpClientT::AddParam: It is not allowed to call this method if the POST context is active.") ;*/ if( _PostContextIsActive () ) { OutputDebugString(L"CHttpClientT::AddParam: It is not allowed to call this method if the POST context is active.\n"); } m_mapParam.Add (szName, szValue, dwParamAttr) ; } /*! * This method modifies the HTTP parameter value of which name is szName. * If the parameter has multiple values, you can specify a zero-based index for a specific value. * If the parameter is not found and the nIdx is zero, it will add a new parameter. * The dwParamAttr specifies parameter attributes. * For more information about parameter attributes, see the ParamAttr enumerator. * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished. * * \param szName [in] A case-insensitive parameter name. NULL is not allowed. * \param szValue [in] A parameter value. The default is NULL. * \param dwParamAttr [in] A parameter attribute. * This is a bitwise Ored value of the ParamAttr enumerator. * The default is ParamNormal. * \param nIdx [in] A zero-based value index if the parameter has multiple values. * The default is zero. * \throw Throws a httpclientexception if an error occurs. * * \sa ParamAttr */ template void CHttpClientT::SetParam (PCSZ szName, PCSZ szValue, DWORD dwParamAttr, DWORD nIdx) throw (Exception &) { // It is not allowed to call this method if the POST context is active. HTTPCLIENT_ASSERT (!_PostContextIsActive () , "CHttpClientT::SetParam: It is not allowed to call this method if the POST context is active.") ; m_mapParam.Set (szName, szValue, dwParamAttr, nIdx) ; } /*! * This method returns a HTTP parameter name at the given index specified by nIdx. * If 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 CHttpClient. So you can not free it. * * \param nIdx [in] A zero-based parameter index. * \return A parameter name. */ template typename CHttpClientT::PCSZ CHttpClientT::GetParamName (DWORD nIdx) throw () { return m_mapParam.GetKey (nIdx) ; } /*! * This method returns a HTTP parameter at the given index specified by nIdx. * If 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 CHttpClient. So you can not free it. * * \param nIdx [in] A zero-based parameter index. * \return A parameter. */ template typename CHttpClientT::PCSZ CHttpClientT::GetParam (DWORD nIdx) throw () { return m_mapParam.GetValue (nIdx) ; } /*! * This method returns a HTTP parameter of which name is szName. * If the parameter has multiple values, you can specify a zero-based index for a specific value. * If the parameter is not found, it will return NULL. Otherwise it always returns a null-terminated string. * The returned string is owned by the CHttpClient. So you can not free it. * * \param szName [in] A case-insensitive parameter name to find. NULL is not allowed. * \param nIdx [in] A zero-based value index if the parameter has multiple values. * The default is zero. * \return A parameter. */ template typename CHttpClientT::PCSZ CHttpClientT::GetParam (PCSZ szName, DWORD nIdx) throw () { return m_mapParam.GetValue (szName, nIdx) ; } /*! * This method returns a HTTP parameter attribute at the given index specified by nIdx. * If the index is out of range or the attribute is 0, it will return 0. * * \param nIdx [in] A zero-based parameter index. * \return A parameter attribute. * * \sa ParamAttr */ template DWORD CHttpClientT::GetParamAttr (DWORD nIdx) throw () { return m_mapParam.GetFlag (nIdx) ; } /*! * This method returns an attribute of the HTTP parameter of which name is szName. * If the parameter has multiple values, you can specify a zero-based index for a specific value. * If the parameter is not found or the attribute is 0, it will return 0. * * \param szName [in] A case-insensitive parameter name to find. NULL is not allowed. * \param nIdx [in] A zero-based value index if the parameter has multiple values. * The default is zero. * \return A parameter attribute. * * \sa ParamAttr */ template DWORD CHttpClientT::GetParamAttr (PCSZ szName, DWORD nIdx) throw () { return m_mapParam.GetFlag (szName, nIdx) ; } /*! * This method returns whether the HTTP parameter of which name is szName exists. * If the parameter has multiple values, you can specify a zero-based index for a specific value. * * \param szName [in] A case-insensitive parameter name to find. NULL is not allowed. * \param nIdx [in] A zero-based value index if the parameter has multiple values. * The default is zero. * \return TRUE if the parameter is found, otherwise FALSE. */ template BOOL CHttpClientT::ParamExists (PCSZ szName, DWORD nIdx) throw () { return m_mapParam.Exists (szName, nIdx) ; } /*! * This method returns the number of HTTP parameters of which name is szName. * If the szName is NULL, it will return the number of all parameters. * * \param szName [in] A case-insensitive parameter name to find. * The default is NULL. * \return The number of parameters. */ template DWORD CHttpClientT::GetParamCount (PCSZ szName) throw () { return m_mapParam.Count (szName) ; } /*! * This method sets parameters for the ::InternetOpen function which is called internally before sending a request. * All parameters passed to this method is forwarded to the ::InternetOpen function. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * The following code snippet demonstrates the usage of this method. * \code ... CHttpClient objHttpReq ; try { // Changes the HTTP user agent. objHttpReq.SetInternet (_T ("My Custom User Agent")) ; ... } catch (httpclientexception & e) { ... // Place error handling codes here. } * \endcode * * \param szUserAgent [in] A User Agent which corresponds to the lpszAgent parameter of the ::InternetOpen function. * \param dwAccessType [in] A type of access which corresponds to the dwAccessType parameter of the ::InternetOpen function. * \param szProxyName [in] A name of a proxy server which corresponds to the lpszProxyName parameter of the ::InternetOpen function. * \param szProxyBypass [in] A proxy bypass list which corresponds to the lpszProxyBypass parameter of the ::InternetOpen function. * \param dwFlags [in] Options which corresponds to the dwFlags parameter of the ::InternetOpen function. * \throw Throws a httpclientexception if an error occurs. * * \sa ::InternetOpen */ template void CHttpClientT::SetInternet (PCSZ szUserAgent, DWORD dwAccessType , PCSZ szProxyName, PCSZ szProxyBypass , DWORD dwFlags) throw (Exception &) { PSZ szNewUserAgent = NULL ; PSZ szNewProxyName = NULL ; PSZ szNewProxyBypass = NULL ; try { szNewUserAgent = _AllocNCopyIfNewString (szUserAgent, m_szInternetUserAgent) ; szNewProxyName = _AllocNCopyIfNewString (szProxyName, m_szInternetProxyName) ; szNewProxyBypass = _AllocNCopyIfNewString (szProxyBypass, m_szInternetProxyBypass) ; } catch (Exception &) { if ( m_szInternetUserAgent != szNewUserAgent ) SAFEFREE (szNewUserAgent) ; if ( m_szInternetProxyName != szNewProxyName ) SAFEFREE (szNewProxyName) ; if ( m_szInternetProxyBypass != szNewProxyBypass ) SAFEFREE (szNewProxyBypass) ; throw httpclientexceptionA(); } if ( m_szInternetUserAgent != szNewUserAgent ) { SAFEFREE (m_szInternetUserAgent) ; m_szInternetUserAgent = szNewUserAgent ; } if ( m_szInternetProxyName != szNewProxyName ) { SAFEFREE (m_szInternetProxyName) ; m_szInternetProxyName = szNewProxyName ; } if ( m_szInternetProxyBypass != szNewProxyBypass ) { SAFEFREE (m_szInternetProxyBypass) ; m_szInternetProxyBypass = szNewProxyBypass ; } m_dwInternetAccessType = dwAccessType ; m_dwInternetFlags = dwFlags ; } /*! * This method sets the lpszAgent parameter for the ::InternetOpen function which is called internally before sending a request. * The parameter passed to this method is forwarded to the ::InternetOpen function. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \param szUserAgent [in] A User Agent which corresponds to the lpszAgent parameter of the ::InternetOpen function. * If this is NULL, The default is used. * \throw Throws a httpclientexception if an error occurs. * * \sa ::InternetOpen, SetInternet */ template void CHttpClientT::SetInternetUserAgent (PCSZ szUserAgent) throw (Exception &) { PSZ szNewUserAgent = NULL ; try { szNewUserAgent = _AllocNCopyIfNewString (szUserAgent, m_szInternetUserAgent) ; } catch (Exception &) { if ( m_szInternetUserAgent != szNewUserAgent ) SAFEFREE (szNewUserAgent) ; throw ; } if ( m_szInternetUserAgent != szNewUserAgent ) { SAFEFREE (m_szInternetUserAgent) ; m_szInternetUserAgent = szNewUserAgent ; } } /*! * This method returns the lpszAgent parameter for the ::InternetOpen function which is called internally before sending a request. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \return A User Agent which corresponds to the lpszAgent parameter of the ::InternetOpen function. * * \sa ::InternetOpen, SetInternet */ template typename CHttpClientT::PCSZ CHttpClientT::GetInternetUserAgent (void) throw () { return m_szInternetUserAgent ? m_szInternetUserAgent : HttpTool::szDefUsrAgent ; } /*! * This method sets the dwAccessType parameter for the ::InternetOpen function which is called internally before sending a request. * The parameter passed to this method is forwarded to the ::InternetOpen function. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \param dwAccessType [in] A type of access which corresponds to the dwAccessType parameter of the ::InternetOpen function. * * \sa ::InternetOpen, SetInternet */ template void CHttpClientT::SetInternetAccessType (DWORD dwAccessType) throw () { m_dwInternetAccessType = dwAccessType ; } /*! * This method returns the dwAccessType parameter for the ::InternetOpen function which is called internally before sending a request. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \return A type of access which corresponds to the dwAccessType parameter of the ::InternetOpen function. * * \sa ::InternetOpen, SetInternet */ template DWORD CHttpClientT::GetInternetAccessType (void) throw () { return m_dwInternetAccessType ; } /*! * This method sets the lpszProxyName parameter for the ::InternetOpen function which is called internally before sending a request. * The parameter passed to this method is forwarded to the ::InternetOpen function. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \param szProxyName [in] A proxy server name which corresponds to the lpszProxyName parameter of the ::InternetOpen function. * \throw Throws a httpclientexception if an error occurs. * * \sa ::InternetOpen, SetInternet */ template void CHttpClientT::SetInternetProxyName (PCSZ szProxyName) throw (Exception &) { PSZ szNewProxyName = NULL ; try { szNewProxyName = _AllocNCopyIfNewString (szProxyName, m_szInternetProxyName) ; } catch (Exception &) { if ( m_szInternetProxyName != szNewProxyName ) SAFEFREE (szNewProxyName) ; throw ; } if ( m_szInternetProxyName != szNewProxyName ) { SAFEFREE (m_szInternetProxyName) ; m_szInternetProxyName = szNewProxyName ; } } /*! * This method returns the lpszProxyName parameter for the ::InternetOpen function which is called internally before sending a request. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \return A proxy server name which corresponds to the lpszProxyName parameter of the ::InternetOpen function. * * \sa ::InternetOpen, SetInternet */ template typename CHttpClientT::PCSZ CHttpClientT::GetInternetProxyName (void) throw () { return m_szInternetProxyName ; } /*! * This method sets the lpszProxyBypass parameter for the ::InternetOpen function which is called internally before sending a request. * The parameter passed to this method is forwarded to the ::InternetOpen function. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \param szProxyBypass [in] A proxy bypass list which corresponds to the lpszProxyBypass parameter of the ::InternetOpen function. * \throw Throws a httpclientexception if an error occurs. * * \sa ::InternetOpen, SetInternet */ template void CHttpClientT::SetInternetProxyBypass (PCSZ szProxyBypass) throw (Exception &) { PSZ szNewProxyBypass = NULL ; try { szNewProxyBypass = _AllocNCopyIfNewString (szProxyBypass, m_szInternetProxyBypass) ; } catch (Exception &) { if ( m_szInternetProxyBypass != szNewProxyBypass ) SAFEFREE (szNewProxyBypass) ; throw ; } if ( m_szInternetProxyBypass != szNewProxyBypass ) { SAFEFREE (m_szInternetProxyBypass) ; m_szInternetProxyBypass = szNewProxyBypass ; } } /*! * This method returns the lpszProxyBypass parameter for the ::InternetOpen function which is called internally before sending a request. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \return A proxy bypass list which corresponds to the lpszProxyBypass parameter of the ::InternetOpen function. * * \sa ::InternetOpen, SetInternet */ template typename CHttpClientT::PCSZ CHttpClientT::GetInternetProxyBypass (void) throw () { return m_szInternetProxyBypass ; } /*! * This method sets the dwFlags parameter for the ::InternetOpen function which is called internally before sending a request. * The parameter passed to this method is forwarded to the ::InternetOpen function. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \param dwFlags [in] Options which corresponds to the dwFlags parameter of the ::InternetOpen function. * * \sa ::InternetOpen, SetInternet */ template void CHttpClientT::SetInternetFlags (DWORD dwFlags) throw () { m_dwInternetFlags = dwFlags ; } /*! * This method returns the dwFlags parameter for the ::InternetOpen function which is called internally before sending a request. * For more infomation about the ::InternetOpen function, see the MSDN documentation. * * \return Options which corresponds to the dwFlags parameter of the ::InternetOpen function. * * \sa ::InternetOpen, SetInternet */ template DWORD CHttpClientT::GetInternetFlags (void) throw () { return m_dwInternetFlags ; } /*! * This method returns the number of characters required to make a URL for a HTTP * GET request. If you want to pass some parameters to a web server using HTTP GET, * you have to append parameters to the requested URL. This method calculates * the length of the URL for that situation. If the CHttpClient does not contain any * HTTP parameters, it returns the length of the szUrl. Otherwise it returns * the calculated length. It does not check whether the szUrl is valid. * If the szUrl is NULL, it returns the length of the appended parameters. * (Not including the beginning '?' character) * The returned value does not include the terminating NULL character. * * \param szUrl [in] An URL which is used to calculate the length of the GET URL. * \return The number of characters required. (Not including the terminating NULL character) * \throw Throws a httpclientexception if an error occurs. */ template DWORD CHttpClientT::MakeGetUrlLen (PCSZ szUrl) throw (Exception &) { // Calculates the number of required characters ::SafeInt cchUrlForGet = 0 ; try { cchUrlForGet = szUrl ? HttpTool::StringLen (szUrl) : 0 ; if ( m_mapParam.Empty () ) return cchUrlForGet.Value () ; for (ConstMapIter iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) { // If the parameter's name is a URL-encoded string if ( (iter->second).dwFlag & ParamEncodedName ) cchUrlForGet += (iter->first ? HttpTool::StringLen (iter->first) : 0) ; else cchUrlForGet += _UrlEncodeLen (iter->first) ; // If the parameter's value is a URL-encoded string if ( (iter->second).dwFlag & ParamEncodedValue ) cchUrlForGet += ((iter->second).szValue ? HttpTool::StringLen ((iter->second).szValue) : 0) ; else cchUrlForGet += _UrlEncodeLen ((iter->second).szValue) ; cchUrlForGet += 2 ; // for ('?' or '&') and '=' } // If the szUrl is NULL, it does not insert '?' or '&' character // which divides the GET URL into the szUrl and HTTP parameters. if ( szUrl == NULL && m_mapParam.Count () >= 0 ) cchUrlForGet-- ; // If the URL does not contain the path (ex. 'http://www.kw.ac.kr') // we have to append '/' to the URL. // It does not care whether the server address exists. // (It does not check the URL) if ( szUrl ) { CHttpUrlAnalyzer objAnalyzer (szUrl, m_nAnsiCodePage) ; if ( objAnalyzer.PathLen () == 0 ) cchUrlForGet++ ; } } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } return cchUrlForGet.Value () ; } /*! * This method returns a URL for a HTTP GET request. * If you want to pass some parameters to a web server using HTTP GET, * you have to append parameters to the requested URL. This method creates a URL for that situation. * If the CHttpClient does not contain any HTTP parameters, it returns the szUrl. * It does not check whether the szUrl is valid. * If the szUrl is NULL, it copies only the appended parameters. * (Not including the beginning '?' character) * * \param szBuff [out] A buffer to save the GET URL. The buffer can not be NULL. * \param szUrl [in] An URL which is used to make a URL for a HTTP GET request. * \return A generated URL. * \throw Throws a httpclientexception if an error occurs. */ template typename CHttpClientT::PSZ CHttpClientT::MakeGetUrl (PSZ szBuff, PCSZ szUrl) throw (Exception &) { HTTPCLIENT_ASSERT (szBuff != NULL, "CHttpClientT::MakeGetUrl: szBuff can not be NULL.") ; if ( m_mapParam.Empty () ) { if ( szUrl == NULL ) { szBuff[0] = '\0' ; return szBuff ; } HttpTool::StringCopy (szBuff, szUrl) ; return szBuff ; } PSZ pchBuff = szBuff ; DWORD i ; CHttpUrlAnalyzer objAnalyzer (szUrl, m_nAnsiCodePage) ; if ( szUrl ) { for (i = 0; i < objAnalyzer.PathIdx (); *(pchBuff++) = szUrl[i], i++) ; // If the path does not exist, it is needed to append '/' to the server address. if ( objAnalyzer.PathLen () == 0 ) *(pchBuff++) = '/' ; // Appends the path and the search string. for (i = objAnalyzer.PathIdx (); i < objAnalyzer.BookmarkIdx (); *(pchBuff++) = szUrl[i], i++) ; // Copies '?' or '&' *(pchBuff++) = objAnalyzer.SearchLen () == 0 ? '?' : '&' ; } // Copies the HTTP parameters for (ConstMapIter iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) { if ( iter != m_mapParam.Begin () ) *(pchBuff++) = '&' ; // If the parameter's name is a URL-encoded string if ( (iter->second).dwFlag & ParamEncodedName ) HttpTool::StringCopy (pchBuff , iter->first ? iter->first : HttpTool::szEmptyString) ; else _UrlEncode (pchBuff, iter->first) ; for (; *pchBuff != '\0'; pchBuff++) ; *(pchBuff++) = '=' ; // If the parameter's value is a URL-encoded string if ( (iter->second).dwFlag & ParamEncodedValue ) HttpTool::StringCopy (pchBuff , (iter->second).szValue ? (iter->second).szValue : HttpTool::szEmptyString) ; else _UrlEncode (pchBuff, (iter->second).szValue) ; for (; *pchBuff != '\0'; pchBuff++) ; } // Appends the bookmark for (i = objAnalyzer.BookmarkIdx (); i < objAnalyzer.BookmarkIdx () + objAnalyzer.BookmarkLen (); *(pchBuff++) = szUrl[i], i++) ; *pchBuff = '\0' ; return szBuff ; } /*! * This method returns a new HINTERNET handle which is returned by ::InternetOpen * using the settings saved in the CHttpClient. * The caller is responsible for closing the returned handle when it has finished with it. * * \return A new HINTERNET handle. * \throw Throws a httpclientexception if an error occurs. * * \sa ::InternetOpen */ template HINTERNET CHttpClientT::OpenInternet (void) throw (Exception &) { return HttpTool::OpenInternet ( GetInternetUserAgent (), GetInternetAccessType (), GetInternetProxyName (), GetInternetProxyBypass (), GetInternetFlags ()) ; } /*! * This method returns a new HINTERNET handle which is returned by ::InternetConnect. * It only checks the protocol, server address and server port of the URL. * So you can use the following URLs. *
    *
  • http://www.k-net.or.kr:80/member.html?type=5 -> A full HTTP URL (or a full HTTPS URL)
  • *
  • http://club.hooriza.com:80 -> A HTTP URL without path (or a HTTPS URL without path)
  • *
  • wedding.makeself.net:80 -> Only a server location (http is assumed)
  • *
  • www.kw.ac.kr/index.html -> A URL without protocol (http is assumed)
  • *
  • ftp://knews.k-net.or.kr -> An URL with another protocol (http is assumed)
  • *
* * The caller is responsible for closing the returned handle when it has finished with it. * * \param hInternet [in] A valid handle returned by ::InternetOpen. * \param szUrl [in] An URL which is used to return a new handle. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A new HINTERNET handle. * \throw Throws a httpclientexception if an error occurs. * * \sa ::InternetConnect */ template HINTERNET CHttpClientT::OpenConnection (HINTERNET hInternet, PCSZ szUrl, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { HTTPCLIENT_ASSERT (hInternet != NULL, "CHttpClientT::OpenConnection: hInternet can not be NULL.") ; INTERNET_PORT nPort = INTERNET_DEFAULT_HTTP_PORT ; CHttpUrlAnalyzer objAnalyzer (szUrl, m_nAnsiCodePage) ; // If the protocol is HTTPS, the default port is INTERNET_DEFAULT_HTTPS_PORT. if ( (objAnalyzer.ProtocolLen () == 5 /* ::strlen ("HTTPS") */) && (0 == HttpTool::StringNICmp ( szUrl + objAnalyzer.ProtocolIdx (), HttpTool::szHTTPS, objAnalyzer.ProtocolLen ())) ) nPort = INTERNET_DEFAULT_HTTPS_PORT ; // Get the server port if ( objAnalyzer.PortLen () > 0 ) { // The port number can not be greater than 65535 if ( objAnalyzer.PortLen () > 5 ) HttpTool::ThrowException (HTTPCLIENT_ERR_INTERNET_PORT_NOT_VALID) ; CharType szPort[6] ; HttpTool::StringNCopy (szPort, szUrl + objAnalyzer.PortIdx (), objAnalyzer.PortLen ()) ; szPort[objAnalyzer.PortLen ()] = '\0' ; unsigned long ulPort ; PSZ pEndPtr ; ulPort = HttpTool::StringToUL (szPort, &pEndPtr, 10) ; if ( *pEndPtr != NULL || ulPort > 65535 ) HttpTool::ThrowException (HTTPCLIENT_ERR_INTERNET_PORT_NOT_VALID) ; nPort = static_cast (ulPort) ; } // Get the server address if ( objAnalyzer.AddressLen () == 0 ) HttpTool::ThrowException (HTTPCLIENT_ERR_INVALID_URL) ; ::SafeInt cbAddress = 0 ; try { cbAddress = objAnalyzer.AddressLen () ; cbAddress += 1 ; cbAddress *= sizeof (CharType) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } PSZ szServerAddr = (PSZ) ::malloc (cbAddress.Value ()) ; if ( szServerAddr == NULL ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; HttpTool::StringNCopy (szServerAddr, szUrl + objAnalyzer.AddressIdx (), objAnalyzer.AddressLen ()) ; szServerAddr[objAnalyzer.AddressLen ()] = '\0' ; HINTERNET hConnection = NULL ; try { hConnection = HttpTool::OpenConnection (hInternet, szServerAddr, nPort, szUsrName, szUsrPwd) ; // Applys proxy server account ApplyProxyAccount (hConnection) ; } catch (Exception &) { SAFEFREE (szServerAddr) ; HttpTool::CloseConnection (hConnection) ; throw ; } SAFEFREE (szServerAddr) ; return hConnection ; } /*! * This method sets a user name and a password which are used to access HTTP proxy server. * If the szUserName parameter is NULL, the proxy account saved in CHttpClient will be removed. * You can handle proxy authorization using this method, but there are some restrictions. * First, The HTTP POST is not safe if you are not an authorized user. * (It is possible that the proxy server closes the connection before the request is finished). * Second, Only the HTTP GET can be used to request proxy authorization. * (This restriction is caused by the WinInet API). * * The following code snippet demonstrates the usage of this method. * \code ... CHttpClient objHttpReq ; CHttpResponse * pobjHttpRes = NULL ; try { ... // Initialize the CHttpClient object // It is safe to use HTTP GET method. pobjHttpRes = objHttpReq.RequestGet (...) ; // If the server requests proxy authorization.. if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) { delete pobjHttpRes ; pobjHttpRes = NULL ; LPTSTR szUserName = NULL ; LPTSTR szPassword = NULL ; ... // Get a proxy user name and password // Set the proxy user name and password objHttpReq.SetProxyAccount (szUserName, szPassword) ; // Only a HTTP GET method can be used to request proxy authorization. pobjHttpRes = objHttpReq.RequestGet (...) ; if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) { ... // If the authorization fails, try again or stop } } ... // Continue to execute } catch (httpclientexception & e) { ... // Place error handling codes here. } * \endcode * * \param szUserName [in] A proxy user name. Empty string is not allowed. * If it is NULL, account information saved in CHttpClient will be removed. * \param szPassword [in] A proxy password. NULL and empty string are not allowed. * \throw Throws a httpclientexception if an error occurs. * * \sa ApplyProxyAccount, ::InternetSetOption, How To Handle Proxy Authorization with WinInet */ template void CHttpClientT::SetProxyAccount (PCSZ szUserName, PCSZ szPassword) throw (Exception &) { if ( szUserName == NULL ) { SAFEFREE (m_szProxyUserName) ; SAFEFREE (m_szProxyPassword) ; return ; } HTTPCLIENT_ASSERT (szUserName != NULL, "CHttpClientT::SetProxyAccount: szUserName can not be NULL.") ; HTTPCLIENT_ASSERT (HttpTool::StringLen (szUserName) > 0, "CHttpClientT::SetProxyAccount: szUserName can not be an empty string.") ; HTTPCLIENT_ASSERT (szPassword != NULL, "CHttpClientT::SetProxyAccount: szPassword can not be NULL.") ; HTTPCLIENT_ASSERT (HttpTool::StringLen (szPassword) > 0, "CHttpClientT::SetProxyAccount: szPassword can not be an empty string.") ; PSZ szNewUserName = NULL ; PSZ szNewPassword = NULL ; try { szNewUserName = _AllocNCopyIfNewString (szUserName, m_szProxyUserName) ; szNewPassword = _AllocNCopyIfNewString (szPassword, m_szProxyPassword) ; } catch (Exception &) { if ( m_szProxyUserName != szNewUserName ) SAFEFREE (szNewUserName) ; if ( m_szProxyPassword != szNewPassword ) SAFEFREE (szNewPassword) ; throw ; } if ( m_szProxyUserName != szNewUserName ) { SAFEFREE (m_szProxyUserName) ; m_szProxyUserName = szNewUserName ; } if ( m_szProxyPassword != szNewPassword ) { SAFEFREE (m_szProxyPassword) ; m_szProxyPassword = szNewPassword ; } } /*! * This method returns a proxy user name which is used to access HTTP proxy server. * It always returns a null-terminated string. The returned string is owned by * the CHttpClientT. So you can not free it. * * \return A proxy user name. */ template typename CHttpClientT::PCSZ CHttpClientT::GetProxyUserName (void) const throw () { return m_szProxyUserName ? m_szProxyUserName : HttpTool::szEmptyString ; } /*! * This method returns the current proxy password. * It always returns a null-terminated string. The returned string is owned by * the CHttpClientT. So you can not free it. * * \return The current proxy password. */ template typename CHttpClientT::PCSZ CHttpClientT::GetProxyPassword (void) const throw () { return m_szProxyPassword ? m_szProxyPassword : HttpTool::szEmptyString ; } /*! * This method sets a proxy user name and password on the hConnection handle * using the settings saved in CHttpClient. * You can handle proxy authorization using this handle, but there are some restrictions. * First, The HTTP POST method is not safe if you are not authorized * (It is possible that the proxy server closes the connection before the request is finished). * Second, Only a HTTP GET method can be used to request proxy authorization * (This restriction is caused by the WinInet API). * If an exception occurs, it is possible that the proxy user name is only applied. * * The following code snippet demonstrates the usage of this method. * \code ... CHttpClient objHttpReq ; CHttpResponse * pobjHttpRes = NULL ; HINTERNET hInternet = NULL ; HINTERNET hConnection = NULL ; try { ... // Initialize the CHttpClient object // Get a new internet handle hInternet = objHttpReq.OpenInternet () ; // Get a new connection handle hConnection = objHttpReq.OpenConnection (hInternet, _T ("http://www.k-net.or.kr")) ; // It is safe to use HTTP GET method. pobjHttpRes = objHttpReq.RequestGet (hInternet, hConnection, ...) ; // If the server requests proxy authorization.. if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) { delete pobjHttpRes ; pobjHttpRes = NULL ; LPTSTR szUserName = NULL ; LPTSTR szPassword = NULL ; ... // Get a proxy user name and password // Set the proxy user name and password objHttpReq.SetProxyAccount (szUserName, szPassword) ; objHttpReq.ApplyProxyAccount (hConnection) ; // Only a HTTP GET method can be used to request proxy authorization. pobjHttpRes = objHttpReq.RequestGet (hInternet, hConnection, ...) ; if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) { ... // If the authorization fails, try again or stop } } ... // Continue to execute } catch (httpclientexception & e) { ... // Place error handling codes here. } * \endcode * * \param hConnection [in] A HINTERNET handle returned by ::InternetConnect function. * NULL is not allowed. * \throw Throws a httpclientexception if an error occurs. * * \sa SetProxyAccount, ::InternetSetOption, How To Handle Proxy Authorization with WinInet */ template void CHttpClientT::ApplyProxyAccount (HINTERNET hConnection) throw (Exception &) { HTTPCLIENT_ASSERT (hConnection != NULL, "CHttpClientT::ApplyProxyAccount: hConnection can not be NULL.") ; if ( m_szProxyUserName == NULL || m_szProxyPassword == NULL ) return ; ::SafeInt cchProxyUserName ; ::SafeInt cchProxyPassword ; try { cchProxyUserName = HttpTool::StringLen (m_szProxyUserName) ; cchProxyPassword = HttpTool::StringLen (m_szProxyPassword) ; } catch (::SafeIntException & e) { throw (e) ; } if ( cchProxyUserName == 0 || cchProxyPassword == 0 ) return ; // Set a user name for Proxy HttpTool::InternetSetOption ( hConnection , INTERNET_OPTION_PROXY_USERNAME , (LPVOID) m_szProxyUserName , cchProxyUserName.Value () ) ; // Set a password for Proxy HttpTool::InternetSetOption ( hConnection , INTERNET_OPTION_PROXY_PASSWORD , (LPVOID) m_szProxyPassword , cchProxyPassword.Value () ) ; } /*! * This method returns a new HINTERNET handle which is returned by ::HttpOpenRequest * using the settings saved in the CHttpClient. * It only checks the protocol and the path of the URL. * So you can use the following URLs. *
    *
  • http://drama.kbs.co.kr/winter/people/cast_02.shtml -> A full HTTP URL (or a full HTTPS URL)
  • *
  • http:///winter/people/cast_02.shtml -> A HTTP URL without server location (or a HTTPS URL without server location)
  • *
  • www.eggfilm.com/classic/intro.htm -> A HTTP URL without protocol (http is assumed)
  • *
  • /classic/intro.htm -> Only A path (http is assumed)
  • *
  • http://www.k-net.or.kr -> An HTTP URL without path (uses "/" as the path)
  • *
  • www.k-net.or.kr -> Only A server location (http is assumed, uses "/" as the path)
  • *
  • -> A NULL or an empty string (http is assumed, uses "/" as the path)
  • *
  • ftp://www.k-net.or.kr/member.html -> An URL with another protocol (http is assumed)
  • *
* * The caller is responsible for closing the returned handle when it has finished with it. * * \param hConnection [in] A valid handle returned by ::InternetConnect. * \param szVerb [in] A HTTP verb which corresponds to the lpszVerb parameter of the ::HttpOpenRequest function. * \param szUrl [in] An URL which is used to return a new handle. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \return A new HINTERNET handle. * \throw Throws a httpclientexception if an error occurs. * * \sa ::HttpOpenRequest */ template HINTERNET CHttpClientT::OpenRequest (HINTERNET hConnection , PCSZ szVerb, PCSZ szUrl , DWORD dwFlags, PCSZ szReferer) throw (Exception &) { HTTPCLIENT_ASSERT (hConnection != NULL, "CHttpClientT::OpenRequest: hConnection can not be NULL.") ; CHttpUrlAnalyzer objAnalyzer (szUrl, m_nAnsiCodePage) ; // If the protocol is HTTPS, adds the SSL flag. if ( (objAnalyzer.ProtocolLen () == 5 /* ::strlen ("HTTPS") */) && (0 == HttpTool::StringNICmp ( szUrl + objAnalyzer.ProtocolIdx (), HttpTool::szHTTPS, objAnalyzer.ProtocolLen ())) ) dwFlags |= INTERNET_FLAG_SECURE ; PCSZ szUrlPath ; BOOL bNeedToFreeUrlPath = FALSE ; // If the path does not exist, use "/" as the path if ( objAnalyzer.PathLen () == 0 ) { size_t cchAfterPath = szUrl ? HttpTool::StringLen (szUrl + objAnalyzer.PathIdx ()) : 0 ; if ( cchAfterPath == 0 ) szUrlPath = HttpTool::szSlash ; else { ::SafeInt cbAfterPath = 0 ; try { cbAfterPath = cchAfterPath ; cbAfterPath += 2 ; // for '/' & '\0' cbAfterPath *= sizeof (CharType) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } szUrlPath = (PCSZ) ::malloc (cbAfterPath.Value ()) ; if ( szUrlPath == NULL ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; bNeedToFreeUrlPath = TRUE ; HttpTool::StringCopy (const_cast (szUrlPath), HttpTool::szSlash) ; HttpTool::StringCat (const_cast (szUrlPath), szUrl + objAnalyzer.PathIdx ()) ; } } else szUrlPath = szUrl + objAnalyzer.PathIdx () ; HINTERNET hRequest = NULL ; try { hRequest = HttpTool::OpenRequest (hConnection, szVerb, szUrlPath, dwFlags, szReferer, m_nAnsiCodePage) ; // Adds the cache-control header if it is needed if ( dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE ) HttpTool::AddHeader (hRequest, HttpTool::szCacheControl, HttpTool::szNoCache, m_nAnsiCodePage) ; } catch (Exception &) { HttpTool::CloseRequest (hRequest) ; if ( bNeedToFreeUrlPath ) SAFEFREE (szUrlPath) ; throw ; } if ( bNeedToFreeUrlPath ) SAFEFREE (szUrlPath) ; return hRequest ; } /*! * This method adds the HTTP headers to the handle which is returned by ::HttpOpenRequest * using the settings saved in the CHttpClient. * It uses the headers which is saved in the CHttpClient. * If an exception occurs, it is possible that the headers are added partially. * * \param hRequest [in] A valid handle which is returned by ::HttpOpenRequest. * \throw Throws a httpclientexception if an error occurs. * * \sa ::HttpAddRequestHeaders */ template void CHttpClientT::AddRequestHeader (HINTERNET hRequest) throw (Exception &) { HTTPCLIENT_ASSERT (hRequest != NULL, "CHttpClientT::AddRequestHeader: hRequest can not be NULL.") ; // Adds user's headers. for (ConstMapIter iter = m_mapHeader.Begin (); iter != m_mapHeader.End (); ++iter) HttpTool::AddHeader (hRequest, iter->first, (iter->second).szValue, m_nAnsiCodePage) ; } /*! * \internal * This method retrieves the resource specified by the szUrl using HTTP GET request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect */ template typename CHttpClientT::CHttpResponse * CHttpClientT::_RequestGetEx (HINTERNET hInternet, HINTERNET hConnection , PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { // Releases any existing Post Context _EndPostContext () ; PCSZ szGetUrl = NULL ; BOOL bNeedToFreeGetUrl = FALSE ; BOOL bBorrowedInternet = TRUE ; BOOL bBorrowedConnection = TRUE ; HINTERNET hRequest = NULL ; CHttpResponse * pobjRes = NULL ; try { // Get WinInet API handles if ( hInternet == NULL ) { hInternet = OpenInternet () ; bBorrowedInternet = FALSE ; } if ( hConnection == NULL ) { hConnection = OpenConnection (hInternet, szUrl, szUsrName, szUsrPwd) ; bBorrowedConnection = FALSE ; } szGetUrl = szUrl ; // Appends the GET parameters to the URL if ( !m_mapParam.Empty () ) { ::SafeInt cbGetUrl ; try { cbGetUrl = MakeGetUrlLen (szUrl) ; cbGetUrl += 1 ; cbGetUrl *= sizeof (CharType) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } if ( NULL == (szGetUrl = (PCSZ) ::malloc (cbGetUrl.Value ())) ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; bNeedToFreeGetUrl = TRUE ; MakeGetUrl (const_cast (szGetUrl), szUrl) ; } hRequest = OpenRequest (hConnection, HttpTool::szGET, szGetUrl, dwFlags, szReferer) ; AddRequestHeader (hRequest) ; // Adds user's custom header if ( bNeedToFreeGetUrl ) { SAFEFREE (szGetUrl) ; bNeedToFreeGetUrl = FALSE ; } // Connect to the HTTP server HttpTool::SendRequest (hRequest, NULL, m_nAnsiCodePage) ; if ( NULL == (pobjRes = new CHttpResponse ( bBorrowedInternet ? NULL : hInternet , bBorrowedConnection ? NULL : hConnection , hRequest)) ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; } catch (Exception &) { if ( bNeedToFreeGetUrl ) SAFEFREE (szGetUrl) ; HttpTool::CloseRequest (hRequest) ; if ( !bBorrowedConnection ) HttpTool::CloseRequest (hConnection) ; if ( !bBorrowedInternet ) HttpTool::CloseRequest (hInternet) ; throw ; } catch (...) { // for the exception thrown by the new operator if ( bNeedToFreeGetUrl ) SAFEFREE (szGetUrl) ; HttpTool::CloseRequest (hRequest) ; if ( !bBorrowedConnection ) HttpTool::CloseRequest (hConnection) ; if ( !bBorrowedInternet ) HttpTool::CloseRequest (hInternet) ; HttpTool::ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ERROR) ; } return pobjRes ; } /*! * This method retrieves the resource specified by the szUrl using HTTP GET request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestGet (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestGetEx (hInternet, hConnection, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP GET request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, ::InternetOpen */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestGet (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestGetEx (hInternet, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP GET request. * If the szUrl does not start with "https://", "http" is used as the protocol. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestGet (PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestGetEx (szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP GET request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestGetEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags , PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { return _RequestGetEx (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP GET request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestGetEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags , PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { return _RequestGetEx (hInternet, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP GET request. * If the szUrl does not start with "https://", "http" is used as the protocol. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestGetEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { return _RequestGetEx (NULL, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; } /*! * This method starts a new HTTP POST request. If you call this method, you can retrieve progress * information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \throw Throws a httpclientexception if an error occurs. * * \sa OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect */ template void CHttpClientT::BeginPost (HINTERNET hInternet, HINTERNET hConnection , PCSZ szUrl, BOOL bUseCache) throw (Exception &) { BeginPostEx (hInternet, hConnection, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method starts a new HTTP POST request. If you call this method, you can retrieve progress * information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \throw Throws a httpclientexception if an error occurs. * * \sa OpenInternet, ::InternetOpen */ template void CHttpClientT::BeginPost (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { BeginPostEx (hInternet, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method starts a new HTTP POST request. If you call this method, you can retrieve progress * information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed */ template void CHttpClientT::BeginPost (PCSZ szUrl, BOOL bUseCache) throw (Exception &) { BeginPostEx (szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method starts a new HTTP POST request. If you call this method, you can retrieve progress * information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template void CHttpClientT::BeginPostEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags , PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { _StartPostContext (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; // Starts a new POST context } /*! * This method starts a new HTTP POST request. If you call this method, you can retrieve progress * information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template void CHttpClientT::BeginPostEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { _StartPostContext (hInternet, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; // Starts a new POST context } /*! * This method starts a new HTTP POST request. If you call this method, you can retrieve progress * information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, ::InternetConnect, ::HttpOpenRequest */ template void CHttpClientT::BeginPostEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { _StartPostContext (NULL, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; // Starts a new POST context } /*! * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data). * If you call this method, you can retrieve progress information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect */ template void CHttpClientT::BeginUpload (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { BeginUploadEx (hInternet, hConnection, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data). * If you call this method, you can retrieve progress information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, OpenInternet, ::InternetOpen */ template void CHttpClientT::BeginUpload (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { BeginUploadEx (hInternet, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data). * If you call this method, you can retrieve progress information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed */ template void CHttpClientT::BeginUpload (PCSZ szUrl, BOOL bUseCache) throw (Exception &) { BeginUploadEx (szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data). * If you call this method, you can retrieve progress information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template void CHttpClientT::BeginUploadEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl , DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { _StartUploadContext (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; // Starts a new UPLOAD context } /*! * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data). * If you call this method, you can retrieve progress information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template void CHttpClientT::BeginUploadEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { _StartUploadContext (hInternet, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; // Starts a new UPLOAD context } /*! * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data). * If you call this method, you can retrieve progress information of the POST request by using the Query method. * To finish the request, you have to call the Proceed method. * If the szUrl does not start with "https://", "http" is used as the protocol. * * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \throw Throws a httpclientexception if an error occurs. * * \sa Query, Proceed, ::InternetConnect, ::HttpOpenRequest */ template void CHttpClientT::BeginUploadEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { // Starts a new UPLOAD context _StartUploadContext (NULL, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; } /*! * This method queries progress information of the current POST context. * If you call the BeginPost or the BeginUpload method which starts a new POST context, * you can retrieve progress information by using this method. * If an exception occurs, the current POST context is automatically canceled. * * \param objPostStat [in] A CHttpPostStatT object. * * \sa CHttpPostStatT, BeginPost, BeginPostEx, BeginUpload, BeginUploadEx, Cancel, Proceed */ template void CHttpClientT::Query (CHttpPostStat & objPostStat) throw () { if ( _PostContextIsPost () ) _QueryPostStat (objPostStat) ; else _QueryUploadStat (objPostStat) ; } /*! * This method cancels the current POST context which is started by * the BeginPost or the BeginUpload method. * * \return Whether the operation canceled. * * \sa BeginPost, BeginPostEx, BeginUpload, BeginUploadEx, Query, Proceed */ template BOOL CHttpClientT::Cancel (void) throw () { if ( _PostContextIsPost () ) return _CancelPostContext () ; return _CancelUploadContext () ; } /*! * This method proceeds with the current POST context which is started by * the BeginPost or the BeginUpload method. * It transmits data of the current POST context to the web server. * If all bytes are transmitted, a pointer to a CHttpResponseT object is returned. * The returned pointer is allocated by the new operator. * So you have to delete the returned pointer. * If an exception occurs, the current POST context is automatically canceled. * * \param cbDesired [in] The number of bytes to be transmitted. * \return A pointer to a CHttpResponseT object if all bytes are transmitted, * otherwise NULL. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, BeginPost, BeginPostEx, BeginUpload, BeginUploadEx, Query, Cancel */ template typename CHttpClientT::CHttpResponse * CHttpClientT::Proceed (DWORD cbDesired) throw (Exception &) { if ( _PostContextIsPost () ) return _ProceedPostContext (cbDesired) ; return _ProceedUploadContext (cbDesired) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP POST request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestPost (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestPostEx (hInternet, hConnection, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP POST request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, ::InternetOpen */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestPost (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestPostEx (hInternet, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP POST request. * If the szUrl does not start with "https://", "http" is used as the protocol. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestPost (PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestPostEx (szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP POST request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestPostEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl , DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { BeginPostEx (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; CHttpResponse * pResponse = NULL ; while ( !(pResponse = Proceed (::SafeInt::MaxInt ())) ) ; return pResponse ; } /*! * This method retrieves the resource specified by the szUrl using HTTP POST request. * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestPostEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags , PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { BeginPostEx (hInternet, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; CHttpResponse * pResponse = NULL ; while ( !(pResponse = Proceed (::SafeInt::MaxInt ())) ) ; return pResponse ; } /*! * This method retrieves the resource specified by the szUrl using HTTP POST request. * If the szUrl does not start with "https://", "http" is used as the protocol. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestPostEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { BeginPostEx (szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; CHttpResponse * pResponse = NULL ; while ( !(pResponse = Proceed (::SafeInt::MaxInt ())) ) ; return pResponse ; } /*! * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request * which is a HTTP POST request with another content-type (multipart/form-data). * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestUpload (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestUploadEx (hInternet, hConnection, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request * which is a HTTP POST request with another content-type (multipart/form-data). * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, ::InternetOpen */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestUpload (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestUploadEx (hInternet, szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request * which is a HTTP POST request with another content-type (multipart/form-data). * If the szUrl does not start with "https://", "http" is used as the protocol. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param szUrl [in] A HTTP URL. * \param bUseCache [in] Specifies whether to use cache. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestUpload (PCSZ szUrl, BOOL bUseCache) throw (Exception &) { return RequestUploadEx (szUrl , bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ; } /*! * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request * which is a HTTP POST request with another content-type (multipart/form-data). * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * If the caller passes the hConnection parameter which is returned by ::InternetConnect, * the CHttpClient also uses the passed handle instead of creating a new one. * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter. * The caller is responsible for closing the hConnection handle when it has finished with it. * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl. * You can omit the server location part of the szUrl. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param hConnection [in] A valid handle returned by ::InternetConnect. NULL is allowed. * If this parameter is not NULL, the caller has to pass the hInternet parameter * which is used to get the hConnection parameter. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestUploadEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { BeginUploadEx (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; CHttpResponse * pResponse = NULL ; while ( !(pResponse = Proceed (::SafeInt::MaxInt ())) ) ; return pResponse ; } /*! * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request * which is a HTTP POST request with another content-type (multipart/form-data). * If the szUrl does not start with "https://", "http" is used as the protocol. * If the caller passes the hInternet parameter which is returned by ::InternetOpen, * the CHttpClient uses the passed handle instead of creating a new one. * The caller is responsible for closing the hInternet handle when it has finished with it. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param hInternet [in] A valid handle returned by ::InternetOpen. NULL is allowed. * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestUploadEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { BeginUploadEx (hInternet, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; CHttpResponse * pResponse = NULL ; while ( !(pResponse = Proceed (::SafeInt::MaxInt ())) ) ; return pResponse ; } /*! * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request * which is a HTTP POST request with another content-type (multipart/form-data). * If the szUrl does not start with "https://", "http" is used as the protocol. * The returned CHttpResponseT object is allocated by the new operator. * So you have to delete the returned CHttpResponseT object. * * \param szUrl [in] A HTTP URL. * \param dwFlags [in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function. * \param szReferer [in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function. * \param szUsrName [in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function. * \param szUsrPwd [in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function. * \return A pointer to a CHttpResponseT object. * \throw Throws a httpclientexception if an error occurs. * * \sa CHttpResponseT, ::InternetConnect, ::HttpOpenRequest */ template typename CHttpClientT::CHttpResponse * CHttpClientT::RequestUploadEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { BeginUploadEx (szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ; CHttpResponse * pResponse = NULL ; while ( !(pResponse = Proceed (::SafeInt::MaxInt ())) ) ; return pResponse ; } template void CHttpClientT::_InitPostContext (void) throw () { m_ahPostedFiles = NULL ; m_aszMimeTypes = NULL ; m_szPostedValue = NULL ; m_bNeedToFreePostedValue = FALSE ; m_hInternet = NULL ; m_bBorrowedInternet = FALSE ; m_hConnection = NULL ; m_bBorrowedConnection = FALSE ; m_hRequest = NULL ; m_bIsPost = TRUE ; m_pbyCntxBuff = NULL ; } template BOOL CHttpClientT::_PostContextIsActive (void) const throw () { return m_objPostStat.IsActive () ; } template BOOL CHttpClientT::_PostContextIsPost (void) const throw () { return m_bIsPost ; } template void CHttpClientT::_SetPostedValue (LPCSTR szPostedValue, BOOL bNeedToFree) throw () { if ( m_bNeedToFreePostedValue ) SAFEFREE (m_szPostedValue) ; m_szPostedValue = szPostedValue ; m_bNeedToFreePostedValue = bNeedToFree ; } template void CHttpClientT::_InitPostCntxBuff (void) throw (Exception &) { // If the buffer is aleady allocated if ( m_pbyCntxBuff ) return ; if ( NULL == (m_pbyCntxBuff = (BYTE *) ::malloc (HTTPCLIENT_POSTCNTX_BUFF_SIZE)) ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; } template void CHttpClientT::_WritePost (BOOL bUseUtf8, PCSZ szValue, BOOL bIsValue) throw (Exception &) { if ( szValue == NULL || szValue[0] == '\0' ) return ; PCSTR szValueA = NULL ; BOOL bNeedToFreeValueA = FALSE ; try { if ( bUseUtf8 ) { szValueA = _Utf8Encode (szValue) ; bNeedToFreeValueA = TRUE ; } else szValueA = _String2Ansi (szValue, bNeedToFreeValueA) ; _WritePost (szValueA, bIsValue) ; } catch (Exception &) { if ( bNeedToFreeValueA ) SAFEFREE (szValueA) ; throw ; } if ( bNeedToFreeValueA ) SAFEFREE (szValueA) ; } template void CHttpClientT::_WritePost (LPCSTR szValue, BOOL bIsValue) throw (Exception &) { HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_WritePost: m_hRequest can not be NULL.") ; if ( szValue == NULL ) return ; ::SafeInt cbValue ; try { cbValue = ::strlen (szValue) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } _WritePost (reinterpret_cast (szValue), cbValue.Value (), bIsValue) ; } template void CHttpClientT::_WritePost (const BYTE * pbyBuff, DWORD cbyBuff, BOOL bIsValue) throw (Exception &) { HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_WritePost: m_hRequest can not be NULL.") ; if ( pbyBuff == NULL || cbyBuff == 0 ) return ; // Before increasing values. Checks overflow exception. if ( bIsValue ) m_objPostStat._TestAddPostedBytes (cbyBuff) ; else m_objPostStat._TestAddActualPostedBytes (cbyBuff) ; HttpTool::InternetWriteFile (m_hRequest, pbyBuff, cbyBuff) ; if ( bIsValue ) m_objPostStat._AddPostedBytes (cbyBuff) ; else m_objPostStat._AddActualPostedBytes (cbyBuff) ; } template void CHttpClientT::_StartPostContext (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer , PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { // Closes the POST context if it exists. _EndPostContext () ; BOOL bBorrowedInternet = TRUE ; BOOL bBorrowedConnection = TRUE ; HINTERNET hRequest = NULL ; try { // Calculates the total number of bytes to upload ::SafeInt nTotalByte = 0 ; ::SafeInt nValuesTotalByte = 0 ; PCSZ szFirstParamName = NULL ; size_t cbFirstParamValue = 0 ; if ( !m_mapParam.Empty () ) { ConstMapIter iter ; size_t cbValue ; for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) { nTotalByte += 1 ; // '&' if ( (iter->second).dwFlag & ParamEncodedName ) nTotalByte += _String2AnsiLen (iter->first) ; else nTotalByte += _UrlEncodeLenA (iter->first) ; nTotalByte += 1 ; // '=' if ( (iter->second).dwFlag & ParamEncodedValue ) cbValue = _String2AnsiLen ((iter->second).szValue) ; else cbValue = _UrlEncodeLenA ((iter->second).szValue) ; nTotalByte += cbValue ; nValuesTotalByte += cbValue ; // If the item is the first item if ( iter == m_mapParam.Begin () ) { szFirstParamName = iter->first ; cbFirstParamValue = cbValue ; } } if ( nTotalByte > 0 ) nTotalByte-- ; // for the first '&' } ::SafeInt dwTotalByte = nTotalByte ; // Get WinInet handles if ( hInternet == NULL ) { hInternet = OpenInternet () ; bBorrowedInternet = FALSE ; } if ( hConnection == NULL ) { hConnection = OpenConnection (hInternet, szUrl, szUsrName, szUsrPwd) ; bBorrowedConnection = FALSE ; } hRequest = OpenRequest (hConnection, HttpTool::szPost, szUrl, dwFlags, szReferer) ; AddRequestHeader (hRequest) ; // Adds user's custom header // Adds the Content-Type header HttpTool::AddHeader (hRequest, HttpTool::szContentType, HttpTool::szFormUrlEncoded, m_nAnsiCodePage) ; // Connects to the HTTP server HttpTool::SendRequestEx (hRequest, dwTotalByte.Value ()) ; // Starts a new POST state m_objPostStat._MakeActive (nTotalByte.Value (), nValuesTotalByte.Value (), m_mapParam.Count (), 0) ; m_bBorrowedInternet = bBorrowedInternet ; m_hInternet = hInternet ; m_bBorrowedConnection = bBorrowedConnection ; m_hConnection = hConnection ; m_hRequest = hRequest ; m_bIsPost = TRUE ; // Initializes the initial POST state if ( !m_mapParam.Empty () ) { m_objInitialStat = m_objPostStat ; // It always does not throw an overflow exception. // So it's safe. (doesn't need to restore the internal states) m_objInitialStat._StartNewEntry (szFirstParamName, cbFirstParamValue) ; } } catch (::SafeIntException & e) { HttpTool::CloseRequest (hRequest) ; if ( !bBorrowedConnection ) HttpTool::CloseRequest (hConnection) ; if ( !bBorrowedInternet ) HttpTool::CloseRequest (hInternet) ; HttpTool::ThrowException (e) ; } catch (Exception &) { HttpTool::CloseRequest (hRequest) ; if ( !bBorrowedConnection ) HttpTool::CloseRequest (hConnection) ; if ( !bBorrowedInternet ) HttpTool::CloseRequest (hInternet) ; throw ; } } template void CHttpClientT::_QueryPostStat (CHttpPostStat & objPostStat) throw () { // If the _ProceedPOSTContext method is not called yet, if ( (m_objPostStat.IsActive ()) && (m_objPostStat.TotalCount () > 0) && (m_objPostStat.PostedCount () == 0) ) objPostStat = m_objInitialStat ; else objPostStat = m_objPostStat ; } template BOOL CHttpClientT::_CancelPostContext (void) throw () { if ( !m_objPostStat.IsActive () ) return FALSE ; // Cancels the POST context _EndPostContext () ; return TRUE ; } // If all parameters are sent, returns a pointer to the CHttpResponseT. template typename CHttpClientT::CHttpResponse * CHttpClientT::_ProceedPostContext (DWORD nDesired) throw (Exception &) { // If the Post context is not started if ( !m_objPostStat.IsActive () ) HttpTool::ThrowException (HTTPCLIENT_ERR_POST_NOT_STARTED) ; HTTPCLIENT_ASSERT (m_hInternet != NULL, "CHttpClientT::_ProceedPostContext: m_hInternet can not be NULL.") ; HTTPCLIENT_ASSERT (m_hConnection != NULL, "CHttpClientT::_ProceedPostContext: m_hConnection can not be NULL.") ; HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_ProceedPostContext: m_hRequest can not be NULL.") ; HTTPCLIENT_ASSERT (nDesired != 0, "CHttpClientT::_ProceedPostContext: nDesired can not be zero.") ; try { // If the current parameter is completed, if ( m_objPostStat.CurrParamIsComplete () ) { // If all parameters are sent if ( m_objPostStat.TotalCount () == m_objPostStat.PostedCount () ) { HttpTool::EndRequest (m_hRequest) ; // Releases the POST context return _ReleasePostResponse () ; } DWORD nNextIdx = m_objPostStat.PostedCount () ; ConstMapIter iter = m_mapParam.Begin () ; for (DWORD i = 0; i < nNextIdx; i++, ++iter) ; if ( nNextIdx != 0 ) _WritePost ("&") ; // Writes the parameter name { PSTR szNameA = NULL ; BOOL bNeedToFreeNameA = FALSE ; try { if ( (iter->second).dwFlag & ParamEncodedName ) { szNameA = const_cast (_String2Ansi (iter->first, bNeedToFreeNameA)) ; _WritePost (szNameA) ; } else { ::SafeInt cbRequired = _UrlEncodeLenA (iter->first) ; if ( cbRequired > 0 ) { cbRequired += 1 ; cbRequired *= sizeof (CHAR) ; if ( NULL == (szNameA = (PSTR) ::malloc (cbRequired.Value ())) ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; bNeedToFreeNameA = TRUE ; _UrlEncodeA (szNameA, iter->first) ; _WritePost (szNameA) ; } } } catch (::SafeIntException & e) { if ( bNeedToFreeNameA ) SAFEFREE (szNameA) ; HttpTool::ThrowException (e) ; } catch (Exception &) { if ( bNeedToFreeNameA ) SAFEFREE (szNameA) ; throw ; } if ( bNeedToFreeNameA ) { SAFEFREE (szNameA) ; bNeedToFreeNameA = FALSE ; } } _WritePost ("=") ; size_t cbValue = 0 ; // Sets the value if ( (iter->second).szValue ) { PSTR szValueA = NULL ; BOOL bNeedToFreeValueA = FALSE ; if ( (iter->second).dwFlag & ParamEncodedValue ) { szValueA = const_cast (_String2Ansi ((iter->second).szValue, bNeedToFreeValueA)) ; cbValue = szValueA ? CHttpToolA::StringLen (szValueA) : 0 ; } else { cbValue = _UrlEncodeLenA ((iter->second).szValue) ; if ( cbValue > 0 ) { ::SafeInt cbRequired = cbValue ; try { cbRequired += 1 ; cbRequired *= sizeof (CHAR) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } if ( NULL == (szValueA = static_cast (::malloc (cbRequired.Value ()))) ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; bNeedToFreeValueA = TRUE ; try { _UrlEncodeA (szValueA, (iter->second).szValue) ; } catch (Exception &) { if ( bNeedToFreeValueA ) SAFEFREE (szValueA) ; throw ; } } } _SetPostedValue (szValueA, bNeedToFreeValueA) ; } // Starts a new parameter m_objPostStat._StartNewEntry (iter->first, cbValue) ; return NULL ; } DWORD cbToWrite = nDesired ; if ( cbToWrite > m_objPostStat.CurrParamRemainByte () ) cbToWrite = static_cast (m_objPostStat.CurrParamRemainByte ()) ; _WritePost ( reinterpret_cast (m_szPostedValue + m_objPostStat.CurrParamPostedByte ()) , cbToWrite, TRUE) ; } catch (Exception &) { _EndPostContext () ; // Aborts the Post Context throw ; } return NULL ; } template typename CHttpClientT::PCSZ CHttpClientT::_GetUploadBoundary (void) throw () { return m_szBoundary ? m_szBoundary : HttpTool::szDefBoundary ; } template LPCSTR CHttpClientT::_GetUploadBoundaryA (void) throw () { return m_szBoundaryA ? m_szBoundaryA : CHttpToolA::szDefBoundary ; } template typename CHttpClientT::PCSZ CHttpClientT::_GetUploadContType (void) throw () { return m_szUploadContType ? m_szUploadContType : HttpTool::szDefUploadContType ; } template void CHttpClientT::_StartUploadContext (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl , DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd) throw (Exception &) { // Closes any existing Post Context _EndPostContext () ; BOOL bBorrowedInternet = TRUE ; BOOL bBorrowedConnection = TRUE ; HINTERNET hRequest = NULL ; HANDLE * ahFileHandles = NULL ; LPSTR * aszMimeTypes = NULL ; ::SafeInt nPostedFileCount = 0 ; try { // Calculates the nubmer of bytes to upload ::SafeInt nTotalByte = 0 ; ::SafeInt nValuesTotalByte = 0 ; size_t cchBoundary = HttpTool::StringLen (_GetUploadBoundary ()) ; PCSZ szFirstParamName = NULL ; PCSZ szFirstParamFileName = NULL ; size_t cbFirstParamValue = 0 ; BOOL bFirstParamIsFile = FALSE ; if ( !m_mapParam.Empty () ) { try { ConstMapIter iter ; // Get the number of files for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) { if ( (iter->second).dwFlag & ParamFile ) nPostedFileCount++ ; } if ( nPostedFileCount.Value () ) { // Allocates memory for handles and MimeTypes of the uploaded files ahFileHandles = (HANDLE *) ::calloc (nPostedFileCount.Value (), sizeof (HANDLE)) ; if ( ahFileHandles == NULL ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; // Initializes the file handles for (DWORD i = 0; i < nPostedFileCount; i++) ahFileHandles[i] = INVALID_HANDLE_VALUE ; aszMimeTypes = (LPSTR *) ::calloc (nPostedFileCount.Value (), sizeof (LPSTR)) ; if ( aszMimeTypes == NULL ) HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; } /* Calculates the total upload size <1> The number of bytes to upload If the parameter is not a file = strlen ("--") + strlen (Boundary) + strlen ("\r\n") + strlen ("Content-Disposition: form-data; name=\"\"\r\n\r\n") + strlen (The name of the parameter) + strlen (The value of the parameter) + strlen ("\r\n") ; <2> The number of bytes to upload If the parameter is a file = strlen ("--") + strlen (Boundary) + strlen ("\r\n") + strlen ("Content-Disposition: form-data; name=\"\"; filename=\"\"\r\n") + strlen (The name of the parameter) + strlen (The value of the parameter) + strlen ("Content-Type: \r\n\r\n") + strlen (The Mime Type of the file) + The length of the file + strlen ("\r\n") The last boundary = strlen ("--") + strlen (Boundary) + strlen ("--\r\n") The total upload size = ( <1> * The number of normal parameters) + ( <2> * The number of file parameters) + The last boundary */ ::SafeInt cbValue ; DWORD nFileIdx = 0 ; nTotalByte = 0 ; nValuesTotalByte = 0 ; for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) { if ( !((iter->second).dwFlag & ParamFile) ) { // If the parameter is not a file // = strlen ("--") + strlen (Boundary) + strlen ("\r\n") // + strlen ("Content-Disposition: form-data; name=\"\"\r\n\r\n") // + strlen (The name of the parameter) + strlen (The value of the parameter) + strlen ("\r\n") ; nTotalByte += (cchBoundary + 4) ; nTotalByte += 43 ; if ( m_bUseUtf8 ) { nTotalByte += _Utf8EncodeLen (iter->first) ; cbValue = _Utf8EncodeLen ((iter->second).szValue) ; } else { nTotalByte += _String2AnsiLen (iter->first) ; cbValue = _String2AnsiLen ((iter->second).szValue) ; } nTotalByte += cbValue ; nValuesTotalByte += cbValue ; nTotalByte += 2 ; // Saves the state of the first parameter if ( iter == m_mapParam.Begin () ) { szFirstParamName = iter->first ; cbFirstParamValue = cbValue.Value () ; bFirstParamIsFile = FALSE ; szFirstParamFileName = NULL ; } } else { // If the parameter is a file // = strlen ("--") + strlen (Boundary) + strlen ("\r\n") // + strlen ("Content-Disposition: form-data; name=\"\"; filename=\"\"\r\n") // + strlen (The name of the parameter) + strlen (The value of the parameter) // + strlen ("Content-Type: \r\n\r\n") + strlen (The Mime Type of the file) // + The length of the file + strlen ("\r\n") nTotalByte += (cchBoundary + 4) ; nTotalByte += 54 ; if ( m_bUseUtf8 ) { nTotalByte += _Utf8EncodeLen (iter->first) ; nTotalByte += _Utf8EncodeLen ((iter->second).szValue) ; } else { nTotalByte += _String2AnsiLen (iter->first) ; nTotalByte += _String2AnsiLen ((iter->second).szValue) ; } nTotalByte += 18 ; // Get the file size and MimeType cbValue = 0 ; if ( (iter->second).szValue ) { // Open the file ahFileHandles[nFileIdx] = HttpTool::OpenFile ((iter->second).szValue) ; // Get the file size if ( ahFileHandles[nFileIdx] != INVALID_HANDLE_VALUE ) cbValue = HttpTool::GetFileSize (ahFileHandles[nFileIdx], (iter->second).szValue) ; } // Throws an exception if ( m_bStrictFileCheck && (ahFileHandles[nFileIdx] == INVALID_HANDLE_VALUE) ) HttpTool::ThrowException (HTTPCLIENT_ERR_OPENFILE_FAILED, ::GetLastError (), (iter->second).szValue) ; // Get the MimeType of the file aszMimeTypes[nFileIdx] = HttpTool::GetMimeType (ahFileHandles[nFileIdx], m_nAnsiCodePage) ; nTotalByte += CHttpToolA::StringLen (aszMimeTypes[nFileIdx]) ; nTotalByte += cbValue ; nValuesTotalByte += cbValue ; nTotalByte += 2 ; nFileIdx++ ; // Saves the state of the first parameter if ( iter == m_mapParam.Begin () ) { szFirstParamName = iter->first ; cbFirstParamValue = cbValue.Value () ; bFirstParamIsFile = TRUE ; szFirstParamFileName = (iter->second).szValue ; } } } // The last boundary // = strlen ("--") + strlen (Boundary) + strlen ("--\r\n") nTotalByte += (cchBoundary + 6) ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } } else { // The total upload size // = (strlen ("\r\n") + strlen ("--") + strlen (Boundary) + strlen ("--\r\n") nTotalByte = cchBoundary + 8 ; nValuesTotalByte = 0 ; } ::SafeInt dwTotalByte ; try { dwTotalByte = nTotalByte ; } catch (::SafeIntException & e) { HttpTool::ThrowException (e) ; } // Get WinInet handles if ( hInternet == NULL ) { hInternet = OpenInternet () ; bBorrowedInternet = FALSE ; } if ( hConnection == NULL ) { hConnection = OpenConnection (hInternet, szUrl, szUsrName, szUsrPwd) ; bBorrowedConnection = FALSE ; } hRequest = OpenRequest (hConnection, HttpTool::szPost, szUrl, dwFlags, szReferer) ; AddRequestHeader (hRequest) ; // Adds user's custom header // Adds the Content-Type header HttpTool::AddHeader (hRequest, HttpTool::szContentType, _GetUploadContType (), m_nAnsiCodePage) ; // Make a connection to the server HttpTool::SendRequestEx (hRequest, dwTotalByte.Value ()) ; // Activates the Post Context m_objPostStat._MakeActive (nTotalByte.Value (), nValuesTotalByte.Value (), m_mapParam.Count (), nPostedFileCount.Value ()) ; m_bBorrowedInternet = bBorrowedInternet ; m_hInternet = hInternet ; m_bBorrowedConnection = bBorrowedConnection ; m_hConnection = hConnection ; m_hRequest = hRequest ; m_ahPostedFiles = ahFileHandles ; ahFileHandles = NULL ; m_aszMimeTypes = aszMimeTypes ; aszMimeTypes = NULL ; m_bIsPost = FALSE ; // Saves the initial Post context if ( !m_mapParam.Empty () ) { m_objInitialStat = m_objPostStat ; // It always does not throw an overflow exception. // So it's safe. (doesn't need to restore the internal states) m_objInitialStat._StartNewEntry (szFirstParamName, cbFirstParamValue , bFirstParamIsFile, szFirstParamFileName) ; } } catch (Exception &) { HttpTool::CloseRequest (hRequest) ; if ( !bBorrowedConnection ) HttpTool::CloseRequest (hConnection) ; if ( !bBorrowedInternet ) HttpTool::CloseRequest (hInternet) ; for (DWORD i = 0; i < nPostedFileCount; i++) { if ( ahFileHandles ) { if ( ahFileHandles[i] != INVALID_HANDLE_VALUE ) { ::CloseHandle (ahFileHandles[i]) ; ahFileHandles[i] = INVALID_HANDLE_VALUE ; } } if ( aszMimeTypes ) SAFEFREE ( (aszMimeTypes[i]) ) ; } SAFEFREE (ahFileHandles) ; SAFEFREE (aszMimeTypes) ; throw httpclientexceptionA(); } } template void CHttpClientT::_QueryUploadStat (CHttpPostStat & objPostStat) throw () { // If the _ProceedUploadContext method is not called yet, if ( (m_objPostStat.IsActive ()) && (m_objPostStat.TotalCount () > 0) && (m_objPostStat.PostedCount () == 0) ) objPostStat = m_objInitialStat ; else objPostStat = m_objPostStat ; } template BOOL CHttpClientT::_CancelUploadContext (void) throw () { if ( !m_objPostStat.IsActive () ) return FALSE ; // Cancels the Post Context _EndPostContext () ; return TRUE ; } // If all parameters are sent, returns a pointer to the CHttpResponseT. template typename CHttpClientT::CHttpResponse * CHttpClientT::_ProceedUploadContext (DWORD nDesired) throw (Exception &) { // If the Post context is not started if ( !m_objPostStat.IsActive () ) HttpTool::ThrowException (HTTPCLIENT_ERR_POST_NOT_STARTED) ; HTTPCLIENT_ASSERT (m_hInternet != NULL, "CHttpClientT::_ProceedUploadContext: m_hInternet can not be NULL.") ; HTTPCLIENT_ASSERT (m_hConnection != NULL, "CHttpClientT::_ProceedUploadContext: m_hConnection can not be NULL.") ; HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_ProceedUploadContext: m_hRequest can not be NULL.") ; HTTPCLIENT_ASSERT (nDesired != 0, "CHttpClientT::_ProceedUploadContext: nDesired can not be zero.") ; try { // If all parameters are posted // releases the Post context if ( m_objPostStat._IsComplete () ) { HttpTool::EndRequest (m_hRequest) ; return _ReleasePostResponse () ; } LPCSTR szBoundary = _GetUploadBoundaryA () ; // If there is no parameter to upload if ( m_objPostStat.TotalCount () == 0 ) { // Writes the last boundary _WritePost ("\r\n--") ; _WritePost (szBoundary) ; _WritePost ("--\r\n") ; return NULL ; } // If the current parameter is completed if ( m_objPostStat.CurrParamIsComplete () ) { // If all parameters are sent if ( m_objPostStat.TotalCount () == m_objPostStat.PostedCount () ) { // Writes the last boundary _WritePost ("--") ; _WritePost (szBoundary) ; _WritePost ("--\r\n") ; return NULL ; } DWORD nNextIdx = m_objPostStat.PostedCount () ; DWORD nNextFileIdx = m_objPostStat.PostedFileCount () ; PCSZ szEntryFile = NULL ; ::SafeInt nEntryValueTotalByte = 0 ; ConstMapIter iter = m_mapParam.Begin () ; for (size_t i = 0; i < nNextIdx; i++, ++iter) ; if ( (iter->second).dwFlag & ParamFile ) { // If the parameter is a file parameter szEntryFile = (iter->second).szValue ; if ( m_ahPostedFiles[nNextFileIdx] != INVALID_HANDLE_VALUE ) nEntryValueTotalByte = HttpTool::GetFileSize (m_ahPostedFiles[nNextFileIdx], szEntryFile) ; // Writes the boundary and headers _WritePost ("--") ; _WritePost (szBoundary) ; _WritePost ("\r\n") ; _WritePost ("Content-Disposition: form-data; name=\"") ; _WritePost (m_bUseUtf8, iter->first) ; // Writes form name _WritePost ("\"; filename=\"") ; _WritePost (m_bUseUtf8, szEntryFile) ; // Writes file path _WritePost ("\"\r\nContent-Type: ") ; _WritePost (m_aszMimeTypes[nNextFileIdx]) ; _WritePost ("\r\n\r\n") ; } else { // If the parameter is not a file parameter if ( (iter->second).szValue && (iter->second).szValue[0] != '\0' ) { LPCSTR szPostedValue ; BOOL bNeedToFree ; if ( m_bUseUtf8 ) { szPostedValue = _Utf8Encode ((iter->second).szValue) ; bNeedToFree = TRUE ; } else // Converts into a Ansi string szPostedValue = _String2Ansi ((iter->second).szValue, bNeedToFree) ; nEntryValueTotalByte = CHttpToolA::StringLen (szPostedValue) ; _SetPostedValue (szPostedValue, bNeedToFree) ; } else { _SetPostedValue (NULL, FALSE) ; nEntryValueTotalByte = 0 ; } // Writes the boundary and headers _WritePost ("--") ; _WritePost (szBoundary) ; _WritePost ("\r\n") ; _WritePost ("Content-Disposition: form-data; name=\"") ; _WritePost (m_bUseUtf8, iter->first) ; // Write form name _WritePost ("\"\r\n\r\n") ; } // Starts a new parameter m_objPostStat._StartNewEntry (iter->first, nEntryValueTotalByte.Value () , static_cast ((iter->second).dwFlag & ParamFile), szEntryFile) ; if ( nEntryValueTotalByte == 0 ) _WritePost ("\r\n") ; return NULL ; } // Writes the requested number of bytes DWORD cbToWrite = nDesired ; if ( cbToWrite > m_objPostStat.CurrParamRemainByte () ) cbToWrite = static_cast (m_objPostStat.CurrParamRemainByte ()) ; DWORD cbWritten = 0 ; if ( m_objPostStat.CurrParamIsFile () ) { _InitPostCntxBuff () ; DWORD cbBuff ; // The number of bytes to read DWORD cbRead ; // The number of bytes read from the file DWORD nFileIdx = m_objPostStat.PostedFileCount () - 1 ; while ( cbWritten < cbToWrite ) { cbBuff = cbToWrite - cbWritten > HTTPCLIENT_POSTCNTX_BUFF_SIZE ? HTTPCLIENT_POSTCNTX_BUFF_SIZE : cbToWrite - cbWritten ; // Read from file if ( !::ReadFile (m_ahPostedFiles[nFileIdx] , m_pbyCntxBuff , cbBuff , &cbRead , NULL) ) HttpTool::ThrowException (HTTPCLIENT_ERR_READFILE_FAILED, ::GetLastError ()) ; // cbBuff and cbRead must be the same if ( cbBuff != cbRead ) HttpTool::ThrowException (HTTPCLIENT_ERR_READ_UNEXPECTED_SIZE) ; _WritePost (m_pbyCntxBuff, cbBuff, TRUE) ; cbWritten += cbBuff ; } } else { _WritePost ( reinterpret_cast (m_szPostedValue + m_objPostStat.CurrParamPostedByte ()) , cbToWrite, TRUE) ; cbWritten = cbToWrite ; } // If all bytes are written, writes the last new line character if ( m_objPostStat.CurrParamRemainByte () == 0 ) _WritePost ("\r\n") ; } catch (::SafeIntException & e) { _EndPostContext () ; // Aborts the POST context if an error occurs HttpTool::ThrowException (e) ; } catch (Exception &) { _EndPostContext () ; // Aborts the POST context if an error occurs throw ; } return NULL ; } template typename CHttpClientT::CHttpResponse * CHttpClientT::_ReleasePostResponse () throw (Exception &) { HTTPCLIENT_ASSERT (m_objPostStat.IsActive (), "CHttpClientT::_ReleasePostResponse: The post context is not active.") ; HINTERNET hReleasedInternet = NULL ; HINTERNET hReleasedConnection = NULL ; HINTERNET hReleasedRequest = NULL ; _ReleasePostContext (hReleasedInternet, hReleasedConnection, hReleasedRequest) ; CHttpResponse * pobjRes = NULL ; try { if ( NULL == (pobjRes = new CHttpResponse (hReleasedInternet, hReleasedConnection, hReleasedRequest)) ) throw "Out of memory" ; } catch (...) { HttpTool::CloseRequest (hReleasedRequest) ; HttpTool::CloseConnection (hReleasedConnection) ; HttpTool::CloseInternet (hReleasedInternet) ; HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ; } return pobjRes ; } // Releases the POST context when the upload task has been completed template void CHttpClientT::_ReleasePostContext (HINTERNET & hInternet, HINTERNET & hConnection, HINTERNET & hRequest) throw (Exception &) { HTTPCLIENT_ASSERT (m_objPostStat.IsActive (), "CHttpClientT::_ReleasePostResponse: The post context is not active.") ; hInternet = m_bBorrowedInternet ? NULL : m_hInternet ; hConnection = m_bBorrowedConnection ? NULL : m_hConnection ; hRequest = m_hRequest ; m_hInternet = NULL ; m_bBorrowedInternet = FALSE ; m_hConnection = NULL ; m_bBorrowedConnection = FALSE ; m_hRequest = NULL ; // Closes all file handles and frees the MimeType if ( m_ahPostedFiles ) { for (DWORD i = 0; i < m_objPostStat.FileCount (); i++) { if ( m_ahPostedFiles[i] != INVALID_HANDLE_VALUE ) { ::CloseHandle (m_ahPostedFiles[i]) ; m_ahPostedFiles[i] = INVALID_HANDLE_VALUE ; } if ( m_aszMimeTypes[i] ) SAFEFREE ( (m_aszMimeTypes[i]) ) ; } SAFEFREE (m_ahPostedFiles) ; SAFEFREE (m_aszMimeTypes) ; } // Removes the posted value _SetPostedValue (NULL, FALSE) ; // Frees the buffer SAFEFREE (m_pbyCntxBuff) ; // Unactivate the Post context m_objPostStat._MakeUnActive () ; } template void CHttpClientT::_EndPostContext (void) throw (Exception &) { if ( !m_objPostStat.IsActive () ) return ; HINTERNET hInternet, hConnection, hRequest ; _ReleasePostContext (hInternet, hConnection, hRequest) ; // Closes internet handles HttpTool::CloseRequest (hRequest) ; HttpTool::CloseConnection (hConnection) ; HttpTool::CloseInternet (hInternet) ; } ///////////////////////////////////////// CHttpClientT ///////////////////////////////////////// }