Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2253 lines
62 KiB

// ==============================================================================
// 3/2/96 - Attachment Manager Class Implementation (sbailey & brents)
// ==============================================================================
#include "pch.hxx"
#include "strconst.h"
#include <mimeole.h>
#include "mimeutil.h"
#include "mimeolep.h"
#include "attman.h"
#include <error.h>
#include <resource.h>
#include "header.h"
#include "note.h"
#include <thormsgs.h>
#include <shlwapi.h>
#include <shlwapip.h>
#include "fonts.h"
#include "secutil.h"
#include <mailnews.h>
#include <multiusr.h>
#include <menures.h>
#include <menuutil.h>
#include <demand.h> // must be last!
#include "mirror.h"
// for dialog idc's
#include "fexist.h"
/*
* c o n s t a n t s
*/
#define CNUMICONSDEFAULT 10
#define CACHE_GROW_SIZE 10
#define MAX_ATTACH_PIXEL_HEIGHT 100
/*
* m a c r o s
*/
/*
* t y p e s
*
*/
/*
* c o n s t a n t s
*
*/
/*
* g l o b a l s
*
*/
/*
* p r o t o t y p e s
*
*/
// ==============================================================================
// CAttMan::CAttMan
// ==============================================================================
CAttMan::CAttMan ()
{
DOUT ("CAttMan::CAttMan");
m_pMsg = NULL;
m_himlSmall = NULL;
m_himlLarge = NULL;
m_cRef = 1;
m_hwndList = NULL;
m_hwndParent=NULL;
m_cfAccept = CF_NULL;
m_dwDragType = 0;
m_grfKeyState = 0;
m_dwEffect = 0;
m_cxMaxText = 0;
m_cyHeight = 0;
m_fReadOnly = 0;
m_fDirty = FALSE;
m_fDragSource = FALSE;
m_fDropTargetRegister=FALSE;
m_fShowingContext = 0;
m_fRightClick = 0;
m_fWarning = 1;
m_fSafeOnly = TRUE;
m_rgpAttach=NULL;
m_cAttach=0;
m_cAlloc=0;
m_iVCard = -1;
m_fDeleteVCards = FALSE;
m_szUnsafeAttachList = NULL;
m_cUnsafeAttach = 0;
}
// ==============================================================================
// CAttMan::~CAttMan
// ==============================================================================
CAttMan::~CAttMan ()
{
DOUT ("CAttMan::~CAttMan");
if (m_himlSmall)
ImageList_Destroy (m_himlSmall);
if (m_himlLarge)
ImageList_Destroy (m_himlLarge);
if (m_szUnsafeAttachList != NULL)
SafeMemFree(m_szUnsafeAttachList);
SafeRelease (m_pMsg);
}
// ==============================================================================
// CAttMan::AddRef
// ==============================================================================
ULONG CAttMan::AddRef()
{
DOUT ("CAttMan::AddRef () Ref Count=%d", m_cRef);
return ++m_cRef;
}
// ==============================================================================
// CAttMan::Release
// ==============================================================================
ULONG CAttMan::Release()
{
ULONG ulCount = --m_cRef;
DOUT ("CAttMan::Release () Ref Count=%d", ulCount);
if (!ulCount)
delete this;
return ulCount;
}
HRESULT STDMETHODCALLTYPE CAttMan::QueryInterface(REFIID riid, void **ppvObj)
{
*ppvObj = NULL; // set to NULL, in case we fail.
if (IsEqualIID(riid, IID_IUnknown))
*ppvObj = (void*)this;
// else if (IsEqualIID(riid, IID_IDropTarget))
// *ppvObj = (void*)(IDropTarget*)this;
else if (IsEqualIID(riid, IID_IDropSource))
*ppvObj = (void*)(IDropSource*)this;
else
return E_NOINTERFACE;
AddRef();
return NOERROR;
}
HRESULT CAttMan::HrGetAttachCount(ULONG *pcAttach)
{
Assert(pcAttach);
//*pcAttach = m_cAttach;
*pcAttach = ListView_GetItemCount(m_hwndList);
return S_OK;
}
ULONG CAttMan::GetUnsafeAttachCount()
{
return m_cUnsafeAttach;
}
LPTSTR CAttMan::GetUnsafeAttachList()
{
return m_szUnsafeAttachList;
}
HRESULT CAttMan::HrUnload()
{
HRESULT hr;
SafeRelease (m_pMsg);
if (m_hwndList)
ListView_DeleteAllItems(m_hwndList);
hr=HrFreeAllData();
if (FAILED(hr))
goto error;
error:
return hr;
}
HRESULT CAttMan::HrInit(HWND hwnd, BOOL fReadOnly, BOOL fDeleteVCards, BOOL fAllowUnsafe)
{
m_fReadOnly = !!fReadOnly;
m_hwndParent = hwnd;
m_fDeleteVCards = !!fDeleteVCards;
m_fSafeOnly = !fAllowUnsafe;
return HrCreateListView(hwnd);
}
HRESULT CAttMan::HrClearDirtyFlag()
{
m_fDirty=FALSE;
return S_OK;
}
HRESULT CAttMan::HrIsDirty()
{
return m_fDirty?S_OK:S_FALSE;
}
HRESULT CAttMan::GetTabStopArray(HWND *rgTSArray, int *pcArrayCount)
{
Assert(rgTSArray);
Assert(pcArrayCount);
Assert(*pcArrayCount > 0);
*rgTSArray = m_hwndList;
*pcArrayCount = 1;
return S_OK;
}
HRESULT CAttMan::HrCreateListView(HWND hwnd)
{
HRESULT hr;
DWORD dwFlags;
dwFlags = 0;//DwGetOption(OPT_ATTACH_VIEW_STYLE);
dwFlags |= WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_TABSTOP|LVS_AUTOARRANGE|
LVS_SMALLICON|LVS_NOSCROLL|LVS_SHAREIMAGELISTS;
m_hwndList = CreateWindowExWrapW(WS_EX_CLIENTEDGE,
WC_LISTVIEWW,
L"",
dwFlags,
0,0,0,0,
hwnd,
(HMENU)idwAttachWell,
g_hInst,
NULL);
if(!m_hwndList)
return E_OUTOFMEMORY;
// Init image list
hr=HrInitImageLists();
if(FAILED(hr))
goto error;
#if 0
// if we're not readonly, register ourselves as a drop target...
if(!m_fReadOnly)
{
hr=CoLockObjectExternal((LPDROPTARGET)this, TRUE, FALSE);
if (FAILED(hr))
goto error;
hr=RegisterDragDrop(m_hwndList, (LPDROPTARGET)this);
if (FAILED(hr))
goto error;
m_fDropTargetRegister=TRUE;
}
#endif
error:
return hr;
}
HRESULT CAttMan::HrBuildAttachList()
{
HRESULT hr = S_OK;
ULONG cAttach=0,
uAttach;
LPHBODY rghAttach=0;
Assert(m_pMsg != NULL);
// secure receipt is not attachment and we don't need to show .DAT file as attachment.
if(CheckSecReceipt(m_pMsg) == S_OK)
return hr;
//GetAttachmentCount(m_pMsg, &cAttach);
hr=m_pMsg->GetAttachments(&cAttach, &rghAttach);
if (FAILED(hr))
goto error;
for(uAttach=0; uAttach<cAttach; uAttach++)
{
hr=HrAddData(rghAttach[uAttach]);
if (FAILED(hr))
goto error;
}
error:
SafeMimeOleFree(rghAttach);
return hr;
}
// Only expected to be used during initialization with original m_pMsg
HRESULT CAttMan::HrFillListView()
{
HRESULT hr;
ULONG uAttach;
CComBSTR bstrUnsafeAttach;
Assert (m_hwndList && IsWindow(m_hwndList) && m_pMsg);
hr = HrCheckVCard();
if (FAILED(hr))
goto error;
if (m_cAttach==0) // nothing to do
return NOERROR;
if(m_iVCard >= 0)
ListView_SetItemCount(m_hwndList, m_cAttach - 1);
else
ListView_SetItemCount(m_hwndList, m_cAttach);
if (m_szUnsafeAttachList != NULL)
SafeMemFree(m_szUnsafeAttachList);
m_cUnsafeAttach = 0;
// walk the attachment data list and add them to the listview
for(uAttach=0; uAttach<m_cAlloc; uAttach++)
{
// if there is one and only one vcare in the read note, don't add it to list view,
// header will show it as a stamp.
if (m_rgpAttach[uAttach] && uAttach!=(ULONG)m_iVCard)
{
hr=HrAddToList(m_rgpAttach[uAttach], TRUE);
if (hr == S_FALSE)
{
if (bstrUnsafeAttach.Length() > 0)
bstrUnsafeAttach.Append(L",");
bstrUnsafeAttach.Append(m_rgpAttach[uAttach]->szFileName);
m_cUnsafeAttach++;
}
if (FAILED(hr))
goto error;
}
}
error:
if (m_cUnsafeAttach)
m_szUnsafeAttachList = PszToANSI(CP_ACP, bstrUnsafeAttach.m_str);
#ifdef DEBUG
if(m_iVCard >= 0)
AssertSz(m_cAttach-1==(ULONG)ListView_GetItemCount(m_hwndList)+m_cUnsafeAttach, "Something failed creating the attachmentlist");
else
AssertSz(m_cAttach==(ULONG)ListView_GetItemCount(m_hwndList)+m_cUnsafeAttach, "Something failed creating the attachmentlist");
#endif
return hr;
}
// tells the note header if there is a vcard it wants.
HRESULT CAttMan::HrFVCard()
{
return (m_iVCard >= 0) ? S_OK : S_FALSE;
}
// note header needs this function to show the property of the vcard
// which is shown as a stamp on the header.
HRESULT CAttMan::HrShowVCardProp()
{
Assert(m_iVCard >= 0);
return HrDoVerb(m_rgpAttach[m_iVCard], ID_OPEN);
}
// checks if we have one and only one vcard in the attachment.
HRESULT CAttMan::HrCheckVCard()
{
HRESULT hr = NOERROR;
ULONG uAttach;
m_iVCard = -1;
// this is only for read note. Since preview doesn't call this function,
// we can check m_fReadOnly to see if it's a read note.
if(!m_fReadOnly)
return hr;
for(uAttach=0; uAttach<m_cAlloc; uAttach++)
{
if (m_rgpAttach[uAttach])
{
if(StrStrIW(PathFindExtensionW((m_rgpAttach[uAttach])->szFileName), L".vcf"))
{
if(m_iVCard >= 0)
{
// there are more than one vcards, we quit.
m_iVCard = -1;
break;
}
else
m_iVCard = uAttach;
}
}
}
return hr;
}
HRESULT CAttMan::HrCheckVCardExists(BOOL fMail)
{
HRESULT hr = S_FALSE;
ULONG uAttach;
TCHAR szVCardName[MAX_PATH];
LPWSTR szVCardNameW = NULL;
if(m_fReadOnly)
return hr;
*szVCardName = 0;
if(fMail)
GetOption(OPT_MAIL_VCARDNAME, szVCardName, MAX_PATH);
else
GetOption(OPT_NEWS_VCARDNAME, szVCardName, MAX_PATH);
if (*szVCardName != '\0')
{
szVCardNameW = PszToUnicode(CP_ACP, szVCardName);
if (szVCardNameW)
{
for(uAttach=0; uAttach<m_cAlloc; uAttach++)
{
if (m_rgpAttach[uAttach])
{
if(0 == StrCmpNIW((m_rgpAttach[uAttach])->szFileName, szVCardNameW, lstrlenW(szVCardNameW)))
{
hr = S_OK;
break;
}
}
}
MemFree(szVCardNameW);
}
else
TraceResult(hr = E_OUTOFMEMORY);
}
return hr;
}
/*
*
* HrInitImageLists
*
* Create an image list and assign it to our listview.
* to contain iicons number of icons
*
*/
HRESULT CAttMan::HrInitImageLists()
{
UINT flags = ILC_MASK;
Assert(m_hwndList && IsWindow(m_hwndList));
Assert(!m_himlLarge);
Assert(!m_himlSmall);
if(IS_WINDOW_RTL_MIRRORED(m_hwndList))
{
flags |= ILC_MIRROR ;
}
m_himlLarge = ImageList_Create( GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON),
flags, CNUMICONSDEFAULT, 0);
if(!m_himlLarge)
return E_OUTOFMEMORY;
m_himlSmall = ImageList_Create( GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
flags, CNUMICONSDEFAULT, 0);
if(!m_himlSmall)
return E_OUTOFMEMORY;
ListView_SetImageList(m_hwndList, m_himlSmall, LVSIL_SMALL);
ListView_SetImageList(m_hwndList, m_himlLarge, LVSIL_NORMAL);
return NOERROR;
}
//
// HrAddToList
//
// adds an attachment to the LV,
// if count was 0 then send a message to parent
// to redraw.
//
HRESULT CAttMan::HrAddToList(LPATTACHDATA pAttach, BOOL fIniting)
{
LV_ITEMW lvi ={0};
INT iPos;
HICON hIcon=0;
RECT rc;
Assert(m_hwndList != NULL);
Assert(pAttach != NULL);
Assert(m_himlSmall != NULL);
Assert(m_himlLarge != NULL);
// don't show attachments which are deemed unsafe
if (m_fReadOnly && m_fSafeOnly && !(pAttach->fSafe))
return S_FALSE;
// if this is the first item
// we need to send a message to parent
lvi.mask = LVIF_PARAM|LVIF_TEXT|LVIF_IMAGE|LVIF_STATE;
lvi.stateMask = 0;
lvi.pszText = L"";
lvi.lParam = (LPARAM)pAttach;
// get icons for image list
if (fIniting)
{
SideAssert(HrGetAttachIcon(m_pMsg, pAttach->hAttach, FALSE, &hIcon)==S_OK);
lvi.iImage = ImageList_AddIcon(m_himlSmall, hIcon);
DestroyIcon(hIcon);
SideAssert(HrGetAttachIcon(m_pMsg, pAttach->hAttach, TRUE, &hIcon)==S_OK);
ImageList_AddIcon(m_himlLarge, hIcon);
DestroyIcon(hIcon);
}
else
{
SideAssert(HrGetAttachIconByFile(pAttach->szFileName, FALSE, &hIcon)==S_OK);
lvi.iImage = ImageList_AddIcon(m_himlSmall, hIcon);
DestroyIcon(hIcon);
SideAssert(HrGetAttachIconByFile(pAttach->szFileName, TRUE, &hIcon)==S_OK);
ImageList_AddIcon(m_himlLarge, hIcon);
DestroyIcon(hIcon);
}
lvi.pszText = pAttach->szDisplay;
iPos = (INT) SendMessage(m_hwndList, LVM_INSERTITEMW, 0, (LPARAM)(LV_ITEMW*)(&lvi));
if (-1 == iPos)
return E_FAIL;
// Must set to LVS_ICON then reset to LVS_SMALLICON
// to get SMALLICONs to come up arranged.
DWORD dwStyle = GetWindowStyle(m_hwndList);
if ((dwStyle & LVS_TYPEMASK) == LVS_SMALLICON)
{
SetWindowLong(m_hwndList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK)|LVS_ICON);
SetWindowLong(m_hwndList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK)|LVS_SMALLICON);
}
return S_OK;
}
BOOL CAttMan::WMNotify(int idFrom, NMHDR *pnmhdr)
{
DOUTLL( DOUTL_ATTMAN, 2, "CAttMan :: WMNotify( ), %d", idFrom );
if (idFrom!=idwAttachWell)
return FALSE;
switch (pnmhdr->code)
{
case LVN_KEYDOWN:
{
LV_KEYDOWN *pnkd = ((LV_KEYDOWN *)pnmhdr);
switch (pnkd->wVKey)
{
case VK_DELETE:
if (!m_fReadOnly)
HrRemoveAttachments();
break;
case VK_INSERT:
if (!m_fReadOnly)
HrInsertFile();
break;
case VK_RETURN:
case VK_EXECUTE:
HrExecFile(ID_OPEN);
break;
}
break;
}
case LVN_BEGINDRAG:
case LVN_BEGINRDRAG:
m_dwDragType = (pnmhdr->code==LVN_BEGINDRAG?MK_LBUTTON:MK_RBUTTON);
HrBeginDrag();
return TRUE;
case NM_DBLCLK:
HrDblClick(idFrom, pnmhdr);
return TRUE;
}
return FALSE;
}
//================================================================
//
// BOOL CAttMan :: OnBeginDrag( )
//
// Purpose: We have received a message that a drag has begun.
//================================================================
HRESULT CAttMan::HrBeginDrag()
{
DWORD dwEffect;
IDataObject *pDataObj=0;
PDATAOBJINFO pdoi = 0;
HRESULT hr;
Assert(m_hwndList);
// BROKEN: this is busted. Creating the tempfile on a dragstart is broken, we should package these better.
hr=HrBuildHDrop(&pdoi);
if (FAILED(hr))
goto error;
hr = CreateDataObject(pdoi, 1, (PFNFREEDATAOBJ)FreeAthenaDataObj, &pDataObj);
if (FAILED(hr))
{
SafeMemFree(pdoi);
goto error;
}
if (m_fReadOnly)
dwEffect = DROPEFFECT_COPY;
else
dwEffect = DROPEFFECT_MOVE|DROPEFFECT_COPY;
// prevent source drags in the body...
m_fDragSource = TRUE;
hr=DoDragDrop((LPDATAOBJECT)pDataObj, (LPDROPSOURCE)this, dwEffect, &dwEffect);
m_fDragSource = FALSE;
if (FAILED(hr))
goto error;
// ok, now lets see if the operation was a move, if so we need to
// delete the source.
if( !m_fReadOnly && (dwEffect & DROPEFFECT_MOVE))
hr=HrRemoveAttachments();
error:
ReleaseObj(pDataObj);
return hr;
}
//================================================================
//
// BOOL CAttMan :: WMContextMenu( )
//
// Displays one of two menus for the lisview.
// Menu is selected depending if items are highlighted.
//
// returns: TRUE => success.
//
//================================================================
BOOL CAttMan::WMContextMenu( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
HMENU hMenu=NULL;
INT cSel;
BOOL fEnable,
fRet = FALSE;
LV_ITEMW lvi;
WCHAR szCommand[MAX_PATH];
DWORD dwPos;
// was it for us?
if ((HWND)wParam != m_hwndList)
goto cleanup;
Assert(m_hwndList);
cSel = ListView_GetSelectedCount(m_hwndList);
hMenu = LoadPopupMenu(IDR_ATTACHMENT_POPUP);
if(!hMenu)
goto cleanup;
// commands that are enabled if only one attachment is selected
fEnable = (cSel == 1);
EnableMenuItem(hMenu, ID_PRINT, MF_BYCOMMAND | (fEnable? MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, ID_QUICK_VIEW, MF_BYCOMMAND | (fEnable? MF_ENABLED:MF_GRAYED));
// enabled in readonly mode and if there is only one attach selected
EnableMenuItem(hMenu, ID_SAVE_ATTACH_AS, MF_BYCOMMAND | ((fEnable && m_fReadOnly)? MF_ENABLED:MF_GRAYED));
// enabled if any attachments selected
EnableMenuItem(hMenu, ID_OPEN, MF_BYCOMMAND | (cSel > 0? MF_ENABLED:MF_GRAYED));
// enabled only in readonly mode
EnableMenuItem(hMenu, ID_SAVE_ATTACHMENTS, MF_BYCOMMAND | (m_fReadOnly? MF_ENABLED:MF_GRAYED));
// enabled only in compose mode
EnableMenuItem(hMenu, ID_ADD, MF_BYCOMMAND | (!m_fReadOnly? MF_ENABLED:MF_GRAYED));
// enabled only in compose mode if there is a valid selection
EnableMenuItem(hMenu, ID_REMOVE, MF_BYCOMMAND | (!m_fReadOnly && cSel > 0? MF_ENABLED:MF_GRAYED));
if ((fIsNT5()) || (IsOS(OS_MILLENNIUM)))
{
// On Both these platforms, Quick View is not supported.
DeleteMenu(hMenu, ID_QUICK_VIEW, MF_BYCOMMAND);
}
else
{
// Disable Quick View if QVIEW.EXE does not exist
GetSystemDirectoryWrapW(szCommand, ARRAYSIZE(szCommand));
StrCatBuffW(szCommand, L"\\VIEWERS\\QUIKVIEW.EXE", ARRAYSIZE(szCommand));
if ((UINT)GetFileAttributesWrapW(szCommand) == (UINT)-1)
{
EnableMenuItem (hMenu, ID_QUICK_VIEW, MF_GRAYED);
}
}
// bold the first non-grey item
MenuUtil_SetPopupDefault(hMenu, ID_OPEN);
// RAID $2129: disable print verb for .eml files
// $49436 - also disable for .lnks
if (cSel==1)
{
LPWSTR pszExt;
lvi.iItem = ListView_GetSelFocused(m_hwndList);
lvi.mask = LVIF_PARAM;
if (SendMessage(m_hwndList, LVM_GETITEMW, 0, (LPARAM)(LV_ITEMW*)(&lvi)))
{
pszExt = PathFindExtensionW(((LPATTACHDATA)lvi.lParam)->szFileName);
if (pszExt && (StrCmpIW(pszExt, c_wszEmlExt)==0 ||
StrCmpIW(pszExt, c_wszNwsExt)==0 ||
StrCmpIW(pszExt, L".lnk")==0))
EnableMenuItem( hMenu, ID_PRINT, MF_GRAYED);
}
}
dwPos=GetMessagePos();
fRet = TrackPopupMenuEx(
hMenu,
TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
LOWORD(dwPos),
HIWORD(dwPos),
hwnd,
NULL);
cleanup:
if(hMenu)
DestroyMenu(hMenu);
return fRet;
}
HRESULT CAttMan::HrDblClick(int idFrom, NMHDR *pnmhdr)
{
DWORD dwPos;
POINT pt;
LV_HITTESTINFO lvhti;
LV_ITEMW lvi;
Assert(m_hwndList);
// Find out where the cursor was
dwPos = GetMessagePos();
pt.x = LOWORD(dwPos);
pt.y = HIWORD(dwPos);
ScreenToClient( m_hwndList, &pt);
lvhti.pt = pt;
if(ListView_HitTest(m_hwndList, &lvhti) != -1)
{
// return 1 here, we passed the HitTest
lvi.iItem = lvhti.iItem;
lvi.mask = LVIF_PARAM;
if (SendMessage(m_hwndList, LVM_GETITEMW, 0, (LPARAM)(LV_ITEMW*)(&lvi)))
return HrDoVerb((LPATTACHDATA)lvi.lParam, ID_OPEN);
}
return S_OK;
}
HRESULT CAttMan::HrGetHeight(INT cxWidth, ULONG *pcy)
{
DWORD dwDims;
LONG cCount;
if (!pcy || cxWidth<=0)
return E_INVALIDARG;
*pcy=0;
cCount = ListView_GetItemCount(m_hwndList);
if (0 == cCount)
*pcy = 0;
else
{
dwDims = ListView_ApproximateViewRect(m_hwndList, cxWidth, 0, cCount);
*pcy = HIWORD(dwDims);
}
return S_OK;
}
HRESULT CAttMan::HrSwitchView(DWORD dwView)
{
DWORD dwStyle = GetWindowStyle(m_hwndList);
WORD ToolbarStyleLookup[]= { LVS_ICON,
LVS_REPORT,
LVS_SMALLICON,
LVS_LIST };
Assert(m_hwndList);
// convert index into lisview style
dwView = ToolbarStyleLookup[dwView];
if ((LVS_ICON != dwView) && (LVS_SMALLICON != dwView))
dwView = LVS_ICON;
// don't change to the same view
if ((dwStyle & LVS_TYPEMASK) != dwView)
{
SetWindowLong(m_hwndList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK)|dwView);
HrResizeParent();
SetDwOption(OPT_ATTACH_VIEW_STYLE, dwView, NULL, 0);
}
return S_OK;
}
HRESULT CAttMan::HrSetSize(RECT *prc)
{
Assert(IsWindow( m_hwndList ));
Assert(prc);
DWORD dwStyle = GetWindowStyle(m_hwndList),
dwPosFlags;
ULONG cAttMan = 0;
HrGetAttachCount(&cAttMan);
if (cAttMan == 1)
SetWindowLong(m_hwndList, GWL_STYLE, dwStyle | LVS_NOSCROLL);
else
SetWindowLong(m_hwndList, GWL_STYLE, dwStyle & ~LVS_NOSCROLL);
dwPosFlags = (cAttMan > 0) ? SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_SHOWWINDOW:
SWP_HIDEWINDOW;
SetWindowPos(m_hwndList, NULL, prc->left, prc->top, prc->right-prc->left, prc->bottom-prc->top, dwPosFlags);
return S_OK;
}
BOOL CAttMan::WMCommand(HWND hwndCmd, INT id, WORD wCmd)
{
// verbs depending on listview mode
if (m_hwndList)
{
switch(id)
{
case ID_SELECT_ALL:
if(GetFocus()!=m_hwndList)
return FALSE;
ListView_SelectAll(m_hwndList);
return TRUE;
case ID_ADD:
HrInsertFile();
return TRUE;
case ID_REMOVE:
HrRemoveAttachments();
return TRUE;
case ID_OPEN:
case ID_QUICK_VIEW:
case ID_PRINT:
case ID_SAVE_ATTACH_AS:
HrExecFile(id);
return TRUE;
case ID_INSERT_ATTACHMENT:
HrInsertFile();
return TRUE;
}
}
return FALSE;
}
//===================================================
//
// HrRemoveAttachment
//
// Purpose:
// Removes an attachment from the ListView
//
// Arguments:
// ili - index of attachment in listview to remove
// fDelete - should we remove it from list
//
// Returns:
///
//===================================================
HRESULT CAttMan::HrRemoveAttachment(int ili)
{
LV_ITEMW lvi;
LPATTACHDATA lpAttach=0;
HRESULT hr=S_OK;
ULONG uAttach;
Assert( m_hwndList );
lvi.mask = LVIF_PARAM;
lvi.iSubItem = 0;
lvi.iItem = ili;
if (!SendMessage(m_hwndList, LVM_GETITEMW, 0, (LPARAM)(LV_ITEMW*)(&lvi)))
{
AssertSz(0, "Attempting to remove an item that is not there");
return E_FAIL; // item does not exist!!!!
}
lpAttach = (LPATTACHDATA)lvi.lParam;
if(!lpAttach)
return E_FAIL;
// find it and kill it from the list
for (uAttach=0; uAttach<m_cAlloc; uAttach++)
{
if (m_rgpAttach[uAttach]==lpAttach)
{
HrFreeAttachData(m_rgpAttach[uAttach]);
m_rgpAttach[uAttach] = NULL;
break;
}
}
// if we actually removed the attachment, make sure we're dirty
m_fDirty = TRUE;
ListView_DeleteItem(m_hwndList, ili);
return hr;
}
/*
* CAttMan::HrDeleteAttachments
*
* Purpose:
* Prompts user to confirm deletion, if IDYES -> blow them away
*
*/
HRESULT CAttMan::HrDeleteAttachments()
{
if (AthMessageBoxW( m_hwndParent,
MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW(idsAttConfirmDeletion),
NULL, MB_YESNO|MB_ICONEXCLAMATION )==IDNO)
return NOERROR;
return HrRemoveAttachments();
}
/*
* CAttMan :: HrRemoveAttachments
*
* Purpose:
* Removes all selected attachments from the Well.
*
* Arguments:
*
*/
HRESULT CAttMan::HrRemoveAttachments()
{
HRESULT hr=NOERROR;
HWND hwnd;
int ili,
iNext,
nPos,
nCount;
Assert(m_hwndList);
while ((ili=ListView_GetNextItem(m_hwndList, -1, LVNI_SELECTED|LVNI_ALL))!=-1)
{
iNext = ili;
hr=HrRemoveAttachment(ili);
if (FAILED(hr))
goto error;
}
if ((nCount=ListView_GetItemCount(m_hwndList))==0)
{
// if there are no attachments left, we need to size the well to 0. and setfocus
// to someother control
m_cyHeight = 0;
HrResizeParent();
if (hwnd = GetNextDlgTabItem(m_hwndParent, m_hwndList, TRUE))
SetFocus(hwnd);
}
else
{
HrResizeParent();
if (iNext<nCount)
nPos = (iNext?iNext-1:iNext);
else
nPos = nCount - 1;
ListView_SelectItem(m_hwndList, nPos);
}
error:
return hr;
}
HRESULT CAttMan::HrResizeParent()
{
RECT rc;
NMHDR nmhdr;
Assert(m_hwndList);
nmhdr.hwndFrom=m_hwndList;
nmhdr.idFrom=GetDlgCtrlID(m_hwndList);
nmhdr.code=ATTN_RESIZEPARENT;
SendMessage(GetParent(m_hwndList), WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
return S_OK;
}
HRESULT GetLastAttachmentPath(LPWSTR pszDefaultDir, DWORD cchSize)
{
HRESULT hr = S_OK;
DWORD dwType;
DWORD cbSize = (cchSize * sizeof(pszDefaultDir[0]));
pszDefaultDir[0] = 0;
DWORD dwError = SHGetValueW(MU_GetCurrentUserHKey(), NULL, L"Attachment Path", &dwType, (void *) pszDefaultDir, &cbSize);
hr = HRESULT_FROM_WIN32(dwError);
return hr;
}
HRESULT SetLastAttachmentPath(LPCWSTR pszDefaultDir)
{
HRESULT hr = S_OK;
DWORD cbSize = ((lstrlenW(pszDefaultDir)+1) * sizeof(pszDefaultDir[0]));
DWORD dwError = SHSetValueW(MU_GetCurrentUserHKey(), NULL, L"Attachment Path", REG_SZ, (void *) pszDefaultDir, cbSize);
hr = HRESULT_FROM_WIN32(dwError);
return hr;
}
#define CCH_INSERTFILE 4096
typedef struct tagATTMANCUSTOM {
BOOL fShortcut;
WCHAR szFiles[CCH_INSERTFILE];
WORD nFileOffset;
} ATTMANCUSTOM;
HRESULT CAttMan::HrInsertFile()
{
OPENFILENAMEW ofn;
HRESULT hr;
WCHAR rgch[MAX_PATH],
pszOpenFileName[CCH_INSERTFILE],
szDefaultDir[MAX_PATH];
ATTMANCUSTOM rCustom;
Assert(m_hwndList);
*pszOpenFileName = 0;
ZeroMemory(&ofn, sizeof(ofn));
AthLoadStringW(idsAllFilesFilter, rgch, MAX_PATH);
ReplaceCharsW(rgch, _T('|'), _T('\0'));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = m_hwndParent;
ofn.hInstance = g_hLocRes;
ofn.lpstrFilter = rgch;
ofn.nFilterIndex = 1;
ofn.lpstrFile = pszOpenFileName;
ofn.nMaxFile = CCH_INSERTFILE;
ofn.lpstrInitialDir = szDefaultDir; //current dir
ofn.Flags = OFN_HIDEREADONLY |
OFN_EXPLORER |
OFN_ALLOWMULTISELECT |
OFN_FILEMUSTEXIST |
OFN_NOCHANGEDIR |
OFN_ENABLEHOOK |
OFN_ENABLETEMPLATE |
OFN_NODEREFERENCELINKS;
ofn.lpTemplateName = MAKEINTRESOURCEW(iddInsertFile);
ofn.lpfnHook = (LPOFNHOOKPROC)InsertFileDlgHookProc;
ofn.lCustData = (LONG_PTR)&rCustom;
if (FAILED(GetLastAttachmentPath(szDefaultDir, ARRAYSIZE(szDefaultDir))) ||
!PathFileExistsW(szDefaultDir))
{
ofn.lpstrInitialDir = NULL;
}
rCustom.szFiles[0] = 0;
rCustom.fShortcut = FALSE;
rCustom.nFileOffset = 0;
// NB: OK button in dialog hook take's care of inserting the attachment.
hr = HrAthGetFileNameW(&ofn, TRUE);
if (SUCCEEDED(hr))
{
WCHAR sz[MAX_PATH];
LPWSTR pszT;
BOOL fShortCut = rCustom.fShortcut,
fUseCustom = (rCustom.szFiles[0]),
fSingleAttach;
// We only generate custom stuff if we have more than one file
fSingleAttach = fUseCustom ? FALSE : (ofn.nFileOffset < lstrlenW(pszOpenFileName));
if (fSingleAttach)
{
StrCpyNW(szDefaultDir, pszOpenFileName, ARRAYSIZE(szDefaultDir));
PathRemoveFileSpecW(szDefaultDir);
SetLastAttachmentPath(szDefaultDir);
// in single-file case, no null between path and filename
hr = HrAddAttachment(pszOpenFileName, NULL, fShortCut);
}
else
{
LPWSTR pszPath;
if (fUseCustom)
{
pszPath = rCustom.szFiles;
pszT = pszPath + rCustom.nFileOffset;
}
else
{
pszPath = pszOpenFileName;
pszT = pszPath + ofn.nFileOffset;
}
SetLastAttachmentPath(pszPath);
while (TRUE)
{
PathCombineW(sz, pszPath, pszT);
hr = HrAddAttachment(sz, NULL, fShortCut);
if (hr != S_OK)
break;
pszT = pszT + lstrlenW(pszT) + 1;
if (*pszT == 0)
break;
}
}
}
return(hr);
}
/*
* HrAddAttachment
*
* adds a file attachment to the list from a stream or filename
*
*
*/
HRESULT CAttMan::HrAddAttachment(LPWSTR lpszPathName, LPSTREAM pstm, BOOL fShortCut)
{
ULONG cbSize=0;
HRESULT hr = S_OK;
HBODY hAttach=0;
LPATTACHDATA pAttach;
WCHAR szLinkPath[MAX_PATH];
LPWSTR pszFileNameToUse;
*szLinkPath = 0;
if(fShortCut)
{
hr = CreateNewShortCut(lpszPathName, szLinkPath, ARRAYSIZE(szLinkPath));
if (FAILED(hr))
return hr;
}
pszFileNameToUse = *szLinkPath ? szLinkPath : lpszPathName;
hr=HrAddData(pszFileNameToUse, pstm, &pAttach);
if (FAILED(hr))
return hr;
hr=HrAddToList(pAttach, FALSE);
if (FAILED(hr))
goto error;
if (ListView_GetItemCount(m_hwndList) == 1)
{
// if we went from 0->1 then select the first item
ListView_SelectItem(m_hwndList, 0);
}
// Adding a new attachment makes us dirty
m_fDirty = TRUE;
HrResizeParent();
error:
return hr;
}
/*
*
* HRESULT CAttMan::HrExecFile
*
* handles one of these verbs against an attachment:
*
* ID_OPEN: - Launch use m_lpMsg
* ID_QUICK_VIEW: - NYI
* ID_PRINT: - NYI
* ID_SAVE_AS: - NYI
*
* returns 1 if handled.
*
*/
HRESULT CAttMan::HrExecFile(int iVerb)
{
LV_ITEMW lvi;
HRESULT hr=E_FAIL;
if (!ListView_GetSelectedCount(m_hwndList))
return NOERROR; // nothing to do...
lvi.mask = LVIF_PARAM;
lvi.iSubItem = 0;
lvi.iItem = -1;
// cycle through all the selected attachments
while ((lvi.iItem = ListView_GetNextItem(m_hwndList, lvi.iItem, LVNI_SELECTED | LVNI_ALL)) != -1)
{
SendMessage(m_hwndList, LVM_GETITEMW, 0, (LPARAM)(LV_ITEMW*)(&lvi));
switch(iVerb)
{
case ID_SAVE_ATTACH_AS:
case ID_OPEN:
case ID_PRINT:
case ID_QUICK_VIEW:
return HrDoVerb((LPATTACHDATA)lvi.lParam, iVerb);
default:
AssertSz(0, "Verb not supported");
}
}
return hr;
}
// ==============================================================================
//
// FUNCTION: CAttMan :: FDropFiles()
//
// Purpose: this method is called with a HDROP, the files
// have been droped. This method assumes
//
// ==============================================================================
HRESULT CAttMan::HrDropFiles(HDROP hDrop, BOOL fMakeLinks)
{
WCHAR wszFile[_MAX_PATH];
UINT cFiles;
UINT iFile;
HCURSOR hcursor;
BOOL fFirstDirectory = TRUE,
fLinkDirectories = FALSE;
HRESULT hr = S_OK;
hcursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
// Let's work through the files given to us
cFiles = DragQueryFileWrapW(hDrop, (UINT) -1, NULL, 0);
for (iFile = 0; iFile < cFiles; ++iFile)
{
DragQueryFileWrapW(hDrop, iFile, wszFile, _MAX_PATH);
if (!fMakeLinks && PathIsDirectoryW(wszFile))
{
// can link to a directory, but not drop one.
if (fFirstDirectory)
{
int id;
// Tell the user that he's been a bad user
id = AthMessageBoxW(m_hwndParent,
MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW(idsDropLinkDirs),
NULL,
MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_YESNOCANCEL);
if (id==IDCANCEL)
return E_FAIL;
if (id == IDYES)
fLinkDirectories = TRUE;
fFirstDirectory = FALSE;
}
if (fLinkDirectories)
hr = HrAddAttachment(wszFile, NULL, TRUE);
}
else
hr = HrAddAttachment(wszFile, NULL, fMakeLinks);
}
if (FAILED(hr))
{
AthMessageBoxW(m_hwndParent,
MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW(idsErrDDFileNotFound),
NULL, MB_ICONEXCLAMATION|MB_SETFOREGROUND|MB_OK);
}
SetCursor(hcursor);
return S_OK;
}
HRESULT CAttMan::HrDropFileDescriptor(LPDATAOBJECT pDataObj, BOOL fLink)
{
HCURSOR hcursor;
BOOL fFirstDirectory = TRUE,
fLinkDirectories = FALSE,
fUnicode = TRUE,
fIsDirectory;
SCODE sc = S_OK;
LPWSTR pwszFileName = NULL;
HRESULT hr = S_OK;
STGMEDIUM stgmedDesc;
FILEGROUPDESCRIPTORA *pfgdA = NULL;
FILEDESCRIPTORA *pfdA = NULL;
FILEGROUPDESCRIPTORW *pfgdW = NULL;
FILEDESCRIPTORW *pfdW = NULL;
UINT uiNumFiles,
uiCurrFile;
FORMATETC fetcFileDescA =
{(CLIPFORMAT)(CF_FILEDESCRIPTORA), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
FORMATETC fetcFileDescW =
{(CLIPFORMAT)(CF_FILEDESCRIPTORW), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
FORMATETC fetcFileContents =
{(CLIPFORMAT)(CF_FILECONTENTS), NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM|
TYMED_HGLOBAL};
hcursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
ZeroMemory(&stgmedDesc, sizeof(STGMEDIUM));
hr = pDataObj->GetData(&fetcFileDescW, &stgmedDesc);
if (SUCCEEDED(hr))
{
pfgdW = (LPFILEGROUPDESCRIPTORW)GlobalLock(stgmedDesc.hGlobal);
uiNumFiles = pfgdW->cItems;
pfdW = &pfgdW->fgd[0];
}
else
{
IF_FAILEXIT(hr = pDataObj->GetData(&fetcFileDescA, &stgmedDesc));
fUnicode = FALSE;
pfgdA = (LPFILEGROUPDESCRIPTORA)GlobalLock(stgmedDesc.hGlobal);
uiNumFiles = pfgdA->cItems;
pfdA = &pfgdA->fgd[0];
}
// Loop through the contents
for (uiCurrFile = 0; uiCurrFile < uiNumFiles; ++uiCurrFile)
{
if (fUnicode)
{
fIsDirectory = (pfdW->dwFlags & FD_ATTRIBUTES) &&
(pfdW->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
IF_NULLEXIT(pwszFileName = PszDupW(pfdW->cFileName));
++pfdW;
}
else
{
fIsDirectory = (pfdA->dwFlags & FD_ATTRIBUTES) &&
(pfdA->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
IF_NULLEXIT(pwszFileName = PszToUnicode(CP_ACP, pfdA->cFileName));
++pfdA;
}
// if we have a directory, there's no contents for it, just filename, so let's
// see if the user wants us to make a link...
if (!fLink && fIsDirectory)
{
if(fFirstDirectory)
{
int id;
// Tell the user that he's been a bad user
id=AthMessageBoxW(m_hwndParent,
MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW(idsDropLinkDirs),
NULL,
MB_ICONEXCLAMATION|MB_SETFOREGROUND|MB_YESNOCANCEL);
if(id==IDCANCEL)
{
hr=NOERROR;
goto exit;
}
fLinkDirectories = (id == IDYES);
fFirstDirectory = FALSE;
}
if(fLinkDirectories)
hr=HrInsertFileFromStgMed(pwszFileName, NULL, TRUE);
}
else
{
// Since we have the UNICODE filename with pwszFileName, we don't
// need to worry about making sure stgmedContents is UNICODE.
STGMEDIUM stgmedContents;
ZeroMemory(&stgmedContents, sizeof(STGMEDIUM));
fetcFileContents.lindex = uiCurrFile;
IF_FAILEXIT(hr = pDataObj->GetData(&fetcFileContents, &stgmedContents));
switch (stgmedContents.tymed)
{
case TYMED_HGLOBAL:
case TYMED_ISTREAM:
hr=HrInsertFileFromStgMed(pwszFileName, &stgmedContents, fLink);
break;
default:
AssertSz(FALSE, "Unexpected TYMED");
break;
}
ReleaseStgMedium(&stgmedContents);
}
SafeMemFree(pwszFileName);
}
exit:
SetCursor(hcursor);
if (pfgdA || pfgdW)
GlobalUnlock(stgmedDesc.hGlobal);
MemFree(pwszFileName);
ReleaseStgMedium(&stgmedDesc);
return hr;
}
static const HELPMAP g_rgCtxMapMailGeneral[] = {
{chx2, IDH_INSERT_ATTACHMENT_MAKE_SHORTCUT},
{0,0}};
BOOL CALLBACK CAttMan::InsertFileDlgHookProc(HWND hwnd, UINT msg, WPARAM wParam,LPARAM lParam)
{
char szTemp[MAX_PATH];
HRESULT hr;
switch (msg)
{
case WM_INITDIALOG:
{
HWND hwndParent = GetParent(hwnd);
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)(((LPOPENFILENAME)lParam)->lCustData));
// Bug 1073: Replace the "Open" button with "Attach"
if (AthLoadString(idsAttach, szTemp, ARRAYSIZE(szTemp)))
SetDlgItemText(hwndParent, IDOK, szTemp);
if (AthLoadString(idsInsertAttachment, szTemp, ARRAYSIZE(szTemp)))
SetWindowText(hwndParent, szTemp);
CenterDialog( hwnd );
return TRUE;
}
case WM_HELP:
case WM_CONTEXTMENU:
return OnContextHelp(hwnd, msg, wParam, lParam, g_rgCtxMapMailGeneral);
case WM_NOTIFY:
{
if (CDN_FILEOK == ((LPNMHDR)lParam)->code)
{
AssertSz(sizeof(OPENFILENAMEW) == sizeof(OPENFILENAMEA), "Win9x will give us OPENFILENAMEA");
OPENFILENAMEW *pofn = ((OFNOTIFYW*)lParam)->lpOFN;
AssertSz(pofn, "Why didn't we get a OPENFILENAMEA struct???");
ATTMANCUSTOM *pCustom = (ATTMANCUSTOM*)(pofn->lCustData);
pCustom->fShortcut = IsDlgButtonChecked(hwnd, chx2);
// If we are ANSI and we have mutiple files, then we need to
// convert the entire filepath and pass it back up to our
// caller since shlwapi doesn't handle multiple files during conversion
if (!IsWindowUnicode(hwnd))
{
LPSTR pszSrc = (LPSTR)pofn->lpstrFile;
LPWSTR pszDest = pCustom->szFiles;
WORD nFilePathLen = (WORD) lstrlen(pszSrc);
if (pofn->nFileOffset > nFilePathLen)
{
pCustom->nFileOffset = nFilePathLen + 1;
int nChars = ARRAYSIZE(pCustom->szFiles);
while (*pszSrc && (nChars>0))
{
DWORD cLenAndNull = lstrlen(pszSrc) + 1;
DWORD cchWideAndNull = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
pszSrc, cLenAndNull,
pszDest, nChars);
// Since the original buffers (custom and lpstrFile) were both static
// sized arrays of the same length, we know that pszDest will never be
// accessed beyond its end.
pszSrc += cLenAndNull;
pszDest += cchWideAndNull;
nChars -= cchWideAndNull;
}
//bobn: 75453 not copying second null
*pszDest=0;
}
}
return TRUE;
}
}
}
return FALSE;
}
HRESULT CAttMan::HrUpdateToolbar(HWND hwndToolbar)
{
if (GetFocus() == m_hwndList)
{
// if we have focus, kill the edit cut|copy paste btns
EnableDisableEditToolbar(hwndToolbar, 0);
}
return S_OK;
}
HRESULT CAttMan::HrInsertFileFromStgMed(LPWSTR pwszFileName, LPSTGMEDIUM pstgmed, BOOL fMakeLinks)
{
HRESULT hr=NOERROR;
LPSTREAM pStmToFree = NULL,
pAttachStm = NULL;
if(!pstgmed)
{
AssertSz(fMakeLinks, "this should always be true if there is no stgmedium!");
fMakeLinks = TRUE;
}
else
switch (pstgmed->tymed)
{
case TYMED_HGLOBAL:
hr=CreateStreamOnHGlobal(pstgmed->hGlobal, TRUE, &pStmToFree);
if(SUCCEEDED(hr))
{
// NULL out the hglobal do it doesn't get free'd
pstgmed->hGlobal=NULL;
pAttachStm = pStmToFree;
}
break;
case TYMED_ISTREAM:
pAttachStm = pstgmed->pstm;
break;
default:
AssertSz(FALSE, "unexpected tymed");
hr = E_UNEXPECTED;
break;
}
if (SUCCEEDED(hr))
hr = HrAddAttachment(pwszFileName, pAttachStm, fMakeLinks);
ReleaseObj(pStmToFree);
return hr;
}
HRESULT CAttMan::HrBuildHDrop(PDATAOBJINFO *ppdoi)
{
LPDROPFILES lpDrop=0;
LPWSTR *rgpwszTemp=NULL,
pwszPath;
LPSTR *rgpszTemp=NULL,
pszPath;
int cFiles,
i;
LV_ITEMW lvi;
ULONG cb;
HRESULT hr = S_OK;
LPATTACHDATA lpAttach;
// Since win9x can't handle unicode names, the fWide parameter in the
// DROPFILES struct is ignored. So in the win9x case, we need to do
// special conversions here when building the HDROP. One thing to note,
// the temp files that are generated on win9x will already be safe for
// the system code page. The temp file names might differ from the actual
// file name, but the temp file name will be ok.
BOOL fWinNT = (VER_PLATFORM_WIN32_NT == g_OSInfo.dwPlatformId);
if(!ppdoi)
return TraceResult(E_INVALIDARG);
*ppdoi=NULL;
cFiles=ListView_GetSelectedCount(m_hwndList);
if(!cFiles)
return TraceResult(E_FAIL); // nothing to build
lvi.mask = LVIF_PARAM;
lvi.iSubItem = 0;
lvi.iItem=-1;
// Walk the list and find out how much space we need.
if (fWinNT)
{
IF_NULLEXIT(MemAlloc((LPVOID *)&rgpwszTemp, sizeof(LPWSTR)*cFiles));
ZeroMemory(rgpwszTemp, sizeof(LPWSTR)*cFiles);
}
else
{
IF_NULLEXIT(MemAlloc((LPVOID *)&rgpszTemp, sizeof(LPSTR)*cFiles));
ZeroMemory(rgpszTemp, sizeof(LPSTR)*cFiles);
}
cFiles = 0;
cb = sizeof(DROPFILES);
while(((lvi.iItem=ListView_GetNextItem(m_hwndList, lvi.iItem,
LVNI_SELECTED|LVNI_ALL))!=-1))
{
if (!SendMessage(m_hwndList, LVM_GETITEMW, 0, (LPARAM)(LV_ITEMW*)(&lvi)))
{
hr=E_FAIL;
goto exit;
}
if (!(lpAttach=(LPATTACHDATA)lvi.lParam))
{
hr=E_FAIL;
goto exit;
}
IF_FAILEXIT(hr = HrGetTempFile(lpAttach));
if (fWinNT)
{
rgpwszTemp[cFiles] = lpAttach->szTempFile;
cb+=(lstrlenW(rgpwszTemp[cFiles++]) + 1)*sizeof(WCHAR);
}
else
{
rgpszTemp[cFiles] = PszToANSI(CP_ACP, lpAttach->szTempFile);
cb+=(lstrlen(rgpszTemp[cFiles++]) + 1)*sizeof(CHAR);
}
}
//double-null term at end.
if (fWinNT)
cb+=sizeof(WCHAR);
else
cb+=sizeof(CHAR);
// Allocate the buffer and fill it in.
IF_NULLEXIT(MemAlloc((LPVOID*) &lpDrop, cb));
ZeroMemory(lpDrop, cb);
lpDrop->pFiles = sizeof(DROPFILES);
lpDrop->fWide = fWinNT;
// Fill in the path names.
if (fWinNT)
{
pwszPath = (LPWSTR)((BYTE *)lpDrop + sizeof(DROPFILES));
PWSTR pwszEnd = (LPWSTR)((BYTE *)lpDrop + cb);
for(i=0; i<cFiles; i++)
{
StrCpyNW(pwszPath, rgpwszTemp[i], (DWORD)(pwszEnd-pwszPath));
pwszPath += lstrlenW(rgpwszTemp[i])+1;
}
}
else
{
pszPath = (LPSTR)((BYTE *)lpDrop + sizeof(DROPFILES));
PSTR pszEnd = (LPSTR)((BYTE *)lpDrop + cb);
for(i=0; i<cFiles; i++)
{
StrCpyN(pszPath, rgpszTemp[i], (DWORD)(pszEnd-pszPath));
pszPath += lstrlen(rgpszTemp[i])+1;
}
}
// Now allocate the DATAOBJECTINFO struct
IF_NULLEXIT(MemAlloc((LPVOID*) ppdoi, sizeof(DATAOBJINFO)));
SETDefFormatEtc((*ppdoi)->fe, CF_HDROP, TYMED_HGLOBAL);
(*ppdoi)->pData = (LPVOID) lpDrop;
(*ppdoi)->cbData = cb;
// Don't free the dropfiles struct
lpDrop = NULL;
exit:
MemFree(lpDrop);
MemFree(rgpwszTemp);
if (rgpszTemp)
{
for(i=0; i<cFiles; i++)
MemFree(rgpszTemp[i]);
MemFree(rgpszTemp);
}
return TraceResult(hr);
}
/*
* IDropSource::
*/
HRESULT CAttMan::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
DOUTL(8, "IDS::QueryContDrag()");
if(fEscapePressed)
return ResultFromScode(DRAGDROP_S_CANCEL);
if(!(grfKeyState & m_dwDragType))
return ResultFromScode(DRAGDROP_S_DROP);
return NOERROR;
}
HRESULT CAttMan::GiveFeedback(DWORD dwEffect)
{
DOUTL(8, "IDS::GiveFeedback()");
return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS);
}
/*
* HrGetRequiredAction()
*
* Purpose: this method is called in response to a
* drag with the right mouse clicked rather
* than the left. A context menu is displayed
*/
HRESULT CAttMan::HrGetRequiredAction(DWORD *pdwEffect, POINTL pt)
{
// Pop up the context menu.
//
HMENU hMenu;
UINT idCmd;
HRESULT hr = E_FAIL;
*pdwEffect = DROPEFFECT_NONE;
Assert(m_hwndList);
hMenu = LoadPopupMenu(IDR_ATTACHMENT_DRAGDROP_POPUP);
if (!hMenu)
goto cleanup;
MenuUtil_SetPopupDefault(hMenu, ID_MOVE);
idCmd = TrackPopupMenuEx(hMenu, TPM_RETURNCMD|TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON,
pt.x, pt.y, m_hwndList, NULL);
switch(idCmd)
{
case ID_MOVE:
*pdwEffect = DROPEFFECT_MOVE;
break;
case ID_COPY:
*pdwEffect = DROPEFFECT_COPY;
break;
case ID_CREATE_SHORTCUT:
*pdwEffect = DROPEFFECT_LINK;
break;
default:
// cancelled
goto cleanup;
}
hr = S_OK;
cleanup:
if(hMenu)
DestroyMenu(hMenu);
return hr;
}
/*
* It is critical that any client of the Attman calls HrClose to drop it's refcounts.
*
*/
HRESULT CAttMan::HrClose()
{
HrUnload();
#if 0
if(m_fDropTargetRegister)
{
Assert(m_hwndList && IsWindow(m_hwndList));
RevokeDragDrop(m_hwndList);
CoLockObjectExternal((LPUNKNOWN)(LPDROPTARGET)this, FALSE, TRUE);
}
#endif
return S_OK;
}
//Adds a new attach to m_rgpAttach and places the new attach in a proper hole
HRESULT CAttMan::HrAllocNewEntry(LPATTACHDATA pAttach)
{
ULONG uAttach;
if (m_cAlloc==m_cAttach)
{
DOUTL(4, "HrGrowAttachStruct:: Growing Table");
// grow time!!
m_cAlloc+=CACHE_GROW_SIZE;
if (!MemRealloc((LPVOID *)&m_rgpAttach, sizeof(LPATTACHDATA)*m_cAlloc))
return E_OUTOFMEMORY;
// zeroinit new memory
ZeroMemory(&m_rgpAttach[m_cAttach], sizeof(LPATTACHDATA)*CACHE_GROW_SIZE);
}
// find a hole to put the new data into
for (uAttach=0; uAttach<m_cAlloc; uAttach++)
if (m_rgpAttach[uAttach]==NULL)
{
m_rgpAttach[uAttach]=pAttach;
break;
}
AssertSz(uAttach!=m_cAlloc, "Woah! we went off the end!");
m_cAttach++;
return S_OK;
}
// Only used when the function is adding attachs from a IMimeMessage
HRESULT CAttMan::HrAddData(HBODY hAttach)
{
LPATTACHDATA pAttach=0;
LPMIMEBODY pBody=0;
HRESULT hr;
Assert(hAttach);
Assert(m_pMsg);
hr = HrAttachDataFromBodyPart(m_pMsg, hAttach, &pAttach);
if (!FAILED(hr))
{
if (m_fDeleteVCards && StrStrIW(PathFindExtensionW(pAttach->szFileName), L".vcf"))
return S_OK;
hr = HrAllocNewEntry(pAttach);
if (!FAILED(hr))
return S_OK; // don't free pAttach as it's owned by the table now
MemFree(pAttach);
}
return S_OK;
}
// Only used when the function is adding attachs from outside of an IMimeMessage
HRESULT CAttMan::HrAddData(LPWSTR lpszPathName, LPSTREAM pstm, LPATTACHDATA *ppAttach)
{
LPATTACHDATA pAttach;
HRESULT hr;
hr = HrAttachDataFromFile(pstm, lpszPathName, &pAttach);
if (!FAILED(hr))
{
hr = HrAllocNewEntry(pAttach);
if (!FAILED(hr))
{
if (ppAttach)
*ppAttach=pAttach;
return S_OK; // don't free pAttach as it's owned by the table now
}
MemFree(pAttach);
}
return hr;
}
HRESULT CAttMan::HrFreeAllData()
{
ULONG uAttach;
for (uAttach=0; uAttach<m_cAlloc; uAttach++)
if (m_rgpAttach[uAttach])
{
HrFreeAttachData(m_rgpAttach[uAttach]);
m_rgpAttach[uAttach] = NULL;
}
SafeMemFree(m_rgpAttach);
m_cAlloc=0;
m_cAttach=0;
m_iVCard = -1;
if (m_szUnsafeAttachList != NULL)
SafeMemFree(m_szUnsafeAttachList);
m_cUnsafeAttach = 0;
return NOERROR;
}
HRESULT CAttMan::HrDoVerb(LPATTACHDATA lpAttach, INT nVerb)
{
HRESULT hr;
ULONG uVerb = AV_MAX;
if (!lpAttach)
return E_INVALIDARG;
switch (nVerb)
{
case ID_SAVE_ATTACH_AS:
uVerb = AV_SAVEAS;
break;
case ID_OPEN:
uVerb = AV_OPEN;
break;
case ID_PRINT:
uVerb = AV_PRINT;
break;
case ID_QUICK_VIEW:
uVerb = AV_QUICKVIEW;
break;
default:
AssertSz(0, "BAD ARGUMENT");
return E_INVALIDARG;
}
hr = HrDoAttachmentVerb(m_hwndParent, uVerb, m_pMsg, lpAttach);
if (FAILED(hr) && hr!=hrUserCancel)
AthMessageBoxW(m_hwndParent,
MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW(idsErrCmdFailed),
NULL, MB_OK|MB_ICONEXCLAMATION);
return hr;
}
//////////////////////////////////////////////////////////////////////////////
// IPersistMime::Load
HRESULT CAttMan::Load(LPMIMEMESSAGE pMsg)
{
HRESULT hr;
if(!pMsg)
return E_INVALIDARG;
HrUnload();
ReplaceInterface(m_pMsg, pMsg);
hr=HrBuildAttachList();
if (FAILED(hr))
goto error;
hr=HrFillListView();
if (ListView_GetItemCount(m_hwndList) > 0)
{
// if we went from 0->1 then select the first item
ListView_SelectItem(m_hwndList, 0);
}
HrResizeParent();
m_fDirty = FALSE;
error:
return hr;
}
HRESULT CAttMan::CheckAttachNameSafeWithCP(CODEPAGEID cpID)
{
LPATTACHDATA *currAttach = m_rgpAttach;
HRESULT hr = S_OK;
for (ULONG uAttach = 0; uAttach<m_cAlloc; uAttach++, currAttach++)
{
if (*currAttach)
{
IF_FAILEXIT(hr = HrSafeToEncodeToCP((*currAttach)->szFileName, cpID));
if (MIME_S_CHARSET_CONFLICT == hr)
goto exit;
}
}
exit:
return hr;
}
// IPersistMime::Save
HRESULT CAttMan::Save(LPMIMEMESSAGE pMsg, DWORD dwFlags)
{
ULONG uAttach;
LPATTACHDATA *currAttach = m_rgpAttach;
HRESULT hr = S_OK;
for (uAttach=0; uAttach<m_cAlloc; uAttach++)
{
if (*currAttach)
{
HBODY currHAttach = (*currAttach)->hAttach;
LPMIMEMESSAGEW pMsgW = NULL;
LPWSTR pszFileName = (*currAttach)->szFileName;
LPSTREAM lpStrmPlaceHolder = (*currAttach)->pstm,
lpstrm = NULL;
BOOL fAttachFile = TRUE;
if (SUCCEEDED(pMsg->QueryInterface(IID_IMimeMessageW, (LPVOID*)&pMsgW)))
{
//If attachment at load time (i.e. from m_pMsg)
if (currHAttach)
{
LPMIMEBODY pBody = NULL;
if (S_OK == m_pMsg->BindToObject(currHAttach, IID_IMimeBody, (LPVOID *)&pBody))
{
if (pBody->GetData(IET_INETCSET, &lpstrm)==S_OK)
lpStrmPlaceHolder = lpstrm;
else
fAttachFile = FALSE;
ReleaseObj(pBody);
}
}
//If attachment was added after load time
if (!fAttachFile || FAILED(pMsgW->AttachFileW(pszFileName, lpStrmPlaceHolder, NULL)))
hr = E_FAIL;
ReleaseObj(lpstrm);
ReleaseObj(pMsgW);
}
else
hr = E_FAIL;
}
currAttach++;
}
if (FAILED(hr))
{
if (AthMessageBoxW( m_hwndParent,
MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW(idsSendWithoutAttach),
NULL, MB_YESNO|MB_ICONEXCLAMATION )==IDYES)
hr = S_OK;
else
hr = MAPI_E_USER_CANCEL;
}
return hr;
}
// IPersist::GetClassID
HRESULT CAttMan::GetClassID(CLSID *pClsID)
{
//TODO: If ever expose, should return a valid ID
return E_NOTIMPL;
}
HRESULT CAttMan::HrSaveAs(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);
StrCpyNW(szFile, lpAttach->szFileName, MAX_PATH);
ZeroMemory (&ofn, sizeof (ofn));
ofn.lStructSize = sizeof (ofn);
ofn.hwndOwner = m_hwndParent;
AthLoadStringW(idsFilterAttSave, szFilter, ARRAYSIZE(szFilter));
ReplaceCharsW(szFilter, _T('|'), _T('\0'));
ofn.lpstrFilter = szFilter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFile;
ofn.nMaxFile = ARRAYSIZE(szFile);
AthLoadStringW(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;
}
// Verify the Attachment's Stream
hr=HrSave(lpAttach->hAttach, szFile);
if (FAILED(hr))
goto error;
error:
return hr;
}
HRESULT CAttMan::HrGetTempFile(LPATTACHDATA lpAttach)
{
HRESULT hr;
if (*lpAttach->szTempFile)
return S_OK;
// Since win9x can't handle filenames very well, let's try to handle this
// by converting the temp names to something workable in win9x.
if (VER_PLATFORM_WIN32_NT == g_OSInfo.dwPlatformId)
{
if (!FBuildTempPathW(lpAttach->szFileName, lpAttach->szTempFile, ARRAYSIZE(lpAttach->szTempFile), FALSE))
{
hr = E_FAIL;
goto error;
}
}
else
{
// Since we are on win95, the temp path will never be bad ANSI. Don't need to bother
// converting to ANSI and back to UNICODE
BOOL fSucceeded = FBuildTempPathW(lpAttach->szFileName, lpAttach->szTempFile, ARRAYSIZE(lpAttach->szTempFile), FALSE);
if (!fSucceeded)
{
hr = E_FAIL;
goto error;
}
}
if (lpAttach->hAttach == NULL && lpAttach->pstm)
{
// if no attachment, but just stream data
hr = WriteStreamToFileW(lpAttach->pstm, lpAttach->szTempFile, CREATE_NEW, GENERIC_WRITE);
}
else
{
hr=HrSave(lpAttach->hAttach, lpAttach->szTempFile);
}
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;
}
HRESULT CAttMan::HrCleanTempFile(LPATTACHDATA lpAttach)
{
if ((lpAttach->szTempFile) && ('\0' != lpAttach->szTempFile[0]))
{
// If the file was launched, don't delete the temp file if the process still has it open
if (lpAttach->hProcess)
{
DWORD dwState = WaitForSingleObject (lpAttach->hProcess, 0);
if (dwState == WAIT_OBJECT_0)
DeleteFileWrapW(lpAttach->szTempFile);
}
else
DeleteFileWrapW(lpAttach->szTempFile);
}
*lpAttach->szTempFile = NULL;
lpAttach->hProcess=NULL;
return NOERROR;
}
HRESULT CAttMan::HrSave(HBODY hAttach, LPWSTR lpszFileName)
{
IMimeBodyW *pBody = NULL;
HRESULT hr;
hr = m_pMsg->BindToObject(hAttach, IID_IMimeBodyW, (LPVOID *)&pBody);
if (SUCCEEDED(hr))
hr = pBody->SaveToFileW(IET_INETCSET, lpszFileName);
ReleaseObj(pBody);
return hr;
}
HRESULT CAttMan::HrCmdEnabled(UINT idm, LPBOOL pbEnable)
{
Assert (pbEnable);
return S_FALSE;
}
HRESULT CAttMan::HrIsDragSource()
{
return (m_fDragSource ? S_OK : S_FALSE);
}