|
|
#include "stdafx.h"
#include "netplace.h"
#include "msdasc.h"
#pragma hdrstop
CNetworkPlace::CNetworkPlace() : _pidl(NULL), _fSupportWebFolders(FALSE), _fIsWebFolder(FALSE), _fDeleteWebFolder(FALSE) { _szTarget[0] = TEXT('\0'); _szName[0] = TEXT('\0'); _szDescription[0] = TEXT('\0'); }
// destructor - clean up our state
CNetworkPlace::~CNetworkPlace() { _InvalidateCache(); }
void CNetworkPlace::_InvalidateCache() { // web folders will create a shortcut to objects if we go through its binding
// process, therefore when we attempt to invalidate our cache we should
// clean up our mess.
//
// if the user has commited the change then we can/will keep the shortcut
// around, otherwise we call the delete verb on it.
if (_fIsWebFolder && _fDeleteWebFolder && _pidl) { IShellFolder *psf; LPCITEMIDLIST pidlLast; HRESULT hr = SHBindToIDListParent(_pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); if (SUCCEEDED(hr)) { IContextMenu *pcm; hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_X_PPV_ARG(IContextMenu, NULL, &pcm)); if (SUCCEEDED(hr)) { CMINVOKECOMMANDINFO ici = {0}; ici.cbSize = sizeof (ici); ici.fMask = CMIC_MASK_FLAG_NO_UI; ici.lpVerb = "Delete"; ici.nShow = SW_SHOWNORMAL;
hr = pcm->InvokeCommand(&ici); pcm->Release(); } psf->Release(); } }
// now clean up the rest of our state.
ILFree(_pidl); _pidl = NULL;
_szTarget[0] = TEXT('\0'); _szName[0] = TEXT('\0'); _szDescription[0] = TEXT('\0');
_fIsWebFolder = FALSE; _fDeleteWebFolder = FALSE; }
HRESULT CNetworkPlace::SetTarget(HWND hwnd, LPCWSTR pszTarget, DWORD dwFlags) { _InvalidateCache();
HRESULT hr = S_OK; if (pszTarget) { // set our state accordingly
_fSupportWebFolders = (dwFlags & NPTF_ALLOWWEBFOLDERS) != 0;
// copy the URL and prepare for parsing
StrCpyN(_szTarget, pszTarget, ARRAYSIZE(_szTarget));
INT cchTarget = lstrlen(_szTarget)-1; if ((_szTarget[cchTarget] == L'\\') || (_szTarget[cchTarget] == '/')) { _szTarget[cchTarget] = TEXT('\0'); }
if (dwFlags & NPTF_VALIDATE) { // connecting to a server root or local path is not supported
if (PathIsUNCServer(_szTarget) || PathGetDriveNumber(_szTarget) != -1) { hr = E_INVALIDARG; } else { // check the policy to see if we are setting this.
if (PathIsUNC(_szTarget) && SHRestricted(REST_NONETCONNECTDISCONNECT)) { hr = E_INVALIDARG; } else { hr = _IDListFromTarget(hwnd); } }
if (FAILED(hr)) { if (hwnd && !(dwFlags & NPTF_SILENT)) { ::DisplayFormatMessage(hwnd, IDS_ANP_CAPTION, PathIsUNCServer(_szTarget) ? IDS_PUB_ONLYSERVER:IDS_CANTFINDFOLDER, MB_OK|MB_ICONERROR); } _InvalidateCache(); } } } return hr; }
HRESULT CNetworkPlace::SetName(HWND hwnd, LPCWSTR pszName) { HRESULT hr = S_OK;
if (!_fIsWebFolder) { // check to see if we are going to overwrite an existing place, if we
// are then display a prompt and let the user choose. if they answer
// yes, then have at it!
TCHAR szPath[MAX_PATH]; if (hwnd && _IsPlaceTaken(pszName, szPath)) { if (IDNO == ::DisplayFormatMessage(hwnd, IDS_ANP_CAPTION , IDS_FRIENDLYNAMEINUSE, MB_YESNO|MB_ICONQUESTION, pszName)) { hr = E_FAIL; } } }
// if we succeed the above then lets use the new name.
if (SUCCEEDED(hr)) StrCpyN(_szName, pszName, ARRAYSIZE(_szName));
return hr; }
HRESULT CNetworkPlace::SetDescription(LPCWSTR pszDescription) { StrCpyN(_szDescription, pszDescription, ARRAYSIZE(_szDescription)); return S_OK; }
// recompute the URL based on the new user/password information that
// we were just given.
HRESULT CNetworkPlace::SetLoginInfo(LPCWSTR pszUser, LPCWSTR pszPassword) { TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH + 1]; TCHAR szUrlPath[INTERNET_MAX_PATH_LENGTH + 1]; TCHAR szExtraInfo[MAX_PATH + 1]; // Includes Port Number and download type (ASCII, Binary, Detect)
URL_COMPONENTS urlComps = {0}; urlComps.dwStructSize = sizeof(urlComps); urlComps.lpszHostName = szServer; urlComps.dwHostNameLength = ARRAYSIZE(szServer); urlComps.lpszUrlPath = szUrlPath; urlComps.dwUrlPathLength = ARRAYSIZE(szUrlPath); urlComps.lpszExtraInfo = szExtraInfo; urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
// WARNING - the ICU_DECODE/ICU_ESCAPE is a lossy roundtrip - ZekeL - 26-MAR-2001
// many escaped characters are not correctly identified and re-escaped.
// any characters that are reserved for URL parsing purposes
// will be interpreted as their parsing char (ie '/').
BOOL fResult = InternetCrackUrl(_szTarget, 0, 0, &urlComps); if (fResult) { urlComps.lpszUserName = (LPTSTR) pszUser; urlComps.dwUserNameLength = (pszUser ? lstrlen(pszUser) : 0); urlComps.lpszPassword = (LPTSTR) pszPassword; urlComps.dwPasswordLength = (pszPassword ? lstrlen(pszPassword) : 0);
DWORD cchSize = ARRAYSIZE(_szTarget); fResult = InternetCreateUrl(&urlComps, (ICU_ESCAPE | ICU_USERNAME), _szTarget, &cchSize);
// if we have a cached IDList then lets ensure that we clear it up
// so that we rebind and the FTP namespace gets a crack at it.
if (fResult && _pidl) { ILFree(_pidl); _pidl = NULL; } } return fResult ? S_OK : HRESULT_FROM_WIN32(GetLastError()); }
HRESULT CNetworkPlace::GetIDList(HWND hwnd, LPITEMIDLIST *ppidl) { HRESULT hr = _IDListFromTarget(hwnd); if (SUCCEEDED(hr)) { hr = SHILClone(_pidl, ppidl); } return hr; }
HRESULT CNetworkPlace::GetObject(HWND hwnd, REFIID riid, void **ppv) { HRESULT hr = _IDListFromTarget(hwnd); if (SUCCEEDED(hr)) { hr = SHBindToObject(NULL, riid, _pidl, ppv); } return hr; }
HRESULT CNetworkPlace::GetName(LPWSTR pszBuffer, int cchBuffer) { HRESULT hr = _IDListFromTarget(NULL); if (SUCCEEDED(hr)) { StrCpyN(pszBuffer, _szName, cchBuffer); hr = S_OK; } return hr; }
// check to see if we are going to overwrite a network place
BOOL CNetworkPlace::_IsPlaceTaken(LPCTSTR pszName, LPTSTR pszPath) { BOOL fOverwriting = FALSE;
SHGetSpecialFolderPath(NULL, pszPath, CSIDL_NETHOOD, TRUE); PathCombine(pszPath, pszPath, pszName); IShellFolder *psf; HRESULT hr = SHGetDesktopFolder(&psf); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL))) { // we think we are going to overwrite an existing net place, so lets
// check first to see if the place which is there is not actually
// pointing at our new target. if its is then we can just
// ignore all of this.
TCHAR szTarget[INTERNET_MAX_URL_LENGTH]; hr = _GetTargetPath(pidl, szTarget, ARRAYSIZE(szTarget)); if (FAILED(hr) || (0 != StrCmpI(szTarget, _szTarget))) { fOverwriting = TRUE; } ILFree(pidl); } psf->Release(); }
return fOverwriting; }
// handle creating the web folders IDLIST for this item. we check with the
// rosebud binder to find out if this scheme is supported, if so then
// we attempt to have the Web Folders code crack the URL
static const BYTE c_pidlWebFolders[] = { 0x14,0x00,0x1F,0x0F,0xE0,0x4F,0xD0,0x20, 0xEA,0x3A,0x69,0x10,0xA2,0xD8,0x08,0x00, 0x2B,0x30,0x30,0x9D,0x14,0x00,0x2E,0x00, 0x00,0xDF,0xEA,0xBD,0x65,0xC2,0xD0,0x11, 0xBC,0xED,0x00,0xA0,0xC9,0x0A,0xB5,0x0F, 0x00,0x00 };
HRESULT CNetworkPlace::_TryWebFolders(HWND hwnd) { // lets see if Rosebud can handle this scheme item by checking the
// scheme and seeing if the rosebud binder can handle it.
TCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH + 1]; DWORD cchScheme = ARRAYSIZE(szScheme); HRESULT hr = UrlGetPart(_szTarget, szScheme, &cchScheme, URL_PART_SCHEME, 0); if (SUCCEEDED(hr)) { IRegisterProvider *prp; hr = CoCreateInstance(CLSID_RootBinder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IRegisterProvider, &prp)); if (SUCCEEDED(hr)) { // let the web folders code have a go at creating a link to this storage,
// the IDLIST we generate points to the folder inside My Computer (hidden)
CLSID clsidOut; hr = prp->GetURLMapping(szScheme, 0, &clsidOut); if (hr == S_OK) { IShellFolder *psf; hr = SHBindToObject(NULL, IID_IShellFolder, (LPCITEMIDLIST)c_pidlWebFolders, (void**)&psf); if (SUCCEEDED(hr)) { IBindCtx *pbc; hr = CreateBindCtx(NULL, &pbc); if (SUCCEEDED(hr)) { BIND_OPTS bo = {sizeof(bo), 0, STGM_CREATE}; hr = pbc->SetBindOptions(&bo); if (SUCCEEDED(hr)) { // we need to pase NULL hWnd to this so that Web Folders doesn't display any
// UI, in particular its ever so useful NULL error message box... mumble mumble
LPITEMIDLIST pidl; hr = psf->ParseDisplayName(NULL, pbc, _szTarget, NULL, &pidl, NULL); if (SUCCEEDED(hr)) { ASSERT(!_pidl); hr = SHILCombine((LPCITEMIDLIST)c_pidlWebFolders, pidl, &_pidl); ILFree(pidl);
_fDeleteWebFolder = TRUE; // we now have the magic web folders link (clean it up)
} }
pbc->Release(); } psf->Release(); } } else { hr = E_FAIL; } prp->Release(); } } return hr; }
// dereference a link and get the target path
HRESULT CNetworkPlace::_GetTargetPath(LPCITEMIDLIST pidl, LPTSTR pszPath, int cchPath) { LPITEMIDLIST pidlTarget; HRESULT hr = SHGetTargetFolderIDList(pidl, &pidlTarget); if (SUCCEEDED(hr)) { SHGetNameAndFlags(pidlTarget, SHGDN_FORPARSING, pszPath, cchPath, NULL); ILFree(pidlTarget); } return hr; }
// create an IDLIST for the target that we have, this code attempts to parse the name and
// then set our state for the item. if we fail to parse then we attempt to have Web Folders
// look at it - this most common scenario for this will be the DAV RDR failing because
// the server isn't a DAV store, so instead we try Web Folders to handle WEC etc.
HRESULT CNetworkPlace::_IDListFromTarget(HWND hwnd) { HRESULT hr = S_OK; if (!_pidl) { if (_szTarget[0]) { _fIsWebFolder = FALSE; // not a web folder
BINDCTX_PARAM rgParams[] = { { STR_PARSE_PREFER_FOLDER_BROWSING, NULL}, { L"BUT NOT WEBFOLDERS", NULL}, }; IBindCtx *pbc; hr = BindCtx_RegisterObjectParams(NULL, rgParams, ARRAYSIZE(rgParams), &pbc); if (SUCCEEDED(hr)) { IBindCtx *pbcWindow; hr = BindCtx_RegisterUIWindow(pbc, hwnd, &pbcWindow); if (SUCCEEDED(hr)) { SFGAOF sfgao; hr = SHParseDisplayName(_szTarget, pbcWindow, &_pidl, SFGAO_FOLDER, &sfgao);
// if we parsed something that turns out to not
// be a folder, we want to throw it away
if (SUCCEEDED(hr) && !(sfgao & SFGAO_FOLDER)) { ILFree(_pidl); _pidl = 0; hr = E_FAIL; }
// if that failed, its is a HTTP/HTTPS and we have web folders support then lets try
// and fall back to the old behaviour.
if (FAILED(hr) && _fSupportWebFolders) { DWORD scheme = GetUrlScheme(_szTarget); if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS) { switch (hr) { #if 0
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND): case HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME): case HRESULT_FROM_WIN32(ERROR_BAD_NETPATH): #endif
case HRESULT_FROM_WIN32(ERROR_CANCELLED): break;
default: { hr = _TryWebFolders(hwnd); if (SUCCEEDED(hr)) { _fIsWebFolder = TRUE; } } } } }
if (SUCCEEDED(hr)) { // given that we may have translated the name above for the parse
// to work, lets read back the name we used into our _szTarget.
SHGetNameAndFlags(_pidl, SHGDN_FORPARSING, _szTarget, ARRAYSIZE(_szTarget), NULL); } pbcWindow->Release(); } // compute the place name for the location we have hit, this includes reusing
// any places we have already created.
if (SUCCEEDED(hr) && !_szName[0]) { SHGetNameAndFlags(_pidl, SHGDN_NORMAL, _szName, ARRAYSIZE(_szName), NULL);
TCHAR szPath[MAX_PATH]; if (!_fIsWebFolder && _IsPlaceTaken(_szName, szPath)) { PathYetAnotherMakeUniqueName(szPath, szPath, NULL, NULL); StrCpyN(_szName, PathFindFileName(szPath), ARRAYSIZE(_szName)); // update our state
} } pbc->Release(); } } else { hr = E_FAIL; } } return hr; }
// handle creating the network place shortcut
HRESULT CNetworkPlace::CreatePlace(HWND hwnd, BOOL fOpen) { HRESULT hr = _IDListFromTarget(hwnd); if (SUCCEEDED(hr)) { // web folders already have their links created, therefore we can ignore this
// whole process for them, and instead fall back to just executing their link.
//
// for regular folders though we must attempt to find a unique name and create
// the link, or if the link already exists that we can use then just open it.
if (!_fIsWebFolder) { IShellLink *psl; hr = CoCreateInstance(CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { hr = psl->SetIDList(_pidl);
if (SUCCEEDED(hr)) hr = psl->SetDescription(_szDescription[0] ? _szDescription:_szTarget);
if (SUCCEEDED(hr)) { IPersistFile *ppf; hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); if (SUCCEEDED(hr)) { // get the name to the shortcut, we assume that this is unique
TCHAR szPath[MAX_PATH]; SHGetSpecialFolderPath(NULL, szPath, CSIDL_NETHOOD, TRUE); PathCombine(szPath, szPath, _szName);
hr = ppf->Save(szPath, TRUE); ppf->Release(); } } psl->Release(); } } else { // this is the web folder case, so we now need to set the display
// name for this guy. note that we don't have any control over
// the description text we are going to be seeing.
IShellFolder *psf; LPCITEMIDLIST pidlLast; hr = SHBindToIDListParent(_pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlNew; hr = psf->SetNameOf(hwnd, pidlLast, _szName, SHGDN_INFOLDER, &pidlNew); if (SUCCEEDED(hr)) { _fDeleteWebFolder = FALSE; //Web folders will return S_FALSE with bogus pidlNew if _szName is the same as the current name
if (S_OK == hr) { ILFree(_pidl); hr = SHILCombine((LPCITEMIDLIST)c_pidlWebFolders, pidlNew, &_pidl); } } psf->Release(); } } // now open the target if thats what they asked for
if (SUCCEEDED(hr) && fOpen) { LPITEMIDLIST pidlNetPlaces; hr = SHGetSpecialFolderLocation(hwnd, CSIDL_NETWORK, &pidlNetPlaces); if (SUCCEEDED(hr)) { IShellFolder *psf; hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlNetPlaces, &psf)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = psf->ParseDisplayName(hwnd, NULL, _szName, NULL, &pidl, NULL); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlToOpen; hr = SHILCombine(pidlNetPlaces, pidl, &pidlToOpen); if (SUCCEEDED(hr)) { BrowseToPidl(pidlToOpen); ILFree(pidlToOpen); } ILFree(pidl); } psf->Release(); } ILFree(pidlNetPlaces); } } }
return hr; }
|