2025-04-27 07:49:33 -04:00

1054 lines
25 KiB
C++

#include <shlwapi.h>
#include "dll.h"
#include "util.h"
#include <wininet.h>
class CUNCFileSource : public IApplicationFileSource
{
public:
CUNCFileSource();
HRESULT CreateNew(
/* [out] */ LPAPPLICATIONFILESOURCE* ppAppFileSrc);
HRESULT GetFile(
/* [in] */ LPCWSTR wzSourceFilename,
/* [in] */ LPCWSTR wzDestinationLocalFilePath,
/* [in] */ LPCWSTR wzHash);
HRESULT GetFile(
/* [in] */ LPCWSTR wzSourceFilePath,
/* [in] */ LPCWSTR wzDestinationLocalFilePath);
HRESULT SetSourcePath(
/* [in] */ LPCWSTR wzSourceFilePath);
HRESULT BuildLocalAppRootHierarchy(
/* [in] */ APPNAME* pAppName,
/* [out] */ LPWSTR wzLocalAppRoot);
HRESULT GetFullFilePath(
/* [in] */ LPCWSTR wzFilename,
/* [out] */ LPWSTR wzFullFilePath);
private:
// this should contain the last / or \, excluding a filename
WCHAR wzSourcePath[MAX_PATH]; // UNC/filesystem restirction
};
CUNCFileSource::CUNCFileSource()
{
wzSourcePath[0] = L'\0';
}
HRESULT
CUNCFileSource::CreateNew(LPAPPLICATIONFILESOURCE* ppAppFileSrc)
{
HRESULT hr = S_OK;
LPAPPLICATIONFILESOURCE pAppFileSrc = NULL;
if (!ppAppFileSrc)
{
hr = E_INVALIDARG;
goto exit;
}
pAppFileSrc = new CUNCFileSource();
if (!pAppFileSrc)
{
hr = E_OUTOFMEMORY;
*ppAppFileSrc = NULL;
goto exit;
}
*ppAppFileSrc = pAppFileSrc;
exit:
return hr;
}
HRESULT
CUNCFileSource::GetFile(LPCWSTR wzSourceFilePath, LPCWSTR wzDestinationLocalFilePath)
{
HRESULT hr = S_OK;
WCHAR* pwzFileName = NULL;
// unconditional get and overwrite
// check the path exist? create (sub)directories?
pwzFileName = PathFindFileName ( wzDestinationLocalFilePath );
if ( pwzFileName > wzDestinationLocalFilePath )
{
*(pwzFileName-1) = L'\0';
int len = wcslen(wzDestinationLocalFilePath);
if (len > 3)
{
/* must be at least c:\a */
hr = CreatePathHierarchy(wzDestinationLocalFilePath);
}
// note: this few lines must follow the previous CreatePathHierarchy() line
*(pwzFileName-1) = L'\\';
if (FAILED(hr))
goto exit;
}
if (CopyFile(wzSourceFilePath, wzDestinationLocalFilePath, FALSE) == 0)
{
hr = GetLastWin32Error();
goto exit;
}
exit:
return hr;
}
HRESULT
CUNCFileSource::GetFile(LPCWSTR wzSourceFilename, LPCWSTR wzDestinationLocalFilePath, LPCWSTR wzHash)
{
// note: unknown behaviour if wzSourcePath\wzSourceFilename == wzDestinationLocalFilePath
HRESULT hr=S_OK;
WCHAR wzFullFilePath[MAX_PATH];
// 1. check if the file exist, if so, check file integrity
if (PathFileExists(wzDestinationLocalFilePath))
{
if (FAILED(hr = CheckIntegrity(wzDestinationLocalFilePath, wzHash)))
goto exit;
if (hr == S_FALSE)
{
// hash mismatch/no hash, force overwrite
hr = S_OK;
}
else
goto exit; // else we are done, file already there and unchanged
}
// 2. assemble the full source path
if (FAILED(hr = GetFullFilePath(wzSourceFilename, wzFullFilePath)))
goto exit;
// 3. copy it over
if (FAILED(hr = GetFile(wzFullFilePath, wzDestinationLocalFilePath)))
goto exit;
// should this copy the file by bytes and check hash the same time?
// 4. check file integrity, assume ok if no hash
if (wzHash != NULL && wzHash[0] != L'\0')
{
if (FAILED(hr = CheckIntegrity(wzDestinationLocalFilePath, wzHash)))
goto exit;
if (hr == S_FALSE)
{
// hash mismatch - something very wrong
hr = CRYPT_E_HASH_VALUE;
goto exit;
}
}
exit:
return hr;
}
// this should only be called once
HRESULT
CUNCFileSource::SetSourcePath(LPCWSTR wzSourceFilePath)
{
// keep the source path
HRESULT hr=S_OK;
WCHAR* p;
// copy and avoid buffer overflows
wcsncpy(wzSourcePath, wzSourceFilePath, MAX_PATH-1);
wzSourcePath[MAX_PATH-1] = L'\0';
// strip out the filename portion of the filepath
p = PathFindFileName(wzSourcePath);
if ((p-wzSourcePath) >= MAX_PATH)
{
hr = CO_E_PATHTOOLONG;
goto exit;
}
else if (p <= wzSourcePath)
{
// this file path has no filename in it or is invalid (eg. root)
hr = E_INVALIDARG;
goto exit;
}
*(p-1) = L'\0';
exit:
if (FAILED(hr))
wzSourcePath[0] = L'\0';
return hr;
}
// returning the local app root path with all directories created
HRESULT
CUNCFileSource::BuildLocalAppRootHierarchy(APPNAME* pAppName, LPWSTR wzLocalAppRoot)
{
// build from wzSourcePath
HRESULT hr=S_OK;
int offset = 0;
int len = wcslen(wzSourcePath);
WCHAR wzAppDir[MAX_PATH];
if (len <3)
{
/* must be at least \\a or c:\*/
hr = E_INVALIDARG;
goto exit;
}
if (PathIsUNC(wzSourcePath))
offset = 2; // UNC source
else if (wzSourcePath[1] == L':' && wzSourcePath[2] == L'\\')
{
offset = 3; // local file source
// check if the string ends at/after the offset!
if (len <= 3)
{
// eg. C:\ - should disallow root
hr = E_INVALIDARG;
goto exit;
}
}
else
{
// what else is this?!
hr = E_FAIL;
goto exit;
}
// 1 get default local root
if (FAILED(hr=GetDefaultLocalRoot(wzLocalAppRoot)))
goto exit;
// 2 append part of source path as the app root
if (FAILED(hr=GetAppDir(pAppName, wzAppDir)))
goto exit;
if (!PathAppend(wzLocalAppRoot, wzAppDir))
{
hr = E_FAIL;
goto exit;
}
// 3 create the directories
if (FAILED(hr=CreatePathHierarchy(wzLocalAppRoot)))
{
goto exit;
}
exit:
if (FAILED(hr))
wzLocalAppRoot[0] = L'\0';
return hr;
}
// returning the source file path from a filename
HRESULT
CUNCFileSource::GetFullFilePath(LPCWSTR wzFilename, LPWSTR wzFullFilePath)
{
// wzFilename cannot = L'\0'
// wzFullFilePath must be of length MAX_PATH
HRESULT hr=S_OK;
if (wzSourcePath[0] == L'\0')
{
// source path not set
hr = E_UNEXPECTED;
goto exit;
}
wcscpy(wzFullFilePath, wzSourcePath);
if (!PathAppend(wzFullFilePath, wzFilename))
{
hr = E_FAIL;
//goto exit;
}
exit:
return hr;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// the following functions acts similar to their shlwapi Path... counterparts
// Returns a pointer to the last component of a path string.
//
// in:
// path name, either fully qualified or not
//
// returns:
// pointer into the path where the path is. if none is found
// returns a poiter to the start of the path
//
// http://www.microsoft.com/foo/bar -> bar
// http://www.microsoft.com/foo -> foo
// http://www.microsoft.com/foo/ -> foo/ (is this busted?)
// http://www.microsoft.com/ -> http://www.microsoft.com/
// http://www.microsoft.com -> http://www.microsoft.com
// http:// -> http://
// http: -> http:
// www.microsoft.com -> www.microsoft.com
STDAPI_(LPWSTR)
UrlFindFileName(LPCWSTR pPath)
{
LPCWSTR pW;
BOOL fSkip = FALSE;
for (pW = pPath; *pPath; pPath = pPath+1)
{
if (pPath[0] == L':' && pPath[1] && pPath[1] == L'/')
fSkip = TRUE;
if (pPath[0] == L'/' && pPath[1] && pPath[1] != L'/')
{
if (fSkip)
fSkip = FALSE;
else
pW = pPath + 1;
}
}
return (LPWSTR)pW; // const -> non const
}
// this returns successfully only if a filename is present
/*STDAPI_(LPWSTR)
UrlFindExtension(LPCWSTR pPath)
{
LPCWSTR pFN = UrlFindFileName(pPath);
int len = wcslen(pPath);
LPCWSTR pW = pPath+len;
// no filename there
if (pFN == pPath)
return (LPWSTR)(pPath+len);
for (; *pFN; pFN = pFN+1)
{
if (pFN[0] == L'.')
pW = pFN;
}
return (LPWSTR)pW;
}*/
// size of pszPath should be set to MAX_URL_LENGTH
STDAPI_(BOOL)
UrlAppend(LPWSTR pszPath, LPCWSTR pszMore)
{
int len = wcslen(pszPath);
int len2 = wcslen(pszMore);
// check string overlaps!
if ((pszPath < pszMore && pszMore < pszPath+len) ||
(pszMore < pszPath && pszPath < pszMore+len2))
return FALSE;
if ((len >= MAX_URL_LENGTH-1) || (len+len2 >= MAX_URL_LENGTH-1))
return FALSE;
if (pszPath[len-1] != L'/')
{
if ((len >= MAX_URL_LENGTH-2) || (len+len2 >= MAX_URL_LENGTH-2))
return FALSE;
pszPath[len] = L'/';
pszPath[len+1] = L'\0';
}
wcsncat(pszPath, pszMore, len2);
return TRUE;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
class CHTTPFileSource : public IApplicationFileSource
{
public:
CHTTPFileSource();
HRESULT CreateNew(
/* [out] */ LPAPPLICATIONFILESOURCE* ppAppFileSrc);
HRESULT GetFile(
/* [in] */ LPCWSTR wzSourceFilename,
/* [in] */ LPCWSTR wzDestinationLocalFilePath,
/* [in] */ LPCWSTR wzHash);
HRESULT GetFile(
/* [in] */ LPCWSTR wzSourceFilePath,
/* [in] */ LPCWSTR wzDestinationLocalFilePath);
HRESULT SetSourcePath(
/* [in] */ LPCWSTR wzSourceFilePath);
HRESULT BuildLocalAppRootHierarchy(
/* [in] */ APPNAME* pAppName,
/* [out] */ LPWSTR wzLocalAppRoot);
HRESULT GetFullFilePath(
/* [in] */ LPCWSTR wzFilename,
/* [out] */ LPWSTR wzFullFilePath);
private:
// this should contain the last / or \, excluding a filename
WCHAR wzSourcePath[MAX_URL_LENGTH]; // http URL restirction
// S_OK == hash match; S_FALSE == hash unmatch
HRESULT GetFileFromInternet(
/* [in] */ LPCWSTR wzSourceFilePath,
/* [in] */ LPCWSTR wzDestinationLocalFilePath,
/* [in] */ LPCWSTR wzHash);
};
CHTTPFileSource::CHTTPFileSource()
{
wzSourcePath[0] = L'\0';
}
HRESULT
CHTTPFileSource::CreateNew(LPAPPLICATIONFILESOURCE* ppAppFileSrc)
{
HRESULT hr = S_OK;
LPAPPLICATIONFILESOURCE pAppFileSrc = NULL;
if (!ppAppFileSrc)
{
hr = E_INVALIDARG;
goto exit;
}
pAppFileSrc = new CHTTPFileSource();
if (!pAppFileSrc)
{
hr = E_OUTOFMEMORY;
*ppAppFileSrc = NULL;
goto exit;
}
*ppAppFileSrc = pAppFileSrc;
exit:
return hr;
}
HRESULT
CHTTPFileSource::GetFileFromInternet(LPCWSTR wzSourceFilePath, LPCWSTR wzDestinationLocalFilePath, LPCWSTR wzHash)
{
HRESULT hr = S_OK;
WCHAR* pwzFileName = NULL;
HINTERNET hInternet = NULL;
HINTERNET hTransfer = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwRead = 0;
DWORD dwWritten = 0;
BYTE buffer[4096];
HASHCONTEXT hc;
WCHAR wzComputedHash[HASHSTRINGLENGTH];
BOOL fDoHash = TRUE;
// unconditional get and overwrite
if (wzHash == NULL || wzHash[0] == L'\0')
fDoHash = FALSE;
// check the path exist? create (sub)directories?
pwzFileName = PathFindFileName( wzDestinationLocalFilePath );
if ( pwzFileName > wzDestinationLocalFilePath )
{
*(pwzFileName-1) = L'\0';
int len = wcslen(wzDestinationLocalFilePath);
if (len > 3)
{
/* must be at least c:\a */
hr = CreatePathHierarchy(wzDestinationLocalFilePath);
}
// note: this few lines must follow the previous CreatePathHierarchy() line
*(pwzFileName-1) = L'\\';
if (FAILED(hr))
goto exit;
}
//////////////////////////////////////////////////////////////////////
// Step 1: Open the http connection
hInternet = InternetOpen(L"App", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(hInternet == NULL)
{
hr = GetLastWin32Error();
goto exit;
}
// note: cache write mean the refresh might not work and old bits could return
hTransfer = InternetOpenUrl(hInternet, wzSourceFilePath, NULL, 0, 0, 0);
if(hTransfer == NULL)
{
hr = GetLastWin32Error();
goto exit;
}
if (fDoHash)
BeginHash(&hc);
// need to check if there's any error, eg. not found (404)... InternetGetLastResponseInfo()?
//////////////////////////////////////////////////////////////////////
// Step 2: Create the file; Copy the files over the internet
// need write access, will open (and replace/overwrite) exiting file
// ??? FILE_SHARE_READ? but we might write to if outdated...
while(InternetReadFile(hTransfer, buffer, sizeof(buffer), &dwRead) && dwRead != 0)
{
// Open the disk file
if (hFile == INVALID_HANDLE_VALUE)
{
hFile = CreateFile(wzDestinationLocalFilePath, GENERIC_WRITE, 0, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
// Failed to open the disk file
if(hFile == INVALID_HANDLE_VALUE)
{
hr = GetLastWin32Error();
goto exit;
}
// Write bytes to the disk file
// synchronous download
if ( !WriteFile(hFile, buffer, dwRead, &dwWritten, NULL) || dwWritten != dwRead)
{
hr = GetLastWin32Error();
goto exit;
}
if (fDoHash)
ContinueHash(&hc, buffer, (unsigned) dwRead);
}
if (dwRead != 0)
{
hr = GetLastWin32Error();
goto exit;
}
if (fDoHash)
{
EndHash(&hc, wzComputedHash);
if (wcscmp(wzHash, wzComputedHash) != 0)
hr = S_FALSE;
else
hr = S_OK;
}
exit:
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
if (hInternet != NULL)
InternetCloseHandle(hInternet);
if (hTransfer != NULL)
InternetCloseHandle(hTransfer);
hInternet = NULL;
hTransfer = NULL;
hFile = INVALID_HANDLE_VALUE;
return hr;
}
HRESULT
CHTTPFileSource::GetFile(LPCWSTR wzSourceFilePath, LPCWSTR wzDestinationLocalFilePath)
{
HRESULT hr = S_OK;
WCHAR* pwzFileName = NULL;
// BUGBUG: this pretty much duplicates CUNCFileSource::GetFile(,)
// get file from a different location - via CopyFile
// unconditional get and overwrite
// check the path exist? create (sub)directories?
pwzFileName = PathFindFileName ( wzDestinationLocalFilePath );
if ( pwzFileName > wzDestinationLocalFilePath )
{
*(pwzFileName-1) = L'\0';
int len = wcslen(wzDestinationLocalFilePath);
if (len > 3)
{
/* must be at least c:\a */
hr = CreatePathHierarchy(wzDestinationLocalFilePath);
}
// note: this few lines must follow the previous CreatePathHierarchy() line
*(pwzFileName-1) = L'\\';
if (FAILED(hr))
goto exit;
}
if (CopyFile(wzSourceFilePath, wzDestinationLocalFilePath, FALSE) == 0)
{
hr = GetLastWin32Error();
goto exit;
}
exit:
return hr;
}
HRESULT
CHTTPFileSource::GetFile(LPCWSTR wzSourceFilename, LPCWSTR wzDestinationLocalFilePath, LPCWSTR wzHash)
{
// note: unknown behaviour if wzSourcePath\wzSourceFilename == wzDestinationLocalFilePath
HRESULT hr=S_OK;
WCHAR wzFullFilePath[MAX_URL_LENGTH];
// 1. check if the file exist, if so, check file integrity
if (PathFileExists(wzDestinationLocalFilePath))
{
if (FAILED(hr = CheckIntegrity(wzDestinationLocalFilePath, wzHash)))
goto exit;
if (hr == S_FALSE)
{
// hash mismatch/no hash, force overwrite
hr = S_OK;
}
else
goto exit; // else we are done, file already there and unchanged
}
// 2. assemble the full source path
if (FAILED(hr = GetFullFilePath(wzSourceFilename, wzFullFilePath)))
goto exit;
// 3. copy it over
if (FAILED(hr = GetFileFromInternet(wzFullFilePath, wzDestinationLocalFilePath, wzHash)))
goto exit;
// should this copy the file by bytes and check hash the same time?
// 4. check file integrity, assume ok if no hash
if (hr == S_FALSE)
{
DbgMsg(L"hash mismatch");
// hash mismatch - something very wrong
hr = CRYPT_E_HASH_VALUE;
goto exit;
}
exit:
return hr;
}
// this should only be called once
HRESULT
CHTTPFileSource::SetSourcePath(LPCWSTR wzSourceFilePath)
{
// keep the source path
HRESULT hr=S_OK;
WCHAR* p;
// copy and avoid buffer overflows
wcsncpy(wzSourcePath, wzSourceFilePath, MAX_URL_LENGTH-1);
wzSourcePath[MAX_URL_LENGTH-1] = L'\0';
// strip out the filename portion of the filepath
p = UrlFindFileName(wzSourcePath);
if ((p-wzSourcePath) >= MAX_URL_LENGTH)
{
hr = CO_E_PATHTOOLONG;
goto exit;
}
else if (p <= wzSourcePath)
{
// this file path has no filename in it or is invalid (eg. root)
hr = E_INVALIDARG;
goto exit;
}
*(p-1) = L'\0';
exit:
if (FAILED(hr))
wzSourcePath[0] = L'\0';
return hr;
}
// returning the local app root path with all directories created
HRESULT
CHTTPFileSource::BuildLocalAppRootHierarchy(APPNAME* pAppName, LPWSTR wzLocalAppRoot)
{
// BUGBUG: this pretty much duplicates CUNCFileSource::BuildLocalAppRootHierarchy(,)
// build from wzSourcePath
HRESULT hr=S_OK;
int len = wcslen(wzSourcePath);
DWORD dwLen = MAX_URL_LENGTH-1;
WCHAR wzHostName[MAX_URL_LENGTH];
WCHAR wzAppDir[MAX_PATH];
if (len <8)
{
/* must be at least http://a */
hr = E_INVALIDARG;
goto exit;
}
if (FAILED(hr = UrlGetPart(wzSourcePath, wzHostName, &dwLen, URL_PART_HOSTNAME, 0)))
{
// what is this?!
hr = E_FAIL;
goto exit;
}
// 1 get default local root
if (FAILED(hr=GetDefaultLocalRoot(wzLocalAppRoot)))
goto exit;
// 2 append part of source path as the app root
if (FAILED(hr=GetAppDir(pAppName, wzAppDir)))
goto exit;
if (!PathAppend(wzLocalAppRoot, wzAppDir))
{
hr = E_FAIL;
goto exit;
}
// 3 create the directories
if (FAILED(hr=CreatePathHierarchy(wzLocalAppRoot)))
{
goto exit;
}
exit:
if (FAILED(hr))
wzLocalAppRoot[0] = L'\0';
return hr;
}
// returning the source file path from a filename
HRESULT
CHTTPFileSource::GetFullFilePath(LPCWSTR wzFilename, LPWSTR wzFullFilePath)
{
// wzFilename cannot = L'\0'
// wzFullFilePath must be of length MAX_URL_LENGTH
HRESULT hr=S_OK;
if (wzSourcePath[0] == L'\0')
{
// source path not set
hr = E_UNEXPECTED;
goto exit;
}
wcscpy(wzFullFilePath, wzSourcePath);
if (!UrlAppend(wzFullFilePath, wzFilename))
{
hr = E_FAIL;
//goto exit;
}
exit:
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT
ProcessUrl(LPCWSTR wzRefFilePath, LPCWSTR wzRefRealFilename, APPNAME* pAppName, LPWSTR wzCodebase)
{
HRESULT hr=S_OK;
CHTTPFileSource httpfs;
WCHAR wzLocalPath[MAX_PATH];
WCHAR *p = NULL;
// step 1: check the commandline argument's extension - somewhat optional
if ((_wcsicmp(PathFindExtension(wzRefRealFilename), SHORTCUTFILEEXT) != 0) ||
!PathIsURL(wzCodebase) || //!PathFileExists(wzRefFilePath) || not work w/ UNC source
UrlIsFileUrl(wzCodebase) || UrlIsOpaque(wzCodebase)) // BUGBUG? :check http:// too
{
hr = E_INVALIDARG; // file ext mismatch OR is a file:// or path is not an URL AND file not exist
goto exit;
}
// step 2: get the shortcut file and the .manifest file
if (FAILED(hr = httpfs.SetSourcePath(wzCodebase)))
goto exit;
if (FAILED(hr = httpfs.BuildLocalAppRootHierarchy(pAppName, wzLocalPath)))
goto exit;
if(!PathAppend(wzLocalPath, wzRefRealFilename))
{
hr = E_FAIL;
DbgMsg(L"path for .app fails");
goto exit;
}
// get file via CopyFile
if (FAILED(hr = httpfs.GetFile(wzRefFilePath, wzLocalPath))) // ignore E_UNEXPECTED?
{
DbgMsg(L"get .app fails");
goto exit;
}
*PathFindFileName(wzLocalPath) = L'\0';
p = UrlFindFileName(wzCodebase);
if (p == wzCodebase || !PathAppend(wzLocalPath, p))
{
hr = E_FAIL;
DbgMsg(L"path for .manifest fails");
goto exit;
}
if (FAILED(hr = httpfs.GetFile(p, wzLocalPath, NULL))) // ignore E_UNEXPECTED?
{
DbgMsg(L"get .manifest fails");
goto exit;
}
// step 3: process the .manifest file
if (FAILED(hr = ProcessAppManifest(wzLocalPath, &httpfs, pAppName)))
goto exit;
exit:
if (hr == E_ABORT)
MsgShow(L"Operation aborted.");
else if (FAILED(hr))
MsgShow(L"Error Encountered.");
else
{
// if SUCCEEDED
// do not overwrite if shortcut exists. ignore error
// note: this should probably be done only in the "1st install" case
// - ie. need feedback from ProcessAppManifest()
CopyToStartMenu(wzRefFilePath, wzRefRealFilename, FALSE);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT
ProcessUNC(LPCWSTR wzRefFilePath, LPCWSTR wzRefRealFilename, APPNAME* pAppName, LPWSTR wzCodebase)
{
// BUGBUG: this pretty much duplicates ProcessUrl
HRESULT hr=S_OK;
CUNCFileSource uncfs;
WCHAR wzLocalPath[MAX_PATH];
WCHAR *p = NULL;
// step 1: check the commandline argument's extension - somewhat optional
if ((_wcsicmp(PathFindExtension(wzRefRealFilename), SHORTCUTFILEEXT) != 0) ||
(!PathIsUNC(wzCodebase) && !PathFileExists(wzCodebase)))
{
hr = E_INVALIDARG; // file ext mismatch OR (path is not an UNC AND file not exist(ie. not local))
goto exit;
}
// step 2: get the shortcut file and the .manifest file
if (FAILED(hr = uncfs.SetSourcePath(wzCodebase)))
goto exit;
if (FAILED(hr = uncfs.BuildLocalAppRootHierarchy(pAppName, wzLocalPath)))
goto exit;
if(!PathAppend(wzLocalPath, wzRefRealFilename))
{
hr = E_FAIL;
DbgMsg(L"path for .app fails");
goto exit;
}
if (FAILED(hr = uncfs.GetFile(wzRefFilePath, wzLocalPath))) // ignore E_UNEXPECTED?
{
DbgMsg(L"get .app fails");
goto exit;
}
*PathFindFileName(wzLocalPath) = L'\0';
p = PathFindFileName(wzCodebase);
if (p == wzCodebase || !PathAppend(wzLocalPath, p))
{
hr = E_FAIL;
DbgMsg(L"path for .manifest fails");
goto exit;
}
if (FAILED(hr = uncfs.GetFile(p, wzLocalPath, NULL))) // ignore E_UNEXPECTED?
{
DbgMsg(L"get .manifest fails");
goto exit;
}
// step 3: process the .manifest file
if (FAILED(hr = ProcessAppManifest(wzLocalPath, &uncfs, pAppName)))
goto exit;
exit:
if (hr == E_ABORT)
MsgShow(L"Operation aborted.");
else if (FAILED(hr))
MsgShow(L"Error Encountered.");
else
{
// if SUCCEEDED
// do not overwrite if shortcut exists. ignore error
// note: this should probably be done only in the "1st install" case
// - ie. need feedback from ProcessAppManifest()
CopyToStartMenu(wzRefFilePath, wzRefRealFilename, FALSE);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
int __cdecl wmain( int argc, wchar_t *argv[], wchar_t *envp[] )
{
BOOL isRefFileTemp = FALSE;
LPWSTR pwzRef = NULL;
LPWSTR pwzRefRealFilename = NULL;
WCHAR wzCodebase[MAX_URL_LENGTH];
APPNAME appName;
// BUGBUG: ** a hack to hide the console window when starts **
//- should convert this to a window-less WinMain app
ShowWindow(GetConsoleWindow(), SW_HIDE);
if (argc < 2)
{
MsgShow(L"No commandline argument specified");
goto exit;
}
if (PathFileExists(argv[1]) || PathIsUNC(argv[1]))
{
// ref local or on a UNC
pwzRef = argv[1];
pwzRefRealFilename = PathFindFileName(argv[1]);
if (pwzRefRealFilename == argv[1])
{
MsgShow(L"Invalid file path");
goto exit;
}
}
else if (PathIsURL(argv[1]))
{
// ref is from a URL
if (argc < 3)
{
// arguments: <url to .app> <local path to a copy of .app as a .tmp>
// msg ambiguity is intented
MsgShow(L"Invalid argument");
goto exit;
}
// should only be called this way by the mimefilter
// if so, the ref file is a temp file that has to be deleted
pwzRef = argv[2];
// note: will delete the file named by argv[2], the temp ref file
isRefFileTemp = TRUE;
pwzRefRealFilename = UrlFindFileName(argv[1]);
if (pwzRefRealFilename == argv[1])
{
MsgShow(L"Invalid file path");
goto exit;
}
}
else
{
MsgShow(L"Invalid commandline argument specified");
goto exit;
}
wzCodebase[0] = L'\0';
appName._wzDisplayName[0] = L'\0';
appName._wzName[0] = L'\0';
appName._wzVersion[0] = L'\0';
appName._wzCulture[0] = L'\0';
appName._wzPKT[0] = L'\0';
// parse the shortcut to get the codebase
if (FAILED(ProcessRef(pwzRef, &appName, wzCodebase)))
{
MsgShow(L".app file processing fails");
goto exit;
}
if (PathIsURL(wzCodebase))
{
// note: this basically allows a ref file on a UNC pointing to
// app files on a web server. possible security risk?
// security token is still computed from the codebase,
// ie, URL, though
// ignore return value
ProcessUrl(pwzRef, pwzRefRealFilename, &appName, wzCodebase);
}
else // default is UNC/local path (PathIsUNC(wzCodebase))
{
// note: this allows a ref file on a web server
// to run something from the disk or UNC. security?
// security token is still computed from the codebase,
// ie, UNC, though
// ignore return value
ProcessUNC(pwzRef, pwzRefRealFilename, &appName, wzCodebase);
}
exit:
if (isRefFileTemp)
{
if (argv[2][0] != L'\0')
{
// ignore return value
DeleteFile(argv[2]);
}
}
return 0;
}