// ============================================================================== // 3/2/96 - Attachment Manager Class Implementation (sbailey & brents) // ============================================================================== #include "pch.hxx" #include "strconst.h" #include #include "mimeutil.h" #include "mimeolep.h" #include "attman.h" #include #include #include "header.h" #include "note.h" #include #include #include #include "fonts.h" #include "secutil.h" #include #include #include #include #include // 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= 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 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; uAttachszFileName), 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; uAttachszFileName, 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 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 (iNext1 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; ife, 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; iszFileName), 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 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; uAttachszFileName, 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; uAttachhAttach; 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); }