Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1106 lines
31 KiB

  1. /*--
  2. Copyright (c) 1995-1998 Microsoft Corporation
  3. Module Name: LISTENER.CPP
  4. Author: Arul Menezes
  5. Abstract: HTTP server initialization & listener thread
  6. --*/
  7. #include "pch.h"
  8. #pragma hdrstop
  9. #include "httpd.h"
  10. #include "uhbase.h"
  11. #include "interfacelist.h"
  12. #include "uhutil.h"
  13. //
  14. //-------------------- Global data --------------
  15. //
  16. CGlobalVariables *g_pVars;
  17. HANDLE g_hListenThread; // handle to the main thread
  18. HINSTANCE g_hInst;
  19. CRITICAL_SECTION g_csConnection; // Used to keep track # of connections under the maximum
  20. BOOL g_fRegistered;
  21. //------------- Const data -----------------------
  22. //
  23. const char cszTextHtml[] = "text/html";
  24. const char cszEmpty[] = "";
  25. const char cszMaxConnectionHeader[] = "HTTP/1.1 503\r\n\r\n";
  26. LONG g_fState;
  27. BOOL g_fFromExe; // Did the executable start us?
  28. //
  29. //------------- Debug data -----------------------
  30. //
  31. #if defined(UNDER_CE) && !defined(OLD_CE_BUILD)
  32. #ifdef DEBUG
  33. DBGPARAM dpCurSettings = {
  34. TEXT("HTTPD"), {
  35. TEXT("Error"),TEXT("Init"),TEXT("Listen"),TEXT("Socket"),
  36. TEXT("Request"),TEXT("Response"),TEXT("ISAPI"),
  37. TEXT("VROOTS"),TEXT("ASP"),TEXT(""),TEXT(""),
  38. TEXT(""),TEXT(""),TEXT("Mem"),TEXT("Parser"),TEXT("Tokens")},
  39. 0x0003
  40. };
  41. #endif
  42. #endif
  43. //
  44. //------------- Prototypes -----------------------
  45. //
  46. PWSTR MassageMultiString(PCWSTR wszIn, PCWSTR wszDefault=NULL);
  47. //
  48. //------------- Startup functions -----------------------
  49. //
  50. HRESULT HrHttpInitialize()
  51. {
  52. HRESULT hr = S_OK;
  53. int err=0, iGLE=0;
  54. SOCKET sockConnection = 0;
  55. SOCKADDR_IN addrListen;
  56. WSADATA wsadata;
  57. CHAR szMaxConnectionMsg[256]; // message sent to client if server is too busy
  58. // Note this will cause the ISAPI DLL to be copied every time the web
  59. // server starts
  60. //
  61. hr = HrMakeIsapiExtensionDirectory();
  62. if (FAILED(hr))
  63. {
  64. myleave(112);
  65. }
  66. TraceTag(ttidWebServer, "HTTPD: Creating Listener thread\r\n");
  67. g_fState = SERVICE_STATE_STARTING_UP;
  68. g_fRegistered = FALSE;
  69. g_hInst = _Module.GetResourceInstance();
  70. InitializeCriticalSection(&g_csConnection);
  71. svsutil_Initialize();
  72. DEBUGCHK (g_fState == SERVICE_STATE_STARTING_UP);
  73. g_pVars = new CGlobalVariables();
  74. // by design, only 1 HttpListenThread can be instantiated at once, and
  75. // it's only thread that can modify g_fState
  76. if (NULL == g_pVars || NULL == g_pVars->m_pVroots || NULL == g_pVars->m_pThreadPool)
  77. {
  78. g_fState = SERVICE_STATE_OFF;
  79. myleave(11);
  80. }
  81. g_pVars->m_fFilters = InitFilters(); // Filters may make reference to logging global var, so
  82. // make call to filters outside constructor.
  83. g_fState = SERVICE_STATE_ON;
  84. strcpy(szMaxConnectionMsg,cszMaxConnectionHeader);
  85. WCHAR wszMaxConnectionMsg[256];
  86. LoadString(g_hInst,IDS_SERVER_BUSY,wszMaxConnectionMsg,celems(wszMaxConnectionMsg));
  87. MyW2A(wszMaxConnectionMsg,szMaxConnectionMsg + sizeof(cszMaxConnectionHeader) - 1,
  88. sizeof(szMaxConnectionMsg) - sizeof(cszMaxConnectionHeader));
  89. if (g_pVars->m_pszStatusBodyBuf)
  90. InitializeResponseCodes(g_pVars->m_pszStatusBodyBuf);
  91. if (iGLE = WSAStartup(MAKEWORD(1,1), &wsadata))
  92. goto done;
  93. if (INVALID_SOCKET == (g_pVars->m_sockListen = socket(AF_INET, SOCK_STREAM, 0)))
  94. myleave(2);
  95. memset(&addrListen, 0, sizeof(addrListen));
  96. addrListen.sin_family = AF_INET;
  97. addrListen.sin_port = htons((WORD)g_pVars->m_dwListenPort);
  98. addrListen.sin_addr.s_addr = INADDR_ANY;
  99. if (bind(g_pVars->m_sockListen, (PSOCKADDR)&addrListen, sizeof(addrListen)))
  100. myleave(3);
  101. if (listen(g_pVars->m_sockListen, SOMAXCONN))
  102. myleave(4);
  103. if (!WSAEventSelect(g_pVars->m_sockListen, g_pVars->m_hEventSelect, FD_ACCEPT))
  104. {
  105. g_hListenThread = MyCreateThread(HandleAccept, 0);
  106. }
  107. else
  108. {
  109. hr = HrFromLastWin32Error();
  110. }
  111. done:
  112. TraceError("HrHttpInitialize", hr);
  113. return hr;
  114. }
  115. HRESULT HrHttpShutdown()
  116. {
  117. HRESULT hr = S_OK;
  118. TraceTag(ttidWebServer, "Shutting down web server...");
  119. g_fState = SERVICE_STATE_SHUTTING_DOWN;
  120. g_pVars->m_fAcceptConnections = FALSE;
  121. // BUGBUG, 11168. It's possible ASP pages or ISAPI extns may have an
  122. // infinite loop in them, in which case we never decrement this value and
  123. // never get to stop the server.
  124. // Fix: None. This behavior has been documented, too much of a pain for us
  125. // to fix.
  126. TraceTag(ttidWebServer, "Wating for %d HTTP threads to come to a halt", g_pVars->m_nConnections);
  127. g_pVars->m_pLog->WriteEvent(IDS_HTTPD_SHUTDOWN_START);
  128. g_pVars->m_pThreadPool->Shutdown();
  129. TraceTag(ttidWebServer, "Signalling accept thread to stop");
  130. SetEvent(g_pVars->m_hEventShutdown);
  131. // Wait until all connections have been closed before shutting down
  132. while (TRUE)
  133. {
  134. DWORD cConnections;
  135. EnterCriticalSection(&g_csConnection);
  136. cConnections = g_pVars->m_nConnections;
  137. LeaveCriticalSection(&g_csConnection);
  138. if (!cConnections)
  139. {
  140. break;
  141. }
  142. // wait a bit
  143. Sleep(100);
  144. }
  145. TraceTag(ttidWebServer, "All HTTPD threads have come to halt, shutting down server");
  146. if (g_hListenThread)
  147. {
  148. TraceTag(ttidWebServer, "Waiting for accept thread to exit");
  149. // Wait for thread to exit
  150. WaitForSingleObject(g_hListenThread, INFINITE);
  151. TraceTag(ttidWebServer, "Accept thread has exited. Closing handle.");
  152. CloseHandle(g_hListenThread);
  153. g_hListenThread = 0; // signifies that we exited normally, don't do a TerminateThread on this.
  154. }
  155. if (g_pVars)
  156. {
  157. closesocket(g_pVars->m_sockListen);
  158. delete g_pVars;
  159. }
  160. DeleteCriticalSection(&g_csConnection);
  161. return hr;
  162. }
  163. HRESULT HrAddVroot(LPWSTR szUrl, LPWSTR szPath)
  164. {
  165. HRESULT hr = S_OK;
  166. if (!g_pVars->m_pVroots->AddVRoot(szUrl, szPath))
  167. {
  168. hr = E_FAIL;
  169. }
  170. TraceError("HrAddVroot", hr);
  171. return hr;
  172. }
  173. HRESULT HrRemoveVroot(LPWSTR szUrl)
  174. {
  175. HRESULT hr = S_OK;
  176. if (!g_pVars->m_pVroots->RemoveVRoot(szUrl))
  177. {
  178. hr = E_FAIL;
  179. }
  180. TraceError("HrRemoveVroot", hr);
  181. return hr;
  182. }
  183. CGlobalVariables::CGlobalVariables()
  184. {
  185. DWORD dwMaxLogSize;
  186. WCHAR wszLogDir[MAX_PATH + 1];
  187. ZEROMEM(this);
  188. m_sockListen = INVALID_SOCKET;
  189. m_pszServerID = NULL;
  190. CReg reg(HKEY_LOCAL_MACHINE, RK_HTTPD);
  191. if ( (HKEY) reg == 0)
  192. {
  193. CLog cLog(4096,L"\\windows\\www");
  194. cLog.WriteEvent(IDS_HTTPD_NO_REGKEY);
  195. TraceTag(ttidWebServer, "HTTPD: No registry key setup, will not handle requests");
  196. return;
  197. }
  198. dwMaxLogSize = reg.ValueDW(RV_MAXLOGSIZE);
  199. if ( ! reg.ValueSZ(RV_LOGDIR,wszLogDir,MAX_PATH+1))
  200. {
  201. wcscpy(wszLogDir,L"\\windows\\www");
  202. }
  203. m_pLog = new CLog(dwMaxLogSize,wszLogDir);
  204. if (!g_fFromExe && (0 == reg.ValueDW(RV_ISENABLED,1)))
  205. {
  206. m_pLog->WriteEvent(IDS_HTTPD_DISABLED);
  207. TraceTag(ttidWebServer, "HTTPD: IsEnable = 0, won't start web server");
  208. return;
  209. }
  210. m_dwListenPort = reg.ValueDW(RV_PORT, IPPORT_HTTP);
  211. DEBUGCHK(m_dwListenPort);
  212. m_dwPostReadSize = reg.ValueDW(RV_POSTREADSIZE, 48*1024); // 48 K default
  213. m_fExtensions = InitExtensions(&m_pISAPICache,&m_dwCacheSleep);
  214. m_fASP = InitASP(&m_ASPScriptLang,&m_lASPCodePage,&m_ASPlcid);
  215. m_fDirBrowse = reg.ValueDW(RV_DIRBROWSE, HTTPD_ALLOW_DIR_BROWSE);
  216. m_wszDefaultPages = MassageMultiString(reg.ValueSZ(RV_DEFAULTPAGE),HTTPD_DEFAULT_PAGES);
  217. m_wszAdminUsers = MassageMultiString(reg.ValueSZ(RV_ADMINUSERS));
  218. m_wszAdminGroups = MassageMultiString(reg.ValueSZ(RV_ADMINGROUPS));
  219. AuthInitialize(&reg,&m_fBasicAuth, &m_fNTLMAuth);
  220. m_pVroots = new CVRoots();
  221. if (!m_pVroots)
  222. {
  223. return;
  224. }
  225. // vroot table must be initialized or web server can't return files. Warn
  226. // user if this is not the case
  227. if (m_pVroots->Count() == 0)
  228. m_pLog->WriteEvent(IDS_HTTPD_NO_VROOTS);
  229. if (NULL == (m_pszStatusBodyBuf = MyRgAllocNZ(CHAR,BODYSTRINGSIZE)))
  230. return;
  231. if (NULL == (m_pszServerID = MyRgAllocNZ(CHAR, 256)))
  232. return;
  233. if (WSA_INVALID_EVENT == (m_hEventSelect = WSACreateEvent()))
  234. return;
  235. if (INVALID_HANDLE_VALUE == (m_hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL)))
  236. return;
  237. lstrcpyA(m_pszServerID, "Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0");
  238. m_nMaxConnections = reg.ValueDW(RV_MAXCONNECTIONS,10);
  239. m_pThreadPool = new SVSThreadPool(m_nMaxConnections + 1); // +1 for ISAPI Cache removal thread
  240. if (!m_pThreadPool)
  241. {
  242. delete m_pVroots;
  243. m_pVroots = NULL;
  244. MyFree(m_pszStatusBodyBuf);
  245. m_pszStatusBodyBuf = NULL;
  246. return;
  247. }
  248. m_fAcceptConnections = TRUE;
  249. }
  250. CGlobalVariables::~CGlobalVariables()
  251. {
  252. MyFree(m_wszDefaultPages);
  253. MyFree(m_wszAdminUsers);
  254. MyFree(m_wszAdminGroups);
  255. MyFree(m_pszStatusBodyBuf);
  256. MyFree(m_pszServerID);
  257. CleanupFilters();
  258. if (m_pVroots)
  259. delete m_pVroots;
  260. if (m_pISAPICache)
  261. {
  262. RemoveUnusedISAPIs((void*)1); // Tell it to flush all ISAPIs. This will blocks until everyoen's unloaded.
  263. delete m_pISAPICache;
  264. }
  265. if (m_pLog)
  266. delete m_pLog;
  267. if (m_pThreadPool)
  268. delete m_pThreadPool;
  269. if (m_hEventSelect != NULL && m_hEventSelect != WSA_INVALID_EVENT)
  270. {
  271. WSACloseEvent(m_hEventSelect);
  272. }
  273. if (m_hEventShutdown != NULL && m_hEventShutdown != INVALID_HANDLE_VALUE)
  274. {
  275. CloseHandle(m_hEventShutdown);
  276. }
  277. MyFreeLib(m_hNTLMLib);
  278. }
  279. //
  280. // Main HTTP listener thread. Launched from HTTPInitialize
  281. // or called directly by main() in test mode
  282. //
  283. DWORD WINAPI HandleAccept(LPVOID lpv)
  284. {
  285. SOCKET sockConnection = 0;
  286. CHAR szMaxConnectionMsg[256]; // message sent to client if server is too busy
  287. HANDLE rgHandles[2];
  288. DWORD dwRet;
  289. WSANETWORKEVENTS wsaNetEvents = {0};
  290. rgHandles[0] = g_pVars->m_hEventSelect;
  291. rgHandles[1] = g_pVars->m_hEventShutdown;
  292. while (TRUE)
  293. {
  294. dwRet = WaitForMultipleObjects(2, rgHandles, FALSE, INFINITE);
  295. if (dwRet == WAIT_OBJECT_0)
  296. {
  297. wsaNetEvents.lNetworkEvents = 0;
  298. if (WSAEnumNetworkEvents(g_pVars->m_sockListen,
  299. g_pVars->m_hEventSelect,
  300. &wsaNetEvents) == SOCKET_ERROR)
  301. {
  302. TraceError("HandleAccept",
  303. HRESULT_FROM_WIN32(WSAGetLastError()));
  304. break;
  305. }
  306. else if (wsaNetEvents.lNetworkEvents & FD_ACCEPT)
  307. {
  308. TraceTag(ttidWebServer, "HTTPD: Calling ACCEPT....");
  309. sockConnection = WSAAccept(g_pVars->m_sockListen, NULL, NULL,
  310. NULL ,NULL);
  311. if (sockConnection != INVALID_SOCKET)
  312. {
  313. int cb = sizeof(SOCKADDR_IN);
  314. SOCKADDR_IN sockLocal;
  315. getsockname(sockConnection, (SOCKADDR *)&sockLocal, &cb);
  316. TraceTag(ttidWebServer, "HTTPD: Received Connection on address, "
  317. "addr = %s!!", inet_ntoa(sockLocal.sin_addr));
  318. if (g_pVars->m_fAcceptConnections)
  319. {
  320. // We decide whether to handle the request based on the number of connections
  321. // We NEVER put a thread into the thread pool wait list because we want
  322. // the web server to respond immediatly to browser if it's too busy.
  323. EnterCriticalSection(&g_csConnection);
  324. DEBUGCHK(g_pVars->m_nConnections <= g_pVars->m_nMaxConnections);
  325. if (g_pVars->m_nConnections >= g_pVars->m_nMaxConnections)
  326. {
  327. LeaveCriticalSection(&g_csConnection);
  328. TraceTag(ttidWebServer, "HTTPD: Connection Count -- Reached "
  329. "maximum # of connections, won't accept current request.");
  330. send(sockConnection,szMaxConnectionMsg,
  331. lstrlenA(szMaxConnectionMsg),0);
  332. closesocket(sockConnection);
  333. }
  334. else if (!CUPnPInterfaceList::Instance().FShouldSendOnInterface(sockLocal.sin_addr.S_un.S_addr))
  335. {
  336. LeaveCriticalSection(&g_csConnection);
  337. TraceTag(ttidWebServer, "HTTPD: We should not be "
  338. "responding to requests that come in on local "
  339. "address %s!", inet_ntoa(sockLocal.sin_addr));
  340. // ISSUE-2000/12/28-danielwe: What to send in response?
  341. //send(sockConnection,szMaxConnectionMsg, lstrlenA(szMaxConnectionMsg),0);
  342. closesocket(sockConnection);
  343. }
  344. else
  345. {
  346. g_pVars->m_nConnections++;
  347. LeaveCriticalSection(&g_csConnection);
  348. QueueUserWorkItem(HttpConnectionThread,
  349. (LPVOID) sockConnection,
  350. WT_EXECUTELONGFUNCTION);
  351. }
  352. }
  353. }
  354. else if (GetLastError() == WSAEWOULDBLOCK)
  355. {
  356. AssertSz(FALSE, "WSAAccept failed with WSAEWOULDBLOCK!");
  357. WSASetEvent(g_pVars->m_hEventSelect);
  358. }
  359. else
  360. {
  361. AssertSz(FALSE, "WSAAccept failed for some other reason!");
  362. }
  363. }
  364. else
  365. {
  366. AssertSz(FALSE, "Did not get an ACCEPT network event!");
  367. }
  368. }
  369. else if (dwRet == WAIT_OBJECT_0 + 1)
  370. {
  371. // Shutting down
  372. Assert(g_fState == SERVICE_STATE_SHUTTING_DOWN);
  373. break;
  374. }
  375. else
  376. {
  377. TraceError("HandleAccept - WaitForMultipleObjects "
  378. "failed! Thread is exiting", HrFromLastWin32Error());
  379. AssertSz(FALSE, "HandleAccept - Wait failed!");
  380. break;
  381. }
  382. }
  383. return 0;
  384. }
  385. DWORD WINAPI HttpConnectionThread(LPVOID lpv)
  386. {
  387. SOCKET sock = (SOCKET) lpv;
  388. // this socket is non blocking and send and recv fucntion is not implemented to take case of non blocking sockets
  389. // This must be changed.
  390. // this outer _try--_except is to catch crashes in the destructor
  391. __try
  392. {
  393. CHttpRequest* pRequest = new CHttpRequest((SOCKET) sock);
  394. if (pRequest)
  395. {
  396. __try
  397. {
  398. // This loops as long the the socket is being kept alive
  399. for (;;)
  400. {
  401. pRequest->HandleRequest();
  402. // figure out if we must keep this connection alive,
  403. // Either session is over through this request's actions or
  404. // globally set to accept no more connections.
  405. // We do the global g_pVars->m_fAcceptConnections check
  406. // because it's possible that we may be in the process of
  407. // shutting down the web server, in which case we want to
  408. // exit even if we're performing a keep alive.
  409. if (! (g_pVars->m_fAcceptConnections && pRequest->m_fKeepAlive))
  410. {
  411. if (g_pVars->m_fFilters)
  412. pRequest->CallFilter(SF_NOTIFY_END_OF_NET_SESSION);
  413. break;
  414. }
  415. // If we're continuing the session don't delete all data, just request specific data
  416. if ( ! pRequest->ReInit())
  417. break;
  418. }
  419. }
  420. __finally
  421. {
  422. // Note: To get this to compile under Visual Studio, the /Gx- compile line option is set
  423. delete pRequest;
  424. pRequest = 0;
  425. }
  426. }
  427. }
  428. __except(1)
  429. {
  430. RETAILMSG(1, (L"HTTP Server got an exception!!!\r\n"));
  431. g_pVars->m_pLog->WriteEvent(IDS_HTTPD_EXCEPTION,GetExceptionCode(),GetLastError());
  432. }
  433. EnterCriticalSection(&g_csConnection);
  434. g_pVars->m_nConnections--;
  435. LeaveCriticalSection(&g_csConnection);
  436. shutdown(sock, 1);
  437. closesocket(sock);
  438. return 0;
  439. }
  440. //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
  441. // UTILITY FUNCTIONS
  442. //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
  443. PWSTR MassageMultiString(PCWSTR wszIn, PCWSTR wszDefault)
  444. {
  445. if (!wszIn) wszIn = wszDefault;
  446. if (!wszIn) return NULL;
  447. PWSTR wszOut = MyRgAllocNZ(WCHAR, 2+wcslen(wszIn)); // +2 for dbl-null term
  448. if (!wszOut)
  449. return NULL;
  450. for (PWSTR wszNext=wszOut; *wszIn; wszIn++, wszNext++)
  451. {
  452. *wszNext = (*wszIn==L';' ? L'\0' : *wszIn);
  453. // Ignore space between ";" and next non-space
  454. if ( L';' == *wszIn)
  455. {
  456. wszIn++;
  457. SkipWWhiteSpace(wszIn);
  458. wszIn--; // otherwise we skip first char of new string.
  459. }
  460. }
  461. wszNext[0] = wszNext[1] = 0; // dbl-null
  462. return wszOut;
  463. }
  464. void GetRemoteAddress(SOCKET sock, PSTR pszBuf)
  465. {
  466. SOCKADDR_IN sockaddr;
  467. int iLen = sizeof(sockaddr);
  468. PSTR pszTemp;
  469. *pszBuf=0;
  470. if (getpeername(sock, (PSOCKADDR)&sockaddr, &iLen))
  471. {
  472. TraceTag(ttidWebServer, "getpeername failed GLE=%d", GetLastError());
  473. return;
  474. }
  475. if (!(pszTemp = inet_ntoa(sockaddr.sin_addr)))
  476. {
  477. TraceTag(ttidWebServer, "inet_ntoa failed GLE=%d", GetLastError());
  478. return;
  479. }
  480. strcpy(pszBuf, pszTemp);
  481. }
  482. void GetLocalAddress(SOCKET sock, PSTR pszBuf)
  483. {
  484. SOCKADDR_IN sockaddr;
  485. int iLen = sizeof(sockaddr);
  486. PSTR pszTemp;
  487. *pszBuf=0;
  488. if (getsockname(sock, (PSOCKADDR)&sockaddr, &iLen))
  489. {
  490. TraceTag(ttidWebServer, "getsockname failed GLE=%d", GetLastError());
  491. return;
  492. }
  493. if (!(pszTemp = inet_ntoa(sockaddr.sin_addr)))
  494. {
  495. TraceTag(ttidWebServer, "inet_ntoa failed GLE=%d", GetLastError());
  496. return;
  497. }
  498. strcpy(pszBuf, pszTemp);
  499. }
  500. PSTR MySzDupA(PCSTR pszIn, int iLen)
  501. {
  502. if (!pszIn) return NULL;
  503. if (!iLen) iLen = strlen(pszIn);
  504. PSTR pszOut=MySzAllocA(iLen);
  505. if (pszOut)
  506. {
  507. memcpy(pszOut, pszIn, iLen);
  508. pszOut[iLen] = 0;
  509. }
  510. return pszOut;
  511. }
  512. PWSTR MySzDupW(PCWSTR wszIn, int iLen)
  513. {
  514. if (!wszIn) return NULL;
  515. if (!iLen) iLen = wcslen(wszIn);
  516. PWSTR wszOut=MySzAllocW(iLen);
  517. if (wszOut)
  518. {
  519. memcpy(wszOut, wszIn, sizeof(WCHAR)*iLen);
  520. wszOut[iLen] = 0;
  521. }
  522. return wszOut;
  523. }
  524. PWSTR MySzDupAtoW(PCSTR pszIn, int iInLen)
  525. {
  526. PWSTR pwszOut = 0;
  527. int iOutLen = MultiByteToWideChar(CP_ACP, 0, pszIn, iInLen, 0, 0);
  528. if (!iOutLen)
  529. goto error;
  530. pwszOut = MySzAllocW(iOutLen);
  531. if (!pwszOut)
  532. goto error;
  533. if (MultiByteToWideChar(CP_ACP, 0, pszIn, iInLen, pwszOut, iOutLen))
  534. return pwszOut;
  535. error:
  536. TraceTag(ttidWebServer, "MySzDupAtoW(%s, %d) failed. pOut=%0x08x GLE=%d", pszIn, iInLen, pwszOut, GetLastError());
  537. MyFree(pwszOut);
  538. return FALSE;
  539. }
  540. PSTR MySzDupWtoA(PCWSTR wszIn, int iInLen)
  541. {
  542. PSTR pszOut = 0;
  543. int iOutLen = WideCharToMultiByte(CP_ACP, 0, wszIn, iInLen, 0, 0, 0, 0);
  544. if (!iOutLen)
  545. goto error;
  546. pszOut = MySzAllocA(iOutLen);
  547. if (!pszOut)
  548. goto error;
  549. if (WideCharToMultiByte(CP_ACP, 0, wszIn, iInLen, pszOut, iOutLen, 0, 0))
  550. return pszOut;
  551. error:
  552. TraceTag(ttidWebServer, "MySzDupWtoA(%s, %d) failed. pOut=%0x08x GLE=%d", wszIn, iInLen, pszOut, GetLastError());
  553. MyFree(pszOut);
  554. return FALSE;
  555. }
  556. BOOL MyStrCatA(PSTR *ppszDest, PSTR pszSource, PSTR pszDivider)
  557. {
  558. DEBUG_CODE_INIT;
  559. BOOL ret = FALSE;
  560. PSTR pszNew = *ppszDest; // protect orignal ptr should realloc fail
  561. PSTR pszTrav;
  562. DWORD dwSrcLen = MyStrlenA(pszSource);
  563. DWORD dwDestLen = MyStrlenA(*ppszDest);
  564. DWORD dwDivLen = MyStrlenA(pszDivider);
  565. if (!pszNew) // do an alloc first time, ignore divider
  566. {
  567. if (NULL == (pszNew = MySzDupA(pszSource,dwSrcLen)))
  568. myleave(260);
  569. }
  570. else
  571. {
  572. if (NULL == (pszNew = MyRgReAlloc(char,pszNew,dwDestLen,dwSrcLen + dwDestLen + dwDivLen + 1)))
  573. myleave(261);
  574. pszTrav = pszNew + dwDestLen;
  575. if (pszDivider)
  576. {
  577. memcpy(pszTrav, pszDivider, dwDivLen);
  578. pszTrav += dwDivLen;
  579. }
  580. strcpy(pszTrav, pszSource);
  581. }
  582. *ppszDest = pszNew;
  583. ret = TRUE;
  584. done:
  585. TraceTag(ttidWebServer, "MyStrCat err = %d",GetLastError());
  586. return ret;
  587. }
  588. //**************************************************************************
  589. // Component Notes
  590. // This is used by Filters and by Authentication components. The only common
  591. // component they both rest on is core, so we include it here.
  592. //**************************************************************************
  593. //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
  594. // BASE64 ENCODE/DECODE FUNCTIONS from sicily.c
  595. //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
  596. const int base642six[256]={
  597. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  598. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
  599. 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,
  600. 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
  601. 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
  602. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  603. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  604. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  605. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  606. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  607. 64,64,64,64,64,64,64,64,64,64,64,64,64
  608. };
  609. const char six2base64[64] = {
  610. 'A','B','C','D','E','F','G','H','I','J','K','L','M',
  611. 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
  612. 'a','b','c','d','e','f','g','h','i','j','k','l','m',
  613. 'n','o','p','q','r','s','t','u','v','w','x','y','z',
  614. '0','1','2','3','4','5','6','7','8','9','+','/'
  615. };
  616. //-----------------------------------------------------------------------------
  617. // Function: encode()
  618. //-----------------------------------------------------------------------------
  619. BOOL Base64Encode(
  620. BYTE * bufin, // in
  621. DWORD nbytes, // in
  622. char * pbuffEncoded) // out
  623. {
  624. unsigned char *outptr;
  625. unsigned int i;
  626. const char *rgchDict = six2base64;
  627. outptr = (unsigned char *)pbuffEncoded;
  628. for (i=0; i < nbytes; i += 3)
  629. {
  630. *(outptr++) = rgchDict[*bufin >> 2]; /* c1 */
  631. *(outptr++) = rgchDict[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
  632. *(outptr++) = rgchDict[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
  633. *(outptr++) = rgchDict[bufin[2] & 077]; /* c4 */
  634. bufin += 3;
  635. }
  636. /* If nbytes was not a multiple of 3, then we have encoded too
  637. * many characters. Adjust appropriately.
  638. */
  639. if (i == nbytes+1)
  640. {
  641. /* There were only 2 bytes in that last group */
  642. outptr[-1] = '=';
  643. }
  644. else if (i == nbytes+2)
  645. {
  646. /* There was only 1 byte in that last group */
  647. outptr[-1] = '=';
  648. outptr[-2] = '=';
  649. }
  650. *outptr = '\0';
  651. return TRUE;
  652. }
  653. //-----------------------------------------------------------------------------
  654. // Function: decode()
  655. //-----------------------------------------------------------------------------
  656. BOOL Base64Decode(
  657. char * bufcoded, // in
  658. char * pbuffdecoded, // out
  659. DWORD * pcbDecoded) // in out
  660. {
  661. INT_PTR nbytesdecoded;
  662. char *bufin;
  663. unsigned char *bufout;
  664. INT_PTR nprbytes;
  665. const int *rgiDict = base642six;
  666. /* Strip leading whitespace. */
  667. while (*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
  668. /* Figure out how many characters are in the input buffer.
  669. * If this would decode into more bytes than would fit into
  670. * the output buffer, adjust the number of input bytes downwards.
  671. */
  672. bufin = bufcoded;
  673. while (rgiDict[*(bufin++)] <= 63);
  674. nprbytes = bufin - bufcoded - 1;
  675. nbytesdecoded = ((nprbytes+3)/4) * 3;
  676. if ( pcbDecoded )
  677. *pcbDecoded = (DWORD)nbytesdecoded;
  678. bufout = (unsigned char *)pbuffdecoded;
  679. bufin = bufcoded;
  680. while (nprbytes > 0)
  681. {
  682. *(bufout++) =
  683. (unsigned char) (rgiDict[*bufin] << 2 | rgiDict[bufin[1]] >> 4);
  684. *(bufout++) =
  685. (unsigned char) (rgiDict[bufin[1]] << 4 | rgiDict[bufin[2]] >> 2);
  686. *(bufout++) =
  687. (unsigned char) (rgiDict[bufin[2]] << 6 | rgiDict[bufin[3]]);
  688. bufin += 4;
  689. nprbytes -= 4;
  690. }
  691. if (nprbytes & 03)
  692. {
  693. if (rgiDict[bufin[-2]] > 63)
  694. nbytesdecoded -= 2;
  695. else
  696. nbytesdecoded -= 1;
  697. }
  698. ((CHAR *)pbuffdecoded)[nbytesdecoded] = '\0';
  699. return TRUE;
  700. }
  701. // Gets version info from the pszVersion string
  702. // HTTP version string come in the following form: HTTP/Major.Minor,
  703. // where Major and Minor are integers.
  704. BOOL SetHTTPVersion(PSTR pszVersion, DWORD *pdwVersion)
  705. {
  706. int iMajor, iMinor;
  707. DWORD cbVer = strlen(cszHTTPVER);
  708. PSTR pszTemp;
  709. if (_memicmp(pszVersion,cszHTTPVER, min(strlen(pszVersion), cbVer)))
  710. {
  711. return FALSE;
  712. }
  713. iMajor = atoi(pszVersion+cbVer);
  714. if (NULL == (pszTemp = strchr(pszVersion+cbVer,'.')))
  715. return FALSE;
  716. iMinor = atoi(pszTemp+1);
  717. *pdwVersion = MAKELONG(iMinor,iMajor);
  718. return TRUE;
  719. }
  720. // Writes http version info in dwVersion into pszVersion
  721. // Substitutes for: sprintf(szBuf, "HTTP/%d.%d", HIWORD(m_dwVersion), LOWORD(m_dwVersion));
  722. void WriteHTTPVersion(PSTR pszVersion, DWORD dwVersion)
  723. {
  724. PSTR pszTrav = strcpyEx(pszVersion,cszHTTPVER);
  725. _itoa(HIWORD(dwVersion),pszTrav, 10);
  726. pszTrav = strchr(pszTrav,'\0');
  727. *pszTrav++ = '.';
  728. _itoa(LOWORD(dwVersion),pszTrav, 10);
  729. }
  730. // Replaces
  731. // i = sscanf(pszTok, cszDateParseFmt, &st.wDay, &szMonth, &st.wYear, &st.wHour, &st.wMinute, &st.wSecond, &m_dwIfModifiedLength);
  732. // const char cszDateParseFmt[] = " %*3s, %02hd %3s %04hd %02hd:%02hd:%02hd GMT; length=%d";
  733. // This macro advance psz n characters ahead. However, if there is a \0 in between
  734. // psz[0] and psz[n-1], it will return FALSE.
  735. // We do this \0 check rathern than psz += n because even
  736. // though we're throwing out the input, we want to make sure we don't walk past the
  737. // end of a buffer.
  738. #define SKIP_INPUT(psz, n) { int j; for (j = 0; j <= (n); j++) { if (! (psz)[j]) return FALSE; } (psz) += (n); }
  739. BOOL SetHTTPDate(PSTR pszDate, PSTR pszMonth, SYSTEMTIME *pst, PDWORD pdwModifiedLength)
  740. {
  741. PSTR pszTrav = pszDate;
  742. int i;
  743. // Skip past day of week (Sun/Mon/...), comma, space. We don't
  744. // do this with pszTrav += 5 because we could have an ilformatted string
  745. // that is too short,
  746. SKIP_INPUT(pszTrav,5);
  747. pst->wDay = (WORD)atoi(pszTrav);
  748. SKIP_INPUT(pszTrav,3);
  749. // Copy month data into pszMonth. Again, don't do memcpy.
  750. for (i = 0; i < 3; i++)
  751. {
  752. if (pszTrav[i] == '\0')
  753. return FALSE;
  754. pszMonth[i] = pszTrav[i];
  755. }
  756. pszMonth[i] = 0;
  757. pszTrav += 4;
  758. if (! (*pszTrav))
  759. return FALSE;
  760. pst->wYear = (WORD)atoi(pszTrav);
  761. SKIP_INPUT(pszTrav,5);
  762. pst->wHour = (WORD)atoi(pszTrav);
  763. pszTrav += 2;
  764. if (':' != *pszTrav)
  765. return FALSE;
  766. pszTrav++;
  767. pst->wMinute = (WORD)atoi(pszTrav);
  768. pszTrav += 2;
  769. if (':' != *pszTrav)
  770. return FALSE;
  771. pszTrav++;
  772. pst->wSecond = (WORD)atoi(pszTrav);
  773. pszTrav += 2;
  774. // NT Port: On NT, two files could have the same date, but using GetFileTime will
  775. // put a non-zero # of milliseconds in the file time value, so the file time
  776. // comparison will think the file is older than the one we're comparing against
  777. // by a few milliseconds.
  778. pst->wMilliseconds = 999;
  779. SKIP_INPUT(pszTrav,sizeof(" GMT; length=") -1);
  780. *pdwModifiedLength = atoi(pszTrav);
  781. return TRUE;
  782. }
  783. // Does the following
  784. // sprintf(szBuffer + i, cszDateOutputFmt,rgWkday[st.wDayOfWeek], st.wDay, rgMonth[st.wMonth], st.wYear, st.wHour, st.wMinute, st.wSecond);
  785. PSTR WriteHTTPDate(PSTR pszDateBuf, SYSTEMTIME *pst, BOOL fAddGMT)
  786. {
  787. PSTR pszTrav;
  788. // Day of week
  789. pszTrav = strcpyEx(pszDateBuf,rgWkday[pst->wDayOfWeek]);
  790. *pszTrav++ = ',';
  791. *pszTrav++ = ' ';
  792. // Day number. Write 0 out if there isn't one already.
  793. if (pst->wDay < 10)
  794. {
  795. _itoa(0,pszTrav,10);
  796. _itoa(pst->wDay,pszTrav+1,10);
  797. }
  798. else
  799. {
  800. _itoa(pst->wDay,pszTrav,10);
  801. }
  802. pszTrav += 2;
  803. *pszTrav++ = ' ';
  804. // Month
  805. pszTrav = strcpyEx(pszTrav,rgMonth[pst->wMonth]);
  806. *pszTrav++ = ' ';
  807. // Year
  808. _itoa(pst->wYear,pszTrav,10);
  809. pszTrav += 4;
  810. *pszTrav++ = ' ';
  811. // Hour
  812. if (pst->wHour < 10)
  813. {
  814. _itoa(0,pszTrav,10);
  815. _itoa(pst->wHour,pszTrav+1,10);
  816. }
  817. else
  818. {
  819. _itoa(pst->wHour,pszTrav,10);
  820. }
  821. pszTrav += 2;
  822. *pszTrav++ = ':';
  823. // Minute
  824. if (pst->wMinute < 10)
  825. {
  826. _itoa(0,pszTrav,10);
  827. _itoa(pst->wMinute,pszTrav+1,10);
  828. }
  829. else
  830. {
  831. _itoa(pst->wMinute,pszTrav,10);
  832. }
  833. pszTrav += 2;
  834. *pszTrav++ = ':';
  835. // Second
  836. if (pst->wSecond < 10)
  837. {
  838. _itoa(0,pszTrav,10);
  839. _itoa(pst->wSecond,pszTrav+1,10);
  840. }
  841. else
  842. {
  843. _itoa(pst->wSecond,pszTrav,10);
  844. }
  845. pszTrav += 2;
  846. if (fAddGMT)
  847. pszTrav = strcpyEx(pszTrav," GMT\r\n");
  848. else
  849. {
  850. *pszTrav++ = ' ';
  851. *pszTrav = '\0';
  852. }
  853. return pszTrav;
  854. }
  855. /*===================================================================
  856. strcpyEx
  857. Copy one string to another, returning a pointer to the NUL character
  858. in the destination
  859. Parameters:
  860. szDest - pointer to the destination string
  861. szSrc - pointer to the source string
  862. Returns:
  863. A pointer to the NUL terminator is returned.
  864. ===================================================================*/
  865. char *strcpyEx(char *szDest, const char *szSrc)
  866. {
  867. while (*szDest++ = *szSrc++)
  868. ;
  869. return szDest - 1;
  870. }
  871. #if defined(OLD_CE_BUILD)
  872. #if (_WIN32_WCE < 210)
  873. VOID GetCurrentFT(LPFILETIME lpFileTime) {
  874. SYSTEMTIME st;
  875. GetSystemTime(&st);
  876. SystemTimeToFileTime(&st,lpFileTime);
  877. LocalFileTimeToFileTime(lpFileTime,lpFileTime);
  878. }
  879. #endif
  880. #endif