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.

1555 lines
38 KiB

  1. //=======================================================================
  2. //
  3. // Copyright (c) 2001 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // File: URLLogging.cpp
  6. //
  7. // Description:
  8. //
  9. // URL logging utility class
  10. // This class helps you construct the server ping URL and
  11. // then send the ping to the designed server.
  12. //
  13. // The default base URL is defined in IUIdent, under section [IUPingServer]
  14. // and entry is "ServerUrl".
  15. //
  16. // This class implements single-thread version only. So it is suitable
  17. // to call it at operation level, i.e., create a separate object
  18. // for each operation in a single thread.
  19. //
  20. // The ping string send to the ping server has the following format:
  21. // /wutrack.bin
  22. // ?U=<user>
  23. // &C=<client>
  24. // &A=<activity>
  25. // &I=<item>
  26. // &D=<device>
  27. // &P=<platform>
  28. // &L=<language>
  29. // &S=<status>
  30. // &E=<error>
  31. // &M=<message>
  32. // &X=<proxy>
  33. // where
  34. // <user> a static 128-bit value that unique-ifies each copy
  35. // of Windows installed. The class will automatically
  36. // reuse one previously assigned to the running OS; or
  37. // will generate one if it does not exist.
  38. // <client> a string that identifies the entity that performed
  39. // activity <activity>. Here are the possible values
  40. // and their meanings:
  41. // "iu" IU control
  42. // "au" Automatic Updates
  43. // "du" Dynamic Update
  44. // "CDM" Code Download Manager
  45. // "IU_SITE" IU Consumer site
  46. // "IU_Corp" IU Catalog site
  47. // <activity> a letter that identifies the activity performed.
  48. // Here are the possible values and their meanings:
  49. // "n" IU control initization
  50. // "d" detection
  51. // "s" self-update
  52. // "w" download
  53. // "i" installation
  54. // <item> a string that identifies an update item.
  55. // <device> a string that identifies either a device's PNPID when
  56. // device driver not found during detection; or a
  57. // PNPID/CompatID used by item <item> for activity
  58. // <activity> if the item is a device driver.
  59. // <platform> a string that identifies the platform of the running
  60. // OS and processor architecture. The class will
  61. // compute this value for the pingback.
  62. // <language> a string that identifies the language of the OS
  63. // binaries. The class will compute this value for the
  64. // pingback.
  65. // <status> a letter that specifies the status that activity
  66. // <activity> reached. Here are the possible values and
  67. // their meanings:
  68. // "s" succeeded
  69. // "r" succeeded (reboot required)
  70. // "f" failed
  71. // "c" cancelled by user
  72. // "d" declined by user
  73. // "n" no items
  74. // "p" pending
  75. // <error> a 32-bit error code in hex (w/o "0x" as prefix).
  76. // <message> a string that provides additional information for the
  77. // status <status>.
  78. // <proxy> a 32-bit random value in hex for overriding proxy
  79. // caching. This class will compute this value for
  80. // each pingback.
  81. //
  82. //=======================================================================
  83. #include <tchar.h>
  84. #include <windows.h> // ZeroMemory()
  85. #include <shlwapi.h> // PathAppend()
  86. #include <stdlib.h> // srand(), rand(), malloc() and free()
  87. #include <sys/timeb.h> // _ftime() and _timeb
  88. #include <malloc.h> // malloc() and free()
  89. #include <fileutil.h> // GetIndustryUpdateDirectory()
  90. #include <logging.h> // LOG_Block, LOG_ErrorMsg, LOG_Error and LOG_Internet
  91. #include <MemUtil.h> // USES_IU_CONVERSION, W2T() and T2W()
  92. #include <osdet.h> // LookupLocaleString()
  93. #include <download.h> // DownloadFile()
  94. #include <wusafefn.h> // PathCchAppend()
  95. #include <safefunc.h> // SafeFreeNULL()
  96. #include <MISTSafe.h>
  97. #include <URLLogging.h>
  98. // Header of the log file
  99. typedef struct tagULHEADER
  100. {
  101. WORD wVersion; // file version
  102. } ULHEADER, PULHEADER;
  103. #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
  104. #define CACHE_FILE_VERSION ((WORD) 10004) // must be bigger what we had in V3 (10001)
  105. // bug 600602: must end all server URL with '/'
  106. const TCHAR c_tszLiveServerUrl[] = _T("http://wustat.windows.com/");
  107. HRESULT ValidateFileHeader(HANDLE hFile, BOOL fCheckHeader, BOOL fFixHeader);
  108. #ifdef DBG
  109. BOOL MustPingOffline(void)
  110. {
  111. BOOL fRet = FALSE;
  112. HKEY hkey;
  113. if (NO_ERROR == RegOpenKeyEx(
  114. HKEY_LOCAL_MACHINE,
  115. _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate"),
  116. 0,
  117. KEY_QUERY_VALUE | KEY_SET_VALUE,
  118. &hkey))
  119. {
  120. DWORD dwForceOfflinePing;
  121. DWORD dwSize = sizeof(dwForceOfflinePing);
  122. DWORD dwType;
  123. if (NO_ERROR == RegQueryValueEx(
  124. hkey,
  125. _T("ForceOfflinePing"),
  126. 0,
  127. &dwType,
  128. (LPBYTE) &dwForceOfflinePing,
  129. &dwSize))
  130. {
  131. if (REG_DWORD == dwType &&
  132. sizeof(dwForceOfflinePing) == dwSize &&
  133. 1 == dwForceOfflinePing)
  134. {
  135. fRet = TRUE;
  136. }
  137. }
  138. RegCloseKey(hkey);
  139. }
  140. return fRet;
  141. }
  142. #endif
  143. // ----------------------------------------------------------------------------------
  144. //
  145. // PUBLIC MEMBER FUNCTIONS
  146. //
  147. // ----------------------------------------------------------------------------------
  148. CUrlLog::CUrlLog(void)
  149. : m_ptszLiveServerUrl(NULL), m_ptszCorpServerUrl(NULL)
  150. {
  151. Init();
  152. m_tszDefaultClientName[0] = _T('\0');
  153. }
  154. CUrlLog::CUrlLog(LPCTSTR ptszClientName, LPCTSTR ptszLiveServerUrl, LPCTSTR ptszCorpServerUrl)
  155. : m_ptszLiveServerUrl(NULL), m_ptszCorpServerUrl(NULL)
  156. {
  157. Init();
  158. (void) SetDefaultClientName(ptszClientName);
  159. (void) SetLiveServerUrl(ptszLiveServerUrl);
  160. (void) SetCorpServerUrl(ptszCorpServerUrl);
  161. }
  162. CUrlLog::~CUrlLog(void)
  163. {
  164. if (NULL != m_ptszLiveServerUrl)
  165. {
  166. free(m_ptszLiveServerUrl);
  167. }
  168. if (NULL != m_ptszCorpServerUrl)
  169. {
  170. free(m_ptszCorpServerUrl);
  171. }
  172. }
  173. // Assume ptszServerUrl, if non-NULL, is of size INTERNET_MAX_URL_LENGTH in TCHARs
  174. BOOL CUrlLog::SetServerUrl(LPCTSTR ptszUrl, LPTSTR & ptszServerUrl)
  175. {
  176. LPTSTR ptszEnd = NULL;
  177. size_t cchRemaining = 0;
  178. if (NULL == ptszUrl ||
  179. _T('\0') == *ptszUrl)
  180. {
  181. SafeFreeNULL(ptszServerUrl);
  182. }
  183. else if (
  184. // Ensure ptszServerUrl is malloc'ed
  185. (NULL == ptszServerUrl &&
  186. NULL == (ptszServerUrl = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH))) ||
  187. // Copy URL
  188. FAILED(StringCchCopyEx(ptszServerUrl, INTERNET_MAX_URL_LENGTH, ptszUrl, &ptszEnd, &cchRemaining, MISTSAFE_STRING_FLAGS)) ||
  189. // Ensure URL ending with '/'
  190. (_T('/') != ptszEnd[-1] &&
  191. FAILED(StringCchCopyEx(ptszEnd, cchRemaining, _T("/"), NULL, NULL, MISTSAFE_STRING_FLAGS))))
  192. {
  193. SafeFreeNULL(ptszServerUrl);
  194. return FALSE;
  195. }
  196. return TRUE;
  197. }
  198. // Watch out for the size of m_tszDefaultClientName.
  199. BOOL CUrlLog::SetDefaultClientName(LPCTSTR ptszClientName)
  200. {
  201. if (NULL == ptszClientName)
  202. {
  203. // E_INVALIDARG
  204. m_tszDefaultClientName[0] = _T('\0');
  205. return FALSE;
  206. }
  207. return SUCCEEDED(StringCchCopyEx(m_tszDefaultClientName, ARRAYSIZE(m_tszDefaultClientName), ptszClientName, NULL, NULL, MISTSAFE_STRING_FLAGS));
  208. }
  209. HRESULT CUrlLog::Ping(
  210. BOOL fOnline, // online or offline ping
  211. URLLOGDESTINATION destination, // live or corp WU ping server
  212. PHANDLE phQuitEvents, // ptr to handles for cancelling the operation
  213. UINT nQuitEventCount, // number of handles
  214. URLLOGACTIVITY activity,// activity code
  215. URLLOGSTATUS status, // status code
  216. DWORD dwError, // error code
  217. LPCTSTR ptszItemID, // uniquely identify an item
  218. LPCTSTR ptszDeviceID, // PNPID or CompatID
  219. LPCTSTR ptszMessage, // additional info
  220. LPCTSTR ptszClientName) // client name string
  221. {
  222. LOG_Block("CUrlLog::Ping");
  223. LPTSTR ptszUrl = NULL;
  224. HRESULT hr = E_FAIL;
  225. switch (activity)
  226. {
  227. case URLLOGACTIVITY_Initialization: // fall thru
  228. case URLLOGACTIVITY_Detection: // fall thru
  229. case URLLOGACTIVITY_SelfUpdate: // fall thru
  230. case URLLOGACTIVITY_Download: // fall thru
  231. case URLLOGACTIVITY_Installation:
  232. break;
  233. default:
  234. hr = E_INVALIDARG;
  235. goto CleanUp;
  236. }
  237. switch (status)
  238. {
  239. case URLLOGSTATUS_Success: // fall thru
  240. case URLLOGSTATUS_Reboot: // fall thru
  241. case URLLOGSTATUS_Failed: // fall thru
  242. case URLLOGSTATUS_Cancelled: // fall thru
  243. case URLLOGSTATUS_Declined: // fall thru
  244. case URLLOGSTATUS_NoItems: // fall thru
  245. case URLLOGSTATUS_Pending:
  246. break;
  247. default:
  248. hr = E_INVALIDARG;
  249. goto CleanUp;
  250. }
  251. //
  252. // handle optional (nullable) arguments
  253. //
  254. if (NULL == ptszClientName)
  255. {
  256. ptszClientName = m_tszDefaultClientName;
  257. }
  258. if (_T('\0') == *ptszClientName)
  259. {
  260. LOG_Error(_T("client name not initialized"));
  261. hr = E_INVALIDARG;
  262. goto CleanUp;
  263. }
  264. switch (destination)
  265. {
  266. case URLLOGDESTINATION_DEFAULT:
  267. destination = (
  268. NULL == m_ptszCorpServerUrl ||
  269. _T('\0') == *m_ptszCorpServerUrl) ?
  270. URLLOGDESTINATION_LIVE :
  271. URLLOGDESTINATION_CORPWU;
  272. break;
  273. case URLLOGDESTINATION_LIVE: // fall thru
  274. case URLLOGDESTINATION_CORPWU:
  275. break;
  276. default:
  277. hr = E_INVALIDARG;
  278. goto CleanUp;
  279. }
  280. LPCTSTR ptszServerUrl;
  281. if (URLLOGDESTINATION_LIVE == destination)
  282. {
  283. if (NULL != m_ptszLiveServerUrl)
  284. {
  285. ptszServerUrl = m_ptszLiveServerUrl;
  286. }
  287. else
  288. {
  289. ptszServerUrl = c_tszLiveServerUrl;
  290. }
  291. }
  292. else
  293. {
  294. ptszServerUrl = m_ptszCorpServerUrl;
  295. }
  296. if (NULL == ptszServerUrl ||
  297. _T('\0') == *ptszServerUrl)
  298. {
  299. LOG_Error(_T("status server Url not initialized"));
  300. hr = E_INVALIDARG;
  301. goto CleanUp;
  302. }
  303. if (NULL == (ptszUrl = (TCHAR*) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)))
  304. {
  305. hr = E_OUTOFMEMORY;
  306. goto CleanUp;
  307. }
  308. if (FAILED(hr = MakePingUrl(
  309. ptszUrl,
  310. INTERNET_MAX_URL_LENGTH,
  311. ptszServerUrl,
  312. ptszClientName,
  313. activity,
  314. ptszItemID,
  315. ptszDeviceID,
  316. status,
  317. dwError,
  318. ptszMessage)))
  319. {
  320. goto CleanUp;
  321. }
  322. if (fOnline)
  323. {
  324. hr = PingStatus(destination, ptszUrl, phQuitEvents, nQuitEventCount);
  325. if (SUCCEEDED(hr))
  326. {
  327. (void) Flush(phQuitEvents, nQuitEventCount);
  328. goto CleanUp;
  329. }
  330. }
  331. {
  332. USES_IU_CONVERSION;
  333. LPWSTR pwszUrl = T2W(ptszUrl);
  334. HRESULT hr2;
  335. if (NULL == pwszUrl)
  336. {
  337. hr = E_OUTOFMEMORY;
  338. goto CleanUp;
  339. }
  340. ULENTRYHEADER ulentryheader;
  341. ulentryheader.progress = URLLOGPROGRESS_ToBeSent;
  342. ulentryheader.destination = destination;
  343. ulentryheader.wRequestSize = lstrlen(ptszUrl) + 1;
  344. ulentryheader.wServerUrlLen = (WORD) lstrlen(ptszServerUrl);
  345. if (SUCCEEDED(hr2 = SaveEntry(ulentryheader, pwszUrl)))
  346. {
  347. hr = S_FALSE;
  348. }
  349. else if (SUCCEEDED(hr))
  350. {
  351. hr = hr2;
  352. }
  353. }
  354. CleanUp:
  355. if (NULL != ptszUrl)
  356. {
  357. free(ptszUrl);
  358. }
  359. return hr;
  360. }
  361. // ----------------------------------------------------------------------------------
  362. //
  363. // PRIVATE MEMBER FUNCTIONS
  364. //
  365. // ----------------------------------------------------------------------------------
  366. // Init member variables within a constructor. No memory clean-up done here.
  367. void CUrlLog::Init()
  368. {
  369. LookupPingID();
  370. LookupPlatform();
  371. LookupSystemLanguage();
  372. GetLogFileName();
  373. }
  374. // ----------------------------------------------------------------------------------
  375. // Construct a URL used to ping server
  376. //
  377. // Returned value indicates success/failure
  378. // ----------------------------------------------------------------------------------
  379. HRESULT CUrlLog::MakePingUrl(
  380. LPTSTR ptszUrl, // buffer to receive result
  381. int cChars, // the number of chars this buffer can take, including ending null
  382. LPCTSTR ptszBaseUrl, // server URL
  383. LPCTSTR ptszClientName, // which client called
  384. URLLOGACTIVITY activity,
  385. LPCTSTR ptszItemID,
  386. LPCTSTR ptszDeviceID,
  387. URLLOGSTATUS status,
  388. DWORD dwError, // return code of activity
  389. LPCTSTR ptszMessage)
  390. {
  391. HRESULT hr = E_FAIL;
  392. LPTSTR ptszEscapedItemID = NULL;
  393. LPTSTR ptszEscapedDeviceID = NULL;
  394. LPTSTR ptszEscapedMessage = NULL;
  395. LOG_Block("CUrlLog::MakePingUrl");
  396. // Retry to get info strings if we failed within the constructor.
  397. if (_T('\0') == m_tszPlatform[0] ||
  398. _T('\0') == m_tszLanguage[0])
  399. {
  400. LOG_Error(_T("Invalid platform or language info string"));
  401. hr = E_UNEXPECTED;
  402. goto CleanUp;
  403. }
  404. // allocate enough memory for URL manipulation. Since the buffer needs
  405. // to be at least 2Kbytes in size, stack buffer is not suitable here.
  406. // we involve mem utility to similate stack memory allocation
  407. if ((NULL != ptszItemID &&
  408. (NULL == (ptszEscapedItemID = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) ||
  409. !EscapeString(ptszItemID, ptszEscapedItemID, INTERNET_MAX_URL_LENGTH))) ||
  410. (NULL != ptszDeviceID &&
  411. (NULL == (ptszEscapedDeviceID = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) ||
  412. !EscapeString(ptszDeviceID, ptszEscapedDeviceID, INTERNET_MAX_URL_LENGTH))) ||
  413. (NULL != ptszMessage &&
  414. (NULL == (ptszEscapedMessage = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) ||
  415. !EscapeString(ptszMessage, ptszEscapedMessage, INTERNET_MAX_URL_LENGTH))))
  416. {
  417. // Either out-of-memory or the escaped string is too lengthy.
  418. LOG_Error(_T("Out of memory or EscapeString failure"));
  419. hr = E_OUTOFMEMORY; // actually could be HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) as well
  420. goto CleanUp;
  421. }
  422. const TCHAR c_tszEmpty[] = _T("");
  423. // Use system time as proxy cache breaker
  424. SYSTEMTIME st;
  425. GetSystemTime(&st);
  426. hr = StringCchPrintfEx(
  427. ptszUrl,
  428. cChars,
  429. NULL,
  430. NULL,
  431. MISTSAFE_STRING_FLAGS,
  432. _T("%swutrack.bin?U=%s&C=%s&A=%c&I=%s&D=%s&P=%s&L=%s&S=%c&E=%08x&M=%s&X=%02d%02d%02d%02d%02d%02d%03d"),
  433. NULL == ptszBaseUrl ? c_tszEmpty : ptszBaseUrl, // server URL
  434. m_tszPingID, // ping ID
  435. ptszClientName, // client name
  436. activity, // activity code
  437. NULL == ptszEscapedItemID ? c_tszEmpty : ptszEscapedItemID, // escaped item ID
  438. NULL == ptszEscapedDeviceID ? c_tszEmpty : ptszEscapedDeviceID, // escaped device ID
  439. m_tszPlatform, // platform info
  440. m_tszLanguage, // sys lang info
  441. status, // status code
  442. dwError, // activity error code
  443. NULL == ptszEscapedMessage ? c_tszEmpty : ptszEscapedMessage, // escaped message str
  444. st.wYear % 100, // proxy override
  445. st.wMonth,
  446. st.wDay,
  447. st.wHour,
  448. st.wMinute,
  449. st.wSecond,
  450. st.wMilliseconds);
  451. CleanUp:
  452. if (NULL != ptszEscapedItemID)
  453. {
  454. free(ptszEscapedItemID);
  455. }
  456. if (NULL != ptszEscapedDeviceID)
  457. {
  458. free(ptszEscapedDeviceID);
  459. }
  460. if (NULL != ptszEscapedMessage)
  461. {
  462. free(ptszEscapedMessage);
  463. }
  464. return hr;
  465. }
  466. // Obtain the existing ping ID from the registry, or generate one if not available.
  467. void CUrlLog::LookupPingID(void)
  468. {
  469. const TCHAR c_tszRegKeyWU[] = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate");
  470. const TCHAR c_tszRegUrlLogPingID[] = _T("PingID");
  471. BOOL fRet = FALSE;
  472. LONG lErr;
  473. HKEY hkey;
  474. UUID uuidPingID;
  475. LOG_Block("CUrlLog::LookupPingID");
  476. //fixcode: wrap registry manipulation w/ a critical section
  477. if (NO_ERROR == (lErr = RegOpenKeyEx(
  478. HKEY_LOCAL_MACHINE,
  479. c_tszRegKeyWU,
  480. 0,
  481. KEY_QUERY_VALUE | KEY_SET_VALUE,
  482. &hkey)))
  483. {
  484. BOOL fFixValue = TRUE;
  485. DWORD dwSize = sizeof(uuidPingID);
  486. DWORD dwType;
  487. lErr = RegQueryValueEx(
  488. hkey,
  489. c_tszRegUrlLogPingID,
  490. 0,
  491. &dwType,
  492. (LPBYTE) &uuidPingID,
  493. &dwSize);
  494. if (NO_ERROR == lErr)
  495. {
  496. if (REG_BINARY == dwType && sizeof(uuidPingID) == dwSize)
  497. {
  498. // successfully read ping ID from registry
  499. fFixValue = FALSE;
  500. fRet = TRUE;
  501. }
  502. }
  503. else if (ERROR_MORE_DATA == lErr || ERROR_FILE_NOT_FOUND == lErr)
  504. {
  505. // Data not of right length or not found.
  506. // We will try to fix it. Treat it as no error for now.
  507. lErr = NO_ERROR;
  508. }
  509. if (NO_ERROR == lErr)
  510. {
  511. if (fFixValue)
  512. {
  513. MakeUUID(&uuidPingID);
  514. lErr = RegSetValueEx(
  515. hkey,
  516. c_tszRegUrlLogPingID,
  517. 0,
  518. REG_BINARY,
  519. (CONST BYTE*) &uuidPingID,
  520. sizeof(uuidPingID));
  521. if (NO_ERROR == lErr)
  522. {
  523. fRet = TRUE; // This is not a final value yet. Still depends on RegCloseKey().
  524. }
  525. #ifdef DBG
  526. else
  527. {
  528. LOG_ErrorMsg(lErr);
  529. }
  530. #endif
  531. }
  532. }
  533. #ifdef DBG
  534. else
  535. {
  536. LOG_ErrorMsg(lErr);
  537. }
  538. #endif
  539. if (NO_ERROR != (lErr = RegCloseKey(hkey)))
  540. {
  541. if (fFixValue)
  542. {
  543. fRet = FALSE;
  544. }
  545. LOG_ErrorMsg(lErr);
  546. }
  547. }
  548. #ifdef DBG
  549. else
  550. {
  551. LOG_ErrorMsg(lErr);
  552. }
  553. #endif
  554. if (!fRet)
  555. {
  556. // Only happens if something failed.
  557. // Make a ping ID of zeroes.
  558. ZeroMemory(&uuidPingID, sizeof(uuidPingID));
  559. }
  560. LPTSTR p = m_tszPingID;
  561. LPBYTE q = (LPBYTE) &uuidPingID;
  562. for (int i = 0; i<sizeof(uuidPingID); i++, q++)
  563. {
  564. BYTE nibble = *q >> 4; // high nibble
  565. *p++ = nibble >= 0xA ? _T('a') + (nibble - 0xA) : _T('0') + nibble;
  566. nibble = *q & 0xF; // low nibble
  567. *p++ = nibble >= 0xA ? _T('a') + (nibble - 0xA) : _T('0') + nibble;
  568. }
  569. *p = _T('\0');
  570. }
  571. // Obtain platfrom info for ping
  572. void CUrlLog::LookupPlatform(void)
  573. {
  574. LOG_Block("CUrlLog::LookupPlatform");
  575. m_tszPlatform[0] = _T('\0');
  576. OSVERSIONINFOEX osversioninfoex;
  577. ZeroMemory(&osversioninfoex, sizeof(osversioninfoex));
  578. // pretend to be OSVERSIONINFO for W9X/Mil
  579. osversioninfoex.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  580. if (!GetVersionEx((LPOSVERSIONINFO) &osversioninfoex))
  581. {
  582. LOG_ErrorMsg(GetLastError());
  583. return;
  584. }
  585. if (VER_PLATFORM_WIN32_NT == osversioninfoex.dwPlatformId &&
  586. (5 <= osversioninfoex.dwMajorVersion ||
  587. (4 == osversioninfoex.dwMajorVersion &&
  588. 6 <= osversioninfoex.wServicePackMajor)))
  589. {
  590. // OS is Windows NT/2000 or later: Windows NT 4.0 SP6 or later.
  591. // It supports OSVERSIONINFOEX.
  592. osversioninfoex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); // use actual size
  593. if (!GetVersionEx((LPOSVERSIONINFO) &osversioninfoex))
  594. {
  595. LOG_ErrorMsg(GetLastError());
  596. return;
  597. }
  598. }
  599. SYSTEM_INFO systeminfo;
  600. GetSystemInfo(&systeminfo);
  601. (void) StringCchPrintfEx(
  602. m_tszPlatform,
  603. ARRAYSIZE(m_tszPlatform),
  604. NULL,
  605. NULL,
  606. MISTSAFE_STRING_FLAGS,
  607. _T("%lx.%lx.%lx.%lx.%x.%x.%x"),
  608. osversioninfoex.dwMajorVersion,
  609. osversioninfoex.dwMinorVersion,
  610. osversioninfoex.dwBuildNumber,
  611. osversioninfoex.dwPlatformId,
  612. osversioninfoex.wSuiteMask,
  613. osversioninfoex.wProductType,
  614. systeminfo.wProcessorArchitecture);
  615. }
  616. // Obtain system language info for ping
  617. void CUrlLog::LookupSystemLanguage(void)
  618. {
  619. LOG_Block("CUrlLog::LookupSystemLanguage");
  620. (void) LookupLocaleString(m_tszLanguage, ARRAYSIZE(m_tszLanguage), FALSE);
  621. if (0 == _tcscmp(m_tszLanguage, _T("Error")))
  622. {
  623. LOG_Error(_T("call to LookupLocaleString() failed."));
  624. m_tszLanguage[0] = _T('\0');
  625. }
  626. }
  627. // Ping server to report status
  628. // ptszUrl - the URL string to be pinged
  629. // phQuitEvents - ptr to handles for cancelling the operation
  630. // nQuitEventCount - number of handles
  631. HRESULT CUrlLog::PingStatus(URLLOGDESTINATION destination, LPCTSTR ptszUrl, PHANDLE phQuitEvents, UINT nQuitEventCount) const
  632. {
  633. #ifdef DBG
  634. LOG_Block("CUrlLog::PingStatus");
  635. LOG_Internet(_T("Ping request=\"%s\""), ptszUrl);
  636. if (MustPingOffline())
  637. {
  638. LOG_Internet(_T("ForceOfflinePing = 1"));
  639. return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
  640. }
  641. #endif
  642. if (!IsConnected(ptszUrl, URLLOGDESTINATION_LIVE == destination))
  643. {
  644. // There is no connection.
  645. LOG_ErrorMsg(ERROR_CONNECTION_INVALID);
  646. return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
  647. }
  648. if (!HandleEvents(phQuitEvents, nQuitEventCount))
  649. {
  650. LOG_ErrorMsg(E_ABORT);
  651. return E_ABORT;
  652. }
  653. TCHAR tszIUdir[MAX_PATH];
  654. GetIndustryUpdateDirectory(tszIUdir);
  655. DWORD dwFlags = WUDF_CHECKREQSTATUSONLY; // we don't actually need a file,
  656. // just need to check return code
  657. if (URLLOGDESTINATION_CORPWU == destination)
  658. {
  659. // don't allow proxy if destination is corp WU
  660. dwFlags |= WUDF_DONTALLOWPROXY;
  661. }
  662. HRESULT hr = DownloadFile(
  663. ptszUrl,
  664. tszIUdir, // local directory to download file to
  665. NULL, // optional local file name for downloaded file
  666. // if pszLocalPath doesn't contain a file name
  667. NULL, // ptr to bytes downloaded for this file
  668. phQuitEvents, // quit event, if signalled, abort downloading
  669. nQuitEventCount,
  670. NULL,
  671. NULL, // parameter for call back function to use
  672. dwFlags
  673. );
  674. #ifdef DBG
  675. if (FAILED(hr))
  676. {
  677. LOG_Error(_T("DownloadFile() returned error %lx"), hr);
  678. }
  679. #endif
  680. return hr;
  681. }
  682. // Obtain file names for offline ping
  683. void CUrlLog::GetLogFileName(void)
  684. {
  685. const TCHAR c_tszLogFile_Local[] = _T("urllog.dat");
  686. GetIndustryUpdateDirectory(m_tszLogFile);
  687. if (FAILED(PathCchAppend(m_tszLogFile, ARRAYSIZE(m_tszLogFile), c_tszLogFile_Local)))
  688. {
  689. m_tszLogFile[0] = _T('\0');
  690. }
  691. }
  692. // Read cache entry header and request in entry
  693. // hFile - an open file handle to read the entry from
  694. // ulentryheader - reference to the struct to store the entry header
  695. // pwszBuffer - the WCHAR buffer to store the request (including trailing null character) in the entry
  696. // dwBufferSize - the size of buffer in WCHARs
  697. // Returned value:
  698. // S_OK - entry successfully read
  699. // S_FALSE - no more entry to read from the file
  700. // other - error codes
  701. HRESULT CUrlLog::ReadEntry(HANDLE hFile, ULENTRYHEADER & ulentryheader, LPWSTR pwszBuffer, DWORD dwBufferSize) const
  702. {
  703. LOG_Block("CUrlLog::ReadEntry");
  704. DWORD dwBytes;
  705. DWORD dwErr;
  706. if (!ReadFile(
  707. hFile,
  708. &ulentryheader,
  709. sizeof(ulentryheader),
  710. &dwBytes,
  711. NULL))
  712. {
  713. // We failed to read the entry header.
  714. // There is nothing we can do at this point.
  715. dwErr = GetLastError();
  716. LOG_ErrorMsg(dwErr);
  717. return HRESULT_FROM_WIN32(dwErr);
  718. }
  719. if (0 == dwBytes)
  720. {
  721. // This is the end of the file.
  722. // There is no other entries after this point.
  723. return S_FALSE;
  724. }
  725. if (sizeof(ulentryheader) < dwBytes ||
  726. (URLLOGPROGRESS_ToBeSent != ulentryheader.progress &&
  727. URLLOGPROGRESS_Sent != ulentryheader.progress) ||
  728. (URLLOGDESTINATION_LIVE != ulentryheader.destination &&
  729. URLLOGDESTINATION_CORPWU != ulentryheader.destination) ||
  730. dwBufferSize < ulentryheader.wRequestSize ||
  731. ulentryheader.wRequestSize <= ulentryheader.wServerUrlLen)
  732. {
  733. LOG_Error(_T("Invalid entry header"));
  734. return E_FAIL;
  735. }
  736. if (!ReadFile(
  737. hFile,
  738. pwszBuffer,
  739. sizeof(WCHAR) * ulentryheader.wRequestSize,
  740. &dwBytes,
  741. NULL))
  742. {
  743. // We failed to read the string in the entry.
  744. dwErr = GetLastError();
  745. LOG_ErrorMsg(dwErr);
  746. return HRESULT_FROM_WIN32(dwErr);
  747. }
  748. if (dwBytes < sizeof(WCHAR) * ulentryheader.wRequestSize ||
  749. _T('\0') != pwszBuffer[ulentryheader.wRequestSize-1] ||
  750. ulentryheader.wRequestSize-1 != lstrlenW(pwszBuffer))
  751. {
  752. // The entry does not contain the complete string.
  753. return E_FAIL;
  754. }
  755. return S_OK;
  756. }
  757. // Save a string to the log file
  758. // destination - going to the live or corp WU ping server
  759. // wServerUrlLen - length of server URL part of the request, in WCHARs (not including trailing NULL)
  760. // pwszString - the string to be saved into the specific log file
  761. // Returned value:
  762. // S_OK - entry was written to file
  763. // S_FALSE - the file was created by a CUrlLog class of newer version than this; entry was not written to file
  764. // other - error codes; entry was not written to file
  765. HRESULT CUrlLog::SaveEntry(ULENTRYHEADER & ulentryheader, LPCWSTR pwszString) const
  766. {
  767. HRESULT hr;
  768. BOOL fDeleteFile = FALSE;
  769. HANDLE hFile = INVALID_HANDLE_VALUE;
  770. DWORD dwBytes;
  771. LOG_Block("CUrlLog::SaveEntry");
  772. LOG_Internet(
  773. _T("destination = %s"),
  774. URLLOGDESTINATION_LIVE == ulentryheader.destination ? _T("live") : _T("corp WU"));
  775. if (_T('\0') == m_tszLogFile[0])
  776. {
  777. hr = E_UNEXPECTED;
  778. LOG_Error(_T("log file name not initialized"));
  779. goto CleanUp;
  780. }
  781. if(INVALID_HANDLE_VALUE == (hFile = CreateFile(
  782. m_tszLogFile,
  783. GENERIC_READ | GENERIC_WRITE,
  784. 0, // no sharing
  785. NULL,
  786. OPEN_ALWAYS,
  787. FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_RANDOM_ACCESS,
  788. NULL)))
  789. {
  790. // We failed to open or create the file.
  791. // Someone may be currently using it.
  792. //fixcode: allow multiple pingback users
  793. // access the file sequentially.
  794. hr = HRESULT_FROM_WIN32(GetLastError());
  795. LOG_ErrorMsg(hr);
  796. goto CleanUp;
  797. }
  798. hr = ValidateFileHeader(hFile, ERROR_ALREADY_EXISTS == GetLastError(), TRUE);
  799. if (S_OK != hr)
  800. {
  801. if (S_FALSE != hr)
  802. {
  803. // The file header is bad or there was problem validating it.
  804. fDeleteFile = TRUE; // destroy the file and fail the function
  805. }
  806. // else
  807. // The file header has a newer version than this library code.
  808. // Keep the file around.
  809. goto CleanUp;
  810. }
  811. // Set outselves to the right position before writing to the file.
  812. DWORD nCurrPos;
  813. if (INVALID_SET_FILE_POINTER == (nCurrPos = SetFilePointer(
  814. hFile,
  815. 0,
  816. NULL,
  817. FILE_END)))
  818. {
  819. hr = HRESULT_FROM_WIN32(GetLastError());
  820. LOG_ErrorMsg(hr);
  821. goto CleanUp;
  822. }
  823. // Write the entry to the log.
  824. if (!WriteFile(
  825. hFile,
  826. &ulentryheader,
  827. sizeof(ulentryheader),
  828. &dwBytes,
  829. NULL))
  830. {
  831. hr = HRESULT_FROM_WIN32(GetLastError());
  832. LOG_ErrorMsg(hr);
  833. }
  834. if (SUCCEEDED(hr) &&
  835. sizeof(ulentryheader) != dwBytes)
  836. {
  837. LOG_Error(_T("Failed to write entry header to file (%d bytes VS %d bytes)"), sizeof(ulentryheader), dwBytes);
  838. hr = E_FAIL;
  839. }
  840. if (SUCCEEDED(hr) &&
  841. !WriteFile(
  842. hFile,
  843. pwszString,
  844. sizeof(WCHAR) * ulentryheader.wRequestSize,
  845. &dwBytes,
  846. NULL))
  847. {
  848. hr = HRESULT_FROM_WIN32(GetLastError());
  849. LOG_ErrorMsg(hr);
  850. }
  851. if (SUCCEEDED(hr) &&
  852. sizeof(WCHAR) * ulentryheader.wRequestSize != dwBytes)
  853. {
  854. LOG_Error(_T("Failed to write entry header to file (%d bytes VS %d bytes)"), sizeof(WCHAR) * ulentryheader.wRequestSize, dwBytes);
  855. hr = E_FAIL;
  856. }
  857. // We failed to wrote the entry into the log.
  858. if (FAILED(hr))
  859. {
  860. // We don't want to get rid of the other entries.
  861. // We can only try to remove the portion of the entry
  862. // we have appended from the file.
  863. if (INVALID_SET_FILE_POINTER == SetFilePointer(
  864. hFile,
  865. nCurrPos,
  866. NULL,
  867. FILE_BEGIN) ||
  868. !SetEndOfFile(hFile))
  869. {
  870. // We failed to remove the new entry.
  871. hr = HRESULT_FROM_WIN32(GetLastError());
  872. LOG_ErrorMsg(hr);
  873. fDeleteFile = TRUE;
  874. }
  875. // else
  876. // We successfully got rid of this entry.
  877. // And preserved existing entries in log.
  878. }
  879. CleanUp:
  880. if (INVALID_HANDLE_VALUE != hFile)
  881. {
  882. CloseHandle(hFile);
  883. }
  884. if (fDeleteFile)
  885. {
  886. (void) DeleteFile(m_tszLogFile);
  887. // We don't delete the log file if the operation was successful.
  888. // Thus, no need to modify the fRet value even if DeleteFile() failed.
  889. }
  890. return hr;
  891. }
  892. // Send all pending (offline) ping requests to server
  893. HRESULT CUrlLog::Flush(PHANDLE phQuitEvents, UINT nQuitEventCount)
  894. {
  895. LPWSTR pwszBuffer = NULL;
  896. LPTSTR ptszUrl = NULL;
  897. HANDLE hFile = INVALID_HANDLE_VALUE;
  898. BOOL fKeepFile = FALSE;
  899. DWORD dwErr;
  900. HRESULT hr;
  901. LOG_Block("CUrlLog::Flush");
  902. if (NULL == (pwszBuffer = (LPWSTR) malloc(sizeof(WCHAR) * INTERNET_MAX_URL_LENGTH)) ||
  903. NULL == (ptszUrl = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)))
  904. {
  905. hr = E_OUTOFMEMORY;
  906. goto CleanUp;
  907. }
  908. if (_T('\0') == m_tszLogFile[0])
  909. {
  910. hr = E_UNEXPECTED;
  911. LOG_Error(_T("log file name not initialized"));
  912. goto CleanUp;
  913. }
  914. // Open existing log
  915. if(INVALID_HANDLE_VALUE == (hFile = CreateFile(
  916. m_tszLogFile,
  917. GENERIC_READ | GENERIC_WRITE,
  918. 0, // no sharing
  919. NULL,
  920. OPEN_EXISTING,
  921. FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_RANDOM_ACCESS,
  922. NULL)))
  923. {
  924. // We failed to open the file.
  925. // The file may not exist or someone may be currently using it.
  926. dwErr = GetLastError();
  927. if (ERROR_FILE_NOT_FOUND == dwErr)
  928. {
  929. // We are done. There is nothing more to do.
  930. hr = S_OK;
  931. }
  932. else
  933. {
  934. //fixcode: allow multiple pingback users
  935. // access the file sequentially.
  936. LOG_ErrorMsg(dwErr);
  937. hr = HRESULT_FROM_WIN32(dwErr);
  938. }
  939. goto CleanUp;
  940. }
  941. // File opened. Check header.
  942. hr = ValidateFileHeader(hFile, TRUE, FALSE);
  943. if (S_OK != hr)
  944. {
  945. if (S_FALSE == hr)
  946. {
  947. // The file header has a newer version than this library code.
  948. goto CleanUp; // Keep the file around.
  949. }
  950. // else
  951. // The file header is bad or there was problem validating it.
  952. // destroy the file and fail the function
  953. }
  954. else
  955. {
  956. BOOL fLiveServerFailed = FALSE;
  957. BOOL fCorpServerFailed = FALSE;
  958. // It is time to read an entry.
  959. for (;;)
  960. {
  961. ULENTRYHEADER ulentryheader;
  962. if (!HandleEvents(phQuitEvents, nQuitEventCount))
  963. {
  964. hr = E_ABORT;
  965. LOG_ErrorMsg(hr);
  966. break;
  967. }
  968. // Assume we are in the right position to read
  969. // the next entry from the file.
  970. // Read the entry header and request in entry.
  971. if (FAILED(hr = ReadEntry(hFile, ulentryheader, pwszBuffer, INTERNET_MAX_URL_LENGTH)))
  972. {
  973. LOG_Error(_T("Failed to read entry from cache (%#lx)"), hr);
  974. break;
  975. }
  976. if (S_FALSE == hr)
  977. {
  978. // There are no more unprocessed entries.
  979. hr = S_OK;
  980. break;
  981. }
  982. // We have successfully read the entry from the cache file.
  983. if (URLLOGPROGRESS_Sent != ulentryheader.progress)
  984. {
  985. // The entry hasn't been successfully sent yet.
  986. LPCTSTR ptszBaseUrl = NULL;
  987. BOOL *pfWhichServerFailed;
  988. if (URLLOGDESTINATION_LIVE == ulentryheader.destination)
  989. {
  990. ptszBaseUrl = m_ptszLiveServerUrl;
  991. pfWhichServerFailed = &fLiveServerFailed;
  992. }
  993. else
  994. {
  995. ptszBaseUrl = m_ptszCorpServerUrl;
  996. pfWhichServerFailed = &fCorpServerFailed;
  997. }
  998. if (*pfWhichServerFailed)
  999. {
  1000. continue; // this base URL has failed before. go on to the next entry.
  1001. }
  1002. LPTSTR ptszRelativeUrl;
  1003. USES_IU_CONVERSION;
  1004. if (NULL == (ptszRelativeUrl = W2T(pwszBuffer + ulentryheader.wServerUrlLen)))
  1005. {
  1006. // Running out of memory. Will retry later.
  1007. hr = E_OUTOFMEMORY;
  1008. break;
  1009. }
  1010. if (NULL != ptszBaseUrl)
  1011. {
  1012. // Form the request URL
  1013. DWORD dwUrlLen = INTERNET_MAX_URL_LENGTH;
  1014. if (S_OK != UrlCombine( // requires IE3 for 95/NT4
  1015. ptszBaseUrl,
  1016. ptszRelativeUrl,
  1017. ptszUrl,
  1018. &dwUrlLen,
  1019. URL_DONT_SIMPLIFY))
  1020. {
  1021. // Either the buffer is too small to hold both the base and
  1022. // the relative URLs, or the host name is invalid.
  1023. // We will retry this entry just in case we will have a
  1024. // shorter/better host name.
  1025. fKeepFile = TRUE;
  1026. continue; // go on to the next entry
  1027. }
  1028. }
  1029. else
  1030. {
  1031. #if defined(UNICODE) || defined(_UNICODE)
  1032. if (FAILED(hr = StringCchCopyExW(ptszUrl, INTERNET_MAX_URL_LENGTH, pwszBuffer, NULL, NULL, MISTSAFE_STRING_FLAGS)))
  1033. {
  1034. LOG_Error(_T("Failed to construct ping URL (%#lx)"), hr);
  1035. break;
  1036. }
  1037. #else
  1038. if (0 == AtlW2AHelper(ptszUrl, pwszBuffer, INTERNET_MAX_URL_LENGTH))
  1039. {
  1040. // The buffer is probably too small to hold both the base and
  1041. // the relative URLs. We will retry this entry just in case
  1042. // we will have a shorter/better host name.
  1043. fKeepFile = TRUE;
  1044. continue; // go on to the next entry
  1045. }
  1046. #endif
  1047. }
  1048. hr = PingStatus(ulentryheader.destination, ptszUrl, phQuitEvents, nQuitEventCount);
  1049. if (FAILED(hr))
  1050. {
  1051. if (E_ABORT == hr)
  1052. {
  1053. break;
  1054. }
  1055. // We will resend this entry later.
  1056. LOG_Internet(_T("Failed to send message (%#lx). Will retry later."), hr);
  1057. *pfWhichServerFailed = TRUE;
  1058. fKeepFile = TRUE;
  1059. if (fLiveServerFailed && fCorpServerFailed)
  1060. {
  1061. // Failed to send ping messages to both destinations.
  1062. hr = S_OK;
  1063. break;
  1064. }
  1065. continue;
  1066. }
  1067. DWORD dwBytes;
  1068. // Mark the entry off the cache file.
  1069. ulentryheader.progress = URLLOGPROGRESS_Sent;
  1070. // Go to the beginning of the current entry and change the entry header.
  1071. if (INVALID_SET_FILE_POINTER == SetFilePointer(
  1072. hFile,
  1073. - ((LONG) (sizeof(ulentryheader) +
  1074. sizeof(WCHAR) * ulentryheader.wRequestSize)),
  1075. NULL,
  1076. FILE_CURRENT) ||
  1077. !WriteFile(
  1078. hFile,
  1079. &ulentryheader,
  1080. sizeof(ulentryheader),
  1081. &dwBytes,
  1082. NULL))
  1083. {
  1084. // We failed to mark this entry 'sent'.
  1085. hr = HRESULT_FROM_WIN32(GetLastError());
  1086. LOG_ErrorMsg(hr);
  1087. break;
  1088. }
  1089. if (sizeof(ulentryheader) != dwBytes)
  1090. {
  1091. // We failed to write the header.
  1092. LOG_Error(_T("Failed to write header (%d bytes VS %d bytes)"), sizeof(ulentryheader), dwBytes);
  1093. hr = E_FAIL;
  1094. break;
  1095. }
  1096. // Set the file pointer to the start of the next entry
  1097. if (INVALID_SET_FILE_POINTER == SetFilePointer(
  1098. hFile,
  1099. sizeof(WCHAR) * ulentryheader.wRequestSize,
  1100. NULL,
  1101. FILE_CURRENT))
  1102. {
  1103. // We failed to skip the current entry.
  1104. hr = HRESULT_FROM_WIN32(GetLastError());
  1105. LOG_ErrorMsg(hr);
  1106. break;
  1107. }
  1108. }
  1109. }
  1110. }
  1111. CloseHandle(hFile);
  1112. hFile = INVALID_HANDLE_VALUE;
  1113. if ((FAILED(hr) && E_ABORT != hr && E_OUTOFMEMORY != hr) ||
  1114. (SUCCEEDED(hr) && !fKeepFile))
  1115. {
  1116. (void) DeleteFile(m_tszLogFile);
  1117. }
  1118. CleanUp:
  1119. if (NULL != pwszBuffer)
  1120. {
  1121. free(pwszBuffer);
  1122. }
  1123. if (NULL != ptszUrl)
  1124. {
  1125. free(ptszUrl);
  1126. }
  1127. if (INVALID_HANDLE_VALUE != hFile)
  1128. {
  1129. CloseHandle(hFile);
  1130. }
  1131. return hr;
  1132. }
  1133. // Escape unsafe chars in a TCHAR string
  1134. // Returned value: non-zero if successful; zero otherwise
  1135. BOOL EscapeString(
  1136. LPCTSTR ptszUnescaped,
  1137. LPTSTR ptszBuffer,
  1138. DWORD dwCharsInBuffer)
  1139. {
  1140. BOOL fRet = FALSE;
  1141. LOG_Block("CUrlLog::EscapeString");
  1142. if (NULL != ptszUnescaped &&
  1143. NULL != ptszBuffer &&
  1144. 0 != dwCharsInBuffer)
  1145. {
  1146. for (DWORD i=0, j=0; _T('\0') != ptszUnescaped[i] && j+1<dwCharsInBuffer; i++, j++)
  1147. {
  1148. TCHAR tch = ptszUnescaped[i];
  1149. if ((_T('a') <= tch && _T('z') >= tch) ||
  1150. (_T('A') <= tch && _T('Z') >= tch) ||
  1151. (_T('0') <= tch && _T('9') >= tch) ||
  1152. NULL != _tcschr(_T("-_.!~*'()"), tch))
  1153. {
  1154. ptszBuffer[j] = tch;
  1155. }
  1156. else if (j+3 >= dwCharsInBuffer)
  1157. {
  1158. // We don't have enough buffer to hold the escaped string.
  1159. // Bail out.
  1160. break;
  1161. }
  1162. else
  1163. {
  1164. TCHAR nibble = tch >> 4;
  1165. ptszBuffer[j++] = _T('%');
  1166. ptszBuffer[j++] = nibble + (nibble >= 0x0a ? _T('A') - 0x0a : _T('0'));
  1167. nibble = tch & 0x0f;
  1168. ptszBuffer[j] = nibble + (nibble >= 0x0a ? _T('A') - 0x0a : _T('0'));
  1169. }
  1170. }
  1171. if (_T('\0') == ptszUnescaped[i])
  1172. {
  1173. ptszBuffer[j] = _T('\0');
  1174. fRet = TRUE;
  1175. }
  1176. #ifdef DBG
  1177. else
  1178. {
  1179. // Couldn't escape the whole string due to insufficient buffer.
  1180. LOG_ErrorMsg(ERROR_INSUFFICIENT_BUFFER);
  1181. }
  1182. #endif
  1183. }
  1184. #ifdef DBG
  1185. else
  1186. {
  1187. LOG_ErrorMsg(E_INVALIDARG);
  1188. }
  1189. #endif
  1190. return fRet;
  1191. }
  1192. // Create a UUID that is not linked to MAC address of a NIC, if any, on the system.
  1193. // pUuid - ptr to the UUID structure to hold the returning value.
  1194. void MakeUUID(UUID* pUuid)
  1195. {
  1196. HRESULT hr;
  1197. OSVERSIONINFO osverinfo;
  1198. LOG_Block("CUrlLog::MakeUUID");
  1199. // check OS version
  1200. osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);
  1201. if (!(GetVersionEx(&osverinfo)))
  1202. {
  1203. hr = GetLastError();
  1204. #ifdef DBG
  1205. if (FAILED(hr))
  1206. {
  1207. LOG_ErrorMsg(hr); // log this error
  1208. }
  1209. #endif
  1210. }
  1211. else if (5 <= osverinfo.dwMajorVersion && // Check for Win2k & up
  1212. VER_PLATFORM_WIN32_NT == osverinfo.dwPlatformId)
  1213. {
  1214. // The OS is Win2K & up.
  1215. // We can safely use CoCreateGuid().
  1216. hr = CoCreateGuid(pUuid);
  1217. if (SUCCEEDED(hr))
  1218. {
  1219. goto Done;
  1220. }
  1221. LOG_ErrorMsg(hr); // log this error
  1222. }
  1223. // Either the OS is something older than Win2K, or
  1224. // somehow we failed to get a GUID with CoCreateGuid.
  1225. // We still have to do something to resolve the proxy caching problem.
  1226. // Here we construct this psudo GUID by using:
  1227. // - ticks since last reboot
  1228. // - the current process ID
  1229. // - time in seconds since 00:00:00 1/1/1970 UTC
  1230. // - fraction of a second in milliseconds for the above time.
  1231. // - a 15-bit unsigned random number
  1232. //
  1233. pUuid->Data1 = GetTickCount();
  1234. *((DWORD*) &pUuid->Data2) = GetCurrentProcessId();
  1235. // Use the first 6 bytes of m_uuidPingID.Data1 to store sys date/time.
  1236. {
  1237. _timeb tm;
  1238. _ftime(&tm);
  1239. *((DWORD*) &pUuid->Data4) = (DWORD) tm.time;
  1240. ((WORD*) &pUuid->Data4)[2] = tm.millitm;
  1241. }
  1242. // Use the last 2 bytes of m_uuidPingID.Data1 to store another random number.
  1243. srand(pUuid->Data1);
  1244. ((WORD*) &pUuid->Data4)[3] = (WORD) rand(); // rand() returns only positive values.
  1245. Done:
  1246. return;
  1247. }
  1248. // Check and/or fix (if necessary) the header of the log file.
  1249. //
  1250. // Returned value:
  1251. // S_OK - the header has been fixed or the file contains
  1252. // a valid header. The file pointer now points to
  1253. // the first entry in the log file.
  1254. // S_FALSE - the file has a valid header but the version
  1255. // of the file is newer than this library code.
  1256. // The caller should not try to overwrite the
  1257. // file's contents.
  1258. // Others (failure) - the header is invalid or there was
  1259. // a problem accessing the file. The
  1260. // file should be deleted.
  1261. HRESULT ValidateFileHeader(HANDLE hFile, BOOL fCheckHeader, BOOL fFixHeader)
  1262. {
  1263. ULHEADER ulheader;
  1264. DWORD dwBytes;
  1265. HRESULT hr = E_FAIL;
  1266. LOG_Block("ValidateFileHeader");
  1267. if (fCheckHeader)
  1268. {
  1269. DWORD dwFileSize = GetFileSize(hFile, NULL);
  1270. // Log file existed before we opened it
  1271. if (INVALID_FILE_SIZE == dwFileSize)
  1272. {
  1273. hr = HRESULT_FROM_WIN32(GetLastError());
  1274. LOG_ErrorMsg(hr);
  1275. }
  1276. else if (1024 * 100 < dwFileSize) // no more than 100Kbytes
  1277. {
  1278. LOG_Error(_T("too many stale entries in cache."));
  1279. }
  1280. else if (!ReadFile(hFile, &ulheader, sizeof(ulheader), &dwBytes, NULL))
  1281. {
  1282. // We failed to read the header. We must then fix up the
  1283. // header.
  1284. hr = HRESULT_FROM_WIN32(GetLastError());
  1285. LOG_ErrorMsg(hr);
  1286. }
  1287. else if (sizeof(ulheader) == dwBytes)
  1288. {
  1289. if (CACHE_FILE_VERSION < ulheader.wVersion)
  1290. {
  1291. // A log file of newer version already exists.
  1292. // We should not mess it up with an entry of older
  1293. // format. The query string will not be saved.
  1294. LOG_Internet(_T("log file is of a newer version. operation cancelled."));
  1295. return S_FALSE;
  1296. }
  1297. if (CACHE_FILE_VERSION == ulheader.wVersion)
  1298. {
  1299. // Correct version number. We're done.
  1300. return S_OK;
  1301. }
  1302. // else
  1303. // out-dated header
  1304. // We don't care about the entries in it. We will replace everything
  1305. // in order to fix the header.
  1306. }
  1307. // else
  1308. // incorrect header size
  1309. // We don't care about the entries in it. We will replace everything
  1310. // in order to fix the header.
  1311. if (!fFixHeader)
  1312. {
  1313. return hr;
  1314. }
  1315. // Truncate the file to zero byte.
  1316. if (INVALID_SET_FILE_POINTER == SetFilePointer(
  1317. hFile,
  1318. 0,
  1319. NULL,
  1320. FILE_BEGIN) ||
  1321. !SetEndOfFile(hFile))
  1322. {
  1323. // Nothing we can do if we failed to clear the
  1324. // contents of the file in order to fix it up.
  1325. hr = HRESULT_FROM_WIN32(GetLastError());
  1326. LOG_ErrorMsg(hr);
  1327. return hr;
  1328. }
  1329. }
  1330. else if (!fFixHeader)
  1331. {
  1332. // The caller needs to pick at least one operation.
  1333. return E_INVALIDARG;
  1334. }
  1335. // Assume we are at the beginning of the file.
  1336. // We need to (re)initialize the file.
  1337. if (fFixHeader)
  1338. {
  1339. ZeroMemory(&ulheader, sizeof(ulheader));
  1340. ulheader.wVersion = CACHE_FILE_VERSION;
  1341. if (!WriteFile(hFile, &ulheader, sizeof(ulheader), &dwBytes, NULL))
  1342. {
  1343. hr = HRESULT_FROM_WIN32(GetLastError());
  1344. LOG_ErrorMsg(hr);
  1345. return hr;
  1346. }
  1347. else if (sizeof(ulheader) != dwBytes)
  1348. {
  1349. LOG_Error(_T("Failed to write file header (%d bytes VS %d bytes)"), sizeof(ulheader), dwBytes);
  1350. return E_FAIL;
  1351. }
  1352. }
  1353. return S_OK;
  1354. }