// // Pei-Hwa Lin (peiwhal), Feb 3, 1997 // // Notes: // Compile switch : NO_FILE_WHEN_CREATE can turn on when wininet cache // would create url cache entry if attached file is empty. // LOG_CACHE_PATH will create log cache at the same level as // content cache. #include "private.h" #undef TF_THISMODULE #define TF_THISMODULE TF_TRACKCACHE #define MY_CACHE_FILE_ENTRY_SIZE 2048 const TCHAR c_szExt[] = TEXT("log"); //used for creating tracking container (registry) const TCHAR c_szLogContainer[] = TEXT("Log"); // if you modify this, modify iedev\inc\inetreg.h REGSTR_PATH_TRACKING too. const TCHAR c_szLogPrefix[] = TEXT("Log:"); const char c_szLogContainerA[] = "Log"; // helper function inline BOOL IsNumber(WCHAR x) { return (x >= L'0' && x <= L'9'); } /*============================================================================= FILEFROMPATH returns the filename of given filename which may include path. =============================================================================*/ LPTSTR FileFromPath( LPCTSTR lpsz ) { LPTSTR lpch; /* Strip path/drive specification from name if there is one */ lpch = CharPrev( lpsz, lpsz + lstrlen(lpsz) ); // special case for "http://server/domain/channel.cdf/" if (*lpch == '/') lpch = CharPrev( lpsz, lpch); while (lpch > lpsz) { if (*lpch == '/') { lpch = CharNext(lpch); break; } lpch = CharPrev( lpsz, lpch); } return(lpch); } /* end FileFromPath */ // CDF updates, create new group and deal with previous posting information void CUrlTrackingCache :: Init(LPCWSTR pwszURL) { DWORD dwRetry; BSTR bstrEncoding = NULL; _groupId = 0; _pwszPostUrl = NULL; _pszChannelUrlSite = NULL; _pszPostUrlSite = NULL; _pwszEncodingMethod = NULL; _groupId = CreateUrlCacheGroup(CACHEGROUP_FLAG_GIDONLY, NULL); WriteLONGLONG(_pCDFStartItem, c_szTrackingCookie, _groupId); // #54653: remove previous tracking information, if any if (SUCCEEDED(ReadBSTR(_pCDFStartItem, c_szPostHeader, &bstrEncoding))) { WriteEMPTY(_pCDFStartItem, c_szPostHeader); SAFEFREEBSTR(bstrEncoding); } if (SUCCEEDED(ReadDWORD(_pCDFStartItem, c_szPostingRetry, &dwRetry))) WriteEMPTY(_pCDFStartItem, c_szPostingRetry); DoBaseURL(pwszURL); return; } void CUrlTrackingCache :: DoBaseURL(LPCWSTR pwszURL) { DWORD cbLen; DWORD useSecurity = 1; ASSERT(!_pszChannelUrlSite) cbLen = (lstrlenW(pwszURL)+1) * sizeof(WCHAR); _pszChannelUrlSite = (LPTSTR)MemAlloc( LPTR, cbLen); #ifdef DEBUG HKEY hkey; // provide security switch for debugging if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, MY_WEBCHECK_POST_REG, 0, KEY_READ, &hkey)) { DWORD cbsize = sizeof(DWORD); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("PostSecurity"), NULL, NULL, (LPBYTE)&useSecurity, &cbsize)) { if ((useSecurity == 0) && (NULL != _pszChannelUrlSite)) StrCpy(_pszChannelUrlSite, TEXT("http://")); } } #endif if ((useSecurity == 1) && (NULL != _pszChannelUrlSite)) { MyOleStrToStrN(_pszChannelUrlSite, cbLen, pwszURL); *(FileFromPath( _pszChannelUrlSite )) = 0; } return; } // only track URLs come from the same server of Channel CDF or LogTarget URL // BOOL CUrlTrackingCache :: IsValidURL(LPCTSTR lpszURL) { BOOL bret; if (!_pszChannelUrlSite || !_pszPostUrlSite) return FALSE; if (!StrCmpNI(lpszURL, _pszChannelUrlSite, lstrlen(_pszChannelUrlSite))) bret = TRUE; else if (!StrCmpNI(lpszURL, _pszPostUrlSite, lstrlen(_pszPostUrlSite))) bret = TRUE; else bret = FALSE; return bret; } #define LOG_CACHE_PATH #ifdef LOG_CACHE_PATH LPSTR PathPreviousBackslashA(LPSTR psz) { LPSTR lpch = CharPrevA(psz, psz + lstrlenA(psz)); for (; *lpch && *lpch != '\\'; lpch=CharPrevA(psz,lpch)); return lpch; } //------------------------------------------------------------------------------ // GetCacheLocation // // Purpose: Return the location of the logging cache // ***** GetUrlCacheConfigInfoW is yet implemented in wininet //------------------------------------------------------------------------------ // HRESULT GetCacheLocation ( LPTSTR pszCacheLocation, DWORD dwSize // no. of chars in pszCacheLocation ) { HRESULT hr = S_OK; DWORD dwLastErr; LPINTERNET_CACHE_CONFIG_INFOA lpCCI = NULL; DWORD dwCCISize = sizeof(INTERNET_CACHE_CONFIG_INFOA); BOOL fOnceErrored = FALSE; while (TRUE) { if ((lpCCI = (LPINTERNET_CACHE_CONFIG_INFOA)MemAlloc(LPTR, dwCCISize)) == NULL) { hr = E_OUTOFMEMORY; goto cleanup; } if (!GetUrlCacheConfigInfoA(lpCCI, &dwCCISize, CACHE_CONFIG_CONTENT_PATHS_FC)) { if ((dwLastErr = GetLastError()) != ERROR_INSUFFICIENT_BUFFER || fOnceErrored) { hr = HRESULT_FROM_WIN32(dwLastErr); goto cleanup; } // // We have insufficient buffer size; reallocate a buffer with the // new dwCCISize set by GetUrlCacheConfigInfo // Set fOnceErrored to TRUE so that we don't loop indefinitely // fOnceErrored = TRUE; } else { // LPSTR pszPath = lpCCI->CachePaths[0].CachePath; INT iLen; PathRemoveBackslashA(pszPath); *(PathPreviousBackslashA(pszPath)) = 0; iLen = lstrlenA(pszPath) + sizeof(CHAR); // + 1 is for the null char if ((((DWORD) iLen + ARRAYSIZE(c_szLogContainer) + 1) * sizeof(TCHAR)) < dwSize) { TCHAR szPathT[MAX_PATH]; SHAnsiToTChar(pszPath, szPathT, ARRAYSIZE(szPathT)); wnsprintf(pszCacheLocation, dwSize, TEXT("%s\\%s"), szPathT, c_szLogContainer); } else { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } break; } SAFELOCALFREE(lpCCI); lpCCI = NULL; } cleanup: if (lpCCI != NULL) { LocalFree(lpCCI); } return hr; } #endif //----------------------------------------------------------------------------- // // ReadTrackingPrefix // // will create tracking container if current profile doesn't have one // **** FindFirstUrlCacheContainerW is yet implemented in wininet // **** FindNextUrlCacheContainerW is yet implemented either //----------------------------------------------------------------------------- LPTSTR ReadTrackingPrefix(void) { LPTSTR lpPfx = NULL; DWORD cbPfx = 0; struct { INTERNET_CACHE_CONTAINER_INFOA cInfo; CHAR szBuffer[MAX_PATH+INTERNET_MAX_URL_LENGTH+1]; } ContainerInfo; DWORD dwModified, dwContainer; HANDLE hEnum; dwContainer = sizeof(ContainerInfo); hEnum = FindFirstUrlCacheContainerA(&dwModified, &ContainerInfo.cInfo, &dwContainer, 0); if (hEnum) { for (;;) { if (!StrCmpIA(ContainerInfo.cInfo.lpszName, c_szLogContainerA)) { ASSERT(ContainerInfo.cInfo.lpszCachePrefix[0]); CHAR szPfx[MAX_PATH]; DWORD cch = ARRAYSIZE(ContainerInfo.cInfo.lpszCachePrefix)+sizeof(CHAR); StrCpyNA(szPfx, ContainerInfo.cInfo.lpszCachePrefix, cch); cch *= sizeof(TCHAR); lpPfx = (LPTSTR)MemAlloc(LPTR, cch); if (!lpPfx) SetLastError(ERROR_OUTOFMEMORY); SHAnsiToTChar(szPfx, lpPfx, cch); break; } dwContainer = sizeof(ContainerInfo); if (!FindNextUrlCacheContainerA(hEnum, &ContainerInfo.cInfo, &dwContainer)) { if (GetLastError() == ERROR_NO_MORE_ITEMS) break; } } FindCloseUrlCache(hEnum); } if (!lpPfx) { LPTSTR pszCachePath = NULL; #ifdef LOG_CACHE_PATH TCHAR szCachePath[MAX_PATH]; pszCachePath = (SUCCEEDED(GetCacheLocation(szCachePath, MAX_PATH))) ? szCachePath : NULL; #endif if (CreateUrlCacheContainer(c_szLogContainer, c_szLogPrefix, pszCachePath, // wininet bug:if NULL, will create under ..\History\Log // 8192, // dwCacheLimit, INTERNET_CACHE_CONTAINER_NOSUBDIRS, // dwContainerType, 0, // dwOptions, NULL, // pvBuffer, 0 // cbBuffer )) { return ReadTrackingPrefix(); } // unable to create Log container, stop. } return lpPfx; } //----------------------------------------------------------------------------- // // ConvertToPrefixedUrl // // caller must release lplpPrefixedUrl //----------------------------------------------------------------------------- BOOL CUrlTrackingCache :: ConvertToPrefixedUrl ( IN LPCTSTR lpUrl, IN LPTSTR* lplpPrefixedUrl ) { BOOL bret = FALSE; ASSERT(lpUrl); if (!_lpPfx) _lpPfx = ReadTrackingPrefix(); if (_lpPfx) { int len = lstrlen(lpUrl) + lstrlen(_lpPfx) + 1; *lplpPrefixedUrl = NULL; *lplpPrefixedUrl = (LPTSTR)MemAlloc(LPTR, len * sizeof(TCHAR)); if (*lplpPrefixedUrl) { wnsprintf(*lplpPrefixedUrl, len, TEXT("%s%s"), _lpPfx, lpUrl); bret = TRUE; } else bret = FALSE; } return bret; } //----------------------------------------------------------------------------- // // RetrieveUrlCacheEntry // // caller must release lpCE //----------------------------------------------------------------------------- LPINTERNET_CACHE_ENTRY_INFO CUrlTrackingCache :: RetrieveUrlCacheEntry ( IN LPCTSTR lpUrl ) { LPINTERNET_CACHE_ENTRY_INFO lpCE = NULL; DWORD cbSize; BOOL bret = FALSE; lpCE = (LPINTERNET_CACHE_ENTRY_INFO)MemAlloc(LPTR, MY_MAX_CACHE_ENTRY_INFO); if (lpCE) { cbSize = MY_MAX_CACHE_ENTRY_INFO; while ((bret = GetUrlCacheEntryInfo(lpUrl, lpCE, &cbSize)) != TRUE) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { SAFELOCALFREE(lpCE); lpCE = (LPINTERNET_CACHE_ENTRY_INFO)MemAlloc(LPTR, cbSize); if (!lpCE) break; } else break; } } if (!bret && lpCE) { SAFELOCALFREE(lpCE); SetLastError(ERROR_FILE_NOT_FOUND); } return lpCE; } #define FILETIME_SEC 100000000 #define SECS_PER_DAY (60 * 60 * 24) DWORD WCTOI(LPCWSTR pwstr) { DWORD dw; int len = lstrlenW(pwstr); dw = 0; for (int i = 0; i> 32); ftModified.dwLowDateTime = (DWORD)(0x00000000ffffffff & _groupId); lpCE = RetrieveUrlCacheEntry(lpPfxUrl); if (lpCE ) { // exist in Tracking bucket, set tracking flag // IE50: save _groupId info in LastModifiedTime lpCE->CacheEntryType |= _ConnectionScope; lpCE->LastModifiedTime.dwHighDateTime = ftModified.dwHighDateTime; lpCE->LastModifiedTime.dwLowDateTime = ftModified.dwLowDateTime; ASSERT(SetUrlCacheEntryInfo(lpCE->lpszSourceUrlName, lpCE, CACHE_ENTRY_ATTRIBUTE_FC | CACHE_ENTRY_MODTIME_FC) == TRUE); hr = S_OK; } else //FILE_NOT_FOUND, create it. { LONGLONG llExpireHorizon; SYSTEMTIME st; FILETIME ftMod, ftExpire; llExpireHorizon = (LONGLONG)(SECS_PER_DAY * _dwPurgeTime / 24); GetLocalTime(&st); SystemTimeToFileTime(&st, &ftMod); llExpireHorizon *= FILETIME_SEC; ftExpire.dwLowDateTime = ftMod.dwLowDateTime + (DWORD)(llExpireHorizon % 0xFFFFFFFF); ftExpire.dwHighDateTime = ftMod.dwHighDateTime + (DWORD)(llExpireHorizon / 0xFFFFFFFF); hr = CreateLogCacheEntry(lpPfxUrl, ftExpire, ftModified, _ConnectionScope); } SAFELOCALFREE(lpCE); return hr; } HRESULT CUrlTrackingCache :: AddToTrackingCacheEntry ( IN LPCWSTR pwszUrl ) { HRESULT hr = E_OUTOFMEMORY; TCHAR szCanonicalUrl[MAX_URL]; DWORD dwSize = MAX_URL; LPTSTR lpUrl = NULL; LPTSTR lpPfxUrl = NULL; DWORD cbSize; PROPVARIANT vProp = {0}; if (pwszUrl == NULL) return E_INVALIDARG; cbSize = lstrlenW(pwszUrl) + 1; lpUrl = (LPTSTR)MemAlloc(LPTR, cbSize * sizeof(TCHAR)); if (!lpUrl) return hr; SHUnicodeToTChar(pwszUrl, lpUrl, cbSize); if (!IsValidURL(lpUrl)) { MemFree(lpUrl); return E_INVALIDARG; } // canonicalize URL InternetCanonicalizeUrl(lpUrl, szCanonicalUrl, &dwSize, ICU_DECODE); SAFELOCALFREE(lpUrl); ConvertToPrefixedUrl(szCanonicalUrl, &lpPfxUrl); if (!lpPfxUrl) { return hr; } hr = CreatePrefixedCacheEntry(lpPfxUrl); if (SUCCEEDED(hr)) { // exist in Tracking bucket, set tracking flag. vProp.vt = VT_UI4; vProp.ulVal = _ConnectionScope; hr = IntSiteHelper(szCanonicalUrl, &c_rgPropRead[PROP_TRACKING], &vProp, 1, TRUE); PropVariantClear( &vProp ); } SAFELOCALFREE(lpPfxUrl); return hr; } //----------------------------------------------------------------------------- // // SchedulePostAgent // // This routine will schedule post agent to upload tracking data //----------------------------------------------------------------------------- HRESULT CUrlTrackingCache :: SchedulePostAgent(void) { return E_NOTIMPL; } //----------------------------------------------------------------------------- // // OnProcessDone // //----------------------------------------------------------------------------- // called by CDF agent // fill information in item that Post agent would need to work HRESULT CUrlTrackingCache :: OnProcessDone ( ) { return E_NOTIMPL; } //----------------------------------------------------------------------------- // // Process log related tags // //----------------------------------------------------------------------------- // // // // // // HRESULT CUrlTrackingCache :: ProcessTrackingInLog ( IXMLElement *pTracking ) { HRESULT hr; LPWSTR pwszScope = NULL; if (_pwszPostUrl) return S_OK; // there are more than 1 logtarget, take whatever first was read hr = ReadAttribute(pTracking, L"HREF", &_pwszPostUrl); // must exist to enalbe logging if (FAILED(hr)) return hr; // fill it in item for post agent WriteOLESTR(_pCDFStartItem, c_szTrackingPostURL, _pwszPostUrl); // #41460: add 2nd domain allowing tracking to DWORD cbLen = (lstrlenW(_pwszPostUrl)+1) * sizeof(WCHAR); _pszPostUrlSite = (LPTSTR)MemAlloc( LPTR, cbLen); MyOleStrToStrN(_pszPostUrlSite, cbLen, _pwszPostUrl); *(FileFromPath( _pszPostUrlSite )) = 0; _ConnectionScope = TRACK_ONLINE_CACHE_ENTRY | TRACK_OFFLINE_CACHE_ENTRY; hr = ReadAttribute(pTracking, L"SCOPE", &pwszScope); if (SUCCEEDED(hr)) { if (!StrCmpIW(pwszScope, L"OFFLINE")) _ConnectionScope = TRACK_OFFLINE_CACHE_ENTRY; else if (!StrCmpIW(pwszScope, L"ONLINE")) _ConnectionScope = TRACK_ONLINE_CACHE_ENTRY; SAFELOCALFREE(pwszScope); } RunChildElement(pTracking); // #42687: save purgetime to item and used later by post agent if (_pwszPurgeTime) // if not specify, default is 24 hours { _dwPurgeTime = WCTOI(_pwszPurgeTime); } DATE dt = 0.0; SYSTEMTIME st; GetLocalTime(&st); SystemTimeToVariantTime(&st, &dt); dt += ((DATE)_dwPurgeTime/24); #ifdef DEBUG VariantTimeToSystemTime(dt, &st); #endif WriteDATE(_pCDFStartItem, c_szPostPurgeTime, &dt); return S_OK; } //----------------------------------------------------------------------------- // // ProcessTrackingItems // // // // or // // // This routine will setup tracking cache entries for all URLs which are // specified in CDF file to track. All URLs entries belong to same channel // are created in same cache group //----------------------------------------------------------------------------- HRESULT CUrlTrackingCache :: ProcessTrackingInItem ( IXMLElement *pItem, //point to tag LPCWSTR pwszUrl, //absolute URL for item BOOL fForceLog //global log flag ) { HRESULT hr = S_OK; _bTrackIt = fForceLog; if (!_bTrackIt) hr = RunChildElement(pItem); if (SUCCEEDED(hr) && _bTrackIt) hr = AddToTrackingCacheEntry(pwszUrl); return (_bTrackIt) ? S_OK : E_FAIL; // #42604: global logging, report if this item needs logged } HRESULT CUrlTrackingCache :: RunChildElement ( IXMLElement* pElement ) { IXMLElementCollection *pCollection; long lIndex = 0; long lMax; VARIANT vIndex, vEmpty; IDispatch *pDisp; IXMLElement *pItem; BSTR bstrTagName; HRESULT hr = E_FAIL; if (SUCCEEDED(pElement->get_children(&pCollection)) && pCollection) { if (SUCCEEDED(pCollection->get_length(&lMax))) { vEmpty.vt = VT_EMPTY; for (; lIndex < lMax; lIndex++) { vIndex.vt = VT_UI4; vIndex.lVal = lIndex; if (SUCCEEDED(pCollection->item(vIndex, vEmpty, &pDisp))) { if (SUCCEEDED(pDisp->QueryInterface(IID_IXMLElement, (void **)&pItem))) { if (SUCCEEDED(pItem->get_tagName(&bstrTagName)) && bstrTagName) { hr = ProcessItemInEnum(bstrTagName, pItem); SysFreeString(bstrTagName); } pItem->Release(); } pDisp->Release(); } } } pCollection->Release(); } return hr; } HRESULT CUrlTrackingCache :: ProcessItemInEnum ( LPCWSTR pwszTagName, IXMLElement *pItem ) { HRESULT hr; LPWSTR pwszName = NULL; if (!StrCmpIW(pwszTagName, L"HTTP-EQUIV")) { DBG("CUrlTrackingCache processing HTTP-EQUIV"); hr = ReadAttribute(pItem, L"NAME", &pwszName); if (SUCCEEDED(hr) && !StrCmpIW(pwszName, L"ENCODING-TYPE")) { hr = ReadAttribute(pItem, L"VALUE", &_pwszEncodingMethod); if (SUCCEEDED(hr) && *_pwszEncodingMethod) WriteOLESTR(_pCDFStartItem, c_szPostHeader, _pwszEncodingMethod); } SAFELOCALFREE(pwszName); } else if (!StrCmpIW(pwszTagName, L"PURGETIME")) { DBG("CUrlTrackingCache processing PurgeTime"); return ReadAttribute(pItem, L"HOUR", &_pwszPurgeTime); } else if (!StrCmpIW(pwszTagName, L"LOG")) { DBG("CUrlTrackingCache processing Log"); hr = ReadAttribute(pItem, L"VALUE", &pwszName); if (SUCCEEDED(hr)) _bTrackIt = (!StrCmpIW(pwszName, L"document:view")) ? TRUE : FALSE; SAFELOCALFREE(pwszName); } return S_OK; } HRESULT CUrlTrackingCache :: ReadAttribute ( IN IXMLElement* pItem, IN LPCWSTR pwszAttributeName, OUT LPWSTR* pwszAttributeValue ) { VARIANT vProp; BSTR bstrName = NULL; HRESULT hr = E_FAIL; DWORD dwLen; vProp.vt = VT_EMPTY; bstrName = SysAllocString(pwszAttributeName); if (bstrName && SUCCEEDED(pItem->getAttribute(bstrName, &vProp))) { if (vProp.vt == VT_BSTR) { dwLen = sizeof(WCHAR) * (lstrlenW(vProp.bstrVal) + 1); *pwszAttributeValue = (LPWSTR)MemAlloc(LPTR, dwLen); if (*pwszAttributeValue) { StrCpyW(*pwszAttributeValue, vProp.bstrVal); hr = S_OK; } VariantClear(&vProp); } } SysFreeString(bstrName); return hr; } //-------------------------------------------------------------------------- // // CUrlTrackingCache // //-------------------------------------------------------------------------- CUrlTrackingCache::CUrlTrackingCache ( ISubscriptionItem *pCDFItem, LPCWSTR pwszURL ) { _lpPfx = NULL; _dwPurgeTime = 24; _pCDFStartItem = pCDFItem; ASSERT(_pCDFStartItem); _pCDFStartItem->AddRef(); Init(pwszURL); } CUrlTrackingCache::~CUrlTrackingCache() { SAFEFREEOLESTR(_pwszPostUrl); SAFEFREEOLESTR(_pwszEncodingMethod); SAFEFREEOLESTR(_pwszPurgeTime); SAFELOCALFREE(_pszChannelUrlSite); SAFELOCALFREE(_pszPostUrlSite); SAFELOCALFREE(_lpPfx); SAFERELEASE(_pCDFStartItem); }