//////////////////////////////////////////////////////////////////////////////////////////////////////////// // // File: MAPIMessage.cpp // Description: MAPI Message class wrapper // // Copyright (C) 2005-2011, Noel Dillabough // // This source code is free to use and modify provided this notice remains intact and that any enhancements // or bug fixes are posted to the CodeProject page hosting this class for the community to benefit. // // Usage: see the CodeProject article at http://www.codeproject.com/internet/CMapiEx.asp // //////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "MAPIExPCH.h" #include "MAPIEx.h" #include const GUID CLSID_MailMessage={ 0x00020D0B, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; ///////////////////////////////////////////////////////////// // CMAPIMessage CMAPIMessage::CMAPIMessage() { m_pRecipients=NULL; } CMAPIMessage::~CMAPIMessage() { Close(); } BOOL CMAPIMessage::Open(CMAPIEx* pMAPI, SBinary entryID) { if(!CMAPIObject::Open(pMAPI,entryID)) return FALSE; GetPropertyString(PR_SENDER_NAME, m_strSenderName); FillSenderEmail(); GetPropertyString(PR_SUBJECT, m_strSubject); return TRUE; } void CMAPIMessage::Close() { RELEASE(m_pRecipients); CMAPIObject::Close(); } BOOL CMAPIMessage::GetHeader(CString& strHeader) { return GetPropertyString(PR_TRANSPORT_MESSAGE_HEADERS, strHeader); } void CMAPIMessage::FillSenderEmail() { CString strAddrType; GetPropertyString(PR_SENDER_ADDRTYPE, strAddrType); GetPropertyString(PR_SENDER_EMAIL_ADDRESS, m_strSenderEmail); // for Microsoft Exchange server internal mails we want to try to resolve the SMTP email address if(strAddrType==_T("EX")) { LPSPropValue pProp; if(GetProperty(PR_SENDER_ENTRYID, pProp)==S_OK) { if(m_pMAPI) m_pMAPI->GetExEmail(pProp->Value.bin, m_strSenderEmail); MAPIFreeBuffer(pProp); } } } BOOL CMAPIMessage::GetReceivedTime(SYSTEMTIME& tmReceived) { LPSPropValue pProp; if(GetProperty(PR_MESSAGE_DELIVERY_TIME, pProp)==S_OK) { FILETIME tmLocal; FileTimeToLocalFileTime(&pProp->Value.ft, &tmLocal); FileTimeToSystemTime(&tmLocal, &tmReceived); MAPIFreeBuffer(pProp); return TRUE; } return FALSE; } BOOL CMAPIMessage::GetReceivedTime(CString& strReceivedTime, LPCTSTR szFormat) { SYSTEMTIME tm; if(GetReceivedTime(tm)) { TCHAR szTime[256]; if(!szFormat) szFormat=_T("MM/dd/yyyy hh:mm:ss tt"); GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &tm, szFormat, szTime, 256); GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &tm, szTime, szTime, 256); strReceivedTime=szTime; return TRUE; } return FALSE; } BOOL CMAPIMessage::GetSubmitTime(SYSTEMTIME& tmSubmit) { LPSPropValue pProp; if(GetProperty(PR_CLIENT_SUBMIT_TIME, pProp)==S_OK) { FILETIME tmLocal; FileTimeToLocalFileTime(&pProp->Value.ft, &tmLocal); FileTimeToSystemTime(&tmLocal, &tmSubmit); MAPIFreeBuffer(pProp); return TRUE; } return FALSE; } BOOL CMAPIMessage::GetSubmitTime(CString& strSubmitTime, LPCTSTR szFormat) { SYSTEMTIME tm; if(GetSubmitTime(tm)) { TCHAR szTime[256]; if(!szFormat) szFormat=_T("MM/dd/yyyy hh:mm:ss tt"); GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &tm, szFormat, szTime,256); GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &tm, szTime, szTime,256); strSubmitTime=szTime; return TRUE; } return FALSE; } BOOL CMAPIMessage::GetTo(CString& strTo) { return GetPropertyString(PR_DISPLAY_TO, strTo); } BOOL CMAPIMessage::GetCC(CString& strCC) { return GetPropertyString(PR_DISPLAY_CC, strCC); } BOOL CMAPIMessage::GetBCC(CString& strBCC) { return GetPropertyString(PR_DISPLAY_BCC, strBCC); } int CMAPIMessage::GetMessageStatus() { return GetPropertyValue(PR_MSG_STATUS, 0); } int CMAPIMessage::GetPriority() { return GetPropertyValue(PR_PRIORITY, PRIO_NONURGENT); } DWORD CMAPIMessage::GetSize() { return GetPropertyValue(PR_MESSAGE_SIZE, 0); } BOOL CMAPIMessage::GetRecipients() { if(!Message()) return FALSE; RELEASE(m_pRecipients); if(Message()->GetRecipientTable(CMAPIEx::cm_nMAPICode, &m_pRecipients)!=S_OK) return FALSE; const int nProperties=RECIPIENT_COLS; SizedSPropTagArray(nProperties, Columns)={nProperties,{PR_RECIPIENT_TYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_ENTRYID }}; return (m_pRecipients->SetColumns((LPSPropTagArray)&Columns, 0)==S_OK); } BOOL CMAPIMessage::GetNextRecipient(CString& strName, CString& strEmail, int& nType) { if(!m_pRecipients) return FALSE; LPSRowSet pRows=NULL; BOOL bResult=FALSE; if(m_pRecipients->QueryRows(1, 0, &pRows)==S_OK) { if(pRows->cRows) { nType=pRows->aRow[0].lpProps[PROP_RECIPIENT_TYPE].Value.ul; strName=CMAPIEx::GetValidString(pRows->aRow[0].lpProps[PROP_RECIPIENT_NAME]); // for Microsoft Exchange server internal mails we want to try to resolve the SMTP email address CString strAddrType=CMAPIEx::GetValidString(pRows->aRow[0].lpProps[PROP_ADDR_TYPE]); if(strAddrType==_T("EX")) { if(m_pMAPI) m_pMAPI->GetExEmail(pRows->aRow[0].lpProps[PROP_ENTRYID].Value.bin, strEmail); } else { strEmail=CMAPIEx::GetValidString(pRows->aRow[0].lpProps[PROP_RECIPIENT_EMAIL]); } bResult=TRUE; } FreeProws(pRows); } return bResult; } BOOL CMAPIMessage::GetReplyTo(CString& strEmail) { BOOL bResult=FALSE; LPSPropValue prop; if(GetProperty(PR_REPLY_RECIPIENT_ENTRIES, prop) == S_OK) { LPFLATENTRYLIST pReplyEntryList=(LPFLATENTRYLIST)prop->Value.bin.lpb; if(pReplyEntryList->cEntries>0) { LPFLATENTRY pReplyEntry=(LPFLATENTRY)pReplyEntryList->abEntries; SBinary entryID; entryID.cb=pReplyEntry->cb; entryID.lpb=pReplyEntry->abEntry; bResult=m_pMAPI->GetExEmail(entryID, strEmail); } MAPIFreeBuffer(prop); } return bResult; } // nPriority defaults to IMPORTANCE_NORMAL, IMPORTANCE_HIGH or IMPORTANCE_LOW also valid BOOL CMAPIMessage::Create(CMAPIEx* pMAPI, int nPriority, BOOL bSaveToSentFolder, CMAPIFolder* pFolder) { if(!pMAPI) return FALSE; if(!pFolder) pFolder=pMAPI->GetFolder(); if(!pFolder) pFolder=pMAPI->OpenOutbox(); if(!CMAPIObject::Create(pMAPI, pFolder)) return FALSE; SPropValue prop; SetMessageFlags(MSGFLAG_UNSENT | MSGFLAG_FROMME); LPSPropValue pProp=NULL; ULONG cValues=0; ULONG rgTags[]={ 1, PR_IPM_SENTMAIL_ENTRYID }; if(m_pMAPI->GetMessageStore()->GetProps((LPSPropTagArray) rgTags, CMAPIEx::cm_nMAPICode, &cValues, &pProp)==S_OK) { if(bSaveToSentFolder) { prop.ulPropTag=PR_SENTMAIL_ENTRYID; prop.Value.bin=pProp[0].Value.bin; } else { prop.ulPropTag=PR_DELETE_AFTER_SUBMIT; prop.Value.b=TRUE; } Message()->SetProps(1, &prop, NULL); SetEntryID(&(pProp[0].Value.bin)); MAPIFreeBuffer(pProp); } if(nPriority!=IMPORTANCE_NORMAL) { prop.ulPropTag=PR_IMPORTANCE; prop.Value.l=nPriority; Message()->SetProps(1, &prop, NULL); } SetMessageClass(_T("IPM.Note")); #ifdef _WIN32_WCE prop.ulPropTag=PR_MSG_STATUS; prop.Value.ul=MSGSTATUS_RECTYPE_SMTP; Message()->SetProps(1, &prop, NULL); #endif return TRUE; } BOOL CMAPIMessage::IsUnread() { return (!(GetMessageFlags()&MSGFLAG_READ)); } BOOL CMAPIMessage::MarkAsRead(BOOL bRead) { #ifdef _WIN32_WCE int ulMessageFlags=GetMessageFlags(); if(bRead) ulMessageFlags|=MSGFLAG_READ; else ulMessageFlags&=~MSGFLAG_READ; return SetMessageFlags(ulMessageFlags); #else return (Message()->SetReadFlag(bRead ? MSGFLAG_READ : CLEAR_READ_FLAG)==S_OK); #endif } BOOL CMAPIMessage::AddRecipients(LPADRLIST pAddressList) { HRESULT hr=E_INVALIDARG; #ifdef _WIN32_WCE hr=Message()->ModifyRecipients(MODRECIP_ADD, pAddressList); #else LPADRBOOK pAddressBook; if(m_pMAPI->GetSession()->OpenAddressBook(0, NULL, AB_NO_DIALOG, &pAddressBook)!=S_OK) return FALSE; if(pAddressBook->ResolveName(0, CMAPIEx::cm_nMAPICode, NULL, pAddressList)==S_OK) hr=Message()->ModifyRecipients(MODRECIP_ADD, pAddressList); RELEASE(pAddressBook); #endif return (hr==S_OK); } // AddrType only needed by Windows CE, use SMTP, or SMS etc, default NULL will not set PR_ADDRTYPE BOOL CMAPIMessage::AddRecipient(LPCTSTR szEmail, int nType, LPCTSTR szAddrType) { if(!Message() || !m_pMAPI) return FALSE; int nBufSize=CbNewADRLIST(1); LPADRLIST pAddressList=NULL; MAPIAllocateBuffer(nBufSize, (LPVOID FAR*)&pAddressList); memset(pAddressList, 0, nBufSize); int nProperties=3; if(szAddrType==NULL) nProperties--; pAddressList->cEntries=1; pAddressList->aEntries[0].ulReserved1=0; pAddressList->aEntries[0].cValues=nProperties; MAPIAllocateBuffer(sizeof(SPropValue)*nProperties, (LPVOID FAR*)&pAddressList->aEntries[0].rgPropVals); memset(pAddressList->aEntries[0].rgPropVals, 0, sizeof(SPropValue)*nProperties); pAddressList->aEntries[0].rgPropVals[0].ulPropTag=PR_RECIPIENT_TYPE; pAddressList->aEntries[0].rgPropVals[0].Value.ul=nType; #ifdef _WIN32_WCE pAddressList->aEntries[0].rgPropVals[1].ulPropTag=PR_EMAIL_ADDRESS; pAddressList->aEntries[0].rgPropVals[1].Value.LPSZ=(TCHAR*)szEmail; #else pAddressList->aEntries[0].rgPropVals[1].ulPropTag=PR_DISPLAY_NAME; pAddressList->aEntries[0].rgPropVals[1].Value.LPSZ=(TCHAR*)szEmail; #endif if(szAddrType!=NULL) { pAddressList->aEntries[0].rgPropVals[2].ulPropTag=PR_ADDRTYPE; pAddressList->aEntries[0].rgPropVals[2].Value.LPSZ=(TCHAR*)szAddrType; } BOOL bResult=AddRecipients(pAddressList); CMAPIEx::ReleaseAddressList(pAddressList); return bResult; } void CMAPIMessage::SetSubject(LPCTSTR szSubject) { m_strSubject=szSubject; SetPropertyString(PR_SUBJECT, szSubject); } void CMAPIMessage::SetSender(LPCTSTR szSenderName, LPCTSTR szSenderEmail) { m_strSenderName=szSenderName; m_strSenderEmail=szSenderEmail; LPTSTR szAddrType=_T("SMTP"); if(m_strSenderName.GetLength() && m_strSenderEmail.GetLength()) { SetPropertyString(PR_SENDER_NAME, szSenderName); SetPropertyString(PR_SENDER_EMAIL_ADDRESS, szSenderEmail); #ifndef _WIN32_WCE LPADRBOOK pAddressBook; if(m_pMAPI->GetSession()->OpenAddressBook(0, NULL, AB_NO_DIALOG, &pAddressBook)==S_OK) { SPropValue prop; if(pAddressBook->CreateOneOff((LPTSTR)szSenderName, szAddrType, (LPTSTR)szSenderEmail, 0, &prop.Value.bin.cb, (LPENTRYID*)&prop.Value.bin.lpb)==S_OK) { prop.ulPropTag=PR_SENT_REPRESENTING_ENTRYID; Message()->SetProps(1, &prop, NULL); SetPropertyString(PR_SENT_REPRESENTING_NAME, szSenderName); SetPropertyString(PR_SENT_REPRESENTING_EMAIL_ADDRESS, szSenderEmail); SetPropertyString(PR_SENT_REPRESENTING_ADDRTYPE, szAddrType); } } RELEASE(pAddressBook); #endif } } BOOL CMAPIMessage::SetReceivedTime(SYSTEMTIME tmReceived, BOOL bLocalTime) { SPropValue prop; prop.ulPropTag=PR_MESSAGE_DELIVERY_TIME; #ifndef _WIN32_WCE if(bLocalTime) TzSpecificLocalTimeToSystemTime(NULL, &tmReceived, &tmReceived); #endif SystemTimeToFileTime(&tmReceived, &prop.Value.ft); return (Message() && Message()->SetProps(1, &prop, NULL)==S_OK); } BOOL CMAPIMessage::SetSubmitTime(SYSTEMTIME tmSubmit, BOOL bLocalTime) { SPropValue prop; prop.ulPropTag=PR_CLIENT_SUBMIT_TIME; #ifndef _WIN32_WCE if(bLocalTime) TzSpecificLocalTimeToSystemTime(NULL, &tmSubmit, &tmSubmit); #endif SystemTimeToFileTime(&tmSubmit, &prop.Value.ft); return (Message() && Message()->SetProps(1, &prop, NULL)==S_OK); } // request a Read Receipt sent to szReceiverEmail BOOL CMAPIMessage::SetReadReceipt(BOOL bSet, LPCTSTR szReceiverEmail) { if(!Message()) return FALSE; SPropValue prop; prop.ulPropTag=PR_READ_RECEIPT_REQUESTED; prop.Value.b=(unsigned short)bSet; if(Message()->SetProps(1, &prop, NULL)!=S_OK) return FALSE; if(bSet && szReceiverEmail && _tcslen(szReceiverEmail)>0) SetPropertyString(PR_READ_RECEIPT_REQUESTED, szReceiverEmail); return TRUE; } BOOL CMAPIMessage::SetDeliveryReceipt(BOOL bSet) { if(!Message()) return FALSE; SPropValue prop; prop.ulPropTag=PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED; prop.Value.b=(unsigned short)bSet; return (Message()->SetProps(1, &prop, NULL)!=S_OK); } // limited compare, compares entry IDs and subject to determine if two emails are equal BOOL CMAPIMessage::operator==(CMAPIMessage& message) { if(!m_entryID.cb || !message.m_entryID.cb || m_entryID.cb!=message.m_entryID.cb) return FALSE; if(memcmp(m_entryID.lpb,message.m_entryID.lpb, m_entryID.cb)) return FALSE; return (!m_strSubject.Compare(message.m_strSubject)); } // Novell GroupWise customization by jcadmin #ifndef GROUPWISE BOOL CMAPIMessage::MarkAsPrivate() { return FALSE; } #else #include GWMAPI.h //(from Novell Developer Kit) #define SEND_OPTIONS_MARK_PRIVATE 0x00080000L void CMAPIMessage::MarkAsPrivate() { SPropValue prop; prop.ulPropTag=PR_NGW_SEND_OPTIONS; prop.Value.l=NGW_SEND_OPTIONS_MARK_PRIVATE; return (Message()->SetProps(1, &prop, NULL)==S_OK); } #endif // In WinCE, use MSGSTATUS_RECTYPE_SMS for an SMS, MSGSTATUS_RECTYPE_SMTP for an email BOOL CMAPIMessage::SetMessageStatus(int nMessageStatus) { SPropValue prop; prop.ulPropTag=PR_MSG_STATUS; prop.Value.ul=nMessageStatus; return (Message()->SetProps(1, &prop, NULL)==S_OK); } // Shows the default MAPI form for IMessage, returns FALSE on failure, IDOK on success or close existing messages // and IDCANCEL on close new messages int CMAPIMessage::ShowForm(CMAPIEx* pMAPI) { CMAPIFolder* pFolder=pMAPI->GetFolder(); IMAPISession* pSession=pMAPI->GetSession(); ULONG ulMessageToken; if(pFolder && pSession && pSession->PrepareForm(NULL,Message(), &ulMessageToken)==S_OK) { ULONG ulMessageStatus=GetPropertyValue(PR_MSG_STATUS, 0); ULONG ulMessageFlags=GetMessageFlags(); ULONG ulAccess=GetPropertyValue(PR_ACCESS, 0); LPSPropValue pProp; if(GetProperty(PR_MESSAGE_CLASS, pProp)==S_OK) { #ifdef UNICODE char szMessageClass[256]; WideCharToMultiByte(CP_ACP, 0, pProp->Value.LPSZ,-1, szMessageClass,255, NULL, NULL); #else char* szMessageClass=pProp->Value.LPSZ; #endif HRESULT hr=pSession->ShowForm(NULL, pMAPI->GetMessageStore(), pFolder->Folder(), NULL,ulMessageToken, NULL, 0,ulMessageStatus,ulMessageFlags,ulAccess, szMessageClass); MAPIFreeBuffer(pProp); if(hr==S_OK) return IDOK; if(hr==MAPI_E_USER_CANCEL) return IDCANCEL; } } return FALSE; } BOOL CMAPIMessage::Send() { if(Message() && Message()->SubmitMessage(0)==S_OK) { Close(); return TRUE; } return FALSE; } BOOL CMAPIMessage::SaveAsMsg(LPCTSTR szPath) { CString strPath=szPath; if(strPath.GetLength()<4 || strPath.Right(4).CompareNoCase(".msg")) strPath+=".msg"; #ifdef _WIN32_WCE return FALSE; #else LPSTORAGE pStorage; DWORD dwFlags=STGM_READWRITE | STGM_TRANSACTED | STGM_CREATE; #ifdef UNICODE if(::StgCreateDocfile(strPath, dwFlags, 0, &pStorage) == S_OK) #else WCHAR wszPath[_MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, (LPCSTR)strPath, -1, wszPath, _MAX_PATH); if(StgCreateDocfile(wszPath, dwFlags, 0, &pStorage) == S_OK) #endif { LPMSGSESS pMsgSession; LPMALLOC pMalloc = MAPIGetDefaultMalloc(); if(OpenIMsgSession(pMalloc, 0, &pMsgSession) == S_OK) { LPMESSAGE pIMsg; if(OpenIMsgOnIStg(pMsgSession, MAPIAllocateBuffer, MAPIAllocateMore, MAPIFreeBuffer, pMalloc, NULL, pStorage, NULL, 0, 0, &pIMsg) == S_OK) { // client must support CLSID_MailMessage as the compound document if(WriteClassStg(pStorage, CLSID_MailMessage) == S_OK) { // exclude these properties from the copy operation to speed things up SizedSPropTagArray ( 7, excludeTags ); excludeTags.cValues=7; excludeTags.aulPropTag[0]=PR_ACCESS; excludeTags.aulPropTag[1]=PR_BODY; excludeTags.aulPropTag[2]=PR_RTF_SYNC_BODY_COUNT; excludeTags.aulPropTag[3]=PR_RTF_SYNC_BODY_CRC; excludeTags.aulPropTag[4]=PR_RTF_SYNC_BODY_TAG; excludeTags.aulPropTag[5]=PR_RTF_SYNC_PREFIX_COUNT; excludeTags.aulPropTag[6]=PR_RTF_SYNC_TRAILING_COUNT; if(Message()->CopyTo(0, NULL, (LPSPropTagArray)&excludeTags, NULL, NULL, (LPIID)&IID_IMessage, pIMsg, 0, NULL ) == S_OK) { pIMsg->SaveChanges(KEEP_OPEN_READWRITE); pStorage->Commit(STGC_DEFAULT); } } RELEASE(pIMsg); } CloseIMsgSession(pMsgSession); } RELEASE(pStorage); return TRUE; } return FALSE; #endif }