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.

3611 lines
84 KiB

  1. // This is a part of the Active Template Library.
  2. // Copyright (C) 1996-2001 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Active Template Library Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Active Template Library product.
  10. #ifndef __ATLUTIL_H__
  11. #define __ATLUTIL_H__
  12. #pragma once
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <crtdbg.h>
  16. #include <stdlib.h>
  17. #include <mbstring.h>
  18. #include <atldef.h>
  19. #include <imagehlp.h>
  20. #include <atlbase.h>
  21. #include <atlstr.h>
  22. #include <atlcoll.h>
  23. #include <atlsiface.h>
  24. #include <atlenc.h>
  25. #include <atlcom.h>
  26. #ifndef _ATL_NO_DEFAULT_LIBS
  27. #pragma comment(lib, "imagehlp.lib")
  28. #endif // !_ATL_NO_DEFAULT_LIBS
  29. #pragma warning( push )
  30. #pragma warning( disable: 4127 )
  31. namespace ATL {
  32. inline BOOL IsFullPath(LPCTSTR szPath)
  33. {
  34. size_t nLen = _tcslen(szPath);
  35. if (nLen <= 1)
  36. return FALSE;
  37. if (*szPath == _T('"'))
  38. {
  39. szPath++;
  40. }
  41. if (szPath[1]==_T(':')) // drive: case
  42. return TRUE;
  43. if (nLen > 2 && szPath[0]==_T('\\') &&
  44. szPath[1]==_T('\\')) // unc path name
  45. return TRUE;
  46. return FALSE;
  47. }
  48. inline BOOL IsFullPathA(LPCSTR szPath)
  49. {
  50. DWORD nLen = (DWORD) strlen(szPath);
  51. if (nLen <= 1)
  52. return FALSE;
  53. if (*szPath == '"')
  54. {
  55. szPath++;
  56. }
  57. if (szPath[1]==':') // drive: case
  58. return TRUE;
  59. if (nLen > 2 && szPath[0]=='\\' &&
  60. szPath[1]=='\\') // unc path name
  61. return TRUE;
  62. return FALSE;
  63. }
  64. #if(_WIN32_WINNT >= 0x0400)
  65. // Helper class for reverting the thread impersonation token
  66. // and then restoring it back to what it was
  67. class CRevertThreadToken
  68. {
  69. public:
  70. HANDLE m_hThreadToken;
  71. CRevertThreadToken()
  72. {
  73. m_hThreadToken = INVALID_HANDLE_VALUE;
  74. }
  75. ~CRevertThreadToken()
  76. {
  77. Restore();
  78. }
  79. // When called, this function
  80. // makes a copy of the thread's impersonation token
  81. // and then calls RevertToSelf() to revert the impersonation
  82. // level to the process
  83. // call Restore() to restore the impersonation
  84. // token
  85. // Restore is automatically called by the destructor
  86. BOOL Initialize()
  87. {
  88. if (OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE, FALSE, &m_hThreadToken))
  89. {
  90. if (!RevertToSelf())
  91. {
  92. CloseHandle(m_hThreadToken);
  93. m_hThreadToken = INVALID_HANDLE_VALUE;
  94. return FALSE;
  95. }
  96. return TRUE;
  97. }
  98. return FALSE;
  99. }
  100. void Restore()
  101. {
  102. if (m_hThreadToken != INVALID_HANDLE_VALUE)
  103. {
  104. SetThreadToken(NULL, m_hThreadToken);
  105. CloseHandle(m_hThreadToken);
  106. m_hThreadToken = INVALID_HANDLE_VALUE;
  107. }
  108. }
  109. };
  110. #else
  111. // Dummy version for downlevel support
  112. class CRevertThreadToken
  113. {
  114. public:
  115. BOOL Initialize()
  116. {
  117. return FALSE;
  118. }
  119. void Restore()
  120. {
  121. }
  122. };
  123. #endif // _WIN32_WINNT >= 0x0400)
  124. #ifndef ATL_ISAPI_BUFFER_SIZE
  125. #define ATL_ISAPI_BUFFER_SIZE 4096
  126. #endif
  127. //typedefs and defines for CUrl (essentially the same as the ones from wininet, but with an ATL_ prepended)
  128. typedef WORD ATL_URL_PORT;
  129. typedef enum {
  130. ATL_URL_SCHEME_UNKNOWN = -1,
  131. ATL_URL_SCHEME_FTP = 0,
  132. ATL_URL_SCHEME_GOPHER = 1,
  133. ATL_URL_SCHEME_HTTP = 2,
  134. ATL_URL_SCHEME_HTTPS = 3,
  135. ATL_URL_SCHEME_FILE = 4,
  136. ATL_URL_SCHEME_NEWS = 5,
  137. ATL_URL_SCHEME_MAILTO = 6,
  138. ATL_URL_SCHEME_SOCKS = 7,
  139. } ATL_URL_SCHEME;
  140. #define ATL_URL_MAX_HOST_NAME_LENGTH 256
  141. #define ATL_URL_MAX_USER_NAME_LENGTH 128
  142. #define ATL_URL_MAX_PASSWORD_LENGTH 128
  143. #define ATL_URL_MAX_PORT_NUMBER_LENGTH 5 // ATL_URL_PORT is unsigned short
  144. #define ATL_URL_MAX_PORT_NUMBER_VALUE 65535 // maximum unsigned short value
  145. #define ATL_URL_MAX_PATH_LENGTH 2048
  146. #define ATL_URL_MAX_SCHEME_LENGTH 32 // longest protocol name length
  147. #define ATL_URL_MAX_URL_LENGTH (ATL_URL_MAX_SCHEME_LENGTH \
  148. + sizeof("://") \
  149. + ATL_URL_MAX_PATH_LENGTH)
  150. #define ATL_URL_INVALID_PORT_NUMBER 0 // use the protocol-specific default
  151. #define ATL_URL_DEFAULT_FTP_PORT 21 // default for FTP servers
  152. #define ATL_URL_DEFAULT_GOPHER_PORT 70 // " " gopher "
  153. #define ATL_URL_DEFAULT_HTTP_PORT 80 // " " HTTP "
  154. #define ATL_URL_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
  155. #define ATL_URL_DEFAULT_SOCKS_PORT 1080 // default for SOCKS firewall servers.
  156. template <DWORD dwSizeT=ATL_ISAPI_BUFFER_SIZE>
  157. class CAtlIsapiBuffer
  158. {
  159. protected:
  160. char m_szBuffer[dwSizeT];
  161. LPSTR m_pBuffer;
  162. DWORD m_dwLen;
  163. DWORD m_dwAlloc;
  164. HANDLE m_hProcHeap;
  165. public:
  166. CAtlIsapiBuffer() throw()
  167. {
  168. if (dwSizeT > 0)
  169. m_szBuffer[0] = 0;
  170. m_pBuffer = m_szBuffer;
  171. m_dwLen = 0;
  172. m_dwAlloc = dwSizeT;
  173. m_hProcHeap = GetProcessHeap();
  174. }
  175. CAtlIsapiBuffer(LPCSTR sz)
  176. {
  177. m_pBuffer = m_szBuffer;
  178. m_dwLen = 0;
  179. m_dwAlloc = dwSizeT;
  180. m_hProcHeap = GetProcessHeap();
  181. if (!Append(sz))
  182. AtlThrow(E_OUTOFMEMORY);
  183. }
  184. ~CAtlIsapiBuffer() throw()
  185. {
  186. Free();
  187. }
  188. BOOL Alloc(DWORD dwSize) throw()
  189. {
  190. if (m_dwAlloc >= dwSize)
  191. {
  192. return TRUE;
  193. }
  194. if (m_pBuffer != m_szBuffer)
  195. {
  196. HeapFree(m_hProcHeap, 0, m_pBuffer);
  197. m_dwLen = 0;
  198. m_dwAlloc = 0;
  199. }
  200. m_pBuffer = (LPSTR)HeapAlloc(m_hProcHeap, 0, dwSize);
  201. if (m_pBuffer)
  202. {
  203. m_dwAlloc = dwSize;
  204. return TRUE;
  205. }
  206. return FALSE;
  207. }
  208. BOOL ReAlloc(DWORD dwNewSize) throw()
  209. {
  210. if (dwNewSize <= m_dwAlloc)
  211. return TRUE;
  212. if (m_pBuffer == m_szBuffer)
  213. {
  214. BOOL bRet = Alloc(dwNewSize);
  215. if (bRet)
  216. memcpy(m_pBuffer, m_szBuffer, m_dwLen);
  217. return bRet;
  218. }
  219. LPSTR pvNew = (LPSTR )HeapReAlloc(m_hProcHeap, 0, m_pBuffer, dwNewSize);
  220. if (pvNew)
  221. {
  222. m_pBuffer = pvNew;
  223. m_dwAlloc = dwNewSize;
  224. return TRUE;
  225. }
  226. return FALSE;
  227. }
  228. void Free() throw()
  229. {
  230. if (m_pBuffer != m_szBuffer)
  231. {
  232. HeapFree(m_hProcHeap,0 , m_pBuffer);
  233. m_dwAlloc = dwSizeT;
  234. m_pBuffer = m_szBuffer;
  235. }
  236. Empty();
  237. }
  238. void Empty() throw()
  239. {
  240. if (m_pBuffer)
  241. {
  242. m_pBuffer[0]=0;
  243. m_dwLen = 0;
  244. }
  245. }
  246. DWORD GetLength() throw()
  247. {
  248. return m_dwLen;
  249. }
  250. BOOL Append(LPCSTR sz, int nLen = -1) throw()
  251. {
  252. if (nLen == -1)
  253. nLen = (int) strlen(sz);
  254. if (m_dwLen + nLen + 1 > m_dwAlloc)
  255. {
  256. if (!ReAlloc(m_dwAlloc + (nLen+1 > ATL_ISAPI_BUFFER_SIZE ? nLen+1 : ATL_ISAPI_BUFFER_SIZE)))
  257. return FALSE;
  258. }
  259. memcpy(m_pBuffer + m_dwLen, sz, nLen);
  260. m_dwLen += nLen;
  261. m_pBuffer[m_dwLen]=0;
  262. return TRUE;
  263. }
  264. operator LPCSTR() throw()
  265. {
  266. return m_pBuffer;
  267. }
  268. CAtlIsapiBuffer& operator+=(LPCSTR sz)
  269. {
  270. if (!Append(sz))
  271. AtlThrow(E_OUTOFMEMORY);
  272. return *this;
  273. }
  274. }; // class CAtlIsapiBuffer
  275. __interface IStackDumpHandler
  276. {
  277. public:
  278. void __stdcall OnBegin();
  279. void __stdcall OnEntry(void *pvAddress, LPCSTR szModule, LPCSTR szSymbol);
  280. void __stdcall OnError(LPCSTR szError);
  281. void __stdcall OnEnd();
  282. };
  283. #define ATL_MODULE_NAME_LEN _MAX_PATH
  284. #define ATL_SYMBOL_NAME_LEN 1024
  285. // Helper class for generating a stack dump
  286. // This is used internally by AtlDumpStack
  287. class CStackDumper
  288. {
  289. public:
  290. struct _ATL_SYMBOL_INFO
  291. {
  292. ULONG_PTR dwAddress;
  293. ULONG_PTR dwOffset;
  294. CHAR szModule[ATL_MODULE_NAME_LEN];
  295. CHAR szSymbol[ATL_SYMBOL_NAME_LEN];
  296. };
  297. static LPVOID __stdcall FunctionTableAccess(HANDLE hProcess, ULONG_PTR dwPCAddress)
  298. {
  299. #ifdef _WIN64
  300. return SymFunctionTableAccess(hProcess, dwPCAddress);
  301. #else
  302. return SymFunctionTableAccess(hProcess, (ULONG)dwPCAddress);
  303. #endif
  304. }
  305. static ULONG_PTR __stdcall GetModuleBase(HANDLE hProcess, ULONG_PTR dwReturnAddress)
  306. {
  307. IMAGEHLP_MODULE moduleInfo;
  308. #ifdef _WIN64
  309. if (SymGetModuleInfo(hProcess, dwReturnAddress, &moduleInfo))
  310. #else
  311. if (SymGetModuleInfo(hProcess, (ULONG)dwReturnAddress, &moduleInfo))
  312. #endif
  313. return moduleInfo.BaseOfImage;
  314. else
  315. {
  316. MEMORY_BASIC_INFORMATION memoryBasicInfo;
  317. if (::VirtualQueryEx(hProcess, (LPVOID) dwReturnAddress,
  318. &memoryBasicInfo, sizeof(memoryBasicInfo)))
  319. {
  320. DWORD cch = 0;
  321. char szFile[MAX_PATH] = { 0 };
  322. cch = GetModuleFileNameA((HINSTANCE)memoryBasicInfo.AllocationBase,
  323. szFile, MAX_PATH);
  324. // Ignore the return code since we can't do anything with it.
  325. SymLoadModule(hProcess,
  326. NULL, ((cch) ? szFile : NULL),
  327. #ifdef _WIN64
  328. NULL, (DWORD_PTR) memoryBasicInfo.AllocationBase, 0);
  329. #else
  330. NULL, (DWORD)(DWORD_PTR)memoryBasicInfo.AllocationBase, 0);
  331. #endif
  332. return (DWORD_PTR) memoryBasicInfo.AllocationBase;
  333. }
  334. }
  335. return 0;
  336. }
  337. static BOOL ResolveSymbol(HANDLE hProcess, UINT_PTR dwAddress,
  338. _ATL_SYMBOL_INFO &siSymbol)
  339. {
  340. BOOL fRetval = TRUE;
  341. siSymbol.dwAddress = dwAddress;
  342. CHAR szUndec[ATL_SYMBOL_NAME_LEN];
  343. CHAR szWithOffset[ATL_SYMBOL_NAME_LEN];
  344. LPSTR pszSymbol = NULL;
  345. IMAGEHLP_MODULE mi;
  346. memset(&siSymbol, 0, sizeof(_ATL_SYMBOL_INFO));
  347. mi.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
  348. #ifdef _WIN64
  349. if (!SymGetModuleInfo(hProcess, dwAddress, &mi))
  350. #else
  351. if (!SymGetModuleInfo(hProcess, (UINT)dwAddress, &mi))
  352. #endif
  353. lstrcpyA(siSymbol.szModule, "<no module>");
  354. else
  355. {
  356. LPSTR pszModule = strchr(mi.ImageName, '\\');
  357. if (pszModule == NULL)
  358. pszModule = mi.ImageName;
  359. else
  360. pszModule++;
  361. lstrcpynA(siSymbol.szModule, pszModule, sizeof(siSymbol.szModule)/sizeof(siSymbol.szModule[0]));
  362. }
  363. __try
  364. {
  365. union
  366. {
  367. CHAR rgchSymbol[sizeof(IMAGEHLP_SYMBOL) + ATL_SYMBOL_NAME_LEN];
  368. IMAGEHLP_SYMBOL sym;
  369. } sym;
  370. memset(&sym.sym, 0x00, sizeof(sym.sym));
  371. sym.sym.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
  372. #ifdef _WIN64
  373. sym.sym.Address = dwAddress;
  374. #else
  375. sym.sym.Address = (DWORD)dwAddress;
  376. #endif
  377. sym.sym.MaxNameLength = ATL_SYMBOL_NAME_LEN;
  378. #ifdef _WIN64
  379. if (SymGetSymFromAddr(hProcess, dwAddress, &(siSymbol.dwOffset), &sym.sym))
  380. #else
  381. if (SymGetSymFromAddr(hProcess, (DWORD)dwAddress, &(siSymbol.dwOffset), &sym.sym))
  382. #endif
  383. {
  384. pszSymbol = sym.sym.Name;
  385. if (UnDecorateSymbolName(sym.sym.Name, szUndec, sizeof(szUndec)/sizeof(szUndec[0]),
  386. UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS))
  387. {
  388. pszSymbol = szUndec;
  389. }
  390. else if (SymUnDName(&sym.sym, szUndec, sizeof(szUndec)/sizeof(szUndec[0])))
  391. {
  392. pszSymbol = szUndec;
  393. }
  394. if (siSymbol.dwOffset != 0)
  395. {
  396. wsprintfA(szWithOffset, "%s + %d bytes", pszSymbol, siSymbol.dwOffset);
  397. pszSymbol = szWithOffset;
  398. }
  399. }
  400. else
  401. pszSymbol = "<no symbol>";
  402. }
  403. __except (EXCEPTION_EXECUTE_HANDLER)
  404. {
  405. pszSymbol = "<EX: no symbol>";
  406. siSymbol.dwOffset = dwAddress - mi.BaseOfImage;
  407. }
  408. lstrcpynA(siSymbol.szSymbol, pszSymbol, sizeof(siSymbol.szSymbol)/sizeof(siSymbol.szSymbol[0]));
  409. return fRetval;
  410. }
  411. };
  412. // Helper function to produce a stack dump
  413. ATL_NOINLINE inline void AtlDumpStack(IStackDumpHandler *pHandler)
  414. {
  415. ATLASSERT(pHandler);
  416. pHandler->OnBegin();
  417. CAtlArray<void *> adwAddress;
  418. HANDLE hProcess = ::GetCurrentProcess();
  419. if (SymInitialize(hProcess, NULL, FALSE))
  420. {
  421. // force undecorated names to get params
  422. DWORD dw = SymGetOptions();
  423. dw &= ~SYMOPT_UNDNAME;
  424. SymSetOptions(dw);
  425. HANDLE hThread = ::GetCurrentThread();
  426. CONTEXT threadContext;
  427. threadContext.ContextFlags = CONTEXT_FULL;
  428. if (::GetThreadContext(hThread, &threadContext))
  429. {
  430. //DumpContext(&threadContext);
  431. STACKFRAME stackFrame;
  432. memset(&stackFrame, 0, sizeof(stackFrame));
  433. stackFrame.AddrPC.Mode = AddrModeFlat;
  434. DWORD dwMachType;
  435. #if defined(_M_IX86)
  436. dwMachType = IMAGE_FILE_MACHINE_I386;
  437. // program counter, stack pointer, and frame pointer
  438. stackFrame.AddrPC.Offset = threadContext.Eip;
  439. stackFrame.AddrStack.Offset = threadContext.Esp;
  440. stackFrame.AddrStack.Mode = AddrModeFlat;
  441. stackFrame.AddrFrame.Offset = threadContext.Ebp;
  442. stackFrame.AddrFrame.Mode = AddrModeFlat;
  443. #elif defined(_M_AMD64)
  444. // only program counter
  445. dwMachType = IMAGE_FILE_MACHINE_AMD64;
  446. stackFrame.AddrPC.Offset = threadContext.Rip;
  447. #elif defined(_M_IA64)
  448. //IA64: What do we need to do here?
  449. dwMachType = IMAGE_FILE_MACHINE_IA64;
  450. #if 0
  451. stackFrame.AddrPC.Offset = threadContext.StIIP;
  452. stackFrame.AddrPC.Mode = AddrModeFlat;
  453. stackFrame.AddrStack.Offset = threadContext.IntSp;
  454. stackFrame.AddrStack.Mode = AddrModeFlat;
  455. stackFrame.AddrFrame.Offset = threadContext.IntSp;
  456. stackFrame.AddrFrame.Mode = AddrModeFlat;
  457. stackFrame.AddrBStore.Offset = threadContext.RsBSP;
  458. stackFrame.AddrBStore.Mode = AddrModeFlat;
  459. stackFrame.AddrReturn.Offset = threadContext.BrRp;
  460. stackFrame.AddrReturn.Mode = AddrModeFlat;
  461. #endif
  462. #else
  463. #error("Unknown Target Machine");
  464. #endif
  465. adwAddress.SetCount(0, 16);
  466. int nFrame;
  467. for (nFrame = 0; nFrame < 5; nFrame++)
  468. {
  469. if (!StackWalk(dwMachType, hProcess, hProcess,
  470. &stackFrame, &threadContext, NULL,
  471. CStackDumper::FunctionTableAccess, CStackDumper::GetModuleBase, NULL))
  472. {
  473. break;
  474. }
  475. adwAddress.SetAtGrow(nFrame, (void*)(DWORD_PTR)stackFrame.AddrPC.Offset);
  476. }
  477. }
  478. }
  479. else
  480. {
  481. DWORD dw = GetLastError();
  482. char sz[100];
  483. wsprintfA(sz,
  484. "AtlDumpStack Error: IMAGEHLP.DLL wasn't found. "
  485. "GetLastError() returned 0x%8.8X\r\n", dw);
  486. pHandler->OnError(sz);
  487. }
  488. // dump it out now
  489. INT_PTR nAddress;
  490. INT_PTR cAddresses = adwAddress.GetCount();
  491. for (nAddress = 0; nAddress < cAddresses; nAddress++)
  492. {
  493. CStackDumper::_ATL_SYMBOL_INFO info;
  494. UINT_PTR dwAddress = (UINT_PTR)adwAddress[nAddress];
  495. LPCSTR szModule = NULL;
  496. LPCSTR szSymbol = NULL;
  497. if (CStackDumper::ResolveSymbol(hProcess, dwAddress, info))
  498. {
  499. szModule = info.szModule;
  500. szSymbol = info.szSymbol;
  501. }
  502. pHandler->OnEntry((void *) dwAddress, szModule, szSymbol);
  503. }
  504. pHandler->OnEnd();
  505. }
  506. #define STACK_TRACE_PART_DELIMITER ';'
  507. #define STACK_TRACE_LINE_DELIMITER '~'
  508. // CReportHookDumpHandler is a stack dump handler
  509. // that gathers the stack dump into the format
  510. // used by CDebugReportHook
  511. class CReportHookDumpHandler : public IStackDumpHandler
  512. {
  513. public:
  514. CReportHookDumpHandler()
  515. {
  516. m_pstr = NULL;
  517. }
  518. void GetStackDump(CStringA *pstr)
  519. {
  520. ATLASSERT(pstr);
  521. SetString(pstr);
  522. AtlDumpStack(this);
  523. SetString(NULL);
  524. }
  525. void SetString(CStringA *pstr)
  526. {
  527. m_pstr = pstr;
  528. }
  529. // implementation
  530. // IStackDumpHandler methods
  531. void __stdcall OnBegin()
  532. {
  533. }
  534. void __stdcall OnEntry(void *pvAddress, LPCSTR szModule, LPCSTR szSymbol)
  535. {
  536. // make sure SetString was called before
  537. // trying to get a stack dump
  538. ATLASSERT(m_pstr);
  539. if (!m_pstr)
  540. return;
  541. char szBuf[100];
  542. sprintf(szBuf, "0x%p;", pvAddress);
  543. *m_pstr += szBuf;
  544. if (!szModule)
  545. szModule = "Unknown";
  546. if (!szSymbol)
  547. szSymbol = "<No Info>";
  548. *m_pstr += szModule;
  549. *m_pstr += STACK_TRACE_PART_DELIMITER;
  550. ATLASSERT(szSymbol);
  551. *m_pstr += szSymbol;
  552. *m_pstr += STACK_TRACE_PART_DELIMITER;
  553. *m_pstr += STACK_TRACE_LINE_DELIMITER;
  554. }
  555. void __stdcall OnError(LPCSTR /*szError*/)
  556. {
  557. }
  558. void __stdcall OnEnd()
  559. {
  560. }
  561. protected:
  562. CStringA *m_pstr;
  563. };
  564. #define PIPE_INPUT_BUFFER_SIZE 4096
  565. #define PIPE_OUTPUT_BUFFER_SIZE 2048
  566. enum { DEBUG_SERVER_MESSAGE_TRACE, DEBUG_SERVER_MESSAGE_ASSERT, DEBUG_SERVER_MESSAGE_QUIT };
  567. struct DEBUG_SERVER_MESSAGE
  568. {
  569. DWORD dwType; // one of DEBUG_SERVER_MESSAGE_*
  570. DWORD dwProcessId; // process id of client
  571. DWORD dwClientNameLen; // length of client name
  572. size_t dwTextLen; // length of text message including null terminator
  573. BOOL bIsDebuggerAttached; // TRUE if the debugger is already attached
  574. };
  575. #ifdef _DEBUG
  576. extern "C" WINBASEAPI
  577. BOOL
  578. WINAPI
  579. IsDebuggerPresent(
  580. VOID
  581. );
  582. class CDebugReportHook
  583. {
  584. protected:
  585. _CRT_REPORT_HOOK m_pfnOldHook;
  586. static char m_szPipeName[MAX_PATH+1];
  587. static DWORD m_dwTimeout;
  588. static DWORD m_dwClientNameLen;
  589. static char m_szClientName[MAX_COMPUTERNAME_LENGTH+1];
  590. public:
  591. CDebugReportHook(LPCSTR szMachineName = ".", LPCSTR szPipeName = "AtlsDbgPipe", DWORD dwTimeout = 20000) throw()
  592. {
  593. if (SetPipeName(szMachineName, szPipeName))
  594. {
  595. SetTimeout(dwTimeout);
  596. SetHook();
  597. }
  598. m_dwClientNameLen = sizeof(m_szClientName);
  599. GetComputerNameA(m_szClientName, &m_dwClientNameLen);
  600. }
  601. ~CDebugReportHook() throw()
  602. {
  603. RemoveHook();
  604. }
  605. BOOL SetPipeName(LPCSTR szMachineName = ".", LPCSTR szPipeName = "AtlsDbgPipe") throw()
  606. {
  607. size_t nLen1 = strlen(szMachineName);
  608. size_t nLen2 = strlen(szPipeName);
  609. if (nLen1 + nLen2 + 8 <= MAX_PATH)
  610. {
  611. _snprintf(m_szPipeName, MAX_PATH, "\\\\%s\\pipe\\%s", szMachineName, szPipeName);
  612. return TRUE;
  613. }
  614. return FALSE;
  615. }
  616. void SetTimeout(DWORD dwTimeout)
  617. {
  618. m_dwTimeout = dwTimeout;
  619. }
  620. void SetHook() throw()
  621. {
  622. m_pfnOldHook = _CrtSetReportHook(CDebugReportHookProc);
  623. }
  624. void RemoveHook() throw()
  625. {
  626. _CrtSetReportHook(m_pfnOldHook);
  627. }
  628. static int __cdecl CDebugReportHookProc(int reportType, char *message, int *returnValue) throw()
  629. {
  630. DWORD dwWritten;
  631. *returnValue = 0;
  632. CRevertThreadToken revert;
  633. revert.Initialize();
  634. CHandle hdlPipe;
  635. while (1)
  636. {
  637. HANDLE hPipe = CreateFileA(m_szPipeName, GENERIC_WRITE | GENERIC_READ,
  638. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
  639. if (hPipe != INVALID_HANDLE_VALUE )
  640. {
  641. hdlPipe.Attach(hPipe);
  642. break;
  643. }
  644. if (GetLastError() != ERROR_PIPE_BUSY)
  645. {
  646. if (reportType == _CRT_ASSERT)
  647. return TRUE;
  648. return FALSE;
  649. }
  650. //If the pipe is busy, we wait for up to m_dwTimeout
  651. if (!WaitNamedPipeA(m_szPipeName, m_dwTimeout))
  652. {
  653. if (reportType == _CRT_ASSERT)
  654. return TRUE;
  655. return FALSE;
  656. }
  657. }
  658. DEBUG_SERVER_MESSAGE Message;
  659. Message.bIsDebuggerAttached = IsDebuggerPresent();
  660. if (reportType == _CRT_ASSERT)
  661. {
  662. Message.dwType = DEBUG_SERVER_MESSAGE_ASSERT;
  663. }
  664. else
  665. {
  666. Message.dwType = DEBUG_SERVER_MESSAGE_TRACE;
  667. }
  668. Message.dwProcessId = GetCurrentProcessId();
  669. Message.dwClientNameLen = m_dwClientNameLen+1; // add 1 for the null terminator
  670. Message.dwTextLen = strlen(message)+1;
  671. int nRet = 1;
  672. WriteFile(hdlPipe, &Message, sizeof(DEBUG_SERVER_MESSAGE), &dwWritten, NULL);
  673. WriteFile(hdlPipe, m_szClientName, Message.dwClientNameLen, &dwWritten, NULL);
  674. WriteFile(hdlPipe, message, (DWORD)Message.dwTextLen, &dwWritten, NULL);
  675. //Check to see whether or not to send stack trace
  676. BOOL bRet = ReadFile(hdlPipe, &nRet, sizeof(nRet), &dwWritten, NULL);
  677. //if nRet == 1, the user wants stack trace info
  678. if (bRet && nRet)
  679. {
  680. _ATLTRY
  681. {
  682. CStringA str;
  683. CReportHookDumpHandler stackDumper;
  684. stackDumper.GetStackDump(&str);
  685. if (!WriteFile(hdlPipe, (LPCSTR)str, str.GetLength(), &dwWritten, NULL))
  686. return (reportType == _CRT_ASSERT ? TRUE : FALSE);
  687. }
  688. _ATLCATCHALL()
  689. {
  690. return (reportType == _CRT_ASSERT ? TRUE : FALSE);
  691. }
  692. }
  693. if (bRet)
  694. bRet = ReadFile(hdlPipe, &nRet, sizeof(nRet), &dwWritten, NULL);
  695. if (!bRet)
  696. nRet = 0;
  697. revert.Restore();
  698. // possible return values
  699. // 0 -> Ignore or cancel
  700. // 1 -> Retry
  701. // 2 -> Abort
  702. if (nRet == 0)
  703. {
  704. return (reportType == _CRT_ASSERT ? TRUE : FALSE);
  705. }
  706. if (nRet == 1)
  707. {
  708. if (IsDebuggerPresent())
  709. {
  710. DebugBreak();
  711. }
  712. }
  713. if (nRet == 2)
  714. abort();
  715. return (reportType == _CRT_ASSERT ? TRUE : FALSE);
  716. }
  717. }; // class CDebugReportHook
  718. __declspec(selectany) char CDebugReportHook::m_szPipeName[MAX_PATH+1];
  719. __declspec(selectany) DWORD CDebugReportHook::m_dwTimeout;
  720. __declspec(selectany) DWORD CDebugReportHook::m_dwClientNameLen;
  721. __declspec(selectany) char CDebugReportHook::m_szClientName[MAX_COMPUTERNAME_LENGTH+1];
  722. #endif
  723. #ifndef ATL_POOL_NUM_THREADS
  724. #define ATL_POOL_NUM_THREADS 0
  725. #endif
  726. #ifndef ATL_POOL_STACK_SIZE
  727. #define ATL_POOL_STACK_SIZE 0
  728. #endif
  729. #ifndef ATLS_DEFAULT_THREADSPERPROC
  730. #define ATLS_DEFAULT_THREADSPERPROC 2
  731. #endif
  732. #ifndef ATLS_DEFAULT_THREADPOOLSHUTDOWNTIMEOUT
  733. #define ATLS_DEFAULT_THREADPOOLSHUTDOWNTIMEOUT 36000
  734. #endif
  735. //
  736. // CThreadPool
  737. // This class is a simple IO completion port based thread pool
  738. // Worker:
  739. // is a class that is responsible for handling requests
  740. // queued on the thread pool.
  741. // It must have a typedef for RequestType, where request type
  742. // is the datatype to be queued on the pool
  743. // RequestType must be castable to (DWORD)
  744. // The value -1 is reserved for shutdown
  745. // of the pool
  746. // Worker must also have a void Execute(RequestType request, void *pvParam, OVERLAPPED *pOverlapped) function
  747. // ThreadTraits:
  748. // is a class that implements a static CreateThread function
  749. // This allows for overriding how the threads are created
  750. #define ATLS_POOL_SHUTDOWN ((OVERLAPPED*) ((__int64) -1))
  751. template <class Worker, class ThreadTraits=DefaultThreadTraits>
  752. class CThreadPool : public IThreadPoolConfig
  753. {
  754. protected:
  755. CSimpleMap<DWORD, HANDLE> m_threadMap;
  756. DWORD m_dwThreadEventId;
  757. CComCriticalSection m_critSec;
  758. DWORD m_dwStackSize;
  759. DWORD m_dwMaxWait;
  760. void *m_pvWorkerParam;
  761. LONG m_bShutdown;
  762. HANDLE m_hThreadEvent;
  763. HANDLE m_hRequestQueue;
  764. public:
  765. CThreadPool() throw() :
  766. m_hRequestQueue(NULL),
  767. m_pvWorkerParam(NULL),
  768. m_dwMaxWait(ATLS_DEFAULT_THREADPOOLSHUTDOWNTIMEOUT),
  769. m_bShutdown(FALSE),
  770. m_dwThreadEventId(0),
  771. m_dwStackSize(0)
  772. {
  773. }
  774. ~CThreadPool() throw()
  775. {
  776. Shutdown();
  777. }
  778. // Initialize the thread pool
  779. // if nNumThreads > 0, then it specifies the number of threads
  780. // if nNumThreads < 0, then it specifies the number of threads per proc (-)
  781. // if nNumThreads == 0, then it defaults to two threads per proc
  782. // hCompletion is a handle of a file to associate with the completion port
  783. // pvWorkerParam is a parameter that will be passed to Worker::Execute
  784. // dwStackSize:
  785. // The stack size to use when creating the threads
  786. HRESULT Initialize(void *pvWorkerParam=NULL, int nNumThreads=0, DWORD dwStackSize=0, HANDLE hCompletion=INVALID_HANDLE_VALUE) throw()
  787. {
  788. ATLASSERT( m_hRequestQueue == NULL );
  789. if (m_hRequestQueue) // Already initialized
  790. return AtlHresultFromWin32(ERROR_ALREADY_INITIALIZED);
  791. if (S_OK != m_critSec.Init())
  792. return E_FAIL;
  793. m_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  794. if (!m_hThreadEvent)
  795. {
  796. m_critSec.Term();
  797. return AtlHresultFromLastError();
  798. }
  799. // Create IO completion port to queue the requests
  800. m_hRequestQueue = CreateIoCompletionPort(hCompletion, NULL, 0, nNumThreads);
  801. if (m_hRequestQueue == NULL)
  802. {
  803. // failed creating the Io completion port
  804. m_critSec.Term();
  805. CloseHandle(m_hThreadEvent);
  806. return AtlHresultFromLastError();
  807. }
  808. m_pvWorkerParam = pvWorkerParam;
  809. m_dwStackSize = dwStackSize;
  810. HRESULT hr = SetSize(nNumThreads);
  811. if (hr != S_OK)
  812. {
  813. // Close the request queue handle
  814. CloseHandle(m_hRequestQueue);
  815. // Clear the queue handle
  816. m_hRequestQueue = NULL;
  817. // Uninitialize the critical sections
  818. m_critSec.Term();
  819. CloseHandle(m_hThreadEvent);
  820. return hr;
  821. }
  822. return S_OK;
  823. }
  824. // Shutdown the thread pool
  825. // This function posts the shutdown request to all the threads in the pool
  826. // It will wait for the threads to shutdown a maximum of dwMaxWait MS.
  827. // If the timeout expires it just returns without terminating the threads.
  828. void Shutdown(DWORD dwMaxWait=0) throw()
  829. {
  830. if (!m_hRequestQueue) // Not initialized
  831. return;
  832. CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
  833. if (FAILED(lock.Lock()))
  834. {
  835. // out of memory
  836. ATLASSERT( FALSE );
  837. return;
  838. }
  839. if (dwMaxWait == 0)
  840. dwMaxWait = m_dwMaxWait;
  841. HRESULT hr = InternalResizePool(0, dwMaxWait);
  842. if (hr != S_OK)
  843. ATLTRACE(atlTraceUtil, 0, _T("Thread pool not shutting down cleanly : %08x"), hr);
  844. // If the threads have not returned, then something is wrong
  845. for (int i = m_threadMap.GetSize() - 1; i >= 0; i--)
  846. {
  847. HANDLE hThread = m_threadMap.GetValueAt(i);
  848. DWORD dwExitCode;
  849. GetExitCodeThread(hThread, &dwExitCode);
  850. if (dwExitCode == STILL_ACTIVE)
  851. {
  852. ATLTRACE(atlTraceUtil, 0, _T("Terminating thread"));
  853. TerminateThread(hThread, 0);
  854. }
  855. CloseHandle(hThread);
  856. }
  857. // Close the request queue handle
  858. CloseHandle(m_hRequestQueue);
  859. // Clear the queue handle
  860. m_hRequestQueue = NULL;
  861. ATLASSERT(m_threadMap.GetSize() == 0);
  862. // Uninitialize the critical sections
  863. lock.Unlock();
  864. m_critSec.Term();
  865. CloseHandle(m_hThreadEvent);
  866. }
  867. // IThreadPoolConfig methods
  868. HRESULT STDMETHODCALLTYPE SetSize(int nNumThreads) throw()
  869. {
  870. if (nNumThreads == 0)
  871. nNumThreads = -ATLS_DEFAULT_THREADSPERPROC;
  872. if (nNumThreads < 0)
  873. {
  874. SYSTEM_INFO si;
  875. GetSystemInfo(&si);
  876. nNumThreads = (int) (-nNumThreads) * si.dwNumberOfProcessors;
  877. }
  878. return InternalResizePool(nNumThreads, m_dwMaxWait);
  879. }
  880. HRESULT STDMETHODCALLTYPE GetSize(int *pnNumThreads) throw()
  881. {
  882. if (!pnNumThreads)
  883. return E_POINTER;
  884. *pnNumThreads = GetNumThreads();
  885. return S_OK;
  886. }
  887. HRESULT STDMETHODCALLTYPE SetTimeout(DWORD dwMaxWait) throw()
  888. {
  889. m_dwMaxWait = dwMaxWait;
  890. return S_OK;
  891. }
  892. HRESULT STDMETHODCALLTYPE GetTimeout(DWORD *pdwMaxWait) throw()
  893. {
  894. if (!pdwMaxWait)
  895. return E_POINTER;
  896. *pdwMaxWait = m_dwMaxWait;
  897. return S_OK;
  898. }
  899. // IUnknown methods
  900. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
  901. {
  902. if (!ppv)
  903. return E_POINTER;
  904. *ppv = NULL;
  905. if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
  906. InlineIsEqualGUID(riid, __uuidof(IThreadPoolConfig)))
  907. {
  908. *ppv = static_cast<IThreadPoolConfig*>(this);
  909. AddRef();
  910. return S_OK;
  911. }
  912. return E_NOINTERFACE;
  913. }
  914. ULONG STDMETHODCALLTYPE AddRef() throw()
  915. {
  916. return 1;
  917. }
  918. ULONG STDMETHODCALLTYPE Release() throw()
  919. {
  920. return 1;
  921. }
  922. HANDLE GetQueueHandle() throw()
  923. {
  924. return m_hRequestQueue;
  925. }
  926. int GetNumThreads() throw()
  927. {
  928. return m_threadMap.GetSize();
  929. }
  930. // QueueRequest adds a request to the thread pool
  931. // it will be picked up by one of the threads and dispatched to the worker
  932. // in WorkerThreadProc
  933. BOOL QueueRequest(Worker::RequestType request) throw()
  934. {
  935. if (!PostQueuedCompletionStatus(m_hRequestQueue, 0, (ULONG_PTR) request, NULL))
  936. return FALSE;
  937. return TRUE;
  938. }
  939. protected:
  940. DWORD ThreadProc() throw()
  941. {
  942. DWORD dwBytesTransfered;
  943. ULONG_PTR dwCompletionKey;
  944. OVERLAPPED* pOverlapped;
  945. // We instantiate an instance of the worker class on the stack
  946. // for the life time of the thread.
  947. Worker theWorker;
  948. if (theWorker.Initialize(m_pvWorkerParam) == FALSE)
  949. {
  950. return 1;
  951. }
  952. SetEvent(m_hThreadEvent);
  953. // Get the request from the IO completion port
  954. while (GetQueuedCompletionStatus(m_hRequestQueue, &dwBytesTransfered, &dwCompletionKey, &pOverlapped, INFINITE))
  955. {
  956. if (pOverlapped == ATLS_POOL_SHUTDOWN) // Shut down
  957. {
  958. m_dwThreadEventId = GetCurrentThreadId();
  959. LONG bResult = InterlockedExchange(&m_bShutdown, FALSE);
  960. if (bResult) // Shutdown has not been cancelled
  961. break;
  962. m_dwThreadEventId = 0;
  963. // else, shutdown has been cancelled -- continue as before
  964. }
  965. else // Do work
  966. {
  967. Worker::RequestType request = (Worker::RequestType) dwCompletionKey;
  968. // Process the request. Notice the following:
  969. // (1) It is the worker's responsibility to free any memory associated
  970. // with the request if the request is complete
  971. // (2) If the request still requires some more processing
  972. // the worker should queue the request again for dispatching
  973. theWorker.Execute(request, m_pvWorkerParam, pOverlapped);
  974. }
  975. }
  976. SetEvent(m_hThreadEvent);
  977. theWorker.Terminate(m_pvWorkerParam);
  978. return 0;
  979. }
  980. static DWORD WINAPI WorkerThreadProc(LPVOID pv) throw()
  981. {
  982. CThreadPool* pThis =
  983. reinterpret_cast< CThreadPool* >(pv);
  984. return pThis->ThreadProc();
  985. }
  986. HRESULT InternalResizePool(int nNumThreads, int dwMaxWait) throw()
  987. {
  988. if (!m_hRequestQueue) // Not initialized
  989. return E_FAIL;
  990. CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
  991. if (FAILED(lock.Lock()))
  992. {
  993. // out of memory
  994. ATLASSERT( FALSE );
  995. return E_FAIL;
  996. }
  997. int nCurThreads = m_threadMap.GetSize();
  998. if (nNumThreads == nCurThreads)
  999. {
  1000. return S_OK;
  1001. }
  1002. else if (nNumThreads < nCurThreads)
  1003. {
  1004. int nNumShutdownThreads = nCurThreads - nNumThreads;
  1005. for (int nThreadIndex = 0; nThreadIndex < nNumShutdownThreads; nThreadIndex++)
  1006. {
  1007. ResetEvent(m_hThreadEvent);
  1008. m_bShutdown = TRUE;
  1009. PostQueuedCompletionStatus(m_hRequestQueue, 0, 0, ATLS_POOL_SHUTDOWN);
  1010. DWORD dwRet = WaitForSingleObject(m_hThreadEvent, dwMaxWait);
  1011. if (dwRet == WAIT_TIMEOUT)
  1012. {
  1013. LONG bResult = InterlockedExchange(&m_bShutdown, FALSE);
  1014. if (bResult) // Nobody picked up the shutdown message
  1015. {
  1016. // m_critSec.Unlock();
  1017. return HRESULT_FROM_WIN32(WAIT_TIMEOUT);
  1018. }
  1019. }
  1020. else if (dwRet != WAIT_OBJECT_0)
  1021. {
  1022. // m_critSec.Unlock();
  1023. return AtlHresultFromLastError();
  1024. }
  1025. int nIndex = m_threadMap.FindKey(m_dwThreadEventId);
  1026. if (nIndex != -1)
  1027. {
  1028. HANDLE hThread = m_threadMap.GetValueAt(nIndex);
  1029. CloseHandle(hThread);
  1030. m_threadMap.RemoveAt(nIndex);
  1031. }
  1032. }
  1033. }
  1034. else
  1035. {
  1036. int nNumNewThreads = nNumThreads - nCurThreads;
  1037. // Create and initialize worker threads
  1038. for (int nThreadIndex = 0; nThreadIndex < nNumNewThreads; nThreadIndex++)
  1039. {
  1040. DWORD dwThreadID;
  1041. ResetEvent(m_hThreadEvent);
  1042. // HANDLE hThread = ThreadTraits::CreateThread(NULL, m_dwStackSize, WorkerThreadProc, (LPVOID)this, 0, &dwThreadID);
  1043. CHandle hdlThread( ThreadTraits::CreateThread(NULL, m_dwStackSize, WorkerThreadProc, (LPVOID)this, 0, &dwThreadID) );
  1044. if (!hdlThread)
  1045. {
  1046. HRESULT hr = AtlHresultFromLastError();
  1047. ATLASSERT(hr != S_OK);
  1048. // m_critSec.Unlock();
  1049. return hr;
  1050. }
  1051. DWORD dwRet = WaitForSingleObject(m_hThreadEvent, dwMaxWait);
  1052. if (dwRet != WAIT_OBJECT_0)
  1053. {
  1054. if (dwRet == WAIT_TIMEOUT)
  1055. {
  1056. // m_critSec.Unlock();
  1057. return HRESULT_FROM_WIN32(WAIT_TIMEOUT);
  1058. }
  1059. else
  1060. {
  1061. // m_critSec.Unlock();
  1062. return AtlHresultFromLastError();
  1063. }
  1064. }
  1065. if (m_threadMap.Add(dwThreadID, hdlThread) != FALSE)
  1066. {
  1067. hdlThread.Detach();
  1068. }
  1069. }
  1070. }
  1071. // m_critSec.Unlock();
  1072. return S_OK;
  1073. }
  1074. }; // class CThreadPool
  1075. //
  1076. // CNonStatelessWorker
  1077. // This class is a simple wrapper for use with CThreadPool.
  1078. // It instantiates one instance of Worker per request
  1079. // this allows Worker to hold state for each request
  1080. // and depend on the destructor being called
  1081. // Worker:
  1082. // is a class that is responsible for handling requests
  1083. // queued on the thread pool (See CThreadPool)
  1084. template <class Worker>
  1085. class CNonStatelessWorker
  1086. {
  1087. public:
  1088. typedef Worker::RequestType RequestType;
  1089. BOOL Initialize(void * /*pvParam*/) throw()
  1090. {
  1091. return TRUE;
  1092. }
  1093. void Execute(Worker::RequestType request, void *pvWorkerParam, OVERLAPPED *pOverlapped)
  1094. {
  1095. Worker worker;
  1096. worker.Execute(request, pvWorkerParam, pOverlapped);
  1097. }
  1098. void Terminate(void* /*pvParam*/) throw()
  1099. {
  1100. }
  1101. }; // class CNonStatelessWorker
  1102. //Flags
  1103. #define ATL_URL_ESCAPE 1 // (un)escape URL characters
  1104. #define ATL_URL_NO_ENCODE 2 // Don't convert unsafe characters to escape sequence
  1105. #define ATL_URL_DECODE 4 // Convert %XX escape sequences to characters
  1106. #define ATL_URL_NO_META 8 // Don't convert .. etc. meta path sequences
  1107. #define ATL_URL_ENCODE_SPACES_ONLY 16 // Encode spaces only
  1108. #define ATL_URL_BROWSER_MODE 32 // Special encode/decode rules for browser
  1109. #define ATL_URL_ENCODE_PERCENT 64 // Encode percent (by default, not encoded)
  1110. #define ATL_URL_CANONICALIZE 128 // Internal: used by Canonicalize for AtlEscapeUrl: Cannot be set via SetFlags
  1111. #define ATL_URL_COMBINE 256 // Internal: Cannot be set via SetFlags
  1112. //Get the decimal value of a hexadecimal character
  1113. inline short AtlHexValue(char chIn)
  1114. {
  1115. unsigned char ch = (unsigned char)chIn;
  1116. if (ch >= '0' && ch <= '9')
  1117. return (short)(ch - '0');
  1118. if (ch >= 'A' && ch <= 'F')
  1119. return (short)(ch - 'A' + 10);
  1120. if (ch >= 'a' && ch <= 'f')
  1121. return (short)(ch - 'a' + 10);
  1122. return -1;
  1123. }
  1124. //Determine if the character is unsafe under the URI RFC document
  1125. inline BOOL AtlIsUnsafeUrlChar(char chIn) throw()
  1126. {
  1127. unsigned char ch = (unsigned char)chIn;
  1128. switch(ch)
  1129. {
  1130. case ';': case '\\': case '?': case '@': case '&':
  1131. case '=': case '+': case '$': case ',': case ' ':
  1132. case '<': case '>': case '#': case '%': case '\"':
  1133. case '{': case '}': case '|':
  1134. case '^': case '[': case ']': case '`':
  1135. return TRUE;
  1136. default:
  1137. {
  1138. if (ch < 32 || ch > 126)
  1139. return TRUE;
  1140. return FALSE;
  1141. }
  1142. }
  1143. }
  1144. //Get the default internet port for a particular scheme
  1145. inline ATL_URL_PORT AtlGetDefaultUrlPort(ATL_URL_SCHEME m_nScheme) throw()
  1146. {
  1147. switch (m_nScheme)
  1148. {
  1149. case ATL_URL_SCHEME_FTP:
  1150. return ATL_URL_DEFAULT_FTP_PORT;
  1151. case ATL_URL_SCHEME_GOPHER:
  1152. return ATL_URL_DEFAULT_GOPHER_PORT;
  1153. case ATL_URL_SCHEME_HTTP:
  1154. return ATL_URL_DEFAULT_HTTP_PORT;
  1155. case ATL_URL_SCHEME_HTTPS:
  1156. return ATL_URL_DEFAULT_HTTPS_PORT;
  1157. case ATL_URL_SCHEME_SOCKS:
  1158. return ATL_URL_DEFAULT_SOCKS_PORT;
  1159. default:
  1160. return ATL_URL_INVALID_PORT_NUMBER;
  1161. }
  1162. }
  1163. //Escape a meta sequence with lpszOutUrl as the base url and lpszInUrl as the relative url
  1164. //i.e. lpszInUrl = ./* or ../*
  1165. ATL_NOINLINE inline BOOL AtlEscapeUrlMetaHelper(
  1166. LPSTR* ppszOutUrl,
  1167. DWORD dwOutLen,
  1168. LPSTR* ppszInUrl,
  1169. DWORD* pdwLen,
  1170. DWORD dwFlags = 0,
  1171. DWORD dwColonPos = ATL_URL_MAX_URL_LENGTH) throw()
  1172. {
  1173. ATLASSERT( ppszOutUrl != NULL );
  1174. ATLASSERT( ppszInUrl != NULL );
  1175. ATLASSERT( pdwLen != NULL);
  1176. LPSTR szOut = *ppszOutUrl;
  1177. LPSTR szIn = *ppszInUrl;
  1178. DWORD dwUrlLen = dwOutLen;
  1179. char chPrev = *(szOut-1);
  1180. BOOL bRet = FALSE;
  1181. //if the previous character is a directory delimiter
  1182. if (chPrev == '/' || chPrev == '\\')
  1183. {
  1184. char chNext = *szIn;
  1185. //if the next character is a directory delimiter
  1186. if (chNext == '/' || chNext == '\\')
  1187. {
  1188. //the meta sequence is of the form /./*
  1189. szIn++;
  1190. bRet = TRUE;
  1191. }
  1192. else if (chNext == '.' && ((chNext = *(szIn+1)) == '/' ||
  1193. chNext == '\\' || chNext == '\0'))
  1194. {
  1195. //otherwise if the meta sequence is of the form "/../"
  1196. //skip the preceding "/"
  1197. szOut--;
  1198. //skip the ".." of the meta sequence
  1199. szIn+= 2;
  1200. DWORD dwOutPos = dwUrlLen-1;
  1201. LPSTR szTmp = szOut;
  1202. //while we are not at the beginning of the base url
  1203. while (dwOutPos)
  1204. {
  1205. szTmp--;
  1206. dwOutPos--;
  1207. //if it is a directory delimiter
  1208. if (*szTmp == '/' || *szTmp == '\\')
  1209. {
  1210. //if we are canonicalizing the url and NOT combining it
  1211. //and if we have encountered the ':' or we are at a position before the ':'
  1212. if ((dwFlags & ATL_URL_CANONICALIZE) && ((dwFlags & ATL_URL_COMBINE) == 0) &&
  1213. (dwColonPos && (dwOutPos <= dwColonPos+1)))
  1214. {
  1215. //NOTE: this is to match the way that InternetCanonicalizeUrl and
  1216. // InternetCombineUrl handle this case
  1217. break;
  1218. }
  1219. //otherwise, set the current output string position to right after the '/'
  1220. szOut = szTmp+1;
  1221. //update the length to match
  1222. dwUrlLen = dwOutPos+1;
  1223. bRet = TRUE;
  1224. break;
  1225. }
  1226. }
  1227. //if we could not properly escape the meta sequence
  1228. if (dwUrlLen != dwOutPos+1)
  1229. {
  1230. //restore everything to its original value
  1231. szIn-= 2;
  1232. szOut++;
  1233. }
  1234. else
  1235. {
  1236. bRet = TRUE;
  1237. }
  1238. }
  1239. }
  1240. //update the strings
  1241. *ppszOutUrl = szOut;
  1242. *ppszInUrl = szIn;
  1243. *pdwLen = dwUrlLen;
  1244. return bRet;
  1245. }
  1246. //Convert all unsafe characters in szStringIn to escape sequences
  1247. //lpszStringIn and lpszStringOut should be different strings
  1248. inline BOOL AtlEscapeUrlA(
  1249. LPCSTR szStringIn,
  1250. LPSTR szStringOut,
  1251. DWORD* pdwStrLen,
  1252. DWORD dwMaxLength,
  1253. DWORD dwFlags = 0) throw()
  1254. {
  1255. ATLASSERT( szStringIn != NULL );
  1256. ATLASSERT( szStringOut != NULL );
  1257. ATLASSERT( szStringIn != szStringOut );
  1258. char ch;
  1259. DWORD dwLen = 0;
  1260. BOOL bRet = TRUE;
  1261. BOOL bSchemeFile = FALSE;
  1262. DWORD dwColonPos = 0;
  1263. DWORD dwFlagsInternal = dwFlags;
  1264. while((ch = *szStringIn++) != '\0')
  1265. {
  1266. //if we are at the maximum length, set bRet to FALSE
  1267. //this ensures no more data is written to szStringOut, but
  1268. //the length of the string is still updated, so the user
  1269. //knows how much space to allocate
  1270. if (dwLen == dwMaxLength)
  1271. {
  1272. bRet = FALSE;
  1273. }
  1274. //Keep track of the first ':' position to match the weird way
  1275. //InternetCanonicalizeUrl handles it
  1276. if (ch == ':' && (dwFlagsInternal & ATL_URL_CANONICALIZE) && !dwColonPos)
  1277. {
  1278. if (bRet)
  1279. {
  1280. *szStringOut = '\0';
  1281. _strlwr(szStringOut-dwLen);
  1282. if (dwLen == 4 && !strncmp("file", (szStringOut-4), 4))
  1283. {
  1284. bSchemeFile = TRUE;
  1285. }
  1286. }
  1287. dwColonPos = dwLen+1;
  1288. }
  1289. else if (ch == '%' && (dwFlagsInternal & ATL_URL_DECODE))
  1290. {
  1291. //decode the escaped sequence
  1292. ch = (char)(16*AtlHexValue(*szStringIn++));
  1293. ch = (char)(ch+AtlHexValue(*szStringIn++));
  1294. }
  1295. else if ((ch == '?' || ch == '#') && (dwFlagsInternal & ATL_URL_BROWSER_MODE))
  1296. {
  1297. //ATL_URL_BROWSER mode does not encode after a '?' or a '#'
  1298. dwFlagsInternal |= ATL_URL_NO_ENCODE;
  1299. }
  1300. if ((dwFlagsInternal & ATL_URL_CANONICALIZE) && (dwFlagsInternal & ATL_URL_NO_ENCODE)==0)
  1301. {
  1302. //canonicalize the '\' to '/'
  1303. if (ch == '\\' && (dwColonPos || (dwFlagsInternal & ATL_URL_COMBINE)) && bRet)
  1304. {
  1305. //if the scheme is not file or it is file and the '\' is in "file:\\"
  1306. //NOTE: This is to match the way InternetCanonicalizeUrl handles this case
  1307. if (!bSchemeFile || (dwLen < 7))
  1308. {
  1309. ch = '/';
  1310. }
  1311. }
  1312. else if (ch == '.' && dwLen > 0 && (dwFlagsInternal & ATL_URL_NO_META)==0)
  1313. {
  1314. //if we are escaping meta sequences, attempt to do so
  1315. if (AtlEscapeUrlMetaHelper(&szStringOut, dwLen, (char**)(&szStringIn), &dwLen, dwFlagsInternal, dwColonPos))
  1316. continue;
  1317. }
  1318. }
  1319. //if we are encoding and it is an unsafe character
  1320. if (AtlIsUnsafeUrlChar(ch) && (dwFlagsInternal & ATL_URL_NO_ENCODE)==0)
  1321. {
  1322. //if we are only encoding spaces, and ch is not a space or
  1323. //if we are not encoding meta sequences and it is a dot or
  1324. //if we not encoding percents and it is a percent
  1325. if (((dwFlagsInternal & ATL_URL_ENCODE_SPACES_ONLY) && ch != ' ') ||
  1326. ((dwFlagsInternal & ATL_URL_NO_META) && ch == '.') ||
  1327. (((dwFlagsInternal & ATL_URL_ENCODE_PERCENT) == 0) && ch == '%'))
  1328. {
  1329. //just output it without encoding
  1330. if (bRet)
  1331. *szStringOut++ = ch;
  1332. }
  1333. else
  1334. {
  1335. //if there is not enough space for the escape sequence
  1336. if (dwLen >= (dwMaxLength-3))
  1337. {
  1338. bRet = FALSE;
  1339. }
  1340. if (bRet)
  1341. {
  1342. //output the percent, followed by the hex value of the character
  1343. *szStringOut++ = '%';
  1344. // sprintf(szStringOut, "%.2X", (unsigned char)(ch));
  1345. _itoa((int)ch, szStringOut, 16);
  1346. szStringOut+= 2;
  1347. }
  1348. dwLen += 2;
  1349. }
  1350. }
  1351. else //safe character
  1352. {
  1353. if (bRet)
  1354. *szStringOut++ = ch;
  1355. }
  1356. dwLen++;
  1357. }
  1358. if ((dwFlags & ATL_URL_BROWSER_MODE)==0)
  1359. {
  1360. //trim trailing whitespace
  1361. szStringOut--;
  1362. while (1)
  1363. {
  1364. if (*szStringOut == ' ')
  1365. {
  1366. --szStringOut;
  1367. continue;
  1368. }
  1369. if (!strncmp(szStringOut-2, "%20", 3))
  1370. {
  1371. szStringOut -= 3;
  1372. continue;
  1373. }
  1374. break;
  1375. }
  1376. szStringOut++;
  1377. }
  1378. if (bRet)
  1379. *szStringOut = '\0';
  1380. if (pdwStrLen)
  1381. *pdwStrLen = dwLen;
  1382. return bRet;
  1383. }
  1384. inline BOOL AtlEscapeUrlW(
  1385. LPCWSTR szStringIn,
  1386. LPWSTR szStringOut,
  1387. DWORD* pdwStrLen,
  1388. DWORD dwMaxLength,
  1389. DWORD dwFlags = 0) throw()
  1390. {
  1391. // convert to UTF8
  1392. BOOL bRet = FALSE;
  1393. int nSrcLen = (int) wcslen(szStringIn);
  1394. int nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, NULL, 0);
  1395. if (nCnt != 0)
  1396. {
  1397. nCnt++;
  1398. CHeapPtr<char> szIn;
  1399. char szInBuf[ATL_URL_MAX_URL_LENGTH];
  1400. char *pszIn = szInBuf;
  1401. // try to avoid allocation
  1402. if (nCnt > ATL_URL_MAX_URL_LENGTH)
  1403. {
  1404. if (!szIn.AllocateBytes(nCnt))
  1405. {
  1406. // out of memory
  1407. return FALSE;
  1408. }
  1409. pszIn = szIn;
  1410. }
  1411. nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, pszIn, nCnt);
  1412. ATLASSERT( nCnt != 0 );
  1413. pszIn[nCnt] = '\0';
  1414. char szOutBuf[ATL_URL_MAX_URL_LENGTH];
  1415. char *pszOut = szOutBuf;
  1416. CHeapPtr<char> szTmp;
  1417. // try to avoid allocation
  1418. if (dwMaxLength > ATL_URL_MAX_URL_LENGTH)
  1419. {
  1420. if (!szTmp.AllocateBytes(dwMaxLength))
  1421. {
  1422. // out of memory
  1423. return FALSE;
  1424. }
  1425. pszOut = szTmp;
  1426. }
  1427. DWORD dwStrLen = 0;
  1428. bRet = AtlEscapeUrlA(pszIn, pszOut, &dwStrLen, dwMaxLength, dwFlags);
  1429. if (bRet != FALSE)
  1430. {
  1431. // it is now safe to convert using any codepage, since there
  1432. // are no non-ASCII characters
  1433. pszOut[dwStrLen] = '\0';
  1434. _ATLTRY
  1435. {
  1436. memcpy(szStringOut, CA2W( pszOut ), dwStrLen*sizeof(wchar_t));
  1437. szStringOut[dwStrLen] = '\0';
  1438. }
  1439. _ATLCATCHALL()
  1440. {
  1441. bRet = FALSE;
  1442. }
  1443. }
  1444. if (pdwStrLen)
  1445. {
  1446. *pdwStrLen = dwStrLen;
  1447. }
  1448. }
  1449. return bRet;
  1450. }
  1451. //Convert all escaped characters in szString to their real values
  1452. //lpszStringIn and lpszStringOut can be the same string
  1453. inline BOOL AtlUnescapeUrlA(
  1454. LPCSTR szStringIn,
  1455. LPSTR szStringOut,
  1456. LPDWORD pdwStrLen,
  1457. DWORD dwMaxLength) throw()
  1458. {
  1459. ATLASSERT(szStringIn != NULL);
  1460. ATLASSERT(szStringOut != NULL);
  1461. int nValue = 0;
  1462. char ch;
  1463. DWORD dwLen = 0;
  1464. BOOL bRet = TRUE;
  1465. while ((ch = *szStringIn) != 0)
  1466. {
  1467. if (dwLen == dwMaxLength)
  1468. bRet = FALSE;
  1469. if (bRet)
  1470. {
  1471. if (ch == '%')
  1472. {
  1473. if ((*(szStringIn+1) == '\0') || (*(szStringIn+2) == '\0'))
  1474. {
  1475. bRet = FALSE;
  1476. break;
  1477. }
  1478. ch = *(++szStringIn);
  1479. //currently assuming 2 hex values after '%'
  1480. //as per the RFC 2396 document
  1481. nValue = 16*AtlHexValue(ch);
  1482. nValue+= AtlHexValue(*(++szStringIn));
  1483. *szStringOut++ = (char) nValue;
  1484. }
  1485. else //non-escape character
  1486. {
  1487. if (bRet)
  1488. *szStringOut++ = ch;
  1489. }
  1490. }
  1491. dwLen++;
  1492. szStringIn++;
  1493. }
  1494. if (bRet)
  1495. *szStringOut = '\0';
  1496. if (pdwStrLen)
  1497. *pdwStrLen = dwLen;
  1498. return TRUE;
  1499. }
  1500. inline BOOL AtlUnescapeUrlW(
  1501. LPCWSTR szStringIn,
  1502. LPWSTR szStringOut,
  1503. LPDWORD pdwStrLen,
  1504. DWORD dwMaxLength) throw()
  1505. {
  1506. /// convert to UTF8
  1507. BOOL bRet = FALSE;
  1508. int nSrcLen = (int) wcslen(szStringIn);
  1509. int nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, NULL, 0);
  1510. if (nCnt != 0)
  1511. {
  1512. nCnt++;
  1513. CHeapPtr<char> szIn;
  1514. char szInBuf[ATL_URL_MAX_URL_LENGTH];
  1515. char *pszIn = szInBuf;
  1516. // try to avoid allocation
  1517. if (nCnt > ATL_URL_MAX_URL_LENGTH)
  1518. {
  1519. if (!szIn.AllocateBytes(nCnt))
  1520. {
  1521. // out of memory
  1522. return FALSE;
  1523. }
  1524. pszIn = szIn;
  1525. }
  1526. nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, pszIn, nCnt);
  1527. ATLASSERT( nCnt != 0 );
  1528. pszIn[nCnt] = '\0';
  1529. char szOutBuf[ATL_URL_MAX_URL_LENGTH];
  1530. char *pszOut = szOutBuf;
  1531. CHeapPtr<char> szTmp;
  1532. // try to avoid allocation
  1533. if (dwMaxLength > ATL_URL_MAX_URL_LENGTH)
  1534. {
  1535. if (!szTmp.AllocateBytes(dwMaxLength))
  1536. {
  1537. // out of memory
  1538. return FALSE;
  1539. }
  1540. pszOut = szTmp;
  1541. }
  1542. DWORD dwStrLen = 0;
  1543. bRet = AtlUnescapeUrlA(pszIn, pszOut, &dwStrLen, dwMaxLength);
  1544. if (bRet != FALSE)
  1545. {
  1546. // it is now safe to convert using any codepage, since there
  1547. // are no non-ASCII characters
  1548. pszOut[dwStrLen] = '\0';
  1549. _ATLTRY
  1550. {
  1551. memcpy(szStringOut, CA2W( pszOut ), dwStrLen*sizeof(wchar_t));
  1552. szStringOut[dwStrLen] = '\0';
  1553. }
  1554. _ATLCATCHALL()
  1555. {
  1556. bRet = FALSE;
  1557. }
  1558. }
  1559. if (pdwStrLen)
  1560. {
  1561. *pdwStrLen = dwStrLen;
  1562. }
  1563. }
  1564. return bRet;
  1565. }
  1566. #ifdef UNICODE
  1567. #define AtlEscapeUrl AtlEscapeUrlW
  1568. #define AtlUnescapeUrl AtlUnescapeUrlW
  1569. #else
  1570. #define AtlEscapeUrl AtlEscapeUrlA
  1571. #define AtlUnescapeUrl AtlUnescapeUrlA
  1572. #endif
  1573. //Canonicalize a URL (same as InternetCanonicalizeUrl)
  1574. inline BOOL AtlCanonicalizeUrl(
  1575. LPCTSTR szUrl,
  1576. LPTSTR szCanonicalized,
  1577. DWORD* pdwMaxLength,
  1578. DWORD dwFlags = 0) throw()
  1579. {
  1580. ATLASSERT( szUrl != NULL );
  1581. ATLASSERT( szCanonicalized != NULL );
  1582. ATLASSERT( pdwMaxLength != NULL);
  1583. return AtlEscapeUrl(szUrl, szCanonicalized, pdwMaxLength, *pdwMaxLength, dwFlags | ATL_URL_CANONICALIZE);
  1584. }
  1585. //Combine a base and relative URL (same as InternetCombineUrl)
  1586. inline BOOL AtlCombineUrl(
  1587. LPCTSTR szBaseUrl,
  1588. LPCTSTR szRelativeUrl,
  1589. LPTSTR szBuffer,
  1590. DWORD* pdwMaxLength,
  1591. DWORD dwFlags = 0) throw()
  1592. {
  1593. ATLASSERT(szBaseUrl != NULL);
  1594. ATLASSERT(szRelativeUrl != NULL);
  1595. ATLASSERT(szBuffer != NULL);
  1596. ATLASSERT(pdwMaxLength != NULL);
  1597. size_t nLen1 = _tcslen(szBaseUrl);
  1598. TCHAR szCombined[2*ATL_URL_MAX_URL_LENGTH];
  1599. if (nLen1 >= 2*ATL_URL_MAX_URL_LENGTH)
  1600. {
  1601. return FALSE;
  1602. }
  1603. _tcscpy(szCombined, szBaseUrl);
  1604. // if last char of szBaseUrl is not a slash, add it.
  1605. if (nLen1 > 0 && szCombined[nLen1-1] != _T('/'))
  1606. {
  1607. szCombined[nLen1] = _T('/');
  1608. nLen1++;
  1609. szCombined[nLen1] = _T('\0');
  1610. }
  1611. size_t nLen2 = _tcslen(szRelativeUrl);
  1612. if (nLen2+nLen1+1 >= 2*ATL_URL_MAX_URL_LENGTH)
  1613. {
  1614. return FALSE;
  1615. }
  1616. _tcsncpy(szCombined+nLen1, szRelativeUrl, nLen2+1);
  1617. DWORD dwLen = (DWORD) (nLen1+nLen2);
  1618. if (dwLen >= *pdwMaxLength)
  1619. {
  1620. *pdwMaxLength = dwLen;
  1621. return FALSE;
  1622. }
  1623. return AtlEscapeUrl(szCombined, szBuffer, pdwMaxLength, *pdwMaxLength, dwFlags | ATL_URL_COMBINE | ATL_URL_CANONICALIZE);
  1624. }
  1625. class CUrl
  1626. {
  1627. private:
  1628. //scheme names cannot contain escape/unsafe characters
  1629. TCHAR m_szScheme[ATL_URL_MAX_SCHEME_LENGTH+1];
  1630. //host names cannot contain escape/unsafe characters
  1631. TCHAR m_szHostName[ATL_URL_MAX_HOST_NAME_LENGTH+1];
  1632. TCHAR m_szUserName[ATL_URL_MAX_USER_NAME_LENGTH+1];
  1633. TCHAR m_szPassword[ATL_URL_MAX_PASSWORD_LENGTH+1];
  1634. TCHAR m_szUrlPath[ATL_URL_MAX_PATH_LENGTH+1];
  1635. TCHAR m_szExtraInfo[ATL_URL_MAX_PATH_LENGTH+1];
  1636. ATL_URL_PORT m_nPortNumber;
  1637. ATL_URL_SCHEME m_nScheme;
  1638. DWORD m_dwSchemeNameLength;
  1639. DWORD m_dwHostNameLength;
  1640. DWORD m_dwUserNameLength;
  1641. DWORD m_dwPasswordLength;
  1642. DWORD m_dwUrlPathLength;
  1643. DWORD m_dwExtraInfoLength;
  1644. public:
  1645. //Empty constructor
  1646. CUrl() throw()
  1647. {
  1648. InitFields();
  1649. SetScheme(ATL_URL_SCHEME_HTTP);
  1650. }
  1651. //Copy constructor--maybe make private
  1652. CUrl(const CUrl& urlThat) throw()
  1653. {
  1654. CopyFields(urlThat);
  1655. }
  1656. //Destructor (empty)
  1657. ~CUrl() throw()
  1658. {
  1659. }
  1660. CUrl& operator=(const CUrl& urlThat) throw()
  1661. {
  1662. CopyFields(urlThat);
  1663. return (*this);
  1664. }
  1665. //Set the url
  1666. BOOL CrackUrl(LPCTSTR lpszUrl, DWORD dwFlags = 0) throw()
  1667. {
  1668. ATLASSERT(lpszUrl != NULL);
  1669. InitFields();
  1670. BOOL bRet = FALSE;
  1671. if (dwFlags & ATL_URL_DECODE)
  1672. {
  1673. //decode the url before parsing it
  1674. TCHAR szDecodedUrl[ATL_URL_MAX_URL_LENGTH];
  1675. DWORD dwLen;
  1676. if (!AtlUnescapeUrl(lpszUrl, szDecodedUrl, &dwLen, ATL_URL_MAX_URL_LENGTH))
  1677. return FALSE;
  1678. bRet = Parse(szDecodedUrl);
  1679. }
  1680. else
  1681. {
  1682. bRet = Parse(lpszUrl);
  1683. }
  1684. if (bRet && (dwFlags & ATL_URL_ESCAPE))
  1685. {
  1686. bRet = AtlUnescapeUrl(m_szUserName, m_szUserName,
  1687. &m_dwUserNameLength, ATL_URL_MAX_USER_NAME_LENGTH);
  1688. if (bRet)
  1689. {
  1690. bRet = AtlUnescapeUrl(m_szPassword, m_szPassword,
  1691. &m_dwPasswordLength, ATL_URL_MAX_PASSWORD_LENGTH);
  1692. if (bRet)
  1693. {
  1694. bRet = AtlUnescapeUrl(m_szUrlPath, m_szUrlPath,
  1695. &m_dwUrlPathLength, ATL_URL_MAX_PATH_LENGTH);
  1696. if (bRet)
  1697. {
  1698. bRet = AtlUnescapeUrl(m_szExtraInfo, m_szExtraInfo,
  1699. &m_dwExtraInfoLength, ATL_URL_MAX_PATH_LENGTH);
  1700. }
  1701. }
  1702. }
  1703. }
  1704. return bRet;
  1705. }
  1706. inline BOOL CreateUrl(LPTSTR lpszUrl, DWORD* pdwMaxLength, DWORD dwFlags = 0) const throw()
  1707. {
  1708. ATLASSERT(lpszUrl != NULL);
  1709. ATLASSERT(pdwMaxLength != NULL);
  1710. //build URL: <scheme>://<user>:<pass>@<domain>:<port><path><extra>
  1711. TCHAR szPortNumber[ATL_URL_MAX_PORT_NUMBER_LENGTH+2];
  1712. DWORD dwLength = *pdwMaxLength;
  1713. *pdwMaxLength = GetUrlLength()+1;
  1714. if (*pdwMaxLength > dwLength)
  1715. return FALSE;
  1716. _stprintf(szPortNumber, _T(":%d"), m_nPortNumber);
  1717. LPTSTR lpszOutUrl = lpszUrl;
  1718. *lpszUrl = '\0';
  1719. if (*m_szScheme)
  1720. {
  1721. _tcsncpy(lpszUrl, m_szScheme, m_dwSchemeNameLength);
  1722. lpszUrl += m_dwSchemeNameLength;
  1723. *lpszUrl++ = ':';
  1724. if (m_nScheme != ATL_URL_SCHEME_MAILTO)
  1725. {
  1726. *lpszUrl++ = '/';
  1727. *lpszUrl++ = '/';
  1728. }
  1729. }
  1730. if (*m_szUserName)
  1731. {
  1732. _tcsncpy(lpszUrl, m_szUserName, m_dwUserNameLength);
  1733. lpszUrl += m_dwUserNameLength;
  1734. if (*m_szPassword)
  1735. {
  1736. *lpszUrl++ = ':';
  1737. _tcsncpy(lpszUrl, m_szPassword, m_dwPasswordLength);
  1738. lpszUrl += m_dwPasswordLength;
  1739. }
  1740. *lpszUrl++ = '@';
  1741. }
  1742. if (*m_szHostName)
  1743. {
  1744. _tcsncpy(lpszUrl, m_szHostName, m_dwHostNameLength);
  1745. lpszUrl += m_dwHostNameLength;
  1746. if (m_nPortNumber != AtlGetDefaultUrlPort(m_nScheme))
  1747. {
  1748. DWORD dwPortLen = (DWORD) _tcslen(szPortNumber);
  1749. _tcsncpy(lpszUrl, szPortNumber, dwPortLen);
  1750. lpszUrl += dwPortLen;
  1751. }
  1752. if (*m_szUrlPath && *m_szUrlPath != '/' && *m_szUrlPath != '\\')
  1753. *lpszUrl++ = '/';
  1754. }
  1755. if (*m_szUrlPath)
  1756. {
  1757. _tcsncpy(lpszUrl, m_szUrlPath, m_dwUrlPathLength);
  1758. lpszUrl+= m_dwUrlPathLength;
  1759. }
  1760. if (*m_szExtraInfo)
  1761. {
  1762. _tcsncpy(lpszUrl, m_szExtraInfo, m_dwExtraInfoLength);
  1763. lpszUrl += m_dwExtraInfoLength;
  1764. }
  1765. *lpszUrl = '\0';
  1766. (*pdwMaxLength)--;
  1767. if (dwFlags & ATL_URL_ESCAPE)
  1768. {
  1769. TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
  1770. _tcsncpy(szUrl, lpszOutUrl, *pdwMaxLength+1);
  1771. return AtlUnescapeUrl(szUrl, lpszOutUrl, pdwMaxLength, dwLength);
  1772. }
  1773. return TRUE;
  1774. }
  1775. inline void Clear() throw()
  1776. {
  1777. InitFields();
  1778. }
  1779. inline DWORD GetUrlLength() const throw()
  1780. {
  1781. //The conditionals in this method are related to the conditionals in the CreateUrl method
  1782. //scheme + ':'
  1783. DWORD dwUrlLength = m_dwSchemeNameLength+1;
  1784. //i.e. "//"
  1785. if (m_nScheme != ATL_URL_SCHEME_MAILTO)
  1786. dwUrlLength += 2;
  1787. dwUrlLength += m_dwUserNameLength;
  1788. //i.e. "username@"
  1789. if (m_dwUserNameLength > 0)
  1790. dwUrlLength += m_dwUserNameLength+1;
  1791. //i.e. ":password"
  1792. if (m_dwPasswordLength > 0)
  1793. dwUrlLength += m_dwPasswordLength+1;
  1794. dwUrlLength += m_dwHostNameLength;
  1795. // will need to add an extra '/' in this case
  1796. if (m_dwHostNameLength && m_dwUrlPathLength && *m_szUrlPath != '/' && *m_szUrlPath != '\\')
  1797. dwUrlLength++;
  1798. //i.e. ":xx" where "xx" is the port number
  1799. if (m_nPortNumber != AtlGetDefaultUrlPort(m_nScheme))
  1800. {
  1801. TCHAR szPortTmp[6];
  1802. dwUrlLength += _stprintf(szPortTmp, _T(":%d"), m_nPortNumber);
  1803. }
  1804. dwUrlLength += m_dwUrlPathLength + m_dwExtraInfoLength;
  1805. return dwUrlLength;
  1806. }
  1807. //Get the Scheme Name (i.e. http, ftp, etc.)
  1808. inline LPCTSTR GetSchemeName() const throw()
  1809. {
  1810. return m_szScheme;
  1811. }
  1812. //Get the Scheme Name length
  1813. inline DWORD GetSchemeNameLength() const throw()
  1814. {
  1815. return m_dwSchemeNameLength;
  1816. }
  1817. //This method will incur the cost of
  1818. //validating the scheme and updating the scheme name
  1819. inline BOOL SetSchemeName(LPCTSTR lpszSchm) throw()
  1820. {
  1821. ATLASSERT(lpszSchm != NULL);
  1822. const _schemeinfo *pSchemes = GetSchemes();
  1823. ATLASSERT( pSchemes != NULL );
  1824. int nScheme = -1;
  1825. for (int i=0; i<s_nSchemes; i++)
  1826. {
  1827. if (_tcsicmp(lpszSchm, pSchemes[i].szSchemeName) == 0)
  1828. {
  1829. nScheme = i;
  1830. break;
  1831. }
  1832. }
  1833. if (nScheme != -1)
  1834. {
  1835. m_nScheme = (ATL_URL_SCHEME) nScheme;
  1836. m_dwSchemeNameLength = pSchemes[nScheme].dwSchemeLength;
  1837. m_nPortNumber = (ATL_URL_PORT) pSchemes[nScheme].nUrlPort;
  1838. }
  1839. else
  1840. {
  1841. // unknown scheme
  1842. m_nScheme = ATL_URL_SCHEME_UNKNOWN;
  1843. m_dwSchemeNameLength = (DWORD) _tcslen(lpszSchm);
  1844. m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
  1845. }
  1846. _tcsncpy(m_szScheme, lpszSchm, m_dwSchemeNameLength);
  1847. m_szScheme[m_dwSchemeNameLength] = '\0';
  1848. return TRUE;
  1849. }
  1850. inline BOOL SetScheme(ATL_URL_SCHEME nScheme) throw()
  1851. {
  1852. if ((nScheme < 0) || (nScheme >= s_nSchemes))
  1853. {
  1854. // invalid scheme
  1855. return FALSE;
  1856. }
  1857. const _schemeinfo *pSchemes = GetSchemes();
  1858. ATLASSERT( pSchemes != NULL );
  1859. m_nScheme = (ATL_URL_SCHEME) nScheme;
  1860. m_dwSchemeNameLength = pSchemes[nScheme].dwSchemeLength;
  1861. m_nPortNumber = (ATL_URL_PORT) pSchemes[nScheme].nUrlPort;
  1862. _tcsncpy(m_szScheme, pSchemes[nScheme].szSchemeName, m_dwSchemeNameLength);
  1863. return TRUE;
  1864. }
  1865. inline ATL_URL_SCHEME GetScheme() const throw()
  1866. {
  1867. return m_nScheme;
  1868. }
  1869. //Get the host name
  1870. inline LPCTSTR GetHostName() const throw()
  1871. {
  1872. return m_szHostName;
  1873. }
  1874. //Get the host name's length
  1875. inline DWORD GetHostNameLength() const throw()
  1876. {
  1877. return m_dwHostNameLength;
  1878. }
  1879. //Set the Host name
  1880. inline BOOL SetHostName(LPCTSTR lpszHost) throw()
  1881. {
  1882. ATLASSERT(lpszHost != NULL);
  1883. DWORD dwLen = (DWORD) _tcslen(lpszHost);
  1884. if (dwLen > ATL_URL_MAX_HOST_NAME_LENGTH)
  1885. return FALSE;
  1886. _tcsncpy(m_szHostName, lpszHost, dwLen+1);
  1887. m_dwHostNameLength = dwLen;
  1888. return TRUE;
  1889. }
  1890. //Get the port number in terms of ATL_URL_PORT
  1891. inline ATL_URL_PORT GetPortNumber() const throw()
  1892. {
  1893. return m_nPortNumber;
  1894. }
  1895. //Set the port number in terms of ATL_URL_PORT
  1896. inline BOOL SetPortNumber(ATL_URL_PORT nPrt) throw()
  1897. {
  1898. m_nPortNumber = nPrt;
  1899. return TRUE;
  1900. }
  1901. //Get the user name
  1902. inline LPCTSTR GetUserName() const throw()
  1903. {
  1904. return m_szUserName;
  1905. }
  1906. //Get the user name's length
  1907. inline DWORD GetUserNameLength() const throw()
  1908. {
  1909. return m_dwUserNameLength;
  1910. }
  1911. //Set the user name
  1912. inline BOOL SetUserName(LPCTSTR lpszUser) throw()
  1913. {
  1914. ATLASSERT(lpszUser != NULL);
  1915. DWORD dwLen = (DWORD) _tcslen(lpszUser);
  1916. if (dwLen > ATL_URL_MAX_USER_NAME_LENGTH)
  1917. return FALSE;
  1918. _tcsncpy(m_szUserName, lpszUser, dwLen+1);
  1919. m_dwUserNameLength = dwLen;
  1920. return TRUE;
  1921. }
  1922. //Get the password
  1923. inline LPCTSTR GetPassword() const throw()
  1924. {
  1925. return m_szPassword;
  1926. }
  1927. //Get the password's length
  1928. inline DWORD GetPasswordLength() const throw()
  1929. {
  1930. return m_dwPasswordLength;
  1931. }
  1932. //Set the password
  1933. inline BOOL SetPassword(LPCTSTR lpszPass) throw()
  1934. {
  1935. ATLASSERT(lpszPass != NULL);
  1936. if (*lpszPass && !*m_szUserName)
  1937. return FALSE;
  1938. DWORD dwLen = (DWORD) _tcslen(lpszPass);
  1939. if (dwLen > ATL_URL_MAX_PASSWORD_LENGTH)
  1940. return FALSE;
  1941. _tcsncpy(m_szPassword, lpszPass, dwLen+1);
  1942. m_dwPasswordLength = dwLen;
  1943. return TRUE;
  1944. }
  1945. //Get the url path (everything after scheme and
  1946. //before extra info)
  1947. inline LPCTSTR GetUrlPath() const throw()
  1948. {
  1949. return m_szUrlPath;
  1950. }
  1951. //Get the url path's length
  1952. inline DWORD GetUrlPathLength() const throw()
  1953. {
  1954. return m_dwUrlPathLength;
  1955. }
  1956. //Set the url path
  1957. inline BOOL SetUrlPath(LPCTSTR lpszPath) throw()
  1958. {
  1959. ATLASSERT(lpszPath != NULL);
  1960. DWORD dwLen = (DWORD) _tcslen(lpszPath);
  1961. if (dwLen > ATL_URL_MAX_PATH_LENGTH)
  1962. return FALSE;
  1963. _tcsncpy(m_szUrlPath, lpszPath, dwLen+1);
  1964. m_dwUrlPathLength = dwLen;
  1965. return TRUE;
  1966. }
  1967. //Get extra info (i.e. ?something or #something)
  1968. inline LPCTSTR GetExtraInfo() const throw()
  1969. {
  1970. return m_szExtraInfo;
  1971. }
  1972. //Get extra info's length
  1973. inline DWORD GetExtraInfoLength() const throw()
  1974. {
  1975. return m_dwExtraInfoLength;
  1976. }
  1977. //Set extra info
  1978. inline BOOL SetExtraInfo(LPCTSTR lpszInfo) throw()
  1979. {
  1980. ATLASSERT(lpszInfo != NULL);
  1981. DWORD dwLen = (DWORD) _tcslen(lpszInfo);
  1982. if (dwLen > ATL_URL_MAX_PATH_LENGTH)
  1983. return FALSE;
  1984. _tcsncpy(m_szExtraInfo, lpszInfo, dwLen+1);
  1985. m_dwExtraInfoLength = dwLen;
  1986. return TRUE;
  1987. }
  1988. //Insert Escape characters into URL
  1989. inline BOOL Canonicalize(DWORD dwFlags = 0) throw()
  1990. {
  1991. _tcslwr(m_szScheme);
  1992. TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
  1993. _tcscpy(szTmp, m_szUserName);
  1994. BOOL bRet = AtlEscapeUrl(szTmp, m_szUserName, &m_dwUserNameLength, ATL_URL_MAX_USER_NAME_LENGTH, dwFlags);
  1995. if (bRet)
  1996. {
  1997. _tcscpy(szTmp, m_szPassword);
  1998. bRet = AtlEscapeUrl(szTmp, m_szPassword, &m_dwPasswordLength, ATL_URL_MAX_PASSWORD_LENGTH, dwFlags);
  1999. }
  2000. if (bRet)
  2001. {
  2002. _tcscpy(szTmp, m_szHostName);
  2003. bRet = AtlEscapeUrl(szTmp, m_szHostName, &m_dwHostNameLength, ATL_URL_MAX_HOST_NAME_LENGTH, dwFlags);
  2004. }
  2005. if (bRet)
  2006. {
  2007. _tcscpy(szTmp, m_szUrlPath);
  2008. bRet = AtlEscapeUrl(szTmp, m_szUrlPath, &m_dwUrlPathLength, ATL_URL_MAX_PATH_LENGTH, dwFlags);
  2009. }
  2010. //in ATL_URL_BROWSER mode, the portion of the URL following the '?' or '#' is not encoded
  2011. if (bRet && (dwFlags & ATL_URL_BROWSER_MODE) == 0)
  2012. {
  2013. _tcscpy(szTmp, m_szExtraInfo);
  2014. bRet = AtlEscapeUrl(szTmp+1, m_szExtraInfo+1, &m_dwExtraInfoLength, ATL_URL_MAX_PATH_LENGTH-1, dwFlags);
  2015. if (bRet)
  2016. m_dwExtraInfoLength++;
  2017. }
  2018. return bRet;
  2019. }
  2020. private:
  2021. const static DWORD s_nSchemes = 8;
  2022. struct _schemeinfo
  2023. {
  2024. LPCTSTR szSchemeName;
  2025. DWORD dwSchemeLength;
  2026. ATL_URL_PORT nUrlPort;
  2027. };
  2028. const _schemeinfo * GetSchemes() throw()
  2029. {
  2030. const static _schemeinfo s_schemes[] =
  2031. {
  2032. { _T("ftp"), sizeof("ftp")-1, ATL_URL_DEFAULT_FTP_PORT },
  2033. { _T("gopher"), sizeof("gopher")-1, ATL_URL_DEFAULT_GOPHER_PORT },
  2034. { _T("http"), sizeof("http")-1, ATL_URL_DEFAULT_HTTP_PORT },
  2035. { _T("https"), sizeof("https")-1, ATL_URL_DEFAULT_HTTPS_PORT },
  2036. { _T("file"), sizeof("file")-1, ATL_URL_INVALID_PORT_NUMBER },
  2037. { _T("news"), sizeof("news")-1, ATL_URL_INVALID_PORT_NUMBER },
  2038. { _T("mailto"), sizeof("mailto")-1, ATL_URL_INVALID_PORT_NUMBER },
  2039. { _T("socks"), sizeof("socks")-1, ATL_URL_DEFAULT_SOCKS_PORT }
  2040. };
  2041. return s_schemes;
  2042. }
  2043. inline BOOL Parse(LPCTSTR lpszUrl) throw()
  2044. {
  2045. ATLASSERT(lpszUrl != NULL);
  2046. TCHAR ch;
  2047. BOOL bGotScheme = FALSE;
  2048. BOOL bGotUserName = FALSE;
  2049. BOOL bGotHostName = FALSE;
  2050. BOOL bGotPortNumber = FALSE;
  2051. TCHAR szCurrentUrl[ATL_URL_MAX_URL_LENGTH+6];
  2052. TCHAR* pszCurrentUrl = szCurrentUrl;
  2053. //parse lpszUrl using szCurrentUrl to store temporary data
  2054. //this loop will get the following if it exists:
  2055. //<protocol>://user:pass@server:port
  2056. while ((ch = *lpszUrl) != '\0')
  2057. {
  2058. if (ch == ':')
  2059. {
  2060. //3 cases:
  2061. //(1) Just encountered a scheme
  2062. //(2) Port number follows
  2063. //(3) Form of username:password@
  2064. // Check to see if we've just encountered a scheme
  2065. *pszCurrentUrl = '\0';
  2066. if (!bGotScheme)
  2067. {
  2068. if (!SetSchemeName(szCurrentUrl))
  2069. goto error;
  2070. //Set a flag to avoid checking for
  2071. //schemes everytime we encounter a :
  2072. bGotScheme = TRUE;
  2073. if (*(lpszUrl+1) == '/')
  2074. {
  2075. if (*(lpszUrl+2) == '/')
  2076. {
  2077. //the mailto scheme cannot have a '/' following the "mailto:" portion
  2078. if (bGotScheme && m_nScheme == ATL_URL_SCHEME_MAILTO)
  2079. goto error;
  2080. //Skip these characters and continue
  2081. lpszUrl+= 2;
  2082. }
  2083. else
  2084. {
  2085. //it is an absolute path
  2086. //no domain name, port, username, or password is allowed in this case
  2087. //break to loop that gets path
  2088. lpszUrl++;
  2089. pszCurrentUrl = szCurrentUrl;
  2090. break;
  2091. }
  2092. }
  2093. //reset pszCurrentUrl
  2094. pszCurrentUrl = szCurrentUrl;
  2095. lpszUrl++;
  2096. //if the scheme is file, skip to getting the path information
  2097. if (m_nScheme == ATL_URL_SCHEME_FILE)
  2098. break;
  2099. continue;
  2100. }
  2101. else if (!bGotUserName || !bGotPortNumber)
  2102. {
  2103. //It must be a username:password or a port number
  2104. *pszCurrentUrl = '\0';
  2105. pszCurrentUrl = szCurrentUrl;
  2106. TCHAR tmpBuf[ATL_URL_MAX_PASSWORD_LENGTH];
  2107. TCHAR* pTmpBuf = tmpBuf;
  2108. int nCnt = 0;
  2109. //get the user or portnumber (break on either '/', '@', or '\0'
  2110. while (((ch = *(++lpszUrl)) != '/') && (ch != '@') && (ch != '\0'))
  2111. {
  2112. if (nCnt >= ATL_URL_MAX_PASSWORD_LENGTH)
  2113. goto error;
  2114. *pTmpBuf++ = ch;
  2115. nCnt++;
  2116. }
  2117. *pTmpBuf = '\0';
  2118. //if we broke on a '/' or a '\0', it must be a port number
  2119. if (!bGotPortNumber && (ch == '/' || ch == '\0'))
  2120. {
  2121. //the host name must immediately preced the port number
  2122. if (!SetHostName(szCurrentUrl))
  2123. goto error;
  2124. //get the port number
  2125. m_nPortNumber = (ATL_URL_PORT) _ttoi(tmpBuf);
  2126. if (m_nPortNumber < 0)
  2127. goto error;
  2128. bGotPortNumber = bGotHostName = TRUE;
  2129. }
  2130. else if (!bGotUserName && ch=='@')
  2131. {
  2132. //otherwise it must be a username:password
  2133. if (!SetUserName(szCurrentUrl) || !SetPassword(tmpBuf))
  2134. goto error;
  2135. bGotUserName = TRUE;
  2136. lpszUrl++;
  2137. }
  2138. else
  2139. {
  2140. goto error;
  2141. }
  2142. }
  2143. }
  2144. else if (ch == '@')
  2145. {
  2146. if (bGotUserName)
  2147. goto error;
  2148. //type is userinfo@
  2149. *pszCurrentUrl = '\0';
  2150. if (!SetUserName(szCurrentUrl))
  2151. goto error;
  2152. bGotUserName = TRUE;
  2153. lpszUrl++;
  2154. pszCurrentUrl = szCurrentUrl;
  2155. }
  2156. else if (ch == '/' || ch == '?' || (!*(lpszUrl+1)))
  2157. {
  2158. //we're at the end of this loop
  2159. //set the domain name and break
  2160. if (!*(lpszUrl+1) && ch != '/' && ch != '?')
  2161. {
  2162. *pszCurrentUrl++ = ch;
  2163. lpszUrl++;
  2164. }
  2165. *pszCurrentUrl = '\0';
  2166. if (!bGotHostName)
  2167. {
  2168. if (!SetHostName(szCurrentUrl))
  2169. goto error;
  2170. }
  2171. pszCurrentUrl = szCurrentUrl;
  2172. break;
  2173. }
  2174. else
  2175. {
  2176. *pszCurrentUrl++ = ch;
  2177. lpszUrl++;
  2178. }
  2179. }
  2180. if (!bGotScheme)
  2181. goto error;
  2182. //Now build the path
  2183. while ((ch = *lpszUrl) != '\0')
  2184. {
  2185. //break on a '#' or a '?', which delimit "extra information"
  2186. if (m_nScheme != ATL_URL_SCHEME_FILE && (ch == '#' || ch == '?'))
  2187. {
  2188. break;
  2189. }
  2190. *pszCurrentUrl++ = ch;
  2191. lpszUrl++;
  2192. }
  2193. *pszCurrentUrl = '\0';
  2194. if (*szCurrentUrl != '\0' && !SetUrlPath(szCurrentUrl))
  2195. goto error;
  2196. pszCurrentUrl = szCurrentUrl;
  2197. while ((ch = *lpszUrl++) != '\0')
  2198. {
  2199. *pszCurrentUrl++ = ch;
  2200. }
  2201. *pszCurrentUrl = '\0';
  2202. if (*szCurrentUrl != '\0' && !SetExtraInfo(szCurrentUrl))
  2203. goto error;
  2204. switch(m_nScheme)
  2205. {
  2206. case ATL_URL_SCHEME_FILE:
  2207. m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
  2208. break;
  2209. case ATL_URL_SCHEME_NEWS:
  2210. m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
  2211. break;
  2212. case ATL_URL_SCHEME_MAILTO:
  2213. m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
  2214. break;
  2215. default:
  2216. if (!bGotPortNumber)
  2217. m_nPortNumber = (unsigned short)AtlGetDefaultUrlPort(m_nScheme);
  2218. }
  2219. return TRUE;
  2220. error:
  2221. InitFields();
  2222. return FALSE;
  2223. }
  2224. ATL_NOINLINE void InitFields() throw()
  2225. {
  2226. m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
  2227. m_nScheme = ATL_URL_SCHEME_UNKNOWN;
  2228. m_dwSchemeNameLength = 0;
  2229. m_dwHostNameLength = 0;
  2230. m_dwUserNameLength = 0;
  2231. m_dwUrlPathLength = 0;
  2232. m_dwPasswordLength = 0;
  2233. m_dwExtraInfoLength = 0;
  2234. m_szScheme[0] = '\0';
  2235. m_szHostName[0] = '\0';
  2236. m_szUserName[0] = '\0';
  2237. m_szPassword[0] = '\0';
  2238. m_szUrlPath[0] = '\0';
  2239. m_szExtraInfo[0] = '\0';
  2240. }
  2241. //copy all fields from urlThat
  2242. inline void CopyFields(const CUrl& urlThat) throw()
  2243. {
  2244. _tcsncpy(m_szScheme, urlThat.m_szScheme, urlThat.m_dwSchemeNameLength+1);
  2245. _tcsncpy(m_szHostName, urlThat.m_szHostName, urlThat.m_dwHostNameLength+1);
  2246. _tcsncpy(m_szUserName, urlThat.m_szUserName, urlThat.m_dwUserNameLength+1);
  2247. _tcsncpy(m_szPassword, urlThat.m_szPassword, urlThat.m_dwPasswordLength+1);
  2248. _tcsncpy(m_szUrlPath, urlThat.m_szUrlPath, urlThat.m_dwUrlPathLength+1);
  2249. _tcsncpy(m_szExtraInfo, urlThat.m_szExtraInfo, urlThat.m_dwExtraInfoLength+1);
  2250. m_nPortNumber = urlThat.m_nPortNumber;
  2251. m_nScheme = urlThat.m_nScheme;
  2252. m_dwSchemeNameLength = urlThat.m_dwSchemeNameLength;
  2253. m_dwHostNameLength = urlThat.m_dwHostNameLength;
  2254. m_dwUserNameLength = urlThat.m_dwUserNameLength;
  2255. m_dwPasswordLength = urlThat.m_dwPasswordLength;
  2256. m_dwUrlPathLength = urlThat.m_dwUrlPathLength;
  2257. m_dwExtraInfoLength = urlThat.m_dwExtraInfoLength;
  2258. }
  2259. }; // class CUrl
  2260. typedef CUrl* LPURL;
  2261. typedef const CUrl * LPCURL;
  2262. #ifndef ATL_WORKER_THREAD_WAIT
  2263. #define ATL_WORKER_THREAD_WAIT 10000 // time to wait when shutting down
  2264. #endif
  2265. //
  2266. // CWorkerThread
  2267. // This class creates a worker thread that waits on kernel
  2268. // object handles and executes a specified client
  2269. // function when the handle is signaled
  2270. // To use it, construct an instance, call Initialize
  2271. // then call add AddHandle with the handle of a kernel
  2272. // object and pass a pointer to your implementation
  2273. // of IWorkerThreadClient. Execute on your IWorkerThreadClient
  2274. // implementation will be called when the handle is signaled
  2275. // You can also use AddTimer() to add a waitable timer
  2276. // to the worker thread.
  2277. // If the thread is still active when your object is destroyed
  2278. // you must call RemoveHandle() on each handle that your object
  2279. // owns.
  2280. // To terminate the thread, call Shutdown
  2281. //
  2282. template <class ThreadTraits=DefaultThreadTraits>
  2283. class CWorkerThread
  2284. {
  2285. protected:
  2286. HANDLE m_hThread;
  2287. DWORD m_dwThreadId;
  2288. CWorkerThread<ThreadTraits> *m_pThread;
  2289. struct WorkerClientEntry
  2290. {
  2291. IWorkerThreadClient *pClient;
  2292. DWORD_PTR dwParam;
  2293. };
  2294. CSimpleArray<HANDLE> m_hWaitHandles;
  2295. CSimpleArray<WorkerClientEntry, CSimpleArrayEqualHelperFalse<WorkerClientEntry> > m_ClientEntries;
  2296. CComCriticalSection m_critSec;
  2297. HANDLE m_hRefreshComplete;
  2298. void Refresh() throw()
  2299. {
  2300. ATLASSERT(m_hRefreshComplete);
  2301. BOOL bRet = SetEvent(m_hWaitHandles[1]);
  2302. ATLASSERT(bRet);
  2303. bRet; // unused
  2304. WaitForSingleObject(m_hRefreshComplete, INFINITE);
  2305. }
  2306. public:
  2307. CWorkerThread() throw() :
  2308. m_hThread(NULL),
  2309. m_dwThreadId(0),
  2310. m_hRefreshComplete(NULL),
  2311. m_pThread(NULL)
  2312. {
  2313. }
  2314. ~CWorkerThread() throw()
  2315. {
  2316. Shutdown();
  2317. }
  2318. DWORD GetThreadId() throw()
  2319. {
  2320. if (m_pThread)
  2321. return m_pThread->GetThreadId();
  2322. return m_dwThreadId;
  2323. }
  2324. HANDLE GetThreadHandle() throw()
  2325. {
  2326. if (m_pThread)
  2327. return m_pThread->GetThreadHandle();
  2328. return m_hThread;
  2329. }
  2330. HRESULT Initialize() throw()
  2331. {
  2332. if (m_pThread)
  2333. return E_UNEXPECTED; // already initialized!
  2334. // the object should be initialized first
  2335. ATLASSERT(m_hWaitHandles.GetSize() == 0);
  2336. m_critSec.Init();
  2337. // create the refresh complete event
  2338. m_hRefreshComplete = CreateEvent(NULL, FALSE, FALSE, NULL);
  2339. if (!m_hRefreshComplete)
  2340. {
  2341. m_critSec.Term();
  2342. return AtlHresultFromLastError();
  2343. }
  2344. // add the shutdown event
  2345. HRESULT hr;
  2346. HANDLE hEventShutdown = CreateEvent(NULL, FALSE, FALSE, NULL);
  2347. if (!hEventShutdown)
  2348. {
  2349. hr = AtlHresultFromLastError();
  2350. Shutdown();
  2351. return hr;
  2352. }
  2353. hr = AddHandle(hEventShutdown, NULL, 0);
  2354. if (FAILED(hr))
  2355. {
  2356. CloseHandle(hEventShutdown);
  2357. Shutdown();
  2358. return hr;
  2359. }
  2360. // create the refresh event
  2361. HANDLE hEventRefresh = CreateEvent(NULL, FALSE, FALSE, NULL);
  2362. if (!hEventRefresh)
  2363. {
  2364. hr = AtlHresultFromLastError();
  2365. Shutdown();
  2366. return hr;
  2367. }
  2368. hr = AddHandle(hEventRefresh, NULL, 0);
  2369. if (FAILED(hr))
  2370. {
  2371. CloseHandle(hEventRefresh);
  2372. Shutdown();
  2373. return hr;
  2374. }
  2375. m_hThread = ThreadTraits::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) _WorkerThreadProc,
  2376. this, 0, &m_dwThreadId);
  2377. if (!m_hThread)
  2378. {
  2379. hr = AtlHresultFromLastError();
  2380. Shutdown();
  2381. return hr;
  2382. }
  2383. WaitForSingleObject(m_hRefreshComplete, INFINITE);
  2384. return S_OK;
  2385. }
  2386. HRESULT Initialize(CWorkerThread<ThreadTraits> *pThread)
  2387. {
  2388. if (!pThread)
  2389. return E_INVALIDARG;
  2390. if (m_hThread)
  2391. return E_UNEXPECTED; // already initialized
  2392. if (!m_pThread)
  2393. {
  2394. m_pThread = pThread;
  2395. }
  2396. return S_OK;
  2397. }
  2398. HRESULT AddHandle(HANDLE hObject, IWorkerThreadClient *pClient, DWORD_PTR dwParam) throw()
  2399. {
  2400. if (m_pThread)
  2401. return m_pThread->AddHandle(hObject, pClient, dwParam);
  2402. // Make sure the object has been initialized
  2403. ATLASSERT(m_hRefreshComplete != NULL);
  2404. CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
  2405. HRESULT hr = lock.Lock();
  2406. if (FAILED(hr))
  2407. return hr;
  2408. if (m_hWaitHandles.GetSize() == MAXIMUM_WAIT_OBJECTS)
  2409. {
  2410. return AtlHresultFromWin32(ERROR_INVALID_PARAMETER);
  2411. }
  2412. BOOL bRet = m_hWaitHandles.Add(hObject);
  2413. if (!bRet)
  2414. {
  2415. return E_OUTOFMEMORY;
  2416. }
  2417. WorkerClientEntry entry;
  2418. entry.pClient = pClient;
  2419. entry.dwParam = dwParam;
  2420. bRet = m_ClientEntries.Add(entry);
  2421. if (!bRet)
  2422. {
  2423. m_hWaitHandles.RemoveAt(m_hWaitHandles.GetSize()-1);
  2424. return E_OUTOFMEMORY;
  2425. }
  2426. if (m_hWaitHandles.GetSize() > 2)
  2427. {
  2428. // tell the worker thread to refresh
  2429. Refresh();
  2430. }
  2431. return S_OK;
  2432. }
  2433. #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
  2434. HRESULT AddTimer(DWORD dwInterval, IWorkerThreadClient *pClient, DWORD_PTR dwParam, HANDLE *phTimer) throw()
  2435. {
  2436. if (m_pThread)
  2437. return m_pThread->AddTimer(dwInterval, pClient, dwParam, phTimer);
  2438. // Make sure the object has been initialized
  2439. ATLASSERT(m_hRefreshComplete != NULL);
  2440. ATLASSERT(phTimer);
  2441. *phTimer = NULL;
  2442. HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
  2443. if (!hTimer)
  2444. {
  2445. return AtlHresultFromLastError();
  2446. }
  2447. HRESULT hr;
  2448. LARGE_INTEGER liDueTime;
  2449. liDueTime.QuadPart = -10000 * (__int64) dwInterval;
  2450. BOOL bRet = SetWaitableTimer(hTimer, &liDueTime, dwInterval, NULL, NULL, FALSE);
  2451. if (!bRet)
  2452. {
  2453. hr = AtlHresultFromLastError();
  2454. CloseHandle(hTimer);
  2455. return hr;
  2456. }
  2457. hr = AddHandle(hTimer, pClient, dwParam);
  2458. if (FAILED(hr))
  2459. {
  2460. CloseHandle(hTimer);
  2461. return hr;
  2462. }
  2463. if (phTimer)
  2464. *phTimer = hTimer;
  2465. return S_OK;
  2466. }
  2467. #endif
  2468. HRESULT RemoveHandle(HANDLE hObject) throw()
  2469. {
  2470. if (m_pThread)
  2471. return m_pThread->RemoveHandle(hObject);
  2472. // Make sure the object has been initialized
  2473. ATLASSERT(m_hRefreshComplete != NULL);
  2474. CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
  2475. HRESULT hr = lock.Lock();
  2476. if (FAILED(hr))
  2477. return hr;
  2478. int nIndex = m_hWaitHandles.Find(hObject);
  2479. if (nIndex >= 0)
  2480. {
  2481. ATLASSERT(nIndex < m_ClientEntries.GetSize());
  2482. IWorkerThreadClient *pClient = m_ClientEntries[nIndex].pClient;
  2483. m_hWaitHandles.RemoveAt(nIndex);
  2484. m_ClientEntries.RemoveAt(nIndex);
  2485. Refresh();
  2486. // now it is safe to close the handle
  2487. if (!pClient || FAILED(pClient->CloseHandle(hObject)))
  2488. CloseHandle(hObject);
  2489. }
  2490. return S_OK;
  2491. }
  2492. HRESULT Shutdown(DWORD dwWait=ATL_WORKER_THREAD_WAIT) throw()
  2493. {
  2494. if (m_pThread)
  2495. return S_OK;
  2496. if (!m_hThread)
  2497. {
  2498. RemoveAllClients();
  2499. m_critSec.Term();
  2500. if (m_hRefreshComplete)
  2501. {
  2502. CloseHandle(m_hRefreshComplete);
  2503. m_hRefreshComplete = NULL;
  2504. }
  2505. return S_OK;
  2506. }
  2507. ATLASSERT(m_hWaitHandles.GetSize() > 0);
  2508. SetEvent(m_hWaitHandles[0]);
  2509. DWORD dwRet = WaitForSingleObject(m_hThread, dwWait);
  2510. RemoveAllClients();
  2511. CloseHandle(m_hThread);
  2512. m_hThread = NULL;
  2513. if (m_hRefreshComplete)
  2514. {
  2515. CloseHandle(m_hRefreshComplete);
  2516. m_hRefreshComplete = NULL;
  2517. }
  2518. m_critSec.Term();
  2519. return (dwRet == WAIT_OBJECT_0) ? S_OK : AtlHresultFromWin32(dwRet);
  2520. }
  2521. protected:
  2522. void RemoveAllClients() throw()
  2523. {
  2524. ATLASSERT(m_hWaitHandles.GetSize() == m_ClientEntries.GetSize());
  2525. int nLen = m_hWaitHandles.GetSize();
  2526. for (int i = 0; i < nLen; i++)
  2527. {
  2528. WorkerClientEntry& entry = m_ClientEntries[i];
  2529. if (!entry.pClient || FAILED(entry.pClient->CloseHandle(m_hWaitHandles[i])))
  2530. CloseHandle(m_hWaitHandles[i]);
  2531. }
  2532. m_hWaitHandles.RemoveAll();
  2533. m_ClientEntries.RemoveAll();
  2534. }
  2535. DWORD WorkerThreadProc() throw()
  2536. {
  2537. // Make sure the object has been initialized
  2538. ATLASSERT(m_hRefreshComplete != NULL);
  2539. CSimpleArray<HANDLE> handles(m_hWaitHandles);
  2540. CSimpleArray<WorkerClientEntry, CSimpleArrayEqualHelperFalse<WorkerClientEntry> > clientEntries(m_ClientEntries);
  2541. // tell the main thread we're done copying
  2542. SetEvent(m_hRefreshComplete);
  2543. while (TRUE)
  2544. {
  2545. DWORD dwRet = WaitForMultipleObjects(handles.GetSize(), handles.GetData(),
  2546. FALSE, INFINITE);
  2547. // check for shutdown
  2548. if (dwRet == WAIT_OBJECT_0)
  2549. return 0;
  2550. else if (dwRet == WAIT_OBJECT_0+1) // check for refresh
  2551. {
  2552. handles = m_hWaitHandles;
  2553. clientEntries = m_ClientEntries;
  2554. // tell the main thread we're done copying
  2555. SetEvent(m_hRefreshComplete);
  2556. continue;
  2557. }
  2558. else if (dwRet > WAIT_OBJECT_0 && dwRet < WAIT_OBJECT_0 + handles.GetSize())
  2559. {
  2560. // execute the approriate client
  2561. WorkerClientEntry& entry = clientEntries[dwRet - WAIT_OBJECT_0];
  2562. // We ignore the error code because nothing useful can be done with it in this
  2563. // implementation
  2564. entry.pClient->Execute(entry.dwParam, handles[dwRet - WAIT_OBJECT_0]);
  2565. }
  2566. else
  2567. {
  2568. // this probably means an invalid handle was added
  2569. ATLASSERT(FALSE);
  2570. return 1;
  2571. }
  2572. }
  2573. return 0;
  2574. }
  2575. static DWORD WINAPI _WorkerThreadProc(CWorkerThread *pThis) throw()
  2576. {
  2577. return pThis->WorkerThreadProc();
  2578. }
  2579. }; // class CWorkerThread
  2580. // Use CNoWorkerThread as a template argument for classes
  2581. // that need a worker thread type as a template argument but
  2582. // don't require the services of a worker thread. An example
  2583. // would be CDllCache (atlutil.h) when you want to create a
  2584. // CDllCache with no sweeper thread.
  2585. class CNoWorkerThread
  2586. {
  2587. public:
  2588. DWORD GetThreadId() throw()
  2589. {
  2590. return 0;
  2591. }
  2592. HANDLE GetThreadHandle() throw()
  2593. {
  2594. return NULL;
  2595. }
  2596. HRESULT Initialize() throw()
  2597. {
  2598. return S_OK;
  2599. }
  2600. HRESULT AddHandle(HANDLE /*hObject*/, IWorkerThreadClient * /*pClient*/, DWORD_PTR /*dwParam*/) throw()
  2601. {
  2602. return S_OK;
  2603. }
  2604. HRESULT AddTimer(DWORD /*dwInterval*/, IWorkerThreadClient * /*pClient*/, DWORD_PTR /*dwParam*/, HANDLE * /*phTimer*/) throw()
  2605. {
  2606. return S_OK;
  2607. }
  2608. HRESULT RemoveHandle(HANDLE /*hObject*/) throw()
  2609. {
  2610. return S_OK;
  2611. }
  2612. HRESULT Shutdown(DWORD dwWait=ATL_WORKER_THREAD_WAIT) throw()
  2613. {
  2614. dwWait;
  2615. return S_OK;
  2616. }
  2617. }; // CNoWorkerThread
  2618. class CBrowserCaps : public IBrowserCaps, public CComObjectRootEx<CComSingleThreadModel>
  2619. {
  2620. public:
  2621. BEGIN_COM_MAP(CBrowserCaps)
  2622. COM_INTERFACE_ENTRY(IBrowserCaps)
  2623. END_COM_MAP()
  2624. CBrowserCaps()
  2625. {
  2626. }
  2627. void FinalRelease()
  2628. {
  2629. if (m_pParent)
  2630. m_pParent->Release();
  2631. }
  2632. HRESULT Initialize(IXMLDOMNode * pNode, IBrowserCapsSvc * pSvc)
  2633. {
  2634. if (!pNode)
  2635. return E_POINTER;
  2636. HRESULT hr = pNode->QueryInterface(__uuidof(IXMLDOMElement), (void **)&m_spElement);
  2637. if (FAILED(hr))
  2638. return hr;
  2639. CComPtr<IXMLDOMNamedNodeMap> spList;
  2640. hr = pNode->get_attributes(&spList);
  2641. if (FAILED(hr))
  2642. return hr;
  2643. CComPtr<IXMLDOMNode> spItem;
  2644. hr = spList->getNamedItem((BSTR)L"parent", &spItem);
  2645. if (FAILED(hr))
  2646. return hr;
  2647. if (hr == S_FALSE)
  2648. m_pParent = NULL;
  2649. else
  2650. {
  2651. if (!spItem)
  2652. return E_FAIL;
  2653. CComVariant varVal;
  2654. hr = spItem->get_nodeValue(&varVal);
  2655. if (FAILED(hr))
  2656. return hr;
  2657. varVal.ChangeType(VT_BSTR);
  2658. hr = pSvc->GetCapsUserAgent(varVal.bstrVal, (IBrowserCaps **)&m_pParent);
  2659. if (FAILED(hr))
  2660. return hr;
  2661. }
  2662. return S_OK;
  2663. }
  2664. HRESULT GetPropertyString(BSTR bstrProperty, BSTR * pbstrOut)
  2665. {
  2666. ATLASSERT(m_spElement);
  2667. if (!m_spElement)
  2668. return E_FAIL;
  2669. if (!pbstrOut)
  2670. return E_POINTER;
  2671. *pbstrOut = NULL;
  2672. CComPtr<IXMLDOMNodeList> spList;
  2673. HRESULT hr = m_spElement->getElementsByTagName(bstrProperty, &spList);
  2674. if (FAILED(hr))
  2675. return hr;
  2676. long nLength;
  2677. hr = spList->get_length(&nLength);
  2678. if (FAILED(hr))
  2679. return hr;
  2680. if (nLength == 0)
  2681. {
  2682. if (m_pParent)
  2683. return m_pParent->GetPropertyString(bstrProperty, pbstrOut);
  2684. else
  2685. return E_FAIL;
  2686. }
  2687. // Assume the first one is the correct node
  2688. CComPtr<IXMLDOMNode> spNode;
  2689. hr = spList->get_item(0, &spNode);
  2690. if (FAILED(hr))
  2691. return hr;
  2692. CComBSTR bstrValue;
  2693. hr = spNode->get_text(&bstrValue);
  2694. if (FAILED(hr))
  2695. return hr;
  2696. *pbstrOut = bstrValue.Detach();
  2697. return hr;
  2698. }
  2699. HRESULT GetBooleanPropertyValue(BSTR bstrProperty, BOOL* pbOut)
  2700. {
  2701. if (!pbOut)
  2702. return E_POINTER;
  2703. CComBSTR bstrOut;
  2704. HRESULT hr = GetPropertyString(bstrProperty, &bstrOut);
  2705. if (FAILED(hr))
  2706. return hr;
  2707. if (bstrOut[0] == L'1' && bstrOut.Length() == 1)
  2708. *pbOut = TRUE;
  2709. else
  2710. *pbOut = FALSE;
  2711. return S_OK;
  2712. }
  2713. HRESULT GetBrowserName(BSTR * pbstrName)
  2714. {
  2715. return GetPropertyString(L"name", pbstrName);
  2716. }
  2717. HRESULT GetPlatform(BSTR * pbstrPlatform)
  2718. {
  2719. return GetPropertyString(L"platform", pbstrPlatform);
  2720. }
  2721. HRESULT GetVersion(BSTR * pbstrVersion)
  2722. {
  2723. return GetPropertyString(L"version", pbstrVersion);
  2724. }
  2725. HRESULT GetMajorVer(BSTR * pbstrMajorVer)
  2726. {
  2727. return GetPropertyString(L"majorver", pbstrMajorVer);
  2728. }
  2729. HRESULT GetMinorVer(BSTR * pbstrMinorVer)
  2730. {
  2731. return GetPropertyString(L"minorver", pbstrMinorVer);
  2732. }
  2733. HRESULT SupportsFrames(BOOL* pbFrames)
  2734. {
  2735. return GetBooleanPropertyValue(L"frames", pbFrames);
  2736. }
  2737. HRESULT SupportsTables(BOOL* pbTables)
  2738. {
  2739. return GetBooleanPropertyValue(L"tables", pbTables);
  2740. }
  2741. HRESULT SupportsCookies(BOOL* pbCookies)
  2742. {
  2743. return GetBooleanPropertyValue(L"cookies", pbCookies);
  2744. }
  2745. HRESULT SupportsBackgroundSounds(BOOL* pbBackgroundSounds)
  2746. {
  2747. return GetBooleanPropertyValue(L"backgroundsounds", pbBackgroundSounds);
  2748. }
  2749. HRESULT SupportsVBScript(BOOL* pbVBScript)
  2750. {
  2751. return GetBooleanPropertyValue(L"vbscript", pbVBScript);
  2752. }
  2753. HRESULT SupportsJavaScript(BOOL* pbJavaScript)
  2754. {
  2755. return GetBooleanPropertyValue(L"javascript", pbJavaScript);
  2756. }
  2757. HRESULT SupportsJavaApplets(BOOL* pbJavaApplets)
  2758. {
  2759. return GetBooleanPropertyValue(L"javaapplets", pbJavaApplets);
  2760. }
  2761. HRESULT SupportsActiveXControls(BOOL* pbActiveXControls)
  2762. {
  2763. return GetBooleanPropertyValue(L"ActiveXControls", pbActiveXControls);
  2764. }
  2765. HRESULT SupportsCDF(BOOL* pbCDF)
  2766. {
  2767. return GetBooleanPropertyValue(L"CDF", pbCDF);
  2768. }
  2769. HRESULT SupportsAuthenticodeUpdate(BOOL* pbAuthenticodeUpdate)
  2770. {
  2771. return GetBooleanPropertyValue(L"AuthenticodeUpdate", pbAuthenticodeUpdate);
  2772. }
  2773. HRESULT IsBeta(BOOL* pbIsBeta)
  2774. {
  2775. return GetBooleanPropertyValue(L"beta", pbIsBeta);
  2776. }
  2777. HRESULT IsCrawler(BOOL* pbIsCrawler)
  2778. {
  2779. return GetBooleanPropertyValue(L"Crawler", pbIsCrawler);
  2780. }
  2781. HRESULT IsAOL(BOOL* pbIsAOL)
  2782. {
  2783. return GetBooleanPropertyValue(L"AOL", pbIsAOL);
  2784. }
  2785. HRESULT IsWin16(BOOL* pbIsWin16)
  2786. {
  2787. return GetBooleanPropertyValue(L"Win16", pbIsWin16);
  2788. }
  2789. HRESULT IsAK(BOOL* pbIsAK)
  2790. {
  2791. return GetBooleanPropertyValue(L"AK", pbIsAK);
  2792. }
  2793. HRESULT IsSK(BOOL* pbIsSK)
  2794. {
  2795. return GetBooleanPropertyValue(L"SK", pbIsSK);
  2796. }
  2797. HRESULT IsUpdate(BOOL* pbIsUpdate)
  2798. {
  2799. return GetBooleanPropertyValue(L"Update", pbIsUpdate);
  2800. }
  2801. private:
  2802. CComPtr<IXMLDOMElement> m_spElement;
  2803. CComObjectNoLock<CBrowserCaps> * m_pParent;
  2804. };
  2805. template <class TVal>
  2806. class CWildCardEqualHelper
  2807. {
  2808. public:
  2809. static bool IsEqualKey(LPCWSTR szPattern, LPCWSTR szInput)
  2810. {
  2811. while (*szPattern && *szInput && *szPattern == *szInput)
  2812. {
  2813. szPattern++;
  2814. szInput++;
  2815. }
  2816. if (*szPattern == *szInput)
  2817. return TRUE;
  2818. if (*szPattern == '*')
  2819. {
  2820. szPattern++;
  2821. if (!*szPattern)
  2822. return true;
  2823. while(*szInput)
  2824. {
  2825. if (IsEqualKey(szPattern, szInput))
  2826. return TRUE;
  2827. szInput++;
  2828. }
  2829. }
  2830. return FALSE;
  2831. }
  2832. static bool IsEqualValue(TVal& v1, const TVal& v2)
  2833. {
  2834. return (v1 == v2);
  2835. }
  2836. };
  2837. class CBrowserCapsSvc : public IBrowserCapsSvc,
  2838. public CComObjectRootEx<CComSingleThreadModel>
  2839. {
  2840. public:
  2841. BEGIN_COM_MAP(CBrowserCapsSvc)
  2842. COM_INTERFACE_ENTRY(IBrowserCapsSvc)
  2843. END_COM_MAP()
  2844. HRESULT GetCaps(IHttpServerContext * pContext, IBrowserCaps ** ppOut)
  2845. {
  2846. if (!pContext)
  2847. return E_POINTER;
  2848. if (!ppOut)
  2849. return E_POINTER;
  2850. *ppOut = NULL;
  2851. char szUserAgent[256];
  2852. DWORD dwSize = sizeof(szUserAgent);
  2853. if (!pContext->GetServerVariable("HTTP_USER_AGENT", szUserAgent, &dwSize))
  2854. return E_FAIL;
  2855. CComBSTR bstrAgent = szUserAgent;
  2856. return GetCapsUserAgent(bstrAgent, ppOut);
  2857. }
  2858. HRESULT GetCapsUserAgent(BSTR bstrAgent, IBrowserCaps ** ppOut)
  2859. {
  2860. if (!bstrAgent)
  2861. return E_POINTER;
  2862. if (!ppOut)
  2863. return E_POINTER;
  2864. *ppOut = NULL;
  2865. CComPtr<IXMLDOMNode> spNode;
  2866. spNode = m_Map.Lookup((LPCWSTR)bstrAgent);
  2867. if (spNode != NULL)
  2868. {
  2869. CComObjectNoLock<CBrowserCaps> *pRet = NULL;
  2870. ATLTRY(pRet = new CComObjectNoLock<CBrowserCaps>);
  2871. if (!pRet)
  2872. return E_OUTOFMEMORY;
  2873. HRESULT hr = pRet->Initialize(spNode, this);
  2874. if (FAILED(hr))
  2875. {
  2876. delete pRet;
  2877. return hr;
  2878. }
  2879. pRet->AddRef();
  2880. *ppOut = pRet;
  2881. return S_OK;
  2882. }
  2883. return E_FAIL;
  2884. }
  2885. HRESULT Initialize(HINSTANCE hInstance)
  2886. {
  2887. HRESULT hr = _Initialize(hInstance);
  2888. if (FAILED(hr))
  2889. Clear();
  2890. return hr;
  2891. }
  2892. HRESULT Uninitialize()
  2893. {
  2894. Clear();
  2895. return S_OK;
  2896. }
  2897. private:
  2898. HRESULT _Initialize(HINSTANCE hInstance)
  2899. {
  2900. if (m_spDoc) // Already initialized
  2901. return S_OK;
  2902. HRESULT hr;
  2903. hr = m_spDoc.CoCreateInstance(__uuidof(DOMDocument), NULL, CLSCTX_INPROC);
  2904. if (FAILED(hr))
  2905. return hr;
  2906. if (!m_spDoc)
  2907. return E_FAIL;
  2908. hr = m_spDoc->put_async(VARIANT_FALSE);
  2909. if (FAILED(hr))
  2910. return hr;
  2911. char szPath[MAX_PATH];
  2912. int nRet = GetModuleFileNameA(hInstance, szPath, MAX_PATH);
  2913. if (!nRet)
  2914. return AtlHresultFromLastError();
  2915. LPSTR szMark = strrchr(szPath, '\\');
  2916. ATLASSERT(szMark);
  2917. if (szMark)
  2918. *szMark = '\0';
  2919. CComBSTR bstrFile;
  2920. bstrFile += "file://";
  2921. bstrFile += szPath ;
  2922. bstrFile += "\\browscap.xml";
  2923. CComVariant varFile(bstrFile);
  2924. VARIANT_BOOL varBool;
  2925. hr = m_spDoc->load(varFile, &varBool);
  2926. if (FAILED(hr))
  2927. return hr;
  2928. if (!varBool)
  2929. return E_FAIL;
  2930. hr = m_spDoc->get_documentElement(&m_spRoot);
  2931. if (FAILED(hr))
  2932. return hr;
  2933. if (!m_spRoot)
  2934. return E_FAIL;
  2935. CComPtr<IXMLDOMElement> spElement;
  2936. hr = m_spRoot->QueryInterface(&spElement);
  2937. if (FAILED(hr))
  2938. return hr;
  2939. if (!spElement)
  2940. return E_FAIL;
  2941. CComPtr<IXMLDOMNodeList> spList;
  2942. hr = spElement->getElementsByTagName(L"browser", &spList);
  2943. if (FAILED(hr))
  2944. return hr;
  2945. if (!spList)
  2946. return E_FAIL;
  2947. CComPtr<IXMLDOMNode> spCurrent;
  2948. hr = spList->nextNode(&spCurrent);
  2949. if (FAILED(hr))
  2950. return hr;
  2951. while (spCurrent)
  2952. {
  2953. CComPtr<IXMLDOMNamedNodeMap> spAttrs;
  2954. CComPtr<IXMLDOMNode> spItem;
  2955. DOMNodeType nodeType;
  2956. hr = spCurrent->get_nodeType(&nodeType);
  2957. if (FAILED(hr))
  2958. return hr;
  2959. if (nodeType == NODE_ELEMENT)
  2960. {
  2961. hr = spCurrent->get_attributes(&spAttrs);
  2962. if (FAILED(hr))
  2963. return hr;
  2964. hr = spAttrs->getNamedItem((BSTR)L"user-agent", &spItem);
  2965. if (FAILED(hr))
  2966. return hr;
  2967. CComVariant varVal;
  2968. hr = spItem->get_nodeValue(&varVal);
  2969. if (FAILED(hr))
  2970. return hr;
  2971. hr = varVal.ChangeType(VT_BSTR);
  2972. if (FAILED(hr))
  2973. return hr;
  2974. CComBSTR bstrValue = varVal.bstrVal;
  2975. m_Map.Add((LPCWSTR)bstrValue, spCurrent);
  2976. bstrValue.Detach();
  2977. }
  2978. CComPtr<IXMLDOMNode> spNext;
  2979. spList->nextNode(&spNext);
  2980. spCurrent = spNext;
  2981. }
  2982. return S_OK;
  2983. }
  2984. void Clear()
  2985. {
  2986. if (!m_spDoc)
  2987. return;
  2988. m_Map.RemoveAll();
  2989. m_spRoot.Release();
  2990. m_spDoc.Release();
  2991. }
  2992. CSimpleMap<LPCWSTR, CComPtr<IXMLDOMNode>, CWildCardEqualHelper< CComPtr<IXMLDOMNode> > > m_Map;
  2993. CComCriticalSection m_critSec;
  2994. CComPtr<IXMLDOMDocument> m_spDoc;
  2995. CComPtr<IXMLDOMElement> m_spRoot;
  2996. };
  2997. // Copies a CString into a null-terminated string.
  2998. // pdwDestLen on input is the size of the buffer in characters (including the null)
  2999. // On success, pdwDestLen contains the length of the string in characters (not including the null)
  3000. // On failure, pdwDestLen contains the length of the string including the null.
  3001. template <class StringType>
  3002. inline BOOL CopyCString(const StringType& str, StringType::PXSTR szDest, DWORD *pdwDestLen) throw()
  3003. {
  3004. if (!pdwDestLen)
  3005. return FALSE;
  3006. DWORD dwLen = str.GetLength();
  3007. if (!szDest || *pdwDestLen < (dwLen + 1))
  3008. {
  3009. *pdwDestLen = dwLen + 1;
  3010. return FALSE;
  3011. }
  3012. StringType::PCXSTR szBuffer = str;
  3013. if (szBuffer)
  3014. {
  3015. memcpy(szDest, szBuffer, (dwLen+1) * sizeof(StringType::XCHAR));
  3016. *pdwDestLen = dwLen;
  3017. return TRUE;
  3018. }
  3019. return FALSE;
  3020. }
  3021. // Call this function to convert from a SYSTEMTIME
  3022. // structure to an Http-date as defined in rfc2616
  3023. inline void SystemTimeToHttpDate(const SYSTEMTIME& st, CStringA &strTime)
  3024. {
  3025. static LPCSTR szDays[] = { "Sun", "Mon", "Tue",
  3026. "Wed", "Thu", "Fri", "Sat" };
  3027. static LPCSTR szMonth[] = { "Jan", "Feb", "Mar", "Apr",
  3028. "May", "Jun", "Jul", "Aug", "Sep",
  3029. "Oct", "Nov", "Dec" };
  3030. strTime.Format("%s, %02d %s %d %02d:%02d:%02d GMT",
  3031. szDays[st.wDayOfWeek], st.wDay, szMonth[st.wMonth-1], st.wYear,
  3032. st.wHour, st.wMinute, st.wSecond);
  3033. }
  3034. // RGBToHtml - Converts a COLORREF to a color that can be used in HTML.
  3035. // Eg. RGB(11,22,33) would be converted to #112233
  3036. // color: The color to convert.
  3037. // pbOut: The output buffer that will hold the the resulting color.
  3038. // nBuffer: Specifies the number of bytes in pbOut.
  3039. bool inline RGBToHtml(COLORREF color, LPTSTR pbOut, long nBuffer)
  3040. {
  3041. // make sure the buffer is big enough
  3042. if (nBuffer < (7 * sizeof(TCHAR)))
  3043. return false;
  3044. wsprintf(pbOut, _T("#%0.2x%0.2x%0.2x"),
  3045. GetRValue(color), GetGValue(color), GetBValue(color));
  3046. return true;
  3047. }
  3048. } // namespace ATL
  3049. #pragma warning( pop )
  3050. #endif // __ATLUTIL_H__