|
|
/*--
Copyright (c) 1995-1998 Microsoft Corporation Module Name: LISTENER.CPP Author: Arul Menezes Abstract: HTTP server initialization & listener thread --*/
#include "pch.h"
#pragma hdrstop
#include "httpd.h"
#include "uhbase.h"
#include "interfacelist.h"
#include "uhutil.h"
//
//-------------------- Global data --------------
//
CGlobalVariables *g_pVars; HANDLE g_hListenThread; // handle to the main thread
HINSTANCE g_hInst; CRITICAL_SECTION g_csConnection; // Used to keep track # of connections under the maximum
BOOL g_fRegistered;
//------------- Const data -----------------------
//
const char cszTextHtml[] = "text/html"; const char cszEmpty[] = ""; const char cszMaxConnectionHeader[] = "HTTP/1.1 503\r\n\r\n"; LONG g_fState; BOOL g_fFromExe; // Did the executable start us?
//
//------------- Debug data -----------------------
//
#if defined(UNDER_CE) && !defined(OLD_CE_BUILD)
#ifdef DEBUG
DBGPARAM dpCurSettings = { TEXT("HTTPD"), { TEXT("Error"),TEXT("Init"),TEXT("Listen"),TEXT("Socket"), TEXT("Request"),TEXT("Response"),TEXT("ISAPI"), TEXT("VROOTS"),TEXT("ASP"),TEXT(""),TEXT(""), TEXT(""),TEXT(""),TEXT("Mem"),TEXT("Parser"),TEXT("Tokens")}, 0x0003 }; #endif
#endif
//
//------------- Prototypes -----------------------
//
PWSTR MassageMultiString(PCWSTR wszIn, PCWSTR wszDefault=NULL);
//
//------------- Startup functions -----------------------
//
HRESULT HrHttpInitialize() { HRESULT hr = S_OK; int err=0, iGLE=0; SOCKET sockConnection = 0; SOCKADDR_IN addrListen; WSADATA wsadata; CHAR szMaxConnectionMsg[256]; // message sent to client if server is too busy
// Note this will cause the ISAPI DLL to be copied every time the web
// server starts
//
hr = HrMakeIsapiExtensionDirectory(); if (FAILED(hr)) { myleave(112); }
TraceTag(ttidWebServer, "HTTPD: Creating Listener thread\r\n");
g_fState = SERVICE_STATE_STARTING_UP; g_fRegistered = FALSE; g_hInst = _Module.GetResourceInstance();
InitializeCriticalSection(&g_csConnection);
svsutil_Initialize(); DEBUGCHK (g_fState == SERVICE_STATE_STARTING_UP);
g_pVars = new CGlobalVariables();
// by design, only 1 HttpListenThread can be instantiated at once, and
// it's only thread that can modify g_fState
if (NULL == g_pVars || NULL == g_pVars->m_pVroots || NULL == g_pVars->m_pThreadPool) { g_fState = SERVICE_STATE_OFF; myleave(11); } g_pVars->m_fFilters = InitFilters(); // Filters may make reference to logging global var, so
// make call to filters outside constructor.
g_fState = SERVICE_STATE_ON;
strcpy(szMaxConnectionMsg,cszMaxConnectionHeader); WCHAR wszMaxConnectionMsg[256];
LoadString(g_hInst,IDS_SERVER_BUSY,wszMaxConnectionMsg,celems(wszMaxConnectionMsg)); MyW2A(wszMaxConnectionMsg,szMaxConnectionMsg + sizeof(cszMaxConnectionHeader) - 1, sizeof(szMaxConnectionMsg) - sizeof(cszMaxConnectionHeader));
if (g_pVars->m_pszStatusBodyBuf) InitializeResponseCodes(g_pVars->m_pszStatusBodyBuf);
if (iGLE = WSAStartup(MAKEWORD(1,1), &wsadata)) goto done;
if (INVALID_SOCKET == (g_pVars->m_sockListen = socket(AF_INET, SOCK_STREAM, 0))) myleave(2);
memset(&addrListen, 0, sizeof(addrListen)); addrListen.sin_family = AF_INET; addrListen.sin_port = htons((WORD)g_pVars->m_dwListenPort);
addrListen.sin_addr.s_addr = INADDR_ANY;
if (bind(g_pVars->m_sockListen, (PSOCKADDR)&addrListen, sizeof(addrListen))) myleave(3);
if (listen(g_pVars->m_sockListen, SOMAXCONN)) myleave(4);
if (!WSAEventSelect(g_pVars->m_sockListen, g_pVars->m_hEventSelect, FD_ACCEPT)) { g_hListenThread = MyCreateThread(HandleAccept, 0); } else { hr = HrFromLastWin32Error(); }
done:
TraceError("HrHttpInitialize", hr); return hr; }
HRESULT HrHttpShutdown() { HRESULT hr = S_OK;
TraceTag(ttidWebServer, "Shutting down web server...");
g_fState = SERVICE_STATE_SHUTTING_DOWN;
g_pVars->m_fAcceptConnections = FALSE;
// BUGBUG, 11168. It's possible ASP pages or ISAPI extns may have an
// infinite loop in them, in which case we never decrement this value and
// never get to stop the server.
// Fix: None. This behavior has been documented, too much of a pain for us
// to fix.
TraceTag(ttidWebServer, "Wating for %d HTTP threads to come to a halt", g_pVars->m_nConnections); g_pVars->m_pLog->WriteEvent(IDS_HTTPD_SHUTDOWN_START);
g_pVars->m_pThreadPool->Shutdown();
TraceTag(ttidWebServer, "Signalling accept thread to stop"); SetEvent(g_pVars->m_hEventShutdown);
// Wait until all connections have been closed before shutting down
while (TRUE) { DWORD cConnections;
EnterCriticalSection(&g_csConnection); cConnections = g_pVars->m_nConnections; LeaveCriticalSection(&g_csConnection);
if (!cConnections) { break; }
// wait a bit
Sleep(100); }
TraceTag(ttidWebServer, "All HTTPD threads have come to halt, shutting down server");
if (g_hListenThread) { TraceTag(ttidWebServer, "Waiting for accept thread to exit");
// Wait for thread to exit
WaitForSingleObject(g_hListenThread, INFINITE);
TraceTag(ttidWebServer, "Accept thread has exited. Closing handle.");
CloseHandle(g_hListenThread);
g_hListenThread = 0; // signifies that we exited normally, don't do a TerminateThread on this.
}
if (g_pVars) { closesocket(g_pVars->m_sockListen); delete g_pVars; }
DeleteCriticalSection(&g_csConnection);
return hr; }
HRESULT HrAddVroot(LPWSTR szUrl, LPWSTR szPath) { HRESULT hr = S_OK;
if (!g_pVars->m_pVroots->AddVRoot(szUrl, szPath)) { hr = E_FAIL; }
TraceError("HrAddVroot", hr); return hr; }
HRESULT HrRemoveVroot(LPWSTR szUrl) { HRESULT hr = S_OK;
if (!g_pVars->m_pVroots->RemoveVRoot(szUrl)) { hr = E_FAIL; }
TraceError("HrRemoveVroot", hr); return hr; }
CGlobalVariables::CGlobalVariables() { DWORD dwMaxLogSize; WCHAR wszLogDir[MAX_PATH + 1]; ZEROMEM(this); m_sockListen = INVALID_SOCKET; m_pszServerID = NULL;
CReg reg(HKEY_LOCAL_MACHINE, RK_HTTPD);
if ( (HKEY) reg == 0) { CLog cLog(4096,L"\\windows\\www"); cLog.WriteEvent(IDS_HTTPD_NO_REGKEY); TraceTag(ttidWebServer, "HTTPD: No registry key setup, will not handle requests"); return; }
dwMaxLogSize = reg.ValueDW(RV_MAXLOGSIZE); if ( ! reg.ValueSZ(RV_LOGDIR,wszLogDir,MAX_PATH+1)) { wcscpy(wszLogDir,L"\\windows\\www"); }
m_pLog = new CLog(dwMaxLogSize,wszLogDir);
if (!g_fFromExe && (0 == reg.ValueDW(RV_ISENABLED,1))) { m_pLog->WriteEvent(IDS_HTTPD_DISABLED); TraceTag(ttidWebServer, "HTTPD: IsEnable = 0, won't start web server"); return; }
m_dwListenPort = reg.ValueDW(RV_PORT, IPPORT_HTTP); DEBUGCHK(m_dwListenPort);
m_dwPostReadSize = reg.ValueDW(RV_POSTREADSIZE, 48*1024); // 48 K default
m_fExtensions = InitExtensions(&m_pISAPICache,&m_dwCacheSleep); m_fASP = InitASP(&m_ASPScriptLang,&m_lASPCodePage,&m_ASPlcid);
m_fDirBrowse = reg.ValueDW(RV_DIRBROWSE, HTTPD_ALLOW_DIR_BROWSE);
m_wszDefaultPages = MassageMultiString(reg.ValueSZ(RV_DEFAULTPAGE),HTTPD_DEFAULT_PAGES); m_wszAdminUsers = MassageMultiString(reg.ValueSZ(RV_ADMINUSERS)); m_wszAdminGroups = MassageMultiString(reg.ValueSZ(RV_ADMINGROUPS));
AuthInitialize(®,&m_fBasicAuth, &m_fNTLMAuth);
m_pVroots = new CVRoots(); if (!m_pVroots) { return; }
// vroot table must be initialized or web server can't return files. Warn
// user if this is not the case
if (m_pVroots->Count() == 0) m_pLog->WriteEvent(IDS_HTTPD_NO_VROOTS);
if (NULL == (m_pszStatusBodyBuf = MyRgAllocNZ(CHAR,BODYSTRINGSIZE))) return;
if (NULL == (m_pszServerID = MyRgAllocNZ(CHAR, 256))) return;
if (WSA_INVALID_EVENT == (m_hEventSelect = WSACreateEvent())) return;
if (INVALID_HANDLE_VALUE == (m_hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL))) return;
lstrcpyA(m_pszServerID, "Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0");
m_nMaxConnections = reg.ValueDW(RV_MAXCONNECTIONS,10);
m_pThreadPool = new SVSThreadPool(m_nMaxConnections + 1); // +1 for ISAPI Cache removal thread
if (!m_pThreadPool) { delete m_pVroots; m_pVroots = NULL;
MyFree(m_pszStatusBodyBuf); m_pszStatusBodyBuf = NULL;
return; }
m_fAcceptConnections = TRUE; }
CGlobalVariables::~CGlobalVariables() { MyFree(m_wszDefaultPages); MyFree(m_wszAdminUsers); MyFree(m_wszAdminGroups); MyFree(m_pszStatusBodyBuf); MyFree(m_pszServerID);
CleanupFilters();
if (m_pVroots) delete m_pVroots;
if (m_pISAPICache) { RemoveUnusedISAPIs((void*)1); // Tell it to flush all ISAPIs. This will blocks until everyoen's unloaded.
delete m_pISAPICache; }
if (m_pLog) delete m_pLog;
if (m_pThreadPool) delete m_pThreadPool;
if (m_hEventSelect != NULL && m_hEventSelect != WSA_INVALID_EVENT) { WSACloseEvent(m_hEventSelect); }
if (m_hEventShutdown != NULL && m_hEventShutdown != INVALID_HANDLE_VALUE) { CloseHandle(m_hEventShutdown); }
MyFreeLib(m_hNTLMLib); }
//
// Main HTTP listener thread. Launched from HTTPInitialize
// or called directly by main() in test mode
//
DWORD WINAPI HandleAccept(LPVOID lpv) { SOCKET sockConnection = 0; CHAR szMaxConnectionMsg[256]; // message sent to client if server is too busy
HANDLE rgHandles[2]; DWORD dwRet; WSANETWORKEVENTS wsaNetEvents = {0};
rgHandles[0] = g_pVars->m_hEventSelect; rgHandles[1] = g_pVars->m_hEventShutdown;
while (TRUE) { dwRet = WaitForMultipleObjects(2, rgHandles, FALSE, INFINITE); if (dwRet == WAIT_OBJECT_0) { wsaNetEvents.lNetworkEvents = 0; if (WSAEnumNetworkEvents(g_pVars->m_sockListen, g_pVars->m_hEventSelect, &wsaNetEvents) == SOCKET_ERROR) { TraceError("HandleAccept", HRESULT_FROM_WIN32(WSAGetLastError())); break; } else if (wsaNetEvents.lNetworkEvents & FD_ACCEPT) { TraceTag(ttidWebServer, "HTTPD: Calling ACCEPT...."); sockConnection = WSAAccept(g_pVars->m_sockListen, NULL, NULL, NULL ,NULL); if (sockConnection != INVALID_SOCKET) { int cb = sizeof(SOCKADDR_IN); SOCKADDR_IN sockLocal;
getsockname(sockConnection, (SOCKADDR *)&sockLocal, &cb); TraceTag(ttidWebServer, "HTTPD: Received Connection on address, " "addr = %s!!", inet_ntoa(sockLocal.sin_addr));
if (g_pVars->m_fAcceptConnections) { // We decide whether to handle the request based on the number of connections
// We NEVER put a thread into the thread pool wait list because we want
// the web server to respond immediatly to browser if it's too busy.
EnterCriticalSection(&g_csConnection);
DEBUGCHK(g_pVars->m_nConnections <= g_pVars->m_nMaxConnections);
if (g_pVars->m_nConnections >= g_pVars->m_nMaxConnections) { LeaveCriticalSection(&g_csConnection);
TraceTag(ttidWebServer, "HTTPD: Connection Count -- Reached " "maximum # of connections, won't accept current request.");
send(sockConnection,szMaxConnectionMsg, lstrlenA(szMaxConnectionMsg),0); closesocket(sockConnection); } else if (!CUPnPInterfaceList::Instance().FShouldSendOnInterface(sockLocal.sin_addr.S_un.S_addr)) { LeaveCriticalSection(&g_csConnection);
TraceTag(ttidWebServer, "HTTPD: We should not be " "responding to requests that come in on local " "address %s!", inet_ntoa(sockLocal.sin_addr));
// ISSUE-2000/12/28-danielwe: What to send in response?
//send(sockConnection,szMaxConnectionMsg, lstrlenA(szMaxConnectionMsg),0);
closesocket(sockConnection); } else { g_pVars->m_nConnections++;
LeaveCriticalSection(&g_csConnection);
QueueUserWorkItem(HttpConnectionThread, (LPVOID) sockConnection, WT_EXECUTELONGFUNCTION); } } } else if (GetLastError() == WSAEWOULDBLOCK) { AssertSz(FALSE, "WSAAccept failed with WSAEWOULDBLOCK!"); WSASetEvent(g_pVars->m_hEventSelect); } else { AssertSz(FALSE, "WSAAccept failed for some other reason!"); } } else { AssertSz(FALSE, "Did not get an ACCEPT network event!"); } } else if (dwRet == WAIT_OBJECT_0 + 1) { // Shutting down
Assert(g_fState == SERVICE_STATE_SHUTTING_DOWN); break; } else { TraceError("HandleAccept - WaitForMultipleObjects " "failed! Thread is exiting", HrFromLastWin32Error());
AssertSz(FALSE, "HandleAccept - Wait failed!"); break; } }
return 0; }
DWORD WINAPI HttpConnectionThread(LPVOID lpv) { SOCKET sock = (SOCKET) lpv; // this socket is non blocking and send and recv fucntion is not implemented to take case of non blocking sockets
// This must be changed.
// this outer _try--_except is to catch crashes in the destructor
__try { CHttpRequest* pRequest = new CHttpRequest((SOCKET) sock); if (pRequest) { __try { // This loops as long the the socket is being kept alive
for (;;) { pRequest->HandleRequest();
// figure out if we must keep this connection alive,
// Either session is over through this request's actions or
// globally set to accept no more connections.
// We do the global g_pVars->m_fAcceptConnections check
// because it's possible that we may be in the process of
// shutting down the web server, in which case we want to
// exit even if we're performing a keep alive.
if (! (g_pVars->m_fAcceptConnections && pRequest->m_fKeepAlive)) { if (g_pVars->m_fFilters) pRequest->CallFilter(SF_NOTIFY_END_OF_NET_SESSION); break; }
// If we're continuing the session don't delete all data, just request specific data
if ( ! pRequest->ReInit()) break; } } __finally { // Note: To get this to compile under Visual Studio, the /Gx- compile line option is set
delete pRequest; pRequest = 0; } } } __except(1) { RETAILMSG(1, (L"HTTP Server got an exception!!!\r\n")); g_pVars->m_pLog->WriteEvent(IDS_HTTPD_EXCEPTION,GetExceptionCode(),GetLastError()); }
EnterCriticalSection(&g_csConnection); g_pVars->m_nConnections--; LeaveCriticalSection(&g_csConnection);
shutdown(sock, 1); closesocket(sock); return 0; }
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
// UTILITY FUNCTIONS
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
PWSTR MassageMultiString(PCWSTR wszIn, PCWSTR wszDefault) { if (!wszIn) wszIn = wszDefault; if (!wszIn) return NULL;
PWSTR wszOut = MyRgAllocNZ(WCHAR, 2+wcslen(wszIn)); // +2 for dbl-null term
if (!wszOut) return NULL;
for (PWSTR wszNext=wszOut; *wszIn; wszIn++, wszNext++) { *wszNext = (*wszIn==L';' ? L'\0' : *wszIn);
// Ignore space between ";" and next non-space
if ( L';' == *wszIn) { wszIn++; SkipWWhiteSpace(wszIn); wszIn--; // otherwise we skip first char of new string.
} } wszNext[0] = wszNext[1] = 0; // dbl-null
return wszOut; }
void GetRemoteAddress(SOCKET sock, PSTR pszBuf) { SOCKADDR_IN sockaddr; int iLen = sizeof(sockaddr); PSTR pszTemp;
*pszBuf=0; if (getpeername(sock, (PSOCKADDR)&sockaddr, &iLen)) { TraceTag(ttidWebServer, "getpeername failed GLE=%d", GetLastError()); return; } if (!(pszTemp = inet_ntoa(sockaddr.sin_addr))) { TraceTag(ttidWebServer, "inet_ntoa failed GLE=%d", GetLastError()); return; } strcpy(pszBuf, pszTemp); }
void GetLocalAddress(SOCKET sock, PSTR pszBuf) { SOCKADDR_IN sockaddr; int iLen = sizeof(sockaddr); PSTR pszTemp;
*pszBuf=0; if (getsockname(sock, (PSOCKADDR)&sockaddr, &iLen)) { TraceTag(ttidWebServer, "getsockname failed GLE=%d", GetLastError()); return; } if (!(pszTemp = inet_ntoa(sockaddr.sin_addr))) { TraceTag(ttidWebServer, "inet_ntoa failed GLE=%d", GetLastError()); return; } strcpy(pszBuf, pszTemp); }
PSTR MySzDupA(PCSTR pszIn, int iLen) { if (!pszIn) return NULL; if (!iLen) iLen = strlen(pszIn); PSTR pszOut=MySzAllocA(iLen); if (pszOut) { memcpy(pszOut, pszIn, iLen); pszOut[iLen] = 0; } return pszOut; }
PWSTR MySzDupW(PCWSTR wszIn, int iLen) { if (!wszIn) return NULL; if (!iLen) iLen = wcslen(wszIn); PWSTR wszOut=MySzAllocW(iLen); if (wszOut) { memcpy(wszOut, wszIn, sizeof(WCHAR)*iLen); wszOut[iLen] = 0; } return wszOut; }
PWSTR MySzDupAtoW(PCSTR pszIn, int iInLen) { PWSTR pwszOut = 0; int iOutLen = MultiByteToWideChar(CP_ACP, 0, pszIn, iInLen, 0, 0); if (!iOutLen) goto error; pwszOut = MySzAllocW(iOutLen); if (!pwszOut) goto error; if (MultiByteToWideChar(CP_ACP, 0, pszIn, iInLen, pwszOut, iOutLen)) return pwszOut;
error: TraceTag(ttidWebServer, "MySzDupAtoW(%s, %d) failed. pOut=%0x08x GLE=%d", pszIn, iInLen, pwszOut, GetLastError()); MyFree(pwszOut); return FALSE; }
PSTR MySzDupWtoA(PCWSTR wszIn, int iInLen) { PSTR pszOut = 0; int iOutLen = WideCharToMultiByte(CP_ACP, 0, wszIn, iInLen, 0, 0, 0, 0); if (!iOutLen) goto error; pszOut = MySzAllocA(iOutLen); if (!pszOut) goto error; if (WideCharToMultiByte(CP_ACP, 0, wszIn, iInLen, pszOut, iOutLen, 0, 0)) return pszOut;
error: TraceTag(ttidWebServer, "MySzDupWtoA(%s, %d) failed. pOut=%0x08x GLE=%d", wszIn, iInLen, pszOut, GetLastError()); MyFree(pszOut); return FALSE; }
BOOL MyStrCatA(PSTR *ppszDest, PSTR pszSource, PSTR pszDivider) { DEBUG_CODE_INIT; BOOL ret = FALSE; PSTR pszNew = *ppszDest; // protect orignal ptr should realloc fail
PSTR pszTrav; DWORD dwSrcLen = MyStrlenA(pszSource); DWORD dwDestLen = MyStrlenA(*ppszDest); DWORD dwDivLen = MyStrlenA(pszDivider);
if (!pszNew) // do an alloc first time, ignore divider
{ if (NULL == (pszNew = MySzDupA(pszSource,dwSrcLen))) myleave(260); } else { if (NULL == (pszNew = MyRgReAlloc(char,pszNew,dwDestLen,dwSrcLen + dwDestLen + dwDivLen + 1))) myleave(261);
pszTrav = pszNew + dwDestLen; if (pszDivider) { memcpy(pszTrav, pszDivider, dwDivLen); pszTrav += dwDivLen; }
strcpy(pszTrav, pszSource); }
*ppszDest = pszNew; ret = TRUE; done: TraceTag(ttidWebServer, "MyStrCat err = %d",GetLastError());
return ret; }
//**************************************************************************
// Component Notes
// This is used by Filters and by Authentication components. The only common
// component they both rest on is core, so we include it here.
//**************************************************************************
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
// BASE64 ENCODE/DECODE FUNCTIONS from sicily.c
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
const int base642six[256]={ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63, 52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27, 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64 };
const char six2base64[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+','/' };
//-----------------------------------------------------------------------------
// Function: encode()
//-----------------------------------------------------------------------------
BOOL Base64Encode( BYTE * bufin, // in
DWORD nbytes, // in
char * pbuffEncoded) // out
{ unsigned char *outptr; unsigned int i; const char *rgchDict = six2base64;
outptr = (unsigned char *)pbuffEncoded;
for (i=0; i < nbytes; i += 3) { *(outptr++) = rgchDict[*bufin >> 2]; /* c1 */ *(outptr++) = rgchDict[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/ *(outptr++) = rgchDict[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/ *(outptr++) = rgchDict[bufin[2] & 077]; /* c4 */
bufin += 3; }
/* If nbytes was not a multiple of 3, then we have encoded too
* many characters. Adjust appropriately. */ if (i == nbytes+1) { /* There were only 2 bytes in that last group */ outptr[-1] = '='; } else if (i == nbytes+2) { /* There was only 1 byte in that last group */ outptr[-1] = '='; outptr[-2] = '='; }
*outptr = '\0';
return TRUE; }
//-----------------------------------------------------------------------------
// Function: decode()
//-----------------------------------------------------------------------------
BOOL Base64Decode( char * bufcoded, // in
char * pbuffdecoded, // out
DWORD * pcbDecoded) // in out
{ INT_PTR nbytesdecoded; char *bufin; unsigned char *bufout; INT_PTR nprbytes; const int *rgiDict = base642six;
/* Strip leading whitespace. */
while (*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
/* Figure out how many characters are in the input buffer.
* If this would decode into more bytes than would fit into * the output buffer, adjust the number of input bytes downwards. */ bufin = bufcoded; while (rgiDict[*(bufin++)] <= 63); nprbytes = bufin - bufcoded - 1; nbytesdecoded = ((nprbytes+3)/4) * 3;
if ( pcbDecoded ) *pcbDecoded = (DWORD)nbytesdecoded;
bufout = (unsigned char *)pbuffdecoded;
bufin = bufcoded;
while (nprbytes > 0) { *(bufout++) = (unsigned char) (rgiDict[*bufin] << 2 | rgiDict[bufin[1]] >> 4); *(bufout++) = (unsigned char) (rgiDict[bufin[1]] << 4 | rgiDict[bufin[2]] >> 2); *(bufout++) = (unsigned char) (rgiDict[bufin[2]] << 6 | rgiDict[bufin[3]]); bufin += 4; nprbytes -= 4; }
if (nprbytes & 03) { if (rgiDict[bufin[-2]] > 63) nbytesdecoded -= 2; else nbytesdecoded -= 1; }
((CHAR *)pbuffdecoded)[nbytesdecoded] = '\0';
return TRUE; }
// Gets version info from the pszVersion string
// HTTP version string come in the following form: HTTP/Major.Minor,
// where Major and Minor are integers.
BOOL SetHTTPVersion(PSTR pszVersion, DWORD *pdwVersion) { int iMajor, iMinor; DWORD cbVer = strlen(cszHTTPVER); PSTR pszTemp;
if (_memicmp(pszVersion,cszHTTPVER, min(strlen(pszVersion), cbVer))) { return FALSE; } iMajor = atoi(pszVersion+cbVer); if (NULL == (pszTemp = strchr(pszVersion+cbVer,'.'))) return FALSE;
iMinor = atoi(pszTemp+1); *pdwVersion = MAKELONG(iMinor,iMajor); return TRUE; }
// Writes http version info in dwVersion into pszVersion
// Substitutes for: sprintf(szBuf, "HTTP/%d.%d", HIWORD(m_dwVersion), LOWORD(m_dwVersion));
void WriteHTTPVersion(PSTR pszVersion, DWORD dwVersion) { PSTR pszTrav = strcpyEx(pszVersion,cszHTTPVER);
_itoa(HIWORD(dwVersion),pszTrav, 10); pszTrav = strchr(pszTrav,'\0'); *pszTrav++ = '.'; _itoa(LOWORD(dwVersion),pszTrav, 10); }
// Replaces
// i = sscanf(pszTok, cszDateParseFmt, &st.wDay, &szMonth, &st.wYear, &st.wHour, &st.wMinute, &st.wSecond, &m_dwIfModifiedLength);
// const char cszDateParseFmt[] = " %*3s, %02hd %3s %04hd %02hd:%02hd:%02hd GMT; length=%d";
// This macro advance psz n characters ahead. However, if there is a \0 in between
// psz[0] and psz[n-1], it will return FALSE.
// We do this \0 check rathern than psz += n because even
// though we're throwing out the input, we want to make sure we don't walk past the
// end of a buffer.
#define SKIP_INPUT(psz, n) { int j; for (j = 0; j <= (n); j++) { if (! (psz)[j]) return FALSE; } (psz) += (n); }
BOOL SetHTTPDate(PSTR pszDate, PSTR pszMonth, SYSTEMTIME *pst, PDWORD pdwModifiedLength) { PSTR pszTrav = pszDate; int i;
// Skip past day of week (Sun/Mon/...), comma, space. We don't
// do this with pszTrav += 5 because we could have an ilformatted string
// that is too short,
SKIP_INPUT(pszTrav,5);
pst->wDay = (WORD)atoi(pszTrav); SKIP_INPUT(pszTrav,3);
// Copy month data into pszMonth. Again, don't do memcpy.
for (i = 0; i < 3; i++) { if (pszTrav[i] == '\0') return FALSE;
pszMonth[i] = pszTrav[i]; } pszMonth[i] = 0;
pszTrav += 4; if (! (*pszTrav)) return FALSE;
pst->wYear = (WORD)atoi(pszTrav); SKIP_INPUT(pszTrav,5);
pst->wHour = (WORD)atoi(pszTrav); pszTrav += 2; if (':' != *pszTrav) return FALSE; pszTrav++;
pst->wMinute = (WORD)atoi(pszTrav); pszTrav += 2; if (':' != *pszTrav) return FALSE; pszTrav++;
pst->wSecond = (WORD)atoi(pszTrav); pszTrav += 2;
// NT Port: On NT, two files could have the same date, but using GetFileTime will
// put a non-zero # of milliseconds in the file time value, so the file time
// comparison will think the file is older than the one we're comparing against
// by a few milliseconds.
pst->wMilliseconds = 999;
SKIP_INPUT(pszTrav,sizeof(" GMT; length=") -1);
*pdwModifiedLength = atoi(pszTrav);
return TRUE; }
// Does the following
// sprintf(szBuffer + i, cszDateOutputFmt,rgWkday[st.wDayOfWeek], st.wDay, rgMonth[st.wMonth], st.wYear, st.wHour, st.wMinute, st.wSecond);
PSTR WriteHTTPDate(PSTR pszDateBuf, SYSTEMTIME *pst, BOOL fAddGMT) { PSTR pszTrav;
// Day of week
pszTrav = strcpyEx(pszDateBuf,rgWkday[pst->wDayOfWeek]); *pszTrav++ = ','; *pszTrav++ = ' ';
// Day number. Write 0 out if there isn't one already.
if (pst->wDay < 10) { _itoa(0,pszTrav,10); _itoa(pst->wDay,pszTrav+1,10); } else { _itoa(pst->wDay,pszTrav,10); } pszTrav += 2; *pszTrav++ = ' ';
// Month
pszTrav = strcpyEx(pszTrav,rgMonth[pst->wMonth]); *pszTrav++ = ' ';
// Year
_itoa(pst->wYear,pszTrav,10); pszTrav += 4; *pszTrav++ = ' ';
// Hour
if (pst->wHour < 10) { _itoa(0,pszTrav,10); _itoa(pst->wHour,pszTrav+1,10); } else { _itoa(pst->wHour,pszTrav,10); } pszTrav += 2; *pszTrav++ = ':';
// Minute
if (pst->wMinute < 10) { _itoa(0,pszTrav,10); _itoa(pst->wMinute,pszTrav+1,10); } else { _itoa(pst->wMinute,pszTrav,10); } pszTrav += 2; *pszTrav++ = ':';
// Second
if (pst->wSecond < 10) { _itoa(0,pszTrav,10); _itoa(pst->wSecond,pszTrav+1,10); } else { _itoa(pst->wSecond,pszTrav,10); } pszTrav += 2;
if (fAddGMT) pszTrav = strcpyEx(pszTrav," GMT\r\n"); else { *pszTrav++ = ' '; *pszTrav = '\0'; }
return pszTrav; }
/*===================================================================
strcpyEx
Copy one string to another, returning a pointer to the NUL character in the destination
Parameters: szDest - pointer to the destination string szSrc - pointer to the source string
Returns: A pointer to the NUL terminator is returned. ===================================================================*/
char *strcpyEx(char *szDest, const char *szSrc) { while (*szDest++ = *szSrc++) ;
return szDest - 1; }
#if defined(OLD_CE_BUILD)
#if (_WIN32_WCE < 210)
VOID GetCurrentFT(LPFILETIME lpFileTime) { SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st,lpFileTime); LocalFileTimeToFileTime(lpFileTime,lpFileTime); } #endif
#endif
|