// ========================================================================== // Class Implementation : CSendMail // ========================================================================== // Source file : SendMail.cpp //----------------- Dundas Software ---------------------------------------- //======================================================================== // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" // standard MFC include #include "SendMail.h" #pragma warning(disable: 4228) #include #include #include #include #define USES_IID_IMAPIFolder #define INITGUID #include #include #pragma warning(default: 4228) #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define _countof(array) (sizeof(array)/sizeof(array[0])) ///////////////////////////////////////////////////////////////////////////// // MAPI implementation helpers and globals static BOOL IsMailAvail = (BOOL)-1; // start out not determined ///////////////////////////////////////////////////////////////////////////// // _AFX_MAIL_STATE class _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; CSendMail::CSendMail() : 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++; } CSendMail::~CSendMail() { nCount--; if (nCount==0) delete _afxMailState; } CString CSendMail::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; } BOOL CSendMail::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 CSendMail::IsInitialized() const { return (m_pMAPISession!=NULL); } void CSendMail::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 CSendMail::GetProfile() const { CString sProfile; if (IsInitialized()) sProfile = m_sMAPIProfile; return sProfile; } BOOL CSendMail::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; ulRowcRows; 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; } BOOL CSendMail::SendMapiMail(CString sTo, CString sSubject, CString sDescription, CString sAtt) { LPMESSAGE pMessage; HRESULT hResult; pMessage = ComposeMessage(sTo, sSubject, sDescription, sAtt); if (pMessage == NULL) return FALSE; hResult = pMessage->SubmitMessage(FORCE_SUBMIT); if (pMessage != NULL) pMessage->Release(); if (FAILED(hResult)) return FALSE; return TRUE; } //************************************************************* LPMESSAGE CSendMail::ComposeMessage(LPCTSTR szRecipient, LPCTSTR szSubject, LPCTSTR szDescription, LPCTSTR szAtt) { HRESULT hrRet = 0; LPMESSAGE pMessage = NULL; SPropValue pMessageProps[5]; LPADRLIST pAddressList = NULL; 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[0].Value.lpszA = "IPM.NOTE"; pMessageProps[1].ulPropTag = PR_PRIORITY; pMessageProps[1].Value.l = 0; pMessageProps[2].ulPropTag = PR_SUBJECT; pMessageProps[2].Value.lpszA = (LPTSTR) szSubject; pMessageProps[3].ulPropTag = PR_BODY; pMessageProps[3].Value.lpszA = (LPTSTR) szDescription; // set the message submission time SYSTEMTIME tSysTime; FILETIME tSubmitTime; GetSystemTime(&tSysTime); SystemTimeToFileTime(&tSysTime, &tSubmitTime); // convert to file time pMessageProps[4].ulPropTag = PR_CLIENT_SUBMIT_TIME; pMessageProps[4].Value.ft = tSubmitTime; // build the recipients list pAddressList = GetRecipient(szRecipient); if (pAddressList) { // set the properties hrRet = pMessage->SetProps(5, pMessageProps, &pPropProblems); if (!FAILED(hrRet)) { // set the recipients to the message hrRet = pMessage->ModifyRecipients(MODRECIP_ADD, pAddressList); if (!FAILED(hrRet)) { // Create the attachment AddAttachments(pMessage, szAtt); hrRet = pMessage->SaveChanges(KEEP_OPEN_READWRITE); } } // GetRecipient allocates memory pointed to by pAddressList. // use MAPIFreeBuffer to free memory after using it // for modifying the recipients MAPIFreeBuffer(pAddressList); if (!FAILED(hrRet)) return pMessage; } if (pMessage != NULL) pMessage->Release(); return NULL; } LPADRLIST CSendMail::GetRecipient(LPCTSTR szRecipient) { HRESULT hrRet; LPADRLIST pAddressList; // allocate address list for one recipient MAPIAllocateBuffer(sizeof(ADRLIST) + sizeof(ADRENTRY) * 1, (LPVOID *) &pAddressList); // allocate space for properties MAPIAllocateBuffer(sizeof(SPropValue) * 2, (LPVOID *) &(pAddressList->aEntries[0].rgPropVals) ); // set properties for address list pAddressList->cEntries = 1; pAddressList->aEntries[0].cValues = 2; pAddressList->aEntries[0].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME; pAddressList->aEntries[0].rgPropVals[0].Value.lpszA = (LPTSTR) szRecipient; pAddressList->aEntries[0].rgPropVals[1].ulPropTag = PR_RECIPIENT_TYPE; pAddressList->aEntries[0].rgPropVals[1].Value.l = MAPI_TO; // resolve the address hrRet = m_pAddressBook->ResolveName(0, 0, NULL, pAddressList); if (FAILED(hrRet)) { MAPIFreeBuffer(pAddressList); pAddressList = NULL; } return pAddressList; } HRESULT CSendMail::AddAttachments(LPMESSAGE pMessage, LPCTSTR szAttachData) { HRESULT hrRet; UINT idx; LPSTR szAttachment; // if there are no attachments, just return if (szAttachData == NULL || _tcslen(szAttachData) == 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]; // Count the number of attachments in the AttachmentData CString sAttachment; int nIndex = 0; if (!AfxExtractSubString(sAttachment, szAttachData, nIndex++, _T('|'))) return 0; while (!sAttachment.IsEmpty()) { LPATTACH lpAttach = NULL; ULONG ulAttachNum = 0; szAttachment = sAttachment.GetBuffer(_MAX_PATH); 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]; _tsplitpath(szAttachment, pszDrive, pszSubdir, pszBaseName, pszExtender); _tmakepath(pszFileName, NULL, NULL, pszBaseName, pszExtender); spvAttachProps[REND_POS].Value.l = -1; spvAttachProps[PATH_NAME].Value.LPSZ = szAttachment; /* ATTACH_BY_REF_RESOLVE means the spooler will resolve the attachment into ATTACH_BY_VALUE and place the attachment data in PR_ATTACH_DATA_BIN */ spvAttachProps[ATT_METHOD].Value.l = ATTACH_BY_REF_RESOLVE; spvAttachProps[DISP_NAME].Value.LPSZ = pszFileName; spvAttachProps[ATT_FILENAME].Value.LPSZ = pszFileName; hrRet = lpAttach->SetProps(ATT_DIM, spvAttachProps, NULL); if (FAILED(hrRet)) return hrRet; hrRet = lpAttach->SaveChanges(KEEP_OPEN_READWRITE); if (FAILED(hrRet)) return hrRet; sAttachment.ReleaseBuffer(); szAttachment = NULL; UlRelease(lpAttach); sAttachment.Empty(); AfxExtractSubString(sAttachment, szAttachData, nIndex++, _T('|')); } return hrRet; }