717 lines
16 KiB
C++
717 lines
16 KiB
C++
/*-------------------------------------------------------*/
|
|
//Copyright (c) 1997 Microsoft Corporation
|
|
//
|
|
//Module Name: Url Tracking Log Interfaces
|
|
//
|
|
// Urltrack.cpp
|
|
//
|
|
//
|
|
//Author:
|
|
//
|
|
// Pei-Hwa Lin (peihwal) 19-March-97
|
|
//
|
|
//Environment:
|
|
//
|
|
// User Mode - Win32
|
|
//
|
|
//Revision History:
|
|
// 5/13/97 due to cache container type change, allow
|
|
// OPEN_ALWAYS when CreateFile
|
|
// 5/14/97 remove IsOnTracking, TRACK_ALL, unused code
|
|
/*-------------------------------------------------------*/
|
|
|
|
#include "priv.h"
|
|
#include <wininet.h>
|
|
#include "basesb.h"
|
|
#include "bindcb.h"
|
|
const WCHAR c_szPropURL[] = L"HREF";
|
|
const WCHAR c_szProptagName[] = L"Item";
|
|
const TCHAR c_szLogContainer[] = TEXT("Log");
|
|
|
|
#define MY_MAX_STRING_LEN 512
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// IUnknown interfaces
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT
|
|
CUrlTrackingStg :: QueryInterface(REFIID riid, PVOID *ppvObj)
|
|
{
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
|
|
*ppvObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IUrlTrackingStg))
|
|
{
|
|
AddRef();
|
|
*ppvObj = (LPVOID) SAFECAST(this, IUrlTrackingStg *);
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
ULONG
|
|
CUrlTrackingStg :: AddRef(void)
|
|
{
|
|
_cRef ++;
|
|
return _cRef;
|
|
}
|
|
|
|
ULONG
|
|
CUrlTrackingStg :: Release(void)
|
|
{
|
|
|
|
ASSERT(_cRef > 0);
|
|
|
|
_cRef--;
|
|
|
|
if (!_cRef)
|
|
{
|
|
//time to go bye bye
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return _cRef;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// C'tor/D'tor
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
CUrlTrackingStg :: CUrlTrackingStg()
|
|
{
|
|
|
|
_hFile = NULL;
|
|
_pRecords = NULL;
|
|
_lpPfx = NULL;
|
|
}
|
|
|
|
CUrlTrackingStg :: ~CUrlTrackingStg()
|
|
{
|
|
// browser exit
|
|
while (_pRecords)
|
|
{
|
|
OnUnload(_pRecords->pthisUrl);
|
|
};
|
|
|
|
// DeleteAllNode();
|
|
|
|
if (_lpPfx)
|
|
{
|
|
GlobalFree(_lpPfx);
|
|
_lpPfx = NULL;
|
|
}
|
|
|
|
if (_hFile)
|
|
{
|
|
CloseHandle(_hFile);
|
|
_hFile = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Helper functions
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
LRecord *
|
|
CUrlTrackingStg :: AddNode()
|
|
{
|
|
LRecord* pTemp;
|
|
LRecord* pNew = NULL;
|
|
|
|
pNew = (LRecord *)LocalAlloc(LPTR, sizeof(LRecord));
|
|
if (pNew == NULL)
|
|
return NULL;
|
|
|
|
pNew->pNext = NULL;
|
|
if (_pRecords == NULL)
|
|
{
|
|
//special case for first node
|
|
_pRecords = pNew;
|
|
}
|
|
else
|
|
{
|
|
for (pTemp = _pRecords; pTemp->pNext; pTemp = pTemp->pNext);
|
|
pTemp->pNext = pNew;
|
|
}
|
|
|
|
return pNew;
|
|
}
|
|
|
|
void
|
|
CUrlTrackingStg :: DeleteAllNode()
|
|
{
|
|
|
|
do
|
|
{
|
|
DeleteFirstNode();
|
|
}
|
|
while (_pRecords);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
CUrlTrackingStg :: DeleteFirstNode()
|
|
{
|
|
LRecord *pTemp;
|
|
|
|
if (!_pRecords)
|
|
return;
|
|
|
|
pTemp = _pRecords;
|
|
_pRecords = pTemp->pNext;
|
|
delete [] pTemp->pthisUrl;
|
|
LocalFree(pTemp);
|
|
pTemp = NULL;
|
|
return;
|
|
}
|
|
|
|
void
|
|
CUrlTrackingStg :: DeleteCurrentNode(LRecord *pThis)
|
|
{
|
|
LRecord *pPrev;
|
|
|
|
if (_pRecords == pThis)
|
|
{
|
|
DeleteFirstNode();
|
|
return;
|
|
}
|
|
|
|
pPrev = _pRecords;
|
|
do
|
|
{
|
|
if (pPrev->pNext == pThis)
|
|
{
|
|
pPrev->pNext = pThis->pNext;
|
|
delete [] pThis->pthisUrl;
|
|
LocalFree(pThis);
|
|
pThis = NULL;
|
|
break;
|
|
}
|
|
pPrev = pPrev->pNext;
|
|
}
|
|
while (pPrev);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// return Current node by comparing url strings
|
|
//
|
|
LRecord*
|
|
CUrlTrackingStg :: FindCurrentNode
|
|
(
|
|
IN LPCTSTR lpUrl
|
|
)
|
|
{
|
|
LRecord* pThis = NULL;
|
|
|
|
ASSERT(_pRecords);
|
|
if (!_pRecords) // missed OnLoad
|
|
return NULL;
|
|
|
|
pThis = _pRecords;
|
|
do
|
|
{
|
|
if (!StrCmpI(lpUrl, pThis->pthisUrl))
|
|
break;
|
|
|
|
pThis = pThis->pNext;
|
|
}
|
|
while (pThis);
|
|
|
|
return pThis;
|
|
}
|
|
|
|
void
|
|
CUrlTrackingStg :: cleanup()
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
CUrlTrackingStg :: DetermineAppModule()
|
|
{
|
|
TCHAR szModule[MAX_PATH];
|
|
LPTSTR szExt;
|
|
|
|
if (GetModuleFileName(NULL, szModule, MAX_PATH))
|
|
{
|
|
szExt = PathFindExtension(szModule);
|
|
TraceMsg(0, "tracking: AppModule %s", szModule);
|
|
|
|
if (StrCmpI(szExt, TEXT(".SCR")) == 0)
|
|
_fScreenSaver = TRUE;
|
|
else
|
|
_fScreenSaver = FALSE;
|
|
|
|
}
|
|
else
|
|
_fScreenSaver = FALSE;
|
|
|
|
_fModule = TRUE;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// OnLoad(LPTSTR lpUrl, BRMODE context, BOOL fUseCache)
|
|
// a new page is loaded
|
|
// this function will remember time entering this page, context browsing
|
|
// from and page URL string.
|
|
// (lpUrl does NOT contain "track:" prefix)
|
|
//---------------------------------------------------------------------------
|
|
HRESULT
|
|
CUrlTrackingStg :: OnLoad
|
|
(
|
|
IN LPCTSTR lpUrl,
|
|
IN BRMODE ContextMode,
|
|
IN BOOL fUseCache
|
|
)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
SYSTEMTIME st;
|
|
LRecord* pNewNode = NULL;
|
|
|
|
GetLocalTime(&st);
|
|
|
|
pNewNode = AddNode();
|
|
if (!pNewNode)
|
|
return hr;
|
|
|
|
int cch = lstrlen(lpUrl)+1;
|
|
pNewNode->pthisUrl = (LPTSTR)LocalAlloc(LPTR, cch * sizeof(TCHAR));
|
|
if (pNewNode->pthisUrl == NULL)
|
|
return hr;
|
|
|
|
// store log info
|
|
StrCpyN(pNewNode->pthisUrl, lpUrl, cch);
|
|
|
|
if (!_fModule)
|
|
DetermineAppModule();
|
|
|
|
// if it's from SS, the fullscreen flag will be set,
|
|
// need to override ContextMode passed in
|
|
if (_fScreenSaver)
|
|
pNewNode->Context = BM_SCREENSAVER;
|
|
else
|
|
pNewNode->Context = (ContextMode > BM_THEATER) ? BM_UNKNOWN : ContextMode;
|
|
|
|
#if 0 //do not open till urlmon support wininet query flag
|
|
DWORD dwOptions = 0;
|
|
DWORD dwSize;
|
|
WCHAR wszURL[MAX_URL_STRING];
|
|
|
|
AnsiToUnicode(lpUrl, wszURL, ARRAYSIZE(wszURL));
|
|
if (SUCCEEDED(CoInitialize(NULL)) &&
|
|
SUCCEEDED(CoInternetQueryInfo(wszURL, (QUERYOPTION)INTERNET_OPTION_REQUEST_FLAGS,
|
|
0, &dwOptions, sizeof(DWORD), &dwSize, 0)))
|
|
{
|
|
pNewNode->fuseCache = dwOptions & INTERNET_REQFLAG_FROM_CACHE;
|
|
CoUninitialize();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
BYTE cei[MAX_CACHE_ENTRY_INFO_SIZE];
|
|
LPINTERNET_CACHE_ENTRY_INFO pcei = (LPINTERNET_CACHE_ENTRY_INFO)cei;
|
|
DWORD cbcei = MAX_CACHE_ENTRY_INFO_SIZE;
|
|
|
|
if (GetUrlCacheEntryInfo(lpUrl, pcei, &cbcei))
|
|
pNewNode->fuseCache = (pcei->dwHitRate - 1) ? TRUE : FALSE; // off 1 by download
|
|
else
|
|
pNewNode->fuseCache = 0;
|
|
|
|
}
|
|
|
|
SystemTimeToFileTime(&st, &(pNewNode->ftIn));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// OnUnLoad(LPTSTR lpUrl)
|
|
// current page is unloaded
|
|
// 1)find url cache entry and get file handle
|
|
// 2)calculate total time duration visiting this page
|
|
// 3)commit delta log string to file cache entry
|
|
// (lpUrl contains "Tracking: " prefix)
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT
|
|
CUrlTrackingStg :: OnUnload
|
|
(
|
|
IN LPCTSTR lpUrl
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPTSTR lpPfxUrl = NULL;
|
|
LRecord* pNode = NULL;;
|
|
SYSTEMTIME st;
|
|
LPINTERNET_CACHE_ENTRY_INFO pce = NULL;
|
|
TCHAR lpFile[MAX_PATH];
|
|
|
|
|
|
//
|
|
GetLocalTime(&st);
|
|
|
|
pNode = FindCurrentNode(lpUrl);
|
|
if (!pNode)
|
|
{
|
|
TraceMsg(DM_ERROR, "CUrlTrackingStg: OnUnload (cannot find internal tracking log");
|
|
return hr;
|
|
}
|
|
|
|
//QueryCacheEntry() and OpenLogFile() can be combined in one if CacheAPI supports
|
|
//WriteUrlCacheEntryStream()
|
|
ConvertToPrefixedURL(lpUrl, &lpPfxUrl);
|
|
if (!lpPfxUrl)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pce = QueryCacheEntry(lpPfxUrl);
|
|
if (!pce)
|
|
{
|
|
TraceMsg(DM_ERROR, "CUrlTrackingStg: OnUnload (cannot find url cache entry)");
|
|
DeleteCurrentNode(pNode);
|
|
|
|
// free pce
|
|
GlobalFree(lpPfxUrl);
|
|
lpPfxUrl = NULL;
|
|
return hr;
|
|
}
|
|
|
|
// work around -- begin
|
|
hr = WininetWorkAround(lpPfxUrl, pce->lpszLocalFileName, &lpFile[0]);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceMsg(DM_ERROR, "CUrlTrackingStg: OnUnload (failed to work around wininet)");
|
|
DeleteCurrentNode(pNode);
|
|
if (_hFile)
|
|
{
|
|
CloseHandle(_hFile);
|
|
_hFile = NULL;
|
|
}
|
|
GlobalFree(lpPfxUrl);
|
|
lpPfxUrl = NULL;
|
|
return hr;
|
|
}
|
|
|
|
hr = UpdateLogFile(pNode, &st);
|
|
|
|
// commit change to cache
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = (CommitUrlCacheEntry(lpPfxUrl,
|
|
lpFile, //
|
|
pce->ExpireTime, //ExpireTime
|
|
pce->LastModifiedTime, //LastModifiedTime
|
|
pce->CacheEntryType,
|
|
NULL, //lpHeaderInfo
|
|
0, //dwHeaderSize
|
|
NULL, //lpszFileExtension
|
|
0) ) ? //reserved
|
|
S_OK : E_FAIL;
|
|
}
|
|
|
|
// work around -- end
|
|
|
|
DeleteCurrentNode(pNode);
|
|
|
|
// free pce
|
|
GlobalFree(pce);
|
|
pce = NULL;
|
|
|
|
GlobalFree(lpPfxUrl);
|
|
lpPfxUrl = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Cache helper funcitons
|
|
// This is a workaround for Wininet cache
|
|
// Later when we commit change to URL cache will fail if localFile size is changed
|
|
// [IN] lpszSourceUrlName and lpszLocalFileName remain the same when calling
|
|
// this routine
|
|
// [OUT] new local file name
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT CUrlTrackingStg :: WininetWorkAround(LPCTSTR lpszUrl, LPCTSTR lpOldFile, LPTSTR lpFile)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
ASSERT(!_hFile);
|
|
|
|
if (!CreateUrlCacheEntry(lpszUrl, 512, TEXT("log"), lpFile, 0))
|
|
return E_FAIL;
|
|
|
|
if (lpOldFile)
|
|
{
|
|
if (!CopyFile(lpOldFile, lpFile, FALSE))
|
|
return E_FAIL;
|
|
|
|
DeleteFile(lpOldFile);
|
|
}
|
|
|
|
_hFile = OpenLogFile(lpFile);
|
|
|
|
if (_hFile != INVALID_HANDLE_VALUE)
|
|
_hFile = NULL;
|
|
|
|
return (_hFile) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
LPINTERNET_CACHE_ENTRY_INFO
|
|
CUrlTrackingStg :: QueryCacheEntry
|
|
(
|
|
IN LPCTSTR lpUrl
|
|
)
|
|
{
|
|
// get cache entry info
|
|
LPINTERNET_CACHE_ENTRY_INFO lpCE = NULL;
|
|
DWORD dwEntrySize;
|
|
BOOL bret = FALSE;
|
|
|
|
lpCE = (LPINTERNET_CACHE_ENTRY_INFO)GlobalAlloc(LPTR, MAX_CACHE_ENTRY_INFO_SIZE);
|
|
if (lpCE)
|
|
{
|
|
dwEntrySize = MAX_CACHE_ENTRY_INFO_SIZE;
|
|
|
|
while (!(bret = GetUrlCacheEntryInfo(lpUrl, lpCE, &dwEntrySize)))
|
|
{
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
GlobalFree(lpCE);
|
|
|
|
lpCE = (LPINTERNET_CACHE_ENTRY_INFO)GlobalAlloc(LPTR, dwEntrySize);
|
|
if (!lpCE)
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bret && lpCE)
|
|
{
|
|
GlobalFree(lpCE);
|
|
lpCE = NULL;
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
return lpCE;
|
|
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// File helper funcitons
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
// 1)open log file
|
|
// 2)move file pointer to end of file
|
|
//
|
|
HANDLE
|
|
CUrlTrackingStg :: OpenLogFile
|
|
(
|
|
IN LPCTSTR lpFileName
|
|
)
|
|
{
|
|
HANDLE hFile = NULL;
|
|
|
|
hFile = CreateFile(lpFileName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL, // | FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return NULL;
|
|
|
|
return hFile;
|
|
|
|
}
|
|
|
|
const TCHAR c_szLogFormat[] = TEXT("hh':'mm':'ss");
|
|
const LPTSTR c_szMode[] = { TEXT("N"), // normal browsing
|
|
TEXT("S"), // screen saver
|
|
TEXT("D"), // desktop component
|
|
TEXT("T"), // theater mode
|
|
TEXT("U"), // unknown
|
|
};
|
|
|
|
HRESULT
|
|
CUrlTrackingStg :: UpdateLogFile
|
|
(
|
|
IN LRecord* pNode,
|
|
IN SYSTEMTIME* pst
|
|
)
|
|
{
|
|
FILETIME ftOut;
|
|
DWORD dwWritten= 0;
|
|
HRESULT hr = E_FAIL;
|
|
ULARGE_INTEGER ulIn, ulOut, ulTotal;
|
|
|
|
ASSERT(_hFile);
|
|
|
|
// calculate delta of time
|
|
SystemTimeToFileTime(pst, &ftOut);
|
|
|
|
// #34829: use 64-bit calculation
|
|
ulIn.LowPart = pNode->ftIn.dwLowDateTime;
|
|
ulIn.HighPart = pNode->ftIn.dwHighDateTime;
|
|
ulOut.LowPart = ftOut.dwLowDateTime;
|
|
ulOut.HighPart = ftOut.dwHighDateTime;
|
|
QUAD_PART(ulTotal) = QUAD_PART(ulOut) - QUAD_PART(ulIn);
|
|
|
|
ftOut.dwLowDateTime = ulTotal.LowPart;
|
|
ftOut.dwHighDateTime = ulTotal.HighPart;
|
|
|
|
// log string: timeEnter+Duration
|
|
SYSTEMTIME stOut, stIn;
|
|
TCHAR lpLogString[MY_MAX_STRING_LEN];
|
|
TCHAR pTimeIn[10], pTimeOut[10];
|
|
|
|
FileTimeToSystemTime(&ftOut, &stOut);
|
|
FileTimeToSystemTime(&(pNode->ftIn), &stIn);
|
|
|
|
GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &stIn, c_szLogFormat, pTimeIn, 10);
|
|
GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &stOut, c_szLogFormat, pTimeOut, 10);
|
|
// #34832: add Date in logs
|
|
// #28266: add LFCR in logs
|
|
lpLogString[0] = '\0';
|
|
wnsprintf(lpLogString, ARRAYSIZE(lpLogString), TEXT("%s %d %.2d-%.2d-%d %s %s\r\n"),
|
|
c_szMode[pNode->Context],
|
|
pNode->fuseCache,
|
|
stIn.wMonth, stIn.wDay, stIn.wYear,
|
|
pTimeIn, pTimeOut);
|
|
|
|
// move file pointer to end
|
|
if (0xFFFFFFFF == SetFilePointer(_hFile, 0, 0, FILE_END))
|
|
{
|
|
CloseHandle(_hFile);
|
|
_hFile = NULL;
|
|
return hr;
|
|
}
|
|
|
|
// write ANSI string to file
|
|
char szLogInfo[MY_MAX_STRING_LEN];
|
|
|
|
SHTCharToAnsi(lpLogString, szLogInfo, ARRAYSIZE(szLogInfo));
|
|
hr = (WriteFile(_hFile, szLogInfo, lstrlenA(szLogInfo), &dwWritten, NULL)) ?
|
|
S_OK : E_FAIL;
|
|
|
|
CloseHandle(_hFile);
|
|
_hFile = NULL;
|
|
return hr;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// ReadTrackingPrefix
|
|
//
|
|
// read prefix string from registry
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
CUrlTrackingStg :: ReadTrackingPrefix(void)
|
|
{
|
|
DWORD cbPfx = 0;
|
|
struct {
|
|
INTERNET_CACHE_CONTAINER_INFO cInfo;
|
|
TCHAR szBuffer[MAX_PATH+MAX_PATH];
|
|
} ContainerInfo;
|
|
DWORD dwModified, dwContainer;
|
|
HANDLE hEnum;
|
|
|
|
dwContainer = sizeof(ContainerInfo);
|
|
hEnum = FindFirstUrlCacheContainer(&dwModified,
|
|
&ContainerInfo.cInfo,
|
|
&dwContainer,
|
|
0);
|
|
|
|
if (hEnum)
|
|
{
|
|
|
|
for (;;)
|
|
{
|
|
if (!StrCmpI(ContainerInfo.cInfo.lpszName, c_szLogContainer))
|
|
{
|
|
DWORD cch = lstrlen(ContainerInfo.cInfo.lpszCachePrefix)+1;
|
|
ASSERT(ContainerInfo.cInfo.lpszCachePrefix[0]);
|
|
|
|
_lpPfx = (LPTSTR)GlobalAlloc(LPTR, cch * sizeof(TCHAR));
|
|
if (!_lpPfx)
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
StrCpyN(_lpPfx, ContainerInfo.cInfo.lpszCachePrefix, cch);
|
|
break;
|
|
}
|
|
|
|
dwContainer = sizeof(ContainerInfo);
|
|
if (!FindNextUrlCacheContainer(hEnum, &ContainerInfo.cInfo, &dwContainer))
|
|
{
|
|
if (GetLastError() == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
FindCloseUrlCache(hEnum);
|
|
}
|
|
}
|
|
|
|
|
|
// caller must free lplpPrefixedUrl
|
|
BOOL
|
|
CUrlTrackingStg :: ConvertToPrefixedURL(LPCTSTR lpszUrl, LPTSTR *lplpPrefixedUrl)
|
|
{
|
|
BOOL bret = FALSE;
|
|
|
|
ASSERT(lpszUrl);
|
|
if (!lpszUrl)
|
|
return bret;
|
|
|
|
//ASSERT(lplpPrefixedUrl);
|
|
|
|
if (!_lpPfx)
|
|
ReadTrackingPrefix();
|
|
|
|
if (_lpPfx)
|
|
{
|
|
int len = lstrlen(lpszUrl) + lstrlen(_lpPfx) + 1;
|
|
|
|
*lplpPrefixedUrl = (LPTSTR)GlobalAlloc(LPTR, len * sizeof(TCHAR));
|
|
if (*lplpPrefixedUrl)
|
|
{
|
|
wnsprintf(*lplpPrefixedUrl, len, TEXT("%s%s"), _lpPfx, lpszUrl);
|
|
bret = TRUE;
|
|
}
|
|
}
|
|
|
|
return bret;
|
|
}
|