/*-- 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