WindowsXP-SP1/shell/shdocvw/history.cpp

530 lines
18 KiB
C++

#include "priv.h"
#include "dochost.h"
#include "resource.h"
#include "urlprop.h"
#include "ishcut.h"
#include "shlguid.h"
#include "mlang.h"
#include <mluisupp.h>
#define DM_HISTORY 0
HRESULT PersistShortcut(IUniformResourceLocator * purl, LPCWSTR pwszFile)
{
IPersistFile *ppf;
HRESULT hres = purl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hres))
{
hres = ppf->Save(pwszFile, TRUE);
if (SUCCEEDED(hres))
ppf->SaveCompleted(pwszFile); // return value always S_OK
ppf->Release();
}
return hres;
}
/*************************************************************\
FUNCTION: GenerateUnknownShortcutName
PARAMETERS:
pwzSourceFilename - TCHAR Source Path and Filename that cannot be created.
This value will be changed to a valid path\filename
pwzDestFilename - After pwzSourceFilename is converted to a valid filename,
the valid path will be returned here in a UNICODE string.
dwSize - Size of the pwzDestFilename buffer in chars.
DESCRIPTION:
This function will replace the filename at the end of
the path in pwzFilename with "Untitled.url". If that file
exists, it will try, "Untitled1.url" and so on until it can
be unique.
WARNING:
This function will only allow the incoming value be in ANSI
because these helper functions (like PathRemoveFileSpecW) won't
work on Win95 when compiled in UNICODE. (CharNextW isn't supported
on Win95)
\*************************************************************/
#define MAX_GEN_TRIES 100
#define GEN_EXTION_LEN (7 * sizeof(TCHAR)) // size == L"000.url" in chars
BOOL GenerateUnknownShortcutName(
IN LPCTSTR pszSourceFilename,
IN LPWSTR pwzDestFilename,
IN DWORD dwSize)
{
TCHAR szTempFilename[MAX_PATH];
TCHAR szUntitledStr[MAX_PATH];
LONG lTry = 1;
if (MLLoadString(IDS_UNTITLE_SHORTCUT, szUntitledStr, ARRAYSIZE(szUntitledStr)))
{
StrCpyN(szTempFilename, pszSourceFilename, ARRAYSIZE(szTempFilename));
PathRemoveFileSpec(szTempFilename); // "Path"
PathAddBackslash(szTempFilename); // "Path\"
// Make sure the string is large enough (including terminator). (Counting chars, not bytes)
if (dwSize > (DWORD)(lstrlen(szTempFilename) + lstrlen(szUntitledStr) + GEN_EXTION_LEN))
{
PathCombine(szUntitledStr, szTempFilename, szUntitledStr); // "Path\untitled"
wnsprintf(szTempFilename, ARRAYSIZE(szTempFilename), TEXT("%s.url"), szUntitledStr); // "Path\untitled.url"
// Make a reasonable number of tries (MAX_GEN_TRIES) to find a unique
// filename. "path\Untitled.url", "path\Untitled1.url", ...
while ((PathFileExists(szTempFilename)) && (lTry < MAX_GEN_TRIES))
wnsprintf(szTempFilename, ARRAYSIZE(szTempFilename), TEXT("%s%ld.url"), szUntitledStr, lTry++);
if (!PathFileExists(szTempFilename))
{
if (SHTCharToUnicode(szTempFilename, pwzDestFilename, dwSize) > 0)
return(TRUE);
}
}
}
return(FALSE);
}
//
// If no directory is specified then it is simply made into a path name without any dir attached
//
STDAPI_(BOOL) GetShortcutFileName(LPCTSTR pszTarget, LPCTSTR pszTitle, LPCTSTR pszDir, LPTSTR pszOut, int cchOut)
{
TCHAR szFullName[MAX_PATH];
BOOL fAddDotUrl = TRUE;
UINT cchMax;
static const TCHAR c_szDotURL[] = TEXT(".url");
TraceMsg(DM_HISTORY, "GetShortcutFileName pszDir = %s", pszDir);
// Check if the title has some characters that just cannot be
// displayed
if(IsOS(OS_WIN95ORGREATER) || (IsOS(OS_NT) && !IsOS(OS_WIN2000ORGREATER)))
{
IMultiLanguage2 *pMultiLanguage = NULL;
if (pszTitle &&
S_OK == CoCreateInstance(
CLSID_CMultiLanguage,
NULL,
CLSCTX_INPROC_SERVER,
IID_IMultiLanguage2,
(void**)&pMultiLanguage))
{
ASSERT(pMultiLanguage);
DWORD dwMode = 0;
UINT uSrcSize = lstrlenW(pszTitle);
UINT uDestSize;
if (S_OK != pMultiLanguage->ConvertStringFromUnicodeEx(&dwMode, GetACP(), (LPTSTR)pszTitle, &uSrcSize,
NULL, &uDestSize, MLCONVCHARF_NOBESTFITCHARS, NULL))
{
pszTitle = NULL; // Don't Use the title
}
pMultiLanguage->Release();
}
}
cchMax = ARRAYSIZE(szFullName) - lstrlen(c_szDotURL);
if (pszTitle && pszTitle[0])
StrCpyN(szFullName, pszTitle, cchMax);
else if (pszTarget && pszTarget[0])
{
UINT cchLen;
StrCpyN(szFullName, PathFindFileName(pszTarget), cchMax);
cchLen = lstrlen(szFullName);
if(szFullName[cchLen -1] == TEXT('/')) // Catch the common case of ftp://foo/
szFullName[cchLen -1] = TEXT('\0');
PathRemoveExtension(szFullName);
}
else
{
fAddDotUrl = FALSE;
MLLoadString(IDS_NEW_INTSHCUT, szFullName, SIZECHARS(szFullName));
}
// We need at least this many characters for the directory + extension + " (nn)" + the null terminator
// If there are multiple shortcuts with the same beginning, we'll append " (nn)", where
// nn represents a two-digit maxiumum
DWORD cc = (DWORD)(lstrlen(pszDir) + (fAddDotUrl ? ARRAYSIZE(c_szDotURL) : 1) + 5);
// We want to allow for at least a one letter filename
if ((cc + 1) > ARRAYSIZE(szFullName))
{
return FALSE;
}
szFullName[ARRAYSIZE(szFullName)-cc] = TEXT('\0');
if(fAddDotUrl)
StrCatBuff(szFullName, c_szDotURL, ARRAYSIZE(szFullName));
if(pszDir)
{
if (PathCleanupSpec(pszDir, szFullName) & PCS_FATAL)
{
return FALSE;
}
PathCombine(pszOut, pszDir, szFullName);
}
else
{
StrCpyN(pszOut, szFullName, cchOut);
}
TraceMsg(DM_HISTORY, "GetShortcutFileName pszOut = %s", pszOut);
return TRUE;
}
// Unfortunately we do not already have something like this around
// If you find duplicate, please nuke this (dli)
// Warning: This function does not consider all possible URL cases.
BOOL _GetPrettyURLName(LPCTSTR pcszURL, LPCTSTR pcszDir, LPTSTR pszUrlFile, int cchUrlFile)
{
BOOL bRet = FALSE;
PARSEDURL pu = {0};
pu.cbSize = SIZEOF(PARSEDURL);
if (SUCCEEDED(ParseURL(pcszURL, &pu)))
{
LPCTSTR pszPrettyName = pu.pszSuffix;
// Get rid of the forward '/'
while (*pszPrettyName && *pszPrettyName == TEXT('/'))
pszPrettyName++;
if (!StrCmpN(pszPrettyName, TEXT("www."), 4))
pszPrettyName += 4;
if (*pszPrettyName)
bRet = GetShortcutFileName(pcszURL, pszPrettyName, pcszDir, pszUrlFile, cchUrlFile);
}
return bRet;
}
/*
* pcszURL -> "ftp://ftp.microsoft.com"
* pcszPath -> "c:\windows\desktop\internet\Microsoft FTP.url"
*/
HRESULT
CreateNewURLShortcut(
IN LPCTSTR pcszURL,
IN LPCITEMIDLIST pidlURL,
IN LPCTSTR pcszURLFile,
IN LPCTSTR pcszDir,
OUT LPTSTR pszOut,
IN int cchOut,
IN BOOL bUpdateProperties,
IN BOOL bUpdateIcon,
IN IOleCommandTarget *pCommandTarget)
{
HRESULT hr;
WCHAR wszFile[MAX_URL_STRING];
if (SHTCharToUnicode(pcszURLFile, wszFile, ARRAYSIZE(wszFile)))
{
IUniformResourceLocator *purl;
hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARG(IUniformResourceLocator, &purl));
if (SUCCEEDED(hr))
{
if (pidlURL)
{
// if we're given a pidl, try to set pidl first.
IShellLink *psl;
hr = purl->QueryInterface(IID_PPV_ARG(IShellLink, &psl));
if (SUCCEEDED(hr))
{
hr = psl->SetIDList(pidlURL);
psl->Release();
}
}
if (!pidlURL || FAILED(hr))
hr = purl->SetURL(pcszURL, 0);
if (S_OK == hr)
IUnknown_SetSite(purl, pCommandTarget);
if (SUCCEEDED(hr))
{
// Persist the internet shortcut
hr = PersistShortcut(purl, wszFile);
// If the previous call fails, try again with a new Filename.
// This is needed because the other filename could have been invalid,
// which will happen if the web page's title was stored in DBCS with
// a non-English code page.
// (dli) First try a file name related to the URL, then the default untitled
if (FAILED(hr))
{
TCHAR tszFile[MAX_PATH];
BOOL bURLname = _GetPrettyURLName(pcszURL, pcszDir, tszFile, ARRAYSIZE(tszFile));
if ((bURLname && SHTCharToUnicode(tszFile, wszFile, ARRAYSIZE(wszFile)) > 0) ||
(!bURLname && GenerateUnknownShortcutName(pcszURLFile, wszFile, ARRAYSIZE(wszFile))))
{
hr = PersistShortcut(purl, wszFile);
}
}
if (SUCCEEDED(hr))
{
TCHAR szFile[MAX_PATH];
VARIANT varIn = {0};
if (bUpdateIcon)
{
HRESULT hrTemp = IUnknown_Exec(purl, &CGID_ShortCut, ISHCUTCMDID_DOWNLOADICON, 0, NULL, NULL);
ASSERT(SUCCEEDED(hrTemp));
}
varIn.vt = VT_UNKNOWN;
varIn.punkVal = purl;
SHUnicodeToTChar(wszFile, szFile, ARRAYSIZE(szFile));
#ifndef UNIX
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szFile, NULL);
#else
// IEUNIX : Synchronous notifications for unix.
SHChangeNotify(SHCNE_CREATE, ( SHCNF_PATH | SHCNF_FLUSH ), szFile, NULL);
#endif
if (pszOut)
{
StrCpyN(pszOut, wszFile, cchOut);
}
}
}
purl->Release();
}
}
else
hr = E_FAIL;
return(hr);
}
BOOL ILCanCreateLNK(LPCITEMIDLIST pidl)
{
HRESULT hr = S_FALSE;
DWORD dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
// Should call IsBrowserFrameOptionsPidlSet(BIF_PREFER_INTERNET_SHORTCUT) instead. Some URL delegate
// NSEs (FTP for one) may want .lnks instead of .url files.
// This would be great for CDocObjFolder to not set this bit so
// for .doc files so they will use the .lnk versions.
if (!pidl || IsURLChild(pidl, TRUE))
return FALSE;
hr = IEGetAttributesOf(pidl, &dwAttributes);
return (SUCCEEDED(hr) &&
(IsFlagSet(dwAttributes, SFGAO_FOLDER) ||
IsFlagSet(dwAttributes, SFGAO_FILESYSANCESTOR) )
);
}
// This API makes a callback via the IN parameter
// pCommand to inform that shortcut creation is over.
// The callback it currently sends back are :
//
STDAPI
CreateShortcutInDirEx(ISHCUT_PARAMS *pParams)
{
LPCITEMIDLIST pidlTarget = pParams->pidlTarget;
TCHAR szFileName[MAX_PATH];
TCHAR szTarget[MAX_URL_STRING];
HRESULT hres;
BOOL bIsURL = IsURLChild(pidlTarget, TRUE);
if (!ILCanCreateLNK(pidlTarget) &&
SUCCEEDED(IEGetDisplayName(pidlTarget, szTarget, SHGDN_FORPARSING)) &&
_ValidateURL(szTarget, UQF_DEFAULT))
{
BOOL bUsePidl;
SHCleanupUrlForDisplay(szTarget);
// Note that _ValidateURL() calls IURLQualify() which adds "file://"
// prefix to szTarget as appropriate.
if (bIsURL ||
(GetUrlScheme(szTarget) == URL_SCHEME_FILE))
{
bUsePidl = FALSE;
}
else
{
// use pidl if it's not URL or file: compatible.
bUsePidl = TRUE;
}
GetShortcutFileName(szTarget, pParams->pszTitle, pParams->pszDir, szFileName, ARRAYSIZE(szFileName));
if(pParams->bUniqueName)
PathYetAnotherMakeUniqueName(szFileName, szFileName, NULL, NULL);
hres = CreateNewURLShortcut(szTarget, bUsePidl ? pidlTarget : NULL, szFileName, pParams->pszDir,
pParams->pszOut, pParams->cchOut, pParams->bUpdateProperties,
pParams->bUpdateIcon, pParams->pCommand);
} else {
hres = CreateLinkToPidl(pidlTarget, pParams->pszDir, pParams->pszTitle, pParams->pszOut, pParams->cchOut);
}
return hres;
}
// pidlTarget ... the thing the shortcut is going to point to
// pszDir .. the directory that should hold the shortcut
// WARNING: if you change any parameters for this function, you
// need to fix up explorer.exe
STDAPI CreateShortcutInDirA(
IN LPCITEMIDLIST pidlTarget,
IN LPSTR pszTitle,
IN LPCSTR pszDir,
OUT LPSTR pszOut,
IN BOOL bUpdateProperties)
{
HRESULT hres = E_FAIL;
TCHAR szTitle[MAX_PATH];
TCHAR szDir[MAX_PATH];
TCHAR szOut[MAX_URL_STRING];
ISHCUT_PARAMS ShCutParams = {0};
SHAnsiToTChar(pszTitle, szTitle, ARRAYSIZE(szTitle));
SHAnsiToTChar(pszDir, szDir, ARRAYSIZE(szDir));
ShCutParams.pidlTarget = pidlTarget;
ShCutParams.pszTitle = szTitle;
ShCutParams.pszDir = szDir;
ShCutParams.pszOut = (pszOut ? szOut : NULL);
ShCutParams.cchOut = (int)((pszOut ? ARRAYSIZE(szOut) : 0));
ShCutParams.bUpdateProperties = bUpdateProperties;
ShCutParams.bUniqueName = FALSE;
ShCutParams.bUpdateIcon = FALSE;
ShCutParams.pCommand = NULL;
ShCutParams.pDoc = NULL;
hres = CreateShortcutInDirEx(&ShCutParams);
if (pszOut && SUCCEEDED(hres))
SHTCharToAnsi(szOut, pszOut, MAX_URL_STRING);
return hres;
}
STDAPI CreateShortcutInDirW(
IN LPCITEMIDLIST pidlTarget,
IN LPWSTR pwszTitle,
IN LPCWSTR pwszDir,
OUT LPWSTR pwszOut,
IN BOOL bUpdateProperties)
{
HRESULT hres = E_FAIL;
TCHAR szTitle[MAX_PATH];
TCHAR szDir[MAX_PATH];
TCHAR szOut[MAX_URL_STRING];
ISHCUT_PARAMS ShCutParams = {0};
SHUnicodeToTChar(pwszTitle, szTitle, ARRAYSIZE(szTitle));
SHUnicodeToTChar(pwszDir, szDir, ARRAYSIZE(szDir));
ShCutParams.pidlTarget = pidlTarget;
ShCutParams.pszTitle = szTitle;
ShCutParams.pszDir = szDir;
ShCutParams.pszOut = (pwszOut ? szOut : NULL);
ShCutParams.cchOut = (int)((pwszOut ? ARRAYSIZE(szOut) : 0));
ShCutParams.bUpdateProperties = bUpdateProperties;
ShCutParams.bUniqueName = FALSE;
ShCutParams.bUpdateIcon = FALSE;
ShCutParams.pCommand = NULL;
ShCutParams.pDoc = NULL;
hres = CreateShortcutInDirEx(&ShCutParams);
if (pwszOut && SUCCEEDED(hres))
SHTCharToUnicode(szOut, pwszOut, MAX_URL_STRING);
return hres;
}
//////////////////////////////
//
// Adds the given URL to the history storage
//
// pwzTitle may be NULL if no title exists
//
// Note this function may be called multiple times in a single
// page-visit. bUpdateProperties is TRUE only once during
// those sequence of calls.
//
HRESULT
AddUrlToUrlHistoryStg(
IN LPCWSTR pwszUrl,
IN LPCWSTR pwszTitle,
IN LPUNKNOWN punk,
IN BOOL fWriteHistory,
IN IOleCommandTarget *poctNotify,
IN IUnknown *punkSFHistory,
OUT UINT* pcodepage)
{
TraceMsg(DM_HISTORY, "AddUrlToUrlHistoryStg() entered url = %s, title = %s, punk = %X, fwrite = %d, poct = %X, punkHist = %X, cp = %d",
pwszUrl, pwszTitle, punk,fWriteHistory,poctNotify,punkSFHistory,pcodepage);
IUrlHistoryPriv *pUrlHistStg;
HRESULT hr;
if (!pwszUrl)
return E_POINTER;
if (punk == NULL)
{
hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER,
IID_IUrlHistoryPriv, (void **)&pUrlHistStg);
}
else
{
// query the pointer for IServiceProvider so we can get the IUrlHistoryStg
hr = IUnknown_QueryService(punk,SID_SUrlHistory, IID_IUrlHistoryPriv, (LPVOID *)&pUrlHistStg);
}
if (SUCCEEDED(hr))
{
//
// This demostrate the mechanism to get the codepage for URL.
//
hr = pUrlHistStg->AddUrlAndNotifyCP(pwszUrl,
pwszTitle,
0,
fWriteHistory,
poctNotify,
punkSFHistory,
pcodepage);
pUrlHistStg->Release();
}
return hr;
}