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.

686 lines
17 KiB

  1. /*-------------------------------------------------------*/
  2. //Copyright (c) 1997 Microsoft Corporation
  3. //
  4. //Module Name: Url Tracking Log Interfaces
  5. //
  6. // Urltrack.cpp
  7. //
  8. //
  9. //Author:
  10. //
  11. // Pei-Hwa Lin (peihwal) 19-March-97
  12. //
  13. //Environment:
  14. //
  15. // User Mode - Win32
  16. //
  17. //Revision History:
  18. // 5/13/97 due to cache container type change, allow
  19. // OPEN_ALWAYS when CreateFile
  20. // 5/14/97 remove IsOnTracking, TRACK_ALL, unused code
  21. /*-------------------------------------------------------*/
  22. #include "priv.h"
  23. #include <wininet.h>
  24. #include "basesb.h"
  25. #include "bindcb.h"
  26. #include <strsafe.h>
  27. const WCHAR c_szPropURL[] = L"HREF";
  28. const WCHAR c_szProptagName[] = L"Item";
  29. const TCHAR c_szLogContainer[] = TEXT("Log");
  30. #define MY_MAX_STRING_LEN 512
  31. //---------------------------------------------------------------------------
  32. //
  33. // IUnknown interfaces
  34. //
  35. //---------------------------------------------------------------------------
  36. HRESULT
  37. CUrlTrackingStg :: QueryInterface(REFIID riid, PVOID *ppvObj)
  38. {
  39. HRESULT hr = E_NOINTERFACE;
  40. *ppvObj = NULL;
  41. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IUrlTrackingStg))
  42. {
  43. AddRef();
  44. *ppvObj = (LPVOID) SAFECAST(this, IUrlTrackingStg *);
  45. hr = S_OK;
  46. }
  47. return hr;
  48. }
  49. ULONG
  50. CUrlTrackingStg :: AddRef(void)
  51. {
  52. _cRef ++;
  53. return _cRef;
  54. }
  55. ULONG
  56. CUrlTrackingStg :: Release(void)
  57. {
  58. ASSERT(_cRef > 0);
  59. _cRef--;
  60. if (!_cRef)
  61. {
  62. //time to go bye bye
  63. delete this;
  64. return 0;
  65. }
  66. return _cRef;
  67. }
  68. //---------------------------------------------------------------------------
  69. //
  70. // C'tor/D'tor
  71. //
  72. //---------------------------------------------------------------------------
  73. CUrlTrackingStg :: CUrlTrackingStg()
  74. {
  75. _hFile = NULL;
  76. _pRecords = NULL;
  77. _lpPfx = NULL;
  78. }
  79. CUrlTrackingStg :: ~CUrlTrackingStg()
  80. {
  81. // browser exit
  82. while (_pRecords)
  83. {
  84. OnUnload(_pRecords->pthisUrl);
  85. };
  86. if (_lpPfx)
  87. {
  88. GlobalFree(_lpPfx);
  89. _lpPfx = NULL;
  90. }
  91. if (_hFile)
  92. {
  93. CloseHandle(_hFile);
  94. _hFile = NULL;
  95. }
  96. }
  97. //---------------------------------------------------------------------------
  98. //
  99. // Helper functions
  100. //
  101. //---------------------------------------------------------------------------
  102. LRecord *
  103. CUrlTrackingStg :: AddNode()
  104. {
  105. LRecord* pTemp;
  106. LRecord* pNew = NULL;
  107. pNew = (LRecord *)LocalAlloc(LPTR, sizeof(LRecord));
  108. if (pNew == NULL)
  109. return NULL;
  110. pNew->pNext = NULL;
  111. if (_pRecords == NULL)
  112. {
  113. //special case for first node
  114. _pRecords = pNew;
  115. }
  116. else
  117. {
  118. for (pTemp = _pRecords; pTemp->pNext; pTemp = pTemp->pNext);
  119. pTemp->pNext = pNew;
  120. }
  121. return pNew;
  122. }
  123. void
  124. CUrlTrackingStg :: DeleteFirstNode()
  125. {
  126. LRecord *pTemp;
  127. if (!_pRecords)
  128. return;
  129. pTemp = _pRecords;
  130. _pRecords = pTemp->pNext;
  131. delete [] pTemp->pthisUrl;
  132. LocalFree(pTemp);
  133. pTemp = NULL;
  134. return;
  135. }
  136. void
  137. CUrlTrackingStg :: DeleteCurrentNode(LRecord *pThis)
  138. {
  139. LRecord *pPrev;
  140. if (_pRecords == pThis)
  141. {
  142. DeleteFirstNode();
  143. return;
  144. }
  145. pPrev = _pRecords;
  146. do
  147. {
  148. if (pPrev->pNext == pThis)
  149. {
  150. pPrev->pNext = pThis->pNext;
  151. delete [] pThis->pthisUrl;
  152. LocalFree(pThis);
  153. pThis = NULL;
  154. break;
  155. }
  156. pPrev = pPrev->pNext;
  157. }
  158. while (pPrev);
  159. return;
  160. }
  161. //
  162. // return Current node by comparing url strings
  163. //
  164. LRecord*
  165. CUrlTrackingStg :: FindCurrentNode
  166. (
  167. IN LPCTSTR lpUrl
  168. )
  169. {
  170. LRecord* pThis = NULL;
  171. ASSERT(_pRecords);
  172. if (!_pRecords) // missed OnLoad
  173. return NULL;
  174. pThis = _pRecords;
  175. do
  176. {
  177. if (!StrCmpI(lpUrl, pThis->pthisUrl))
  178. break;
  179. pThis = pThis->pNext;
  180. }
  181. while (pThis);
  182. return pThis;
  183. }
  184. void
  185. CUrlTrackingStg :: DetermineAppModule()
  186. {
  187. TCHAR szModule[MAX_PATH];
  188. LPTSTR szExt;
  189. if (GetModuleFileName(NULL, szModule, MAX_PATH))
  190. {
  191. szExt = PathFindExtension(szModule);
  192. TraceMsg(0, "tracking: AppModule %s", szModule);
  193. if (StrCmpI(szExt, TEXT(".SCR")) == 0)
  194. _fScreenSaver = TRUE;
  195. else
  196. _fScreenSaver = FALSE;
  197. }
  198. else
  199. _fScreenSaver = FALSE;
  200. _fModule = TRUE;
  201. }
  202. //---------------------------------------------------------------------------
  203. //
  204. // OnLoad(LPTSTR lpUrl, BRMODE context, BOOL fUseCache)
  205. // a new page is loaded
  206. // this function will remember time entering this page, context browsing
  207. // from and page URL string.
  208. // (lpUrl does NOT contain "track:" prefix)
  209. //---------------------------------------------------------------------------
  210. HRESULT
  211. CUrlTrackingStg :: OnLoad
  212. (
  213. IN LPCTSTR lpUrl,
  214. IN BRMODE ContextMode,
  215. IN BOOL fUseCache
  216. )
  217. {
  218. HRESULT hr = E_OUTOFMEMORY;
  219. SYSTEMTIME st;
  220. LRecord* pNewNode = NULL;
  221. GetLocalTime(&st);
  222. pNewNode = AddNode();
  223. if (!pNewNode)
  224. return hr;
  225. int cch = lstrlen(lpUrl)+1;
  226. pNewNode->pthisUrl = (LPTSTR)LocalAlloc(LPTR, cch * sizeof(TCHAR));
  227. if (pNewNode->pthisUrl == NULL)
  228. return hr;
  229. // store log info
  230. hr = StringCchCopy(pNewNode->pthisUrl, cch, lpUrl);
  231. if (SUCCEEDED(hr))
  232. {
  233. if (!_fModule)
  234. DetermineAppModule();
  235. // if it's from SS, the fullscreen flag will be set,
  236. // need to override ContextMode passed in
  237. if (_fScreenSaver)
  238. pNewNode->Context = BM_SCREENSAVER;
  239. else
  240. pNewNode->Context = (ContextMode > BM_THEATER) ? BM_UNKNOWN : ContextMode;
  241. BYTE cei[MAX_CACHE_ENTRY_INFO_SIZE];
  242. LPINTERNET_CACHE_ENTRY_INFO pcei = (LPINTERNET_CACHE_ENTRY_INFO)cei;
  243. DWORD cbcei = MAX_CACHE_ENTRY_INFO_SIZE;
  244. if (GetUrlCacheEntryInfo(lpUrl, pcei, &cbcei))
  245. pNewNode->fuseCache = (pcei->dwHitRate - 1) ? TRUE : FALSE; // off 1 by download
  246. else
  247. pNewNode->fuseCache = 0;
  248. SystemTimeToFileTime(&st, &(pNewNode->ftIn));
  249. }
  250. return hr;
  251. }
  252. //---------------------------------------------------------------------------
  253. //
  254. // OnUnLoad(LPTSTR lpUrl)
  255. // current page is unloaded
  256. // 1)find url cache entry and get file handle
  257. // 2)calculate total time duration visiting this page
  258. // 3)commit delta log string to file cache entry
  259. // (lpUrl contains "Tracking: " prefix)
  260. //
  261. //---------------------------------------------------------------------------
  262. HRESULT
  263. CUrlTrackingStg :: OnUnload
  264. (
  265. IN LPCTSTR lpUrl
  266. )
  267. {
  268. HRESULT hr = E_FAIL;
  269. LPTSTR lpPfxUrl = NULL;
  270. LRecord* pNode = NULL;;
  271. SYSTEMTIME st;
  272. LPINTERNET_CACHE_ENTRY_INFO pce = NULL;
  273. TCHAR lpFile[MAX_PATH];
  274. //
  275. GetLocalTime(&st);
  276. pNode = FindCurrentNode(lpUrl);
  277. if (!pNode)
  278. {
  279. TraceMsg(DM_ERROR, "CUrlTrackingStg: OnUnload (cannot find internal tracking log");
  280. return hr;
  281. }
  282. //QueryCacheEntry() and OpenLogFile() can be combined in one if CacheAPI supports
  283. //WriteUrlCacheEntryStream()
  284. ConvertToPrefixedURL(lpUrl, &lpPfxUrl);
  285. if (!lpPfxUrl)
  286. {
  287. return E_OUTOFMEMORY;
  288. }
  289. pce = QueryCacheEntry(lpPfxUrl);
  290. if (!pce)
  291. {
  292. TraceMsg(DM_ERROR, "CUrlTrackingStg: OnUnload (cannot find url cache entry)");
  293. DeleteCurrentNode(pNode);
  294. // free pce
  295. GlobalFree(lpPfxUrl);
  296. lpPfxUrl = NULL;
  297. return hr;
  298. }
  299. // work around -- begin
  300. hr = WininetWorkAround(lpPfxUrl, pce->lpszLocalFileName, &lpFile[0]);
  301. if (FAILED(hr))
  302. {
  303. TraceMsg(DM_ERROR, "CUrlTrackingStg: OnUnload (failed to work around wininet)");
  304. DeleteCurrentNode(pNode);
  305. if (_hFile)
  306. {
  307. CloseHandle(_hFile);
  308. _hFile = NULL;
  309. }
  310. GlobalFree(lpPfxUrl);
  311. lpPfxUrl = NULL;
  312. return hr;
  313. }
  314. hr = UpdateLogFile(pNode, &st);
  315. // commit change to cache
  316. if(SUCCEEDED(hr))
  317. {
  318. hr = (CommitUrlCacheEntry(lpPfxUrl,
  319. lpFile, //
  320. pce->ExpireTime, //ExpireTime
  321. pce->LastModifiedTime, //LastModifiedTime
  322. pce->CacheEntryType,
  323. NULL, //lpHeaderInfo
  324. 0, //dwHeaderSize
  325. NULL, //lpszFileExtension
  326. 0) ) ? //reserved
  327. S_OK : E_FAIL;
  328. }
  329. // work around -- end
  330. DeleteCurrentNode(pNode);
  331. // free pce
  332. GlobalFree(pce);
  333. pce = NULL;
  334. GlobalFree(lpPfxUrl);
  335. lpPfxUrl = NULL;
  336. return hr;
  337. }
  338. //---------------------------------------------------------------------------
  339. //
  340. // Cache helper funcitons
  341. // This is a workaround for Wininet cache
  342. // Later when we commit change to URL cache will fail if localFile size is changed
  343. // [IN] lpszSourceUrlName and lpszLocalFileName remain the same when calling
  344. // this routine
  345. // [OUT] new local file name
  346. //
  347. //---------------------------------------------------------------------------
  348. HRESULT CUrlTrackingStg :: WininetWorkAround(LPCTSTR lpszUrl, LPCTSTR lpOldFile, LPTSTR lpFile)
  349. {
  350. HRESULT hr = E_FAIL;
  351. ASSERT(!_hFile);
  352. if (!CreateUrlCacheEntry(lpszUrl, 512, TEXT("log"), lpFile, 0))
  353. return E_FAIL;
  354. if (lpOldFile)
  355. {
  356. if (!CopyFile(lpOldFile, lpFile, FALSE))
  357. return E_FAIL;
  358. DeleteFile(lpOldFile);
  359. }
  360. _hFile = OpenLogFile(lpFile);
  361. if (_hFile != INVALID_HANDLE_VALUE)
  362. _hFile = NULL;
  363. return (_hFile) ? S_OK : E_FAIL;
  364. }
  365. LPINTERNET_CACHE_ENTRY_INFO
  366. CUrlTrackingStg :: QueryCacheEntry
  367. (
  368. IN LPCTSTR lpUrl
  369. )
  370. {
  371. // get cache entry info
  372. LPINTERNET_CACHE_ENTRY_INFO lpCE = NULL;
  373. DWORD dwEntrySize;
  374. BOOL bret = FALSE;
  375. lpCE = (LPINTERNET_CACHE_ENTRY_INFO)GlobalAlloc(LPTR, MAX_CACHE_ENTRY_INFO_SIZE);
  376. if (lpCE)
  377. {
  378. dwEntrySize = MAX_CACHE_ENTRY_INFO_SIZE;
  379. while (!(bret = GetUrlCacheEntryInfo(lpUrl, lpCE, &dwEntrySize)))
  380. {
  381. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  382. {
  383. GlobalFree(lpCE);
  384. lpCE = (LPINTERNET_CACHE_ENTRY_INFO)GlobalAlloc(LPTR, dwEntrySize);
  385. if (!lpCE)
  386. break;
  387. }
  388. else
  389. break;
  390. }
  391. }
  392. if (!bret && lpCE)
  393. {
  394. GlobalFree(lpCE);
  395. lpCE = NULL;
  396. SetLastError(ERROR_FILE_NOT_FOUND);
  397. }
  398. return lpCE;
  399. }
  400. //---------------------------------------------------------------------------
  401. //
  402. // File helper funcitons
  403. //
  404. //---------------------------------------------------------------------------
  405. //
  406. // 1)open log file
  407. // 2)move file pointer to end of file
  408. //
  409. HANDLE
  410. CUrlTrackingStg :: OpenLogFile
  411. (
  412. IN LPCTSTR lpFileName
  413. )
  414. {
  415. HANDLE hFile = NULL;
  416. hFile = CreateFile(lpFileName,
  417. GENERIC_WRITE,
  418. FILE_SHARE_READ,
  419. NULL,
  420. OPEN_ALWAYS,
  421. FILE_ATTRIBUTE_NORMAL, // | FILE_FLAG_SEQUENTIAL_SCAN,
  422. NULL);
  423. if (hFile == INVALID_HANDLE_VALUE)
  424. return NULL;
  425. return hFile;
  426. }
  427. const TCHAR c_szLogFormat[] = TEXT("hh':'mm':'ss");
  428. const LPTSTR c_szMode[] = { TEXT("N"), // normal browsing
  429. TEXT("S"), // screen saver
  430. TEXT("D"), // desktop component
  431. TEXT("T"), // theater mode
  432. TEXT("U"), // unknown
  433. };
  434. HRESULT
  435. CUrlTrackingStg :: UpdateLogFile
  436. (
  437. IN LRecord* pNode,
  438. IN SYSTEMTIME* pst
  439. )
  440. {
  441. FILETIME ftOut;
  442. DWORD dwWritten= 0;
  443. HRESULT hr = E_FAIL;
  444. ULARGE_INTEGER ulIn, ulOut, ulTotal;
  445. ASSERT(_hFile);
  446. // calculate delta of time
  447. SystemTimeToFileTime(pst, &ftOut);
  448. // #34829: use 64-bit calculation
  449. ulIn.LowPart = pNode->ftIn.dwLowDateTime;
  450. ulIn.HighPart = pNode->ftIn.dwHighDateTime;
  451. ulOut.LowPart = ftOut.dwLowDateTime;
  452. ulOut.HighPart = ftOut.dwHighDateTime;
  453. QUAD_PART(ulTotal) = QUAD_PART(ulOut) - QUAD_PART(ulIn);
  454. ftOut.dwLowDateTime = ulTotal.LowPart;
  455. ftOut.dwHighDateTime = ulTotal.HighPart;
  456. // log string: timeEnter+Duration
  457. SYSTEMTIME stOut, stIn;
  458. TCHAR lpLogString[MY_MAX_STRING_LEN];
  459. TCHAR pTimeIn[10], pTimeOut[10];
  460. FileTimeToSystemTime(&ftOut, &stOut);
  461. FileTimeToSystemTime(&(pNode->ftIn), &stIn);
  462. GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &stIn, c_szLogFormat, pTimeIn, 10);
  463. GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &stOut, c_szLogFormat, pTimeOut, 10);
  464. // #34832: add Date in logs
  465. // #28266: add LFCR in logs
  466. lpLogString[0] = '\0';
  467. hr = StringCchPrintf(lpLogString, ARRAYSIZE(lpLogString), TEXT("%s %d %.2d-%.2d-%d %s %s\r\n"),
  468. c_szMode[pNode->Context],
  469. pNode->fuseCache,
  470. stIn.wMonth, stIn.wDay, stIn.wYear,
  471. pTimeIn, pTimeOut);
  472. if (SUCCEEDED(hr))
  473. {
  474. // move file pointer to end
  475. if (0xFFFFFFFF == SetFilePointer(_hFile, 0, 0, FILE_END))
  476. {
  477. CloseHandle(_hFile);
  478. _hFile = NULL;
  479. return hr;
  480. }
  481. // write ANSI string to file
  482. char szLogInfo[MY_MAX_STRING_LEN];
  483. SHTCharToAnsi(lpLogString, szLogInfo, ARRAYSIZE(szLogInfo));
  484. hr = (WriteFile(_hFile, szLogInfo, lstrlenA(szLogInfo), &dwWritten, NULL)) ?
  485. S_OK : E_FAIL;
  486. CloseHandle(_hFile);
  487. _hFile = NULL;
  488. }
  489. return hr;
  490. }
  491. //-----------------------------------------------------------------------------
  492. //
  493. // ReadTrackingPrefix
  494. //
  495. // read prefix string from registry
  496. //-----------------------------------------------------------------------------
  497. void
  498. CUrlTrackingStg :: ReadTrackingPrefix(void)
  499. {
  500. DWORD cbPfx = 0;
  501. struct {
  502. INTERNET_CACHE_CONTAINER_INFO cInfo;
  503. TCHAR szBuffer[MAX_PATH+MAX_PATH];
  504. } ContainerInfo;
  505. DWORD dwModified, dwContainer;
  506. HANDLE hEnum;
  507. dwContainer = sizeof(ContainerInfo);
  508. hEnum = FindFirstUrlCacheContainer(&dwModified,
  509. &ContainerInfo.cInfo,
  510. &dwContainer,
  511. 0);
  512. if (hEnum)
  513. {
  514. for (;;)
  515. {
  516. if (!StrCmpI(ContainerInfo.cInfo.lpszName, c_szLogContainer))
  517. {
  518. DWORD cch = lstrlen(ContainerInfo.cInfo.lpszCachePrefix)+1;
  519. ASSERT(ContainerInfo.cInfo.lpszCachePrefix[0]);
  520. _lpPfx = (LPTSTR)GlobalAlloc(LPTR, cch * sizeof(TCHAR));
  521. if (!_lpPfx)
  522. SetLastError(ERROR_OUTOFMEMORY);
  523. StringCchCopy(_lpPfx, cch, ContainerInfo.cInfo.lpszCachePrefix);
  524. break;
  525. }
  526. dwContainer = sizeof(ContainerInfo);
  527. if (!FindNextUrlCacheContainer(hEnum, &ContainerInfo.cInfo, &dwContainer))
  528. {
  529. // This code used to check GetLastError() for ERROR_NO_MORE_ITEMS before
  530. // it would break. Well, that could put us in an infinite loop if the
  531. // reason for failure were something else (like insufficient buffer) because
  532. // wininet would not move forward in it's enumeration and we would not
  533. // have done anything to address the error.
  534. break;
  535. }
  536. }
  537. FindCloseUrlCache(hEnum);
  538. }
  539. }
  540. // caller must free lplpPrefixedUrl
  541. BOOL
  542. CUrlTrackingStg :: ConvertToPrefixedURL(LPCTSTR lpszUrl, LPTSTR *lplpPrefixedUrl)
  543. {
  544. BOOL bret = FALSE;
  545. ASSERT(lpszUrl);
  546. if (!lpszUrl)
  547. return bret;
  548. //ASSERT(lplpPrefixedUrl);
  549. if (!_lpPfx)
  550. ReadTrackingPrefix();
  551. if (_lpPfx)
  552. {
  553. int len = lstrlen(lpszUrl) + lstrlen(_lpPfx) + 1;
  554. *lplpPrefixedUrl = (LPTSTR)GlobalAlloc(LPTR, len * sizeof(TCHAR));
  555. if (*lplpPrefixedUrl)
  556. {
  557. bret = SUCCEEDED(StringCchPrintf(*lplpPrefixedUrl, len, TEXT("%s%s"), _lpPfx, lpszUrl));
  558. }
  559. }
  560. return bret;
  561. }