// ==========================================================================
// Class Implementation : COXSendMail
// ==========================================================================
// Source file : SendMail.cpp
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// //////////////////////////////////////////////////////////////////////////
#include "stdafx.h" // standard MFC include
#include "OXSendMail.h"
#pragma warning(disable: 4228)
#include <mapi.h>
#include <mapix.h>
#include <mapiwin.h>
#include <mapiutil.h>
#define USES_IID_IMAPIFolder
#define INITGUID
#include <initguid.h>
#include <mapiguid.h>
#pragma warning(default: 4228)
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
COXSendMail::CMessage::CMessage()
{
}
COXSendMail::CMessage::~CMessage()
{
int i;
for (i=0 ; i<m_recipients.GetSize() ; i++)
delete m_recipients[i];
for (i=0 ; i<m_attachments.GetSize() ; i++)
delete m_attachments[i];
}
void COXSendMail::CMessage::AddRecipient(CString sRecipient, ERecipientType eType)
{
SRecipient* pRecipient = new SRecipient;
pRecipient->sName = sRecipient;
pRecipient->eType = eType;
m_recipients.Add(pRecipient);
}
void COXSendMail::CMessage::AddAttachment(CString sAttachment, EAttachmentType eType, CString sOleDocument /*= _T("")*/)
{
SAttachment* pAttachment = new SAttachment;
pAttachment->sPath = sAttachment;
pAttachment->eType = eType;
pAttachment->sOleDocument = sOleDocument;
m_attachments.Add(pAttachment);
}
#ifndef OX_MAPI_DIRECT
COXSendMail::COXSendMail()
{
}
COXSendMail::~COXSendMail()
{
if (IsInitialized())
Uninitialize();
}
BOOL COXSendMail::Initialize(CString sProfileName, CString sPassword)
{
ASSERT_VALID(this);
LPMAPISESSION pMAPISession;
HRESULT hResult;
BOOL bMAPIInitialized = FALSE;
BOOL bResult = TRUE;
TRY
{
MAPIINIT_0 MAPIINIT = { 0, MAPI_NT_SERVICE | MAPI_MULTITHREAD_NOTIFICATIONS };
hResult = MAPIInitialize(&MAPIINIT);
if (FAILED (hResult))
return FALSE;
bMAPIInitialized = TRUE;
hResult = MAPILogonEx(
(ULONG)NULL,
sProfileName.GetBuffer(256),
sPassword.GetBuffer(256),
MAPI_LOGON_UI | MAPI_NO_MAIL | MAPI_EXTENDED | MAPI_EXPLICIT_PROFILE | MAPI_NEW_SESSION,
&pMAPISession);
sProfileName.ReleaseBuffer();
sPassword.ReleaseBuffer();
if (FAILED (hResult))
{
MAPIUninitialize();
return FALSE;
}
if (m_objSession.m_lpDispatch == NULL)
{
if (m_objSession.CreateDispatch(_T("MAPI.Session")) == FALSE)
{
MAPIUninitialize();
bResult = FALSE;
}
}
if (bResult)
m_objSession.SetMapiobject(pMAPISession);
}
CATCH_ALL(e)
{
if (bMAPIInitialized)
MAPIUninitialize();
bResult = FALSE;
}
END_CATCH_ALL
if (bResult)
m_sMAPIProfile = sProfileName;
return bResult;
}
BOOL COXSendMail::IsInitialized()
{
if (m_objSession.m_lpDispatch)
if (m_objSession.GetMapiobject() != NULL)
return TRUE;
return FALSE;
}
void COXSendMail::Uninitialize()
{
TRY
{
ASSERT(m_objSession.m_lpDispatch);
if (m_objSession.m_lpDispatch)
m_objSession.Logoff();
}
CATCH_ALL(e)
{
}
END_CATCH_ALL
m_objSession.ReleaseDispatch();
MAPIUninitialize();
}
CString COXSendMail::GetProfile()
{
CString sProfile;
if (IsInitialized())
sProfile = m_sMAPIProfile;
return sProfile;
}
BOOL COXSendMail::SendMessage(CMessage& message)
{
COXMMessage objMessage;
COXMRecipient objOneRecip;
COXMAttachment objAttachment;
ASSERT(m_objSession.m_lpDispatch);
if (m_objSession.m_lpDispatch == NULL)
return FALSE;
TRY
{
// create new message and set subject and content
objMessage = m_objSession.GetOutbox().GetMessages().Add();
objMessage.SetSubject(message.m_sSubject);
objMessage.SetText(message.m_sContent);
// set the recipients
for (int i=0 ; i<message.m_recipients.GetSize() ; i++)
{
objOneRecip = objMessage.GetRecipients().Add();
objOneRecip.SetName(message.m_recipients[i]->sName);
switch (message.m_recipients[i]->eType)
{
case rtCC:
objOneRecip.SetType(oxmrtCc);
break;
case rtBCC:
objOneRecip.SetType(oxmrtBcc);
break;
default:
objOneRecip.SetType(oxmrtTo);
}
objOneRecip.Resolve(FALSE);
}
// add the attachments
for (i=0 ; i<message.m_attachments.GetSize() ; i++)
{
CString sAttachment;
TCHAR pszDrive[_MAX_DRIVE];
TCHAR pszSubdir[_MAX_DIR];
TCHAR pszBaseName[_MAX_FNAME];
TCHAR pszExtender[_MAX_EXT];
sAttachment = message.m_attachments[i]->sPath;
_tsplitpath(sAttachment, pszDrive, pszSubdir, pszBaseName, pszExtender);
_tmakepath(sAttachment.GetBuffer(_MAX_PATH), NULL, NULL, pszBaseName, pszExtender);
sAttachment.ReleaseBuffer();
objAttachment = objMessage.GetAttachments().Add(sAttachment);
objAttachment.SetName(sAttachment);
objAttachment.SetType((EOXMAttachmentType) message.m_attachments[i]->eType);
objAttachment.ReadFromFile(message.m_attachments[i]->sPath);
}
// send the message
objMessage.Send();
}
CATCH_ALL(e)
{
return FALSE;
}
END_CATCH_ALL
return TRUE;
}
#endif
CString COXSendMail::GetReturnMsg(int nErr)
{
CString sErrorMsg;
switch(nErr)
{
case SUCCESS_SUCCESS:
sErrorMsg = _T("Sending of Mail was succesful");
break;
case MAPI_E_USER_ABORT:
sErrorMsg = _T("Sending of Mail has been aborted by user");
break;
case MAPI_E_FAILURE:
sErrorMsg = _T("Sending of Mail was not succesful");
break;
case MAPI_E_LOGIN_FAILURE:
sErrorMsg = _T("login failure");
break;
case MAPI_E_DISK_FULL:
sErrorMsg = _T("Disk full");
break;
case MAPI_E_INSUFFICIENT_MEMORY:
sErrorMsg = _T("not enough memory");
break;
case MAPI_E_ACCESS_DENIED:
sErrorMsg = _T("access denied");
break;
case MAPI_E_TOO_MANY_SESSIONS:
sErrorMsg = _T("too many sessions open");
break;
case MAPI_E_TOO_MANY_FILES:
sErrorMsg = _T("too many open files");
break;
case MAPI_E_TOO_MANY_RECIPIENTS:
sErrorMsg = _T("too many recipients specified");
break;
case MAPI_E_ATTACHMENT_NOT_FOUND:
sErrorMsg = _T("attachment not found");
break;
case MAPI_E_ATTACHMENT_OPEN_FAILURE:
sErrorMsg = _T("could not open attachment");
break;
case MAPI_E_ATTACHMENT_WRITE_FAILURE:
sErrorMsg = _T("could not write attachment");
break;
case MAPI_E_UNKNOWN_RECIPIENT:
sErrorMsg = _T("unknown recipient");
break;
case MAPI_E_BAD_RECIPTYPE:
sErrorMsg = _T("unknown reciptype");
break;
case MAPI_E_NO_MESSAGES:
sErrorMsg = _T("no messages");
break;
case MAPI_E_INVALID_MESSAGE:
sErrorMsg = _T("invalid message");
break;
case MAPI_E_TEXT_TOO_LARGE:
sErrorMsg = _T("text too large");
break;
case MAPI_E_INVALID_SESSION:
sErrorMsg = _T("invalid session");
break;
case MAPI_E_TYPE_NOT_SUPPORTED:
sErrorMsg = _T("type not supported");
break;
case MAPI_E_AMBIG_RECIP:
sErrorMsg = _T("ambiguous recipiebt");
break;
case MAPI_E_MESSAGE_IN_USE:
sErrorMsg = _T("message in use");
break;
case MAPI_E_NETWORK_FAILURE:
sErrorMsg = _T("network failure");
break;
case MAPI_E_INVALID_EDITFIELDS:
sErrorMsg = _T("invalid editfields");
break;
case MAPI_E_INVALID_RECIPS:
sErrorMsg = _T("invalid recipients");
break;
case MAPI_E_NOT_SUPPORTED:
sErrorMsg = _T("not supported request");
break;
default:
sErrorMsg = _T("unknown error");
break;
}
return sErrorMsg;
}
/////////////////////////////////////////////////////////////////////////////
// The Extended MAPI alternative
#ifdef OX_MAPI_DIRECT
/////////////////////////////////////////////////////////////////////////////
// MAPI implementation helpers and globals
#define _countof(array) (sizeof(array)/sizeof(array[0]))
static BOOL IsMailAvail = (BOOL)-1; // start out not determined
// _AFX_MAIL_STATE
class OX_CLASS_DECL _AFX_MAIL_STATE : public CNoTrackObject
{
public:
HINSTANCE m_hInstMail; // handle to MAPI32.DLL
virtual ~_AFX_MAIL_STATE();
};
_AFX_MAIL_STATE::~_AFX_MAIL_STATE()
{
if (m_hInstMail != NULL)
::FreeLibrary(m_hInstMail);
}
_AFX_MAIL_STATE* _afxMailState;
int nCount = 0;
COXSendMail::COXSendMail() :
m_pMAPISession(NULL),
m_pAddressBook(NULL),
m_peidDefaultMDB(NULL),
m_cbeidDefaultMDB(0),
m_pDefaultMDB(NULL),
m_pRootFolder(NULL),
m_szRecipient(NULL),
m_uLogoffFlags(LOGOFF_ORDERLY),
m_szAttachData(NULL)
{
if (nCount==0)
_afxMailState = new _AFX_MAIL_STATE;
nCount++;
}
COXSendMail::~COXSendMail()
{
if (IsInitialized())
Uninitialize();
nCount--;
if (nCount==0)
delete _afxMailState;
}
BOOL COXSendMail::Initialize(CString sProfileName, CString sPassword)
{
ASSERT_VALID(this);
HRESULT hResult;
if (IsInitialized())
Uninitialize();
// Initialize the MAPI libraries before calling ANY MAPI function
MAPIINIT_0 MAPIINIT = { 0, MAPI_NT_SERVICE | MAPI_MULTITHREAD_NOTIFICATIONS };
hResult = MAPIInitialize(&MAPIINIT);
if (!FAILED (hResult))
{
// Logon onto the message subsystem. We are going to ask the user to
// select a profile to log into. The UI for this will be provided by MAPI.
hResult = MAPILogonEx((ULONG)NULL,
sProfileName.GetBuffer(256),
sPassword.GetBuffer(256),
MAPI_NO_MAIL | MAPI_EXTENDED | MAPI_EXPLICIT_PROFILE /*| MAPI_USE_DEFAULT */| MAPI_NEW_SESSION,
&m_pMAPISession);
sProfileName.ReleaseBuffer();
sPassword.ReleaseBuffer();
if (!FAILED (hResult))
{
// get the address book
// be sure to have the correct address book set as the default
// see the Addressing tab in the profile config control panel applet
hResult = m_pMAPISession->OpenAddressBook(NULL, NULL, 0, &m_pAddressBook);
if (!FAILED(hResult))
{
if (InitializeMessageStore())
{
// get the root folder
ULONG uObjType;
hResult = m_pDefaultMDB->OpenEntry(0,
NULL,
(LPIID)&IID_IMAPIFolder,
MAPI_MODIFY,
&uObjType,
(LPUNKNOWN *)&m_pRootFolder);
if (!FAILED(hResult))
{
m_sMAPIProfile = sProfileName;
return TRUE;
}
}
}
}
Uninitialize();
}
else
MAPIUninitialize();
return FALSE;
}
BOOL COXSendMail::IsInitialized()
{
return (m_pMAPISession!=NULL);
}
void COXSendMail::Uninitialize()
{
if (m_pRootFolder != NULL)
{
m_pRootFolder->Release();
m_pRootFolder = NULL;
}
if (m_pDefaultMDB != NULL)
{
m_pDefaultMDB->StoreLogoff(&m_uLogoffFlags);
m_pDefaultMDB->Release();
m_pDefaultMDB = NULL;
}
if (m_peidDefaultMDB != NULL)
{
MAPIFreeBuffer(m_peidDefaultMDB);
m_peidDefaultMDB = NULL;
}
if (m_pAddressBook != NULL)
{
m_pAddressBook->Release();
m_pAddressBook = NULL;
}
if (m_pMAPISession!=NULL)
{
ULONG ulUIParam = (ULONG)NULL; // null window handle
ULONG ulFlags = 0;
ULONG ulReserved = 0;
m_pMAPISession->Logoff(ulUIParam, ulFlags, ulReserved);
m_pMAPISession->Release();
m_pMAPISession = NULL;
MAPIUninitialize();
}
}
CString COXSendMail::GetProfile()
{
CString sProfile;
if (IsInitialized())
sProfile = m_sMAPIProfile;
return sProfile;
}
BOOL COXSendMail::SendMessage(CMessage& message)
{
LPMESSAGE pMessage;
HRESULT hResult;
pMessage = ComposeMessage(message);
if (pMessage == NULL)
return FALSE;
hResult = pMessage->SubmitMessage(FORCE_SUBMIT);
if (pMessage != NULL)
pMessage->Release();
if (FAILED(hResult))
return FALSE;
return TRUE;
}
// protected functions
BOOL COXSendMail::InitializeMessageStore()
{
LPSRowSet pRow = NULL;
ULONG ulRow;
ULONG uRowCount;
HRESULT hResult;
LPMAPITABLE pStoresTable;
LPSPropValue pProps;
BOOL bFound;
// The order for this enumaration must match the order of the properties
// in the tag array below
enum
{
DEFAULT_STORE,
STORE_EID,
MSG_STORES_TABLE_PROPS // number columns in the proptagarray
};
SizedSPropTagArray (MSG_STORES_TABLE_PROPS, sptMsgStores) =
{
MSG_STORES_TABLE_PROPS,
{
PR_DEFAULT_STORE,
PR_ENTRYID,
}
};
///// end of local declarations
m_cbeidDefaultMDB = 0;
m_peidDefaultMDB = NULL;
// first get a table of the message stores available
hResult = m_pMAPISession->GetMsgStoresTable (0, &pStoresTable);
if (FAILED(hResult))
return FALSE;
// we only want the default store flag and the entry id
hResult = pStoresTable->SetColumns((LPSPropTagArray)&sptMsgStores, 0);
if (FAILED(hResult))
return FALSE;
pStoresTable->GetRowCount(0, &uRowCount); // one row corresponds to one MDB provider
// typically no one should have more than
// 6 or 7 MDB providers.
// Get row(s) from the message stores table
hResult = pStoresTable->QueryRows(uRowCount, 0, &pRow);
if (FAILED(hResult))
return FALSE;
bFound = FALSE;
// loop through each row -- each row represents the properties of a message store
for (ulRow=0; ulRow<pRow->cRows; ulRow++)
{
pProps = pRow->aRow[ulRow].lpProps;
if (pProps[DEFAULT_STORE].Value.b == 1) // we found it!
{
m_cbeidDefaultMDB = pProps[STORE_EID].Value.bin.cb;
hResult = MAPIAllocateBuffer(m_cbeidDefaultMDB, (LPVOID *)&m_peidDefaultMDB);
if (FAILED(hResult))
return FALSE;
memcpy(m_peidDefaultMDB, pProps[STORE_EID].Value.bin.lpb, m_cbeidDefaultMDB);
bFound = TRUE;
}
/// else ignore the non-defaults
}
if (bFound)
hResult = S_OK;
else
hResult = MAPI_E_NOT_FOUND;
// clean up if neccessary
if (pRow != NULL)
FreeProws(pRow);
if (pStoresTable != NULL)
pStoresTable->Release();
if (FAILED(hResult)) // there was an error
return FALSE;
// open the default message store
m_pDefaultMDB = NULL;
hResult = m_pMAPISession->OpenMsgStore ((ULONG)NULL,
m_cbeidDefaultMDB,
m_peidDefaultMDB,
NULL,
MDB_WRITE,
&m_pDefaultMDB);
if (FAILED(hResult))
return FALSE;
return TRUE;
}
LPMESSAGE COXSendMail::ComposeMessage(CMessage& message)
{
HRESULT hrRet = 0;
LPMESSAGE pMessage = NULL;
SPropValue pMessageProps[5];
LPSPropProblemArray pPropProblems;
// create the message
hrRet = m_pRootFolder->CreateMessage(NULL, 0, &pMessage);
if (FAILED(hrRet))
return NULL;
// setup the properties
pMessageProps[0].ulPropTag = PR_MESSAGE_CLASS;
pMessageProps[1].ulPropTag = PR_PRIORITY;
pMessageProps[2].ulPropTag = PR_SUBJECT;
pMessageProps[3].ulPropTag = PR_BODY;
pMessageProps[4].ulPropTag = PR_CLIENT_SUBMIT_TIME;
pMessageProps[0].Value.lpszA = "IPM.NOTE";
pMessageProps[1].Value.l = PRIO_NORMAL;
#ifdef _UNICODE
pMessageProps[2].Value.lpszW = message.m_sSubject.GetBuffer(message.m_sSubject.GetLength());
pMessageProps[3].Value.lpszW = message.m_sContent.GetBuffer(message.m_sContent.GetLength());
#else
pMessageProps[2].Value.lpszA = message.m_sSubject.GetBuffer(message.m_sSubject.GetLength());
pMessageProps[3].Value.lpszA = message.m_sContent.GetBuffer(message.m_sContent.GetLength());
#endif
message.m_sSubject.ReleaseBuffer();
message.m_sContent.ReleaseBuffer();
// set the message submission time
SYSTEMTIME tSysTime;
FILETIME tSubmitTime;
GetSystemTime(&tSysTime);
SystemTimeToFileTime(&tSysTime, &tSubmitTime); // convert to file time
pMessageProps[4].Value.ft = tSubmitTime;
// set the properties
hrRet = pMessage->SetProps(5, pMessageProps, &pPropProblems);
if (!FAILED(hrRet))
{
hrRet = AddRecipients(pMessage, message.m_recipients);
if (!FAILED(hrRet))
{
// Create the attachment
AddAttachments(pMessage, message.m_attachments);
hrRet = pMessage->SaveChanges(KEEP_OPEN_READWRITE);
}
if (!FAILED(hrRet))
return pMessage;
}
if (pMessage != NULL)
pMessage->Release();
return NULL;
}
HRESULT COXSendMail::AddRecipients(LPMESSAGE pMessage, CRecipientArray& recipients)
{
HRESULT hrRet;
LPADRLIST pAddressList;
// allocate address list for one recipient
MAPIAllocateBuffer(sizeof(ADRLIST) + sizeof(ADRENTRY) * recipients.GetSize(),
(LPVOID *) &pAddressList);
// set properties for address list
pAddressList->cEntries = recipients.GetSize();
for (int i=0 ; i<recipients.GetSize() ; i++)
{
// allocate space for properties
MAPIAllocateBuffer(sizeof(SPropValue) * 2,
(LPVOID *) &(pAddressList->aEntries[i].rgPropVals) );
pAddressList->aEntries[i].cValues = 2;
pAddressList->aEntries[i].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME;
pAddressList->aEntries[i].rgPropVals[0].Value.lpszA = recipients[i]->sName.GetBuffer(recipients[i]->sName.GetLength());
pAddressList->aEntries[i].rgPropVals[1].ulPropTag = PR_RECIPIENT_TYPE;
recipients[i]->sName.ReleaseBuffer();
switch (recipients[i]->eType)
{
case rtTO:
pAddressList->aEntries[i].rgPropVals[1].Value.l = MAPI_TO;
break;
case rtCC:
pAddressList->aEntries[i].rgPropVals[1].Value.l = MAPI_CC;
break;
case rtBCC:
pAddressList->aEntries[i].rgPropVals[1].Value.l = MAPI_BCC;
break;
}
}
// resolve the address
hrRet = m_pAddressBook->ResolveName(0, 0, NULL, pAddressList);
if (!FAILED(hrRet))
{
// set the recipients to the message
hrRet = pMessage->ModifyRecipients(MODRECIP_ADD, pAddressList);
}
for (i=0 ; i<recipients.GetSize() ; i++)
MAPIFreeBuffer(pAddressList->aEntries[i].rgPropVals);
MAPIFreeBuffer(pAddressList);
return hrRet;
}
HRESULT COXSendMail::AddAttachments(LPMESSAGE pMessage, CAttachmentArray& attachments)
{
HRESULT hrRet;
UINT idx;
LPTSTR szAttachment;
// if there are no attachments, just return
if (attachments.GetSize() == 0)
return 0;
enum
{
REND_POS,
PATH_NAME,
ATT_METHOD,
DISP_NAME,
ATT_FILENAME,
ATT_DIM
};
SizedSPropTagArray(ATT_DIM , sptAttachTags) =
{
ATT_DIM,
{
PR_RENDERING_POSITION,
PR_ATTACH_PATHNAME,
PR_ATTACH_METHOD,
PR_DISPLAY_NAME,
PR_ATTACH_FILENAME
}
};
SPropValue spvAttachProps[ATT_DIM];
CString sAttachment;
for (int i=0 ; i<attachments.GetSize() ; i++)
{
sAttachment = attachments[i]->sPath;
LPATTACH lpAttach = NULL;
ULONG ulAttachNum = 0;
hrRet = pMessage->CreateAttach(NULL, 0, &ulAttachNum, &lpAttach);
if (FAILED(hrRet))
return hrRet;
for(idx = 0; idx < ATT_DIM; ++idx)
{
spvAttachProps[idx].ulPropTag = sptAttachTags.aulPropTag[idx];
spvAttachProps[idx].dwAlignPad = 0;
}
// Split the path of the attachment, so we can extract the filename
// to display under the attachment
TCHAR pszDrive[_MAX_DRIVE];
TCHAR pszSubdir[_MAX_DIR];
TCHAR pszBaseName[_MAX_FNAME];
TCHAR pszExtender[_MAX_EXT];
TCHAR pszFileName[_MAX_FNAME + _MAX_EXT];
szAttachment = sAttachment.GetBuffer(_MAX_PATH);
_tsplitpath(szAttachment, pszDrive, pszSubdir, pszBaseName, pszExtender);
_tmakepath(pszFileName, NULL, NULL, pszBaseName, pszExtender);
spvAttachProps[REND_POS].Value.l = -1;
spvAttachProps[PATH_NAME].Value.LPSZ = szAttachment;
spvAttachProps[ATT_METHOD].Value.l = ATTACH_BY_REF_RESOLVE;
// spvAttachProps[ATT_METHOD].Value.l = ATTACH_BY_VALUE;
spvAttachProps[DISP_NAME].Value.LPSZ = pszFileName;
spvAttachProps[ATT_FILENAME].Value.LPSZ = pszFileName;
hrRet = lpAttach->SetProps(ATT_DIM, spvAttachProps, NULL);
if (FAILED(hrRet))
{
sAttachment.ReleaseBuffer();
return hrRet;
}
hrRet = lpAttach->SaveChanges(KEEP_OPEN_READWRITE);
sAttachment.ReleaseBuffer();
if (FAILED(hrRet))
return hrRet;
szAttachment = NULL;
UlRelease(lpAttach);
}
return hrRet;
}
#endif