Leaked source code of windows server 2003
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.

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