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.

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