You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
804 lines
23 KiB
804 lines
23 KiB
/*
|
|
* a t t a c h . c p p
|
|
*
|
|
* Purpose:
|
|
* Attachment utilities
|
|
*
|
|
* History
|
|
*
|
|
* Copyright (C) Microsoft Corp. 1995, 1996.
|
|
*/
|
|
|
|
|
|
|
|
#include <pch.hxx>
|
|
#include "dllmain.h"
|
|
#include "resource.h"
|
|
#include "error.h"
|
|
#include "mimeolep.h"
|
|
#include "shellapi.h"
|
|
#include "shlobj.h"
|
|
#include "shlwapi.h"
|
|
#include "shlwapip.h"
|
|
#include "mimeole.h"
|
|
#include "commdlg.h"
|
|
#include "demand.h"
|
|
#include "saferun.h"
|
|
|
|
ASSERTDATA
|
|
|
|
/*
|
|
* t y p e d e f s
|
|
*/
|
|
|
|
/*
|
|
* m a c r o s
|
|
*/
|
|
|
|
/*
|
|
* c o n s t a n t s
|
|
*/
|
|
#define MAX_CHARS_FOR_NUM 20
|
|
|
|
static const CHAR c_szWebMark[] = "<!-- saved from url=(0022)http://internet.e-mail -->\r\n";
|
|
static const WCHAR c_wszWebMark[] = L"<!-- saved from url=(0022)http://internet.e-mail -->\r\n";
|
|
/*
|
|
* g l o b a l s
|
|
*/
|
|
|
|
|
|
/*
|
|
* p r o t o t y p e s
|
|
*/
|
|
HRESULT HrCleanTempFile(LPATTACHDATA pAttach);
|
|
HRESULT HrGetTempFile(IMimeMessage *pMsg, LPATTACHDATA lpAttach);
|
|
DWORD AthGetShortPathName(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer);
|
|
|
|
STDAPI HrGetAttachIconByFile(LPWSTR szFilename, BOOL fLargeIcon, HICON *phIcon)
|
|
{
|
|
HICON hIcon = NULL;
|
|
LPWSTR lpszExt;
|
|
SHFILEINFOW rShFileInfo;
|
|
|
|
if (szFilename)
|
|
{
|
|
// Does the file exist already ?
|
|
if ((UINT)GetFileAttributesWrapW(szFilename) != (UINT)-1)
|
|
{
|
|
// Try to get the icon out of the file
|
|
SHGetFileInfoWrapW(szFilename, 0, &rShFileInfo, sizeof(rShFileInfo), SHGFI_ICON |(fLargeIcon ? 0 : SHGFI_SMALLICON));
|
|
hIcon=rShFileInfo.hIcon;
|
|
}
|
|
else
|
|
if (lpszExt = PathFindExtensionW(szFilename))
|
|
{
|
|
// Lookup the icon for lpszExt
|
|
SHGetFileInfoWrapW(lpszExt, FILE_ATTRIBUTE_NORMAL, &rShFileInfo, sizeof (rShFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | (fLargeIcon ? 0 : SHGFI_SMALLICON));
|
|
hIcon=rShFileInfo.hIcon;
|
|
}
|
|
}
|
|
|
|
if (!hIcon)
|
|
hIcon = CopyIcon(LoadIcon (g_hLocRes, MAKEINTRESOURCE (idiDefaultAtt)));
|
|
|
|
*phIcon=hIcon;
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI HrGetAttachIcon(IMimeMessage *pMsg, HBODY hAttach, BOOL fLargeIcon, HICON *phIcon)
|
|
{
|
|
// Locals
|
|
HRESULT hr = S_OK;
|
|
LPWSTR lpszFile=0;
|
|
|
|
if (!phIcon || !hAttach || !pMsg)
|
|
return E_INVALIDARG;
|
|
|
|
*phIcon=NULL;
|
|
|
|
// Get file name for body part. If get an error, doesn't matter. Still use
|
|
// a default icon that will be provided through HrGetAttachIconByFile
|
|
MimeOleGetBodyPropW(pMsg, hAttach, PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &lpszFile);
|
|
hr = HrGetAttachIconByFile(lpszFile, fLargeIcon, phIcon);
|
|
SafeMemFree(lpszFile);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// GetUIVersion()
|
|
//
|
|
// returns the version of shell32
|
|
// 3 == win95 gold / NT4
|
|
// 4 == IE4 Integ / win98
|
|
// 5 == win2k / millennium
|
|
//
|
|
UINT GetUIVersion()
|
|
{
|
|
static UINT s_uiShell32 = 0;
|
|
|
|
if (s_uiShell32 == 0)
|
|
{
|
|
HINSTANCE hinst = LoadLibrary("SHELL32.DLL");
|
|
if (hinst)
|
|
{
|
|
DLLGETVERSIONPROC pfnGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinst, "DllGetVersion");
|
|
DLLVERSIONINFO dllinfo;
|
|
|
|
dllinfo.cbSize = sizeof(DLLVERSIONINFO);
|
|
if (pfnGetVersion && pfnGetVersion(&dllinfo) == NOERROR)
|
|
s_uiShell32 = dllinfo.dwMajorVersion;
|
|
else
|
|
s_uiShell32 = 3;
|
|
|
|
FreeLibrary(hinst);
|
|
}
|
|
}
|
|
return s_uiShell32;
|
|
}
|
|
|
|
STDAPI HrDoAttachmentVerb(HWND hwnd, ULONG uVerb, IMimeMessage *pMsg, LPATTACHDATA pAttach)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SHELLEXECUTEINFOW rShellExec;
|
|
LPSTREAM lpstmAtt = NULL;
|
|
LPWSTR lpszShortName = NULL,
|
|
lpszParameters = NULL,
|
|
lpszExt = NULL;
|
|
DWORD dwFlags;
|
|
WCHAR szCommand[MAX_PATH];
|
|
HKEY hKeyAssoc = NULL;
|
|
|
|
AssertSz( uVerb < AV_MAX, "Bad Verb");
|
|
|
|
if (uVerb == AV_PROPERTIES)
|
|
{
|
|
AssertSz(FALSE, "AV_PROPERTIES is NYI!!");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
if (!pAttach)
|
|
return E_INVALIDARG;
|
|
|
|
// Save As - much simpler case
|
|
if (uVerb == AV_SAVEAS)
|
|
{
|
|
hr=HrSaveAttachmentAs(hwnd, pMsg, pAttach);
|
|
goto error; // done
|
|
}
|
|
|
|
// If opening attachment, lets verify thsi
|
|
if (uVerb == AV_OPEN)
|
|
{
|
|
// If nVerb is to open the attachment, lets verify that with the user
|
|
hr = IsSafeToRun(hwnd, pAttach->szFileName, TRUE);
|
|
|
|
if (hr == MIMEEDIT_E_USERCANCEL) // map mimeedit error to athena error
|
|
hr = hrUserCancel;
|
|
|
|
if (FAILED(hr)) // user doesn't want to do this
|
|
goto error;
|
|
|
|
if (hr == MIMEEDIT_S_SAVEFILE)
|
|
{
|
|
hr=HrSaveAttachmentAs(hwnd, pMsg, pAttach);
|
|
// done
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
// get temp file
|
|
hr = HrGetTempFile(pMsg, pAttach);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
Assert(lstrlenW(pAttach->szTempFile));
|
|
|
|
// Setup Shell Execute Info Struct
|
|
ZeroMemory (&rShellExec, sizeof (rShellExec));
|
|
rShellExec.cbSize = sizeof (rShellExec);
|
|
rShellExec.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
rShellExec.hwnd = hwnd;
|
|
rShellExec.nShow = SW_SHOWNORMAL;
|
|
|
|
// Verb
|
|
if (uVerb == AV_OPEN)
|
|
{
|
|
// Were going to run the file
|
|
hr = VerifyTrust(hwnd, pAttach->szFileName, pAttach->szTempFile);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = hrUserCancel;
|
|
goto error;
|
|
}
|
|
|
|
StrCpyNW(szCommand, pAttach->szTempFile, ARRAYSIZE(szCommand));
|
|
rShellExec.lpFile = szCommand;
|
|
|
|
// If the file does not have an associated type, do an OpenAs.
|
|
// We're not testing the result of AssocQueryKeyW because even if it fails,
|
|
// we want to try to open the file.
|
|
lpszExt = PathFindExtensionW(pAttach->szTempFile);
|
|
AssocQueryKeyW(NULL, ASSOCKEY_CLASS, lpszExt, NULL, &hKeyAssoc);
|
|
if((hKeyAssoc == NULL) && (GetUIVersion() != 5))
|
|
rShellExec.lpVerb = L"OpenAs";
|
|
else
|
|
RegCloseKey(hKeyAssoc);
|
|
}
|
|
|
|
else if (uVerb == AV_PRINT)
|
|
{
|
|
StrCpyNW(szCommand, pAttach->szTempFile, ARRAYSIZE(szCommand));
|
|
rShellExec.lpFile = szCommand;
|
|
rShellExec.lpVerb = L"Print";
|
|
}
|
|
|
|
else if (uVerb == AV_QUICKVIEW)
|
|
{
|
|
UINT uiSysDirLen;
|
|
const WCHAR c_szSubDir[] = L"\\VIEWERS\\QUIKVIEW.EXE";
|
|
|
|
// Find out where the viewer lives
|
|
uiSysDirLen = GetSystemDirectoryWrapW(szCommand, ARRAYSIZE(szCommand));
|
|
if (0 == uiSysDirLen || uiSysDirLen >= ARRAYSIZE(szCommand) ||
|
|
uiSysDirLen + ARRAYSIZE(c_szSubDir) > ARRAYSIZE(szCommand))
|
|
{
|
|
hr = E_FAIL;
|
|
goto error;
|
|
}
|
|
|
|
StrCpyNW(szCommand + uiSysDirLen, c_szSubDir, ARRAYSIZE(szCommand) - uiSysDirLen);
|
|
|
|
// Alloc for short file name
|
|
ULONG cchShortName = MAX_PATH + lstrlenW(pAttach->szTempFile) + 1;
|
|
if (!MemAlloc ((LPVOID *)&lpszShortName, cchShortName * sizeof (WCHAR)))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
// Alloc a string for the parameters
|
|
ULONG cchParameters = 30 + cchShortName;
|
|
if (!MemAlloc ((LPVOID *)&lpszParameters, cchParameters * sizeof (WCHAR)))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
// Get Short File Name
|
|
if (0 == AthGetShortPathName(pAttach->szTempFile, lpszShortName, cchShortName))
|
|
StrCpyNW(lpszShortName, pAttach->szTempFile, cchShortName);
|
|
|
|
// Put together the paramenters for QVSTUB.EXE
|
|
Assert(cchParameters > cchShortName);
|
|
StrCpyNW(lpszParameters, L"-v -f:", cchParameters);
|
|
StrCatBuffW(lpszParameters, lpszShortName, cchParameters);
|
|
|
|
// Setup Shellexec
|
|
rShellExec.lpParameters = lpszParameters;
|
|
rShellExec.lpFile = szCommand;
|
|
}
|
|
else
|
|
Assert (FALSE);
|
|
|
|
if (fIsNT5()) // quoted paths cause problems downlevel
|
|
PathQuoteSpacesW(szCommand);
|
|
|
|
// Execute it - even if this fails, we handled it - it will give a good error
|
|
ShellExecuteExWrapW(&rShellExec);
|
|
pAttach->hProcess = rShellExec.hProcess;
|
|
|
|
|
|
error:
|
|
MemFree(lpszParameters);
|
|
MemFree(lpszShortName);
|
|
return hr;
|
|
}
|
|
|
|
DWORD AthGetShortPathName(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cchBuffer)
|
|
{
|
|
CHAR szShortPath[MAX_PATH*2]; // Each Unicode char might go multibyte
|
|
LPSTR pszLongPath = NULL;
|
|
DWORD result = 0;
|
|
|
|
Assert(pwszLongPath);
|
|
pszLongPath = PszToANSI(CP_ACP, pwszLongPath);
|
|
|
|
if (pszLongPath)
|
|
{
|
|
result = GetShortPathName(pszLongPath, szShortPath, ARRAYSIZE(szShortPath));
|
|
if (result && result <= ARRAYSIZE(szShortPath))
|
|
{
|
|
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szShortPath, lstrlen(szShortPath), pwszShortPath, cchBuffer);
|
|
pwszShortPath[cchBuffer - 1] = 0;
|
|
}
|
|
|
|
MemFree(pszLongPath);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
STDAPI HrAttachDataFromBodyPart(IMimeMessage *pMsg, HBODY hAttach, LPATTACHDATA *ppAttach)
|
|
{
|
|
LPATTACHDATA pAttach;
|
|
LPWSTR pszW;
|
|
IMimeBodyW *pBody;
|
|
|
|
Assert (pMsg && ppAttach && hAttach);
|
|
|
|
if (!MemAlloc((LPVOID *)&pAttach, sizeof(ATTACHDATA)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
// fill in attachment data
|
|
ZeroMemory(pAttach, sizeof(ATTACHDATA));
|
|
|
|
if (pMsg->BindToObject(hAttach, IID_IMimeBodyW, (LPVOID *)&pBody)==S_OK)
|
|
{
|
|
if (pBody->GetDisplayNameW(&pszW)==S_OK)
|
|
{
|
|
StrCpyNW(pAttach->szDisplay, pszW, ARRAYSIZE(pAttach->szDisplay));
|
|
MemFree(pszW);
|
|
}
|
|
ReleaseObj(pBody);
|
|
}
|
|
|
|
if (MimeOleGetBodyPropW(pMsg, hAttach, PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &pszW)==S_OK)
|
|
{
|
|
if(IsPlatformWinNT() != S_OK)
|
|
{
|
|
CHAR pszFile[MAX_PATH*2];
|
|
DWORD cchSizeW = (lstrlenW(pszW) + 1);
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, pszW, -1, pszFile, ARRAYSIZE(pszFile), NULL, NULL);
|
|
pszFile[ARRAYSIZE(pszFile) - 1] = '\0';
|
|
MultiByteToWideChar(CP_ACP, 0, pszFile, -1, pszW, cchSizeW);
|
|
pszW[cchSizeW - 1] = 0;
|
|
|
|
CleanupFileNameInPlaceW(pszW);
|
|
}
|
|
|
|
StrCpyNW(pAttach->szFileName, pszW, ARRAYSIZE(pAttach->szFileName));
|
|
MemFree(pszW);
|
|
}
|
|
|
|
SideAssert(HrGetAttachIcon(pMsg, hAttach, FALSE, &pAttach->hIcon)==S_OK);
|
|
pAttach->hAttach = hAttach;
|
|
pAttach->fSafe = (IsSafeToRun(NULL, pAttach->szFileName, FALSE) == MIMEEDIT_S_OPENFILE);
|
|
|
|
*ppAttach = pAttach;
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI HrAttachDataFromFile(IStream *pstm, LPWSTR pszFileName, LPATTACHDATA *ppAttach)
|
|
{
|
|
LPATTACHDATA pAttach=0;
|
|
LPMIMEBODY pBody=0;
|
|
HRESULT hr;
|
|
int cFileNameLength;
|
|
|
|
Assert(ppAttach);
|
|
|
|
if (!MemAlloc((LPVOID *)&pAttach, sizeof(ATTACHDATA)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
// fill in attachment data
|
|
ZeroMemory(pAttach, sizeof(ATTACHDATA));
|
|
|
|
HrGetDisplayNameWithSizeForFile(pszFileName, pAttach->szDisplay, ARRAYSIZE(pAttach->szDisplay));
|
|
StrCpyNW(pAttach->szFileName, pszFileName, ARRAYSIZE(pAttach->szFileName));
|
|
if (!pstm)
|
|
{
|
|
// for new attachments set tempfile to be the same as filename
|
|
// note: if creating from an IStream then pszFileName is not a valid path
|
|
// we can create a tempfile later
|
|
StrCpyNW(pAttach->szTempFile, pszFileName, ARRAYSIZE(pAttach->szTempFile));
|
|
}
|
|
ReplaceInterface(pAttach->pstm, pstm);
|
|
SideAssert(HrGetAttachIconByFile(pszFileName, FALSE, &pAttach->hIcon)==S_OK);
|
|
|
|
if (ppAttach)
|
|
*ppAttach=pAttach;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI HrFreeAttachData(LPATTACHDATA pAttach)
|
|
{
|
|
if (pAttach)
|
|
{
|
|
HrCleanTempFile(pAttach);
|
|
ReleaseObj(pAttach->pstm);
|
|
if (pAttach->hIcon)
|
|
DestroyIcon(pAttach->hIcon);
|
|
|
|
MemFree(pAttach);
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
HRESULT HrCleanTempFile(LPATTACHDATA pAttach)
|
|
{
|
|
if (pAttach)
|
|
{
|
|
if (*pAttach->szTempFile && pAttach->hAttach)
|
|
{
|
|
// we only clean the tempfile if hAttach != NULL. Otherwise we assume it's a new attachment
|
|
// and szTempFile points to the source file
|
|
// If the file was launched, don't delete the temp file if the process still has it open
|
|
if (pAttach->hProcess)
|
|
{
|
|
if (WaitForSingleObject (pAttach->hProcess, 0) == WAIT_OBJECT_0)
|
|
DeleteFileWrapW(pAttach->szTempFile);
|
|
}
|
|
else
|
|
DeleteFileWrapW(pAttach->szTempFile);
|
|
}
|
|
*pAttach->szTempFile = NULL;
|
|
pAttach->hProcess=NULL;
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT HrAttachSafetyFromBodyPart(IMimeMessage *pMsg, HBODY hAttach, BOOL *pfSafe)
|
|
{
|
|
LPWSTR pszW = NULL;
|
|
|
|
Assert (pMsg && pfSafe && hAttach);
|
|
|
|
*pfSafe = FALSE;
|
|
if (MimeOleGetBodyPropW(pMsg, hAttach, PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &pszW)==S_OK)
|
|
{
|
|
if(IsPlatformWinNT() != S_OK)
|
|
{
|
|
CHAR pszFile[MAX_PATH*2];
|
|
DWORD cchSizeW = lstrlenW(pszW)+1;
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, pszW, -1, pszFile, ARRAYSIZE(pszFile), NULL, NULL);
|
|
pszFile[ARRAYSIZE(pszFile) - 1] = 0;
|
|
MultiByteToWideChar(CP_ACP, 0, pszFile, -1, pszW, cchSizeW);
|
|
pszW[cchSizeW - 1] = 0;
|
|
|
|
CleanupFileNameInPlaceW(pszW);
|
|
}
|
|
*pfSafe = (IsSafeToRun(NULL, pszW, FALSE) == MIMEEDIT_S_OPENFILE);
|
|
MemFree(pszW);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI HrGetDisplayNameWithSizeForFile(LPWSTR pszPathName, LPWSTR pszDisplayName, int cchMaxDisplayName)
|
|
{
|
|
HANDLE hFile;
|
|
DWORD uFileSize;
|
|
WCHAR szSize[MAX_CHARS_FOR_NUM+1+3],
|
|
szBuff[MAX_CHARS_FOR_NUM+1];
|
|
LPWSTR pszFileName = NULL,
|
|
pszFirst;
|
|
int iSizeLen = 0,
|
|
iLenFirst;
|
|
|
|
szSize[0] = L'\0';
|
|
|
|
hFile = CreateFileWrapW(pszPathName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
uFileSize = GetFileSize(hFile, NULL);
|
|
CloseHandle(hFile);
|
|
if ((uFileSize != 0xffffffff) && StrFormatByteSizeW(uFileSize, szBuff, ARRAYSIZE(szBuff) - 1))
|
|
{
|
|
StrCpyNW(szSize, L" (", ARRAYSIZE(szSize));
|
|
StrCatBuffW(szSize, szBuff, ARRAYSIZE(szSize));
|
|
StrCatBuffW(szSize, L")", ARRAYSIZE(szSize));
|
|
}
|
|
}
|
|
pszFileName = PathFindFileNameW(pszPathName);
|
|
|
|
iSizeLen = lstrlenW(szSize);
|
|
if (pszFileName)
|
|
pszFirst = pszFileName;
|
|
else
|
|
pszFirst = pszPathName;
|
|
|
|
iLenFirst = lstrlenW(pszFirst);
|
|
StrCpyNW(pszDisplayName, pszFirst, cchMaxDisplayName);
|
|
|
|
if (iLenFirst + iSizeLen + 1 > cchMaxDisplayName)
|
|
return E_FAIL;
|
|
|
|
StrCpyNW(pszDisplayName + iLenFirst, szSize, cchMaxDisplayName - iLenFirst);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDAPI HrSaveAttachmentAs(HWND hwnd, IMimeMessage *pMsg, LPATTACHDATA lpAttach)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
OPENFILENAMEW ofn;
|
|
WCHAR szTitle[CCHMAX_STRINGRES],
|
|
szFilter[CCHMAX_STRINGRES],
|
|
szFile[MAX_PATH];
|
|
|
|
*szFile=0;
|
|
*szFilter=0;
|
|
*szTitle=0;
|
|
|
|
Assert (lpAttach->szFileName);
|
|
if (lpAttach->szFileName)
|
|
StrCpyNW(szFile, lpAttach->szFileName, ARRAYSIZE(szFile));
|
|
|
|
ZeroMemory (&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hwnd;
|
|
LoadStringWrapW(g_hLocRes, idsFilterAttSave, szFilter, ARRAYSIZE(szFilter));
|
|
ReplaceCharsW(szFilter, L'|', L'\0');
|
|
ofn.lpstrFilter = szFilter;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrFile = szFile;
|
|
ofn.nMaxFile = ARRAYSIZE(szFile);
|
|
LoadStringWrapW(g_hLocRes, idsSaveAttachmentAs, szTitle, ARRAYSIZE(szTitle));
|
|
ofn.lpstrTitle = szTitle;
|
|
ofn.Flags = OFN_NOCHANGEDIR | OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
|
|
|
|
// Show SaveAs Dialog
|
|
if (HrAthGetFileNameW(&ofn, FALSE) != S_OK)
|
|
{
|
|
hr = hrUserCancel;
|
|
goto error;
|
|
}
|
|
|
|
if (lpAttach->hAttach == NULL)
|
|
{
|
|
if (!PathFileExistsW(lpAttach->szFileName))
|
|
{
|
|
hr = hrNotFound;
|
|
goto error;
|
|
}
|
|
|
|
// if hAttach == NULL then try and copy the file
|
|
CopyFileWrapW(lpAttach->szFileName, szFile, TRUE);
|
|
}
|
|
else
|
|
{
|
|
// Verify the Attachment's Stream
|
|
hr=HrSaveAttachToFile(pMsg, lpAttach->hAttach, szFile);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT HrGetTempFile(IMimeMessage *pMsg, LPATTACHDATA lpAttach)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (*lpAttach->szTempFile)
|
|
return S_OK;
|
|
|
|
if (!FBuildTempPathW(lpAttach->szFileName, lpAttach->szTempFile, ARRAYSIZE(lpAttach->szTempFile), FALSE))
|
|
{
|
|
hr = E_FAIL;
|
|
goto error;
|
|
}
|
|
|
|
if (lpAttach->hAttach)
|
|
{
|
|
hr=HrSaveAttachToFile(pMsg, lpAttach->hAttach, lpAttach->szTempFile);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
AssertSz(lpAttach->pstm, "if no hAttach then pstm should be set");
|
|
|
|
hr = WriteStreamToFileW(lpAttach->pstm, lpAttach->szTempFile, CREATE_ALWAYS, GENERIC_WRITE);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
if (FAILED(hr))
|
|
{
|
|
// Null out temp file as we didn't really create it
|
|
*(lpAttach->szTempFile)=0;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
const BYTE c_rgbUTF[3] = {0xEF, 0xBB, 0xBF};
|
|
STDAPI HrSaveAttachToFile(IMimeMessage *pMsg, HBODY hAttach, LPWSTR lpszFileName)
|
|
{
|
|
HRESULT hr;
|
|
LPSTREAM pstm=NULL,
|
|
pstmOut=NULL;
|
|
BOOL fEndian,
|
|
fUTFEncoded = FALSE;
|
|
BYTE rgbBOM[2];
|
|
BYTE rgbUTF[3];
|
|
DWORD cbRead;
|
|
|
|
if (pMsg == NULL || hAttach == NULL)
|
|
IF_FAILEXIT(hr = E_INVALIDARG);
|
|
|
|
// bind to the attachment data
|
|
IF_FAILEXIT(hr=pMsg->BindToObject(hAttach, IID_IStream, (LPVOID *)&pstm));
|
|
|
|
// create a file stream
|
|
IF_FAILEXIT(hr = OpenFileStreamW(lpszFileName, CREATE_ALWAYS, GENERIC_WRITE, &pstmOut));
|
|
|
|
// if we have an .HTM file, then pre-pend 'mark of the web' comment
|
|
// If we are a unicode file, the BOM will be "0xFFFE"
|
|
// If we are a UTF8 file, the BOM will be "0xEFBBBF"
|
|
if (PathIsHTMLFileW(lpszFileName))
|
|
{
|
|
if (S_OK == HrIsStreamUnicode(pstm, &fEndian))
|
|
{
|
|
// Don't rewind otherwise end up with BOM after 'mark of the web' as well.
|
|
IF_FAILEXIT(hr = pstm->Read(rgbBOM, sizeof(rgbBOM), &cbRead));
|
|
|
|
// Since HrIsStreamUnicode succeeded, there should be at least two
|
|
Assert(sizeof(rgbBOM) == cbRead);
|
|
|
|
IF_FAILEXIT(hr = pstmOut->Write(rgbBOM, cbRead, NULL));
|
|
IF_FAILEXIT(hr = pstmOut->Write(c_wszWebMark, sizeof(c_wszWebMark)-sizeof(WCHAR), NULL));
|
|
}
|
|
else
|
|
{
|
|
// Check for UTF8 file BOM
|
|
IF_FAILEXIT(hr = pstm->Read(rgbUTF, sizeof(rgbUTF), &cbRead));
|
|
if (sizeof(rgbUTF) == cbRead)
|
|
{
|
|
fUTFEncoded = (0 == memcmp(c_rgbUTF, rgbUTF, sizeof(rgbUTF)));
|
|
}
|
|
|
|
// If we are not UTF8 encoded, then rewind, else write out BOM
|
|
if (!fUTFEncoded)
|
|
IF_FAILEXIT(hr = HrRewindStream(pstm));
|
|
else
|
|
IF_FAILEXIT(hr = pstmOut->Write(c_rgbUTF, sizeof(c_rgbUTF), NULL));
|
|
|
|
IF_FAILEXIT(hr = pstmOut->Write(c_szWebMark, sizeof(c_szWebMark)-sizeof(CHAR), NULL));
|
|
|
|
}
|
|
}
|
|
|
|
// write out the actual file data
|
|
IF_FAILEXIT(hr = HrCopyStream(pstm, pstmOut, NULL));
|
|
|
|
exit:
|
|
ReleaseObj(pstm);
|
|
ReleaseObj(pstmOut);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Why?
|
|
*
|
|
* we wrap this as if we don't use NOCHANGEDIR, then things like ShellExec
|
|
* fail if the current directory is nolonger valid. Eg: attach a file from a:\
|
|
* and remove the floppy. Then all ShellExec's fail. So we maintain our own
|
|
* last directory buffer. We should use thread-local for this, as it is possible
|
|
* that two thread could whack the same buffer, it is unlikley that a user action
|
|
* could cause two open file dialogs in the note and the view to open at exactly the
|
|
* same time.
|
|
*/
|
|
|
|
WCHAR g_wszLastDir[MAX_PATH];
|
|
|
|
HRESULT SetDefaultSpecialFolderPath()
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl)==S_OK)
|
|
{
|
|
if (SHGetPathFromIDListWrapW(pidl, g_wszLastDir))
|
|
hr = S_OK;
|
|
|
|
SHFree(pidl);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDAPI HrAthGetFileName(OPENFILENAME *pofn, BOOL fOpen)
|
|
{
|
|
BOOL fRet;
|
|
LPSTR pszDir = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
Assert(pofn != NULL);
|
|
|
|
// force NOCHANGEDIR
|
|
pofn->Flags |= OFN_NOCHANGEDIR;
|
|
|
|
if (pofn->lpstrInitialDir == NULL)
|
|
{
|
|
if (!PathFileExistsW(g_wszLastDir))
|
|
SideAssert(SetDefaultSpecialFolderPath()==S_OK);
|
|
|
|
IF_NULLEXIT(pszDir = PszToANSI(CP_ACP, g_wszLastDir));
|
|
|
|
pofn->lpstrInitialDir = pszDir;
|
|
}
|
|
|
|
if (fOpen)
|
|
fRet = GetOpenFileName(pofn);
|
|
else
|
|
fRet = GetSaveFileName(pofn);
|
|
|
|
if (fRet)
|
|
{
|
|
// store the last path
|
|
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pofn->lpstrFile, lstrlen(pofn->lpstrFile), g_wszLastDir, ARRAYSIZE(g_wszLastDir));
|
|
if (!PathIsDirectoryW(g_wszLastDir))
|
|
PathRemoveFileSpecW(g_wszLastDir);
|
|
}
|
|
else
|
|
TraceResult(hr = E_FAIL);
|
|
|
|
exit:
|
|
MemFree(pszDir);
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDAPI HrAthGetFileNameW(OPENFILENAMEW *pofn, BOOL fOpen)
|
|
{
|
|
BOOL fRet;
|
|
|
|
Assert(pofn != NULL);
|
|
|
|
// force NOCHANGEDIR
|
|
pofn->Flags |= OFN_NOCHANGEDIR;
|
|
|
|
if (pofn->lpstrInitialDir == NULL)
|
|
{
|
|
if (!PathFileExistsW(g_wszLastDir))
|
|
SideAssert(SetDefaultSpecialFolderPath()==S_OK);
|
|
|
|
pofn->lpstrInitialDir = g_wszLastDir;
|
|
}
|
|
|
|
if (fOpen)
|
|
fRet = GetOpenFileNameWrapW(pofn);
|
|
else
|
|
fRet = GetSaveFileNameWrapW(pofn);
|
|
|
|
if (fRet)
|
|
{
|
|
// store the last path
|
|
StrCpyNW(g_wszLastDir, pofn->lpstrFile, ARRAYSIZE(g_wszLastDir));
|
|
if (!PathIsDirectoryW(g_wszLastDir))
|
|
PathRemoveFileSpecW(g_wszLastDir);
|
|
}
|
|
|
|
return fRet?S_OK:E_FAIL;
|
|
}
|
|
|
|
STDAPI HrGetLastOpenFileDirectory(int cchMax, LPSTR lpsz)
|
|
{
|
|
if (!PathFileExistsW(g_wszLastDir))
|
|
SideAssert(SetDefaultSpecialFolderPath()==S_OK);
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, g_wszLastDir, lstrlenW(g_wszLastDir), lpsz, cchMax, NULL, NULL);
|
|
lpsz[cchMax - 1] = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI HrGetLastOpenFileDirectoryW(int cchMax, LPWSTR lpsz)
|
|
{
|
|
if (!PathFileExistsW(g_wszLastDir))
|
|
SideAssert(SetDefaultSpecialFolderPath()==S_OK);
|
|
|
|
StrCpyNW(lpsz, g_wszLastDir, cchMax);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|