// // SMAPI.CPP - Simple MAPI implementation // #include "pch.hxx" #include "note.h" #include #include #include #include #include #include #include #include "smapimem.h" #include #include #include #include "instance.h" #include "msgfldr.h" #include #include #include "multiusr.h" #include <..\help\mailnews.h> #include #include "mapidlg.h" #include "demand.h" ASSERTDATA static LPWAB s_lpWab; static LPWABOBJECT s_lpWabObject; static IAddrBook* s_lpAddrBook; extern HANDLE hSmapiEvent; HINITREF hInitRef=NULL; HRESULT HrAdrlistFromRgrecip(ULONG nRecips, lpMapiRecipDesc lpRecips, LPADRLIST *ppadrlist); HRESULT HrRgrecipFromAdrlist(LPADRLIST lpAdrList, lpMapiRecipDesc * lppRecips); void ParseEmailAddress(LPSTR pszEmail, LPSTR *ppszAddrType, LPSTR *ppszAddress); void FreePadrlist(LPADRLIST padrlist); ULONG HrFillMessage(LPMIMEMESSAGE *pmsg, lpMapiMessage lpMessage, BOOL *pfWebPage, BOOL bValidateRecips, BOOL fOriginator); BOOL HrReadMail (IMessageFolder *pFolder, LPSTR lpszMessageID, lpMapiMessage FAR *lppMessage, FLAGS flFlags); ULONG HrValidateMessage(lpMapiMessage lpMessage); BOOL HrSMAPISend(HWND hWnd, IMimeMessage *pMsg); HRESULT HrFromIDToNameAndAddress(LPTSTR *pszLocalName, LPTSTR *pszLocalAddress, ULONG cbEID, LPENTRYID lpEID); INT_PTR CALLBACK WarnSendMailDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); // Fix for Bug #62129 (v-snatar) HRESULT HrSendAndRecv(); typedef enum tagINTERACTIVESTATE { IS_UNINIT, IS_INTERACTIVE, IS_NOTINTERACTIVE, } INTERACTIVESTATE; // Determine if the current process is a service or not BOOL IsProcessInteractive(void); /////////////////////////////////////////////////////////////////////// // // UlSimpleMAPIInit // /////////////////////////////////////////////////////////////////////// ULONG UlSimpleMAPIInit(BOOL fInit, HWND hwnd, BOOL fLogonUI) { ULONG ulRet = SUCCESS_SUCCESS; BOOL bCoIncrementFailure = FALSE; if (fInit) { // [PaulHi] 5/17/99 @todo @bug // The "SimpleMAPIInit" name is used in debug only builds to track users of // OE. However, if this function (CoIncrementInit) fails (if user doesn't // provide identity) then a memory leak will occur because CoDecrementInit() // is called with "COutlookExpress" and so the "SimpleMAPIInit" node isn't // free'd. Again, this is for debug binaries only. /* Yet more cludgyness: If SMAPI is not allowed to show logon UI, we ask OE to use the default identity. However, the default identity could have a password on it. In this case, SMAPI would like to fail the logon. Unfortunately, the identity manager does not make it easy to discover if an identity has a a password (we would need to grok the registry). This limitation is currently moot as OE will logon to the default identity without requiring the user to supply the required password. If this is fixed, we will have to change this code. */ if (FAILED(CoIncrementInit("SimpleMAPIInit", MSOEAPI_START_SHOWERRORS | ((fLogonUI) ? 0 : MSOEAPI_START_DEFAULTIDENTITY ), NULL, &hInitRef))) { ulRet = MAPI_E_FAILURE; bCoIncrementFailure = TRUE; goto exit; } if (S_OK != ProcessICW(hwnd, FOLDER_LOCAL, TRUE, fLogonUI)) { ulRet = MAPI_E_LOGON_FAILURE; goto exit; } if (NULL == s_lpWab) { if (FAILED(HrCreateWabObject(&s_lpWab))) { ulRet = MAPI_E_FAILURE; goto exit; } Assert(s_lpWab); if (FAILED(s_lpWab->HrGetAdrBook(&s_lpAddrBook))) { ulRet = MAPI_E_FAILURE; goto exit; } Assert(s_lpAddrBook); if (FAILED(s_lpWab->HrGetWabObject(&s_lpWabObject))) { ulRet = MAPI_E_FAILURE; goto exit; } Assert(s_lpWabObject); } else { if (FAILED(s_lpWab->HrGetWabObject(&s_lpWabObject))) { ulRet = MAPI_E_FAILURE; goto exit; } Assert(s_lpWabObject); } } exit: if (FALSE == fInit || (fInit && SUCCESS_SUCCESS != ulRet && !bCoIncrementFailure)) { CoDecrementInit("SimpleMAPIInit", NULL); } return ulRet; } /////////////////////////////////////////////////////////////////////// // // SimpleMAPICleanup // /////////////////////////////////////////////////////////////////////// void SimpleMAPICleanup(void) { SafeRelease(s_lpWab); s_lpWabObject = NULL; s_lpAddrBook = NULL; } /////////////////////////////////////////////////////////////////////// // // Simple MAPI Session Implementation // /////////////////////////////////////////////////////////////////////// #define SESSION_MAGIC 0xEA030571 class CSession { public: CSession(); ~CSession(); ULONG UlInit(HWND hwnd, BOOL fLogonUI); ULONG m_cRef; DWORD m_dwSessionMagic; IMessageFolder *m_pfldrInbox; BOOL m_fDllInited; }; typedef CSession * PSESS; CSession::CSession() { m_cRef = 0; m_dwSessionMagic = SESSION_MAGIC; m_pfldrInbox = NULL; m_fDllInited = FALSE; } CSession::~CSession() { if (m_pfldrInbox) m_pfldrInbox->Release(); if (m_fDllInited) { UlSimpleMAPIInit(FALSE, NULL, FALSE); } } ULONG CSession::UlInit(HWND hwnd, BOOL fLogonUI) { ULONG ulRet = SUCCESS_SUCCESS; ulRet = UlSimpleMAPIInit(TRUE, hwnd, fLogonUI); if (SUCCESS_SUCCESS == ulRet) { m_fDllInited = TRUE; } return ulRet; } ULONG UlGetSession(LHANDLE lhSession, PSESS *ppSession, ULONG_PTR ulUIParam, BOOL fLogonUI, BOOL fNeedInbox) { ULONG ulRet = SUCCESS_SUCCESS; PSESS pSession = NULL; if (lhSession && IsBadWritePtr((LPVOID)lhSession, sizeof(CSession))) { ulRet = MAPI_E_INVALID_SESSION; goto exit; } if (lhSession) { pSession = (PSESS)lhSession; if (pSession->m_dwSessionMagic != SESSION_MAGIC) { ulRet = MAPI_E_INVALID_SESSION; goto exit; } } else { pSession = new CSession(); if (NULL == pSession) { ulRet = MAPI_E_INSUFFICIENT_MEMORY; goto exit; } ulRet = pSession->UlInit((HWND) ulUIParam, fLogonUI); if (SUCCESS_SUCCESS != ulRet) { delete pSession; goto exit; } } if (fNeedInbox && !pSession->m_pfldrInbox) { if (FAILED(g_pStore->OpenSpecialFolder(FOLDERID_LOCAL_STORE, NULL, FOLDER_INBOX, &pSession->m_pfldrInbox))) { ulRet = MAPI_E_FAILURE; goto exit; } } pSession->m_cRef++; *ppSession = pSession; // Set the return value ulRet = SUCCESS_SUCCESS; exit: return ulRet; } ULONG ReleaseSession(PSESS pSession) { HRESULT hr =S_OK; if (NULL == pSession) return MAPI_E_INVALID_SESSION; if (IsBadWritePtr(pSession, sizeof(CSession))) return MAPI_E_INVALID_SESSION; if (pSession->m_dwSessionMagic != SESSION_MAGIC) return MAPI_E_INVALID_SESSION; if (--pSession->m_cRef == 0) { delete pSession; /* if(hInitRef) IF_FAILEXIT(hr = CoDecrementInit("SimpleMAPIInit", &hInitRef)); hInitRef = NULL; */ } return SUCCESS_SUCCESS; // exit: return(hr); } /////////////////////////////////////////////////////////////////////// // // MAPILogon // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPILogon(ULONG_PTR ulUIParam, LPSTR lpszProfileName, LPSTR lpszPassword, FLAGS flFlags, ULONG ulReserved, LPLHANDLE lplhSession) { ULONG ulRet = SUCCESS_SUCCESS; PSESS pSession = NULL; BOOL fLogonUI; fLogonUI = (0 != (flFlags & MAPI_LOGON_UI)); // If the process is not interactive, they should not have // allowed any UI if (!IsProcessInteractive() && fLogonUI) { ulRet = MAPI_E_FAILURE; goto exit; } ulRet = UlGetSession(NULL, &pSession, ulUIParam, fLogonUI, FALSE); if (SUCCESS_SUCCESS != ulRet) return ulRet; *lplhSession = (LHANDLE)pSession; // Fix for Bug #62129 (v-snatar) if (flFlags & MAPI_FORCE_DOWNLOAD) HrSendAndRecv(); exit: return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPILogoff // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPILogoff(LHANDLE lhSession, ULONG_PTR ulUIParam, FLAGS flFlags, ULONG ulReserved) { return ReleaseSession((PSESS)lhSession); } /////////////////////////////////////////////////////////////////////// // // MAPIFreeBuffer // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPIFreeBuffer(LPVOID lpv) { LPBufInternal lpBufInt; LPBufInternal lpT; if (!lpv) return(0L); // for callers who don't check for NULL themselves. lpBufInt = LPBufIntFromLPBufExt(lpv); if (IsBadWritePtr(lpBufInt, sizeof(BufInternal))) { TellBadBlock(lpv, "fails address check"); return MAPI_E_FAILURE; } if (GetFlags(lpBufInt->ulAllocFlags) != ALLOC_WITH_ALLOC) { TellBadBlock(lpv, "has invalid allocation flags"); return MAPI_E_FAILURE; } #ifdef DEBUG if (!FValidAllocChain(lpBufInt)) goto ret; #endif // Free the first block lpT = lpBufInt->pLink; g_pMalloc->Free(lpBufInt); lpBufInt = lpT; while (lpBufInt) { if (IsBadWritePtr(lpBufInt, sizeof(BufInternal)) || GetFlags(lpBufInt->ulAllocFlags) != ALLOC_WITH_ALLOC_MORE) goto ret; lpT = lpBufInt->pLink; g_pMalloc->Free(lpBufInt); lpBufInt = lpT; } ret: return SUCCESS_SUCCESS; } /////////////////////////////////////////////////////////////////////// // // MAPISendMail // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPISendMail(LHANDLE lhSession, // ignored ULONG_PTR ulUIParam, lpMapiMessage lpMessage, FLAGS flFlags, ULONG ulReserved) { ULONG ulRet = SUCCESS_SUCCESS; LPMIMEMESSAGE pMsg = NULL; HRESULT hr; BOOL fWebPage; PSESS pSession = NULL; BOOL fLogonUI; BOOL fOleInit = FALSE; // validate parameters if (NULL == lpMessage || IsBadReadPtr(lpMessage, sizeof(MapiMessage))) return MAPI_E_INVALID_MESSAGE; fLogonUI = (0 != (flFlags & MAPI_LOGON_UI)); // If the process is not interactive, they should not allow any UI if (!IsProcessInteractive() && fLogonUI) { return MAPI_E_FAILURE; } if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; if (!(flFlags & MAPI_DIALOG)) { ulRet = HrValidateMessage(lpMessage); if (ulRet) return ulRet; } ulRet = UlGetSession(lhSession, &pSession, ulUIParam, fLogonUI, FALSE); if (SUCCESS_SUCCESS != ulRet) return ulRet; // display warning dialog if app is sending mail without ui and the users // wish to be alerted if (!(flFlags & MAPI_DIALOG) && !!DwGetOption(OPT_SECURITY_MAPI_SEND)) { if (IDCANCEL == DialogBoxParam(g_hLocRes,MAKEINTRESOURCE(iddMapiSend), NULL, WarnSendMailDlgProc, (LPARAM)lpMessage)) goto error; } // Make sure OLE is initialized OleInitialize(NULL); fOleInit = TRUE; // Fill IMimeMessage with the lpMessage structure members ulRet = HrFillMessage(&pMsg, lpMessage, &fWebPage, !(flFlags & MAPI_DIALOG), !(flFlags & MAPI_DIALOG)); if (ulRet) goto error; if (flFlags & MAPI_DIALOG) { INIT_MSGSITE_STRUCT rInitSite; DWORD dwAction, dwCreateFlags = OENCF_SENDIMMEDIATE | OENCF_MODAL; // always on dllentry points... if (fWebPage) dwAction = OENA_WEBPAGE; else dwAction = OENA_COMPOSE; rInitSite.dwInitType = OEMSIT_MSG; rInitSite.pMsg = pMsg; rInitSite.folderID = FOLDERID_INVALID; hr = CreateAndShowNote(dwAction, dwCreateFlags, &rInitSite, (HWND)ulUIParam); hInitRef = NULL; } else hr = HrSMAPISend((HWND)ulUIParam, pMsg); // Send the Message without displaying it if (SUCCEEDED(hr)) ulRet = SUCCESS_SUCCESS; else ulRet = MAPI_E_FAILURE; error: if (pMsg) pMsg->Release(); ReleaseSession(pSession); // Make sure we clean up OLE afterwords if (fOleInit) OleUninitialize(); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPISendDocuments // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPISendDocuments(ULONG_PTR ulUIParam, LPSTR lpszDelimChar, LPSTR lpszFullPaths, LPSTR lpszFileNames, ULONG ulReserved) { ULONG ulRet = MAPI_E_FAILURE; int cch; LPMIMEMESSAGE pMsg = NULL; HRESULT hr; CStringParser spPath; int nCount=0; // Used to find the number of files to be attached PSESS pSession = NULL; INIT_MSGSITE_STRUCT rInitSite; DWORD dwAction, dwCreateFlags = OENCF_SENDIMMEDIATE | OENCF_MODAL; //always on dllentry points... // check for the Delimiter Assert(lpszDelimChar); if (lpszDelimChar == NULL) return MAPI_E_FAILURE; // check for the Paths Assert (lpszFullPaths); if (lpszFullPaths == NULL) return MAPI_E_FAILURE; // MAPISendDocuments is documented as always bringing up UI // A service should not call this function if (!IsProcessInteractive()) return MAPI_E_LOGIN_FAILURE; if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; ulRet = UlGetSession(NULL, &pSession, ulUIParam, TRUE, FALSE); if (SUCCESS_SUCCESS != ulRet) return ulRet; // create an empty message hr = HrCreateMessage(&pMsg); if (FAILED(hr)) goto error; dwAction = OENA_COMPOSE; // ~~~ Do I need to do something with OEMSIT_VIRGIN? rInitSite.dwInitType = OEMSIT_MSG; rInitSite.pMsg = pMsg; rInitSite.folderID = FOLDERID_INVALID; // Determine the number of attachments (nCount), indiviual file names and pathnames // call pMsg->AttachFile with appropriate parameters nCount times // To parse the lpszFullPaths and lpszFileNames use CStringParser class spPath.Init(lpszFullPaths, lstrlen(lpszFullPaths), 0); //Parse the path for the delimiter spPath.ChParse(lpszDelimChar); while (spPath.CchValue()) { // Add the attachment hr = pMsg->AttachFile(spPath.PszValue(), NULL, NULL); if (FAILED(hr)) goto error; nCount++; //Parse the path for the delimiter spPath.ChParse(lpszDelimChar); } // set the subject on the message if (nCount == 1) { if (lpszFileNames) hr = MimeOleSetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, lpszFileNames); } else { TCHAR szBuf[CCHMAX_STRINGRES]; AthLoadString(idsAttachedFiles, szBuf, ARRAYSIZE(szBuf)); hr = MimeOleSetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, szBuf); } if (FAILED(hr)) goto error; hr = CreateAndShowNote(dwAction, dwCreateFlags, &rInitSite, (HWND)ulUIParam); if (SUCCEEDED(hr)) ulRet = SUCCESS_SUCCESS; error: if (pMsg) pMsg->Release(); ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPIAddress // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPIAddress(LHANDLE lhSession, ULONG_PTR ulUIParam, LPTSTR lpszCaption, ULONG nEditFields, LPTSTR lpszLabels, ULONG nRecips, lpMapiRecipDesc lpRecips, FLAGS flFlags, ULONG ulReserved, LPULONG lpnNewRecips, lpMapiRecipDesc FAR * lppNewRecips) { ULONG ul, ulRet = MAPI_E_FAILURE; HRESULT hr; LPADRLIST lpAdrList = 0; ADRPARM AdrParms = {0}; static ULONG rgulTypes[3] = {MAPI_TO, MAPI_CC, MAPI_BCC}; PSESS pSession = NULL; BOOL fLogonUI; // Validate Parameters - Begin if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; if (lpszCaption && IsBadStringPtr(lpszCaption, (UINT)0xFFFF)) return MAPI_E_FAILURE; if (nEditFields > 4) return MAPI_E_INVALID_EDITFIELDS; if (nEditFields == 1 && lpszLabels && IsBadStringPtr(lpszLabels, (UINT)0xFFFF)) return MAPI_E_INVALID_EDITFIELDS; if (nEditFields && IsBadWritePtr(lpnNewRecips, (UINT)sizeof(ULONG))) return MAPI_E_INVALID_EDITFIELDS; if (nEditFields && IsBadWritePtr(lppNewRecips, (UINT)sizeof(lpMapiRecipDesc))) return MAPI_E_INVALID_EDITFIELDS; if (nRecips && IsBadReadPtr(lpRecips, (UINT)nRecips * sizeof(MapiRecipDesc))) return MAPI_E_INVALID_RECIPS; fLogonUI = (0 != (flFlags & MAPI_LOGON_UI)); // Services shouldn't ask for UI if (!IsProcessInteractive() && fLogonUI) return MAPI_E_LOGIN_FAILURE; // Validate parameters - End // init output parameters if (nEditFields) { *lppNewRecips = NULL; *lpnNewRecips = 0; } ulRet = UlGetSession(lhSession, &pSession, ulUIParam, fLogonUI, FALSE); if (SUCCESS_SUCCESS != ulRet) return ulRet; // build an adrlist from the lpRecips if (nRecips) { ULONG ulMax = MAPI_TO; hr = HrAdrlistFromRgrecip(nRecips, lpRecips, &lpAdrList); if (hr) goto exit; Assert(nRecips == lpAdrList->cEntries); // we need to grow nEditFields if it isn't big enough for (ul = 0; ul < nRecips; ul++) { if (ulMax < lpRecips[ul].ulRecipClass && lpRecips[ul].ulRecipClass <= MAPI_BCC) ulMax = lpRecips[ul].ulRecipClass; } Assert(ulMax >= MAPI_TO && ulMax <= MAPI_BCC); if (ulMax > nEditFields) { DOUT("MAPIAddress: growing nEditFields from %ld to %ld\r\n", nEditFields, ulMax); nEditFields = ulMax; } } // Fill the AdrParm structure AdrParms.ulFlags = DIALOG_MODAL; AdrParms.lpszCaption = lpszCaption; AdrParms.cDestFields = nEditFields == 4 ? 3 : nEditFields; if (nEditFields == 1 && lpszLabels && *lpszLabels) AdrParms.lppszDestTitles = &lpszLabels; AdrParms.lpulDestComps = rgulTypes; if (NULL == s_lpAddrBook) { ulRet = MAPI_E_FAILURE; goto exit; } hr = s_lpAddrBook->Address(&ulUIParam, &AdrParms, &lpAdrList); if (hr) { if (MAPI_E_USER_CANCEL == hr) ulRet = MAPI_E_USER_ABORT; else if (MAPI_E_NO_RECIPIENTS == hr || MAPI_E_AMBIGUOUS_RECIP == hr) ulRet = MAPI_E_INVALID_RECIPS; goto exit; } if (nEditFields && lpAdrList && lpAdrList->cEntries) { hr = HrRgrecipFromAdrlist(lpAdrList, lppNewRecips); if (hr) goto exit; *lpnNewRecips = lpAdrList->cEntries; } ulRet = SUCCESS_SUCCESS; exit: FreePadrlist(lpAdrList); ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPIDetails // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPIDetails(LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiRecipDesc lpRecip, FLAGS flFlags, ULONG ulReserved) { ULONG ulRet = MAPI_E_FAILURE; HRESULT hr; LPSTR pszAddrType = 0; LPSTR pszAddress = 0; ULONG cbEntryID; LPENTRYID lpEntryID = 0; PSESS pSession = NULL; BOOL fLogonUI; // Validate parameters - Begin if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; if (IsBadReadPtr(lpRecip, (UINT)sizeof(MapiRecipDesc))) return MAPI_E_INVALID_RECIPS; if (lpRecip->ulEIDSize == 0 && !lpRecip->lpszAddress) return MAPI_E_INVALID_RECIPS; fLogonUI = (0 != (flFlags & MAPI_LOGON_UI)); // Services shouldn't ask for UI if (!IsProcessInteractive() && fLogonUI) return MAPI_E_LOGIN_FAILURE; // Validate parameters - End ulRet = UlGetSession(lhSession, &pSession, ulUIParam, fLogonUI, FALSE); if (SUCCESS_SUCCESS != ulRet) return ulRet; if (NULL == s_lpAddrBook) { ulRet = MAPI_E_FAILURE; goto exit; } if (lpRecip->ulEIDSize) { cbEntryID = lpRecip->ulEIDSize; lpEntryID = (LPENTRYID)lpRecip->lpEntryID; } else { ParseEmailAddress(lpRecip->lpszAddress, &pszAddrType, &pszAddress); CHECKHR(hr = s_lpAddrBook->CreateOneOff(lpRecip->lpszName, pszAddrType, pszAddress, 0, &cbEntryID, &lpEntryID)); } CHECKHR(hr = s_lpAddrBook->Details(&ulUIParam, NULL, NULL, cbEntryID, lpEntryID, NULL, NULL, NULL, DIALOG_MODAL)); ulRet = SUCCESS_SUCCESS; exit: if (pszAddrType) MemFree(pszAddrType); if (pszAddress) MemFree(pszAddress); if (lpEntryID && lpEntryID != lpRecip->lpEntryID && NULL != s_lpWabObject) s_lpWabObject->FreeBuffer(lpEntryID); ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPIResolveName // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPIResolveName(LHANDLE lhSession, ULONG_PTR ulUIParam, LPSTR lpszName, FLAGS flFlags, ULONG ulReserved, lpMapiRecipDesc FAR *lppRecip) { ULONG ulRet = SUCCESS_SUCCESS, ulNew; LPADRLIST lpAdrList = 0; HRESULT hr; LPADRENTRY lpAdrEntry; PSESS pSession = NULL; BOOL fLogonUI; // Validate parameters - Begin if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; /* HACK: #68119 Excel doesn't pass in a parent. This isn't the best thing to do, but this handle shouldn't be 0. The only thing to watch out for is fast actions while this processing is happening could make this dialog modal to the wrong window, but that us much more unlikely than the bug this fixes. */ if(!ulUIParam) ulUIParam = (ULONG_PTR)GetForegroundWindow(); if (!lpszName || IsBadStringPtr(lpszName, (UINT)0xFFFF) || !*lpszName) return MAPI_E_FAILURE; if (IsBadWritePtr(lppRecip, (UINT)sizeof(lpMapiRecipDesc))) return MAPI_E_FAILURE; fLogonUI = (0 != (flFlags & MAPI_LOGON_UI)); // Services shouldn't ask for UI if (!IsProcessInteractive() && fLogonUI) return MAPI_E_LOGIN_FAILURE; // Validate parameters - End *lppRecip = NULL; ulRet = UlGetSession(lhSession, &pSession, ulUIParam, fLogonUI, FALSE); if (SUCCESS_SUCCESS != ulRet) return ulRet; // Allocate memory for lpAdrList // Determine number of bytes needed ulNew = sizeof(ADRLIST) + sizeof(ADRENTRY); // Allocate new buffer if (NULL == s_lpWabObject) { ulRet = MAPI_E_FAILURE; goto exit; } hr = s_lpWabObject->AllocateBuffer(ulNew, (LPVOID *)&lpAdrList); if (hr) goto exit; lpAdrList->cEntries = 1; lpAdrEntry = lpAdrList->aEntries; // Allocate memory for SPropValue hr = s_lpWabObject->AllocateBuffer(sizeof(SPropValue), (LPVOID *)&lpAdrEntry->rgPropVals); if (hr) goto exit; lpAdrEntry->cValues = 1; lpAdrEntry->rgPropVals[0].ulPropTag = PR_DISPLAY_NAME; ULONG cchName = lstrlen(lpszName) + 1; hr = s_lpWabObject->AllocateMore(cchName, lpAdrEntry->rgPropVals, (LPVOID*)(&(lpAdrEntry->rgPropVals[0].Value.lpszA))); if (FAILED (hr)) goto exit; // Fill in the name StrCpyN(lpAdrEntry->rgPropVals[0].Value.lpszA, lpszName, cchName); // Call ResolveName of IAddrBook if (NULL == s_lpAddrBook) { ulRet = MAPI_E_FAILURE; goto exit; } hr = s_lpAddrBook->ResolveName(ulUIParam, flFlags & MAPI_DIALOG, NULL, lpAdrList); if (hr) { if ((hr == MAPI_E_NOT_FOUND) || (hr == MAPI_E_USER_CANCEL)) ulRet = MAPI_E_UNKNOWN_RECIPIENT; else if (hr == MAPI_E_AMBIGUOUS_RECIP) ulRet = MAPI_E_AMBIGUOUS_RECIPIENT; else ulRet = MAPI_E_FAILURE; goto exit; } else if ((lpAdrList->cEntries != 1) || !lpAdrList->aEntries->cValues) { ulRet = MAPI_E_AMBIGUOUS_RECIPIENT; goto exit; } ulRet = HrRgrecipFromAdrlist(lpAdrList, lppRecip); exit: FreePadrlist(lpAdrList); ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPIFindNext // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPIFindNext(LHANDLE lhSession, ULONG_PTR ulUIParam, LPSTR lpszMessageType, LPSTR lpszSeedMessageID, FLAGS flFlags, ULONG ulReserved, LPSTR lpszMessageID) { MESSAGEINFO MsgInfo={0}; ULONG ulRet = MAPI_E_FAILURE; HRESULT hr; MESSAGEID idMessage; MESSAGEID dwMsgIdPrev; PSESS pSession = NULL; HROWSET hRowset=NULL; // Validate parameters - begin if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; if (lpszSeedMessageID && IsBadStringPtr(lpszSeedMessageID, (UINT)0xFFFF)) return MAPI_E_INVALID_MESSAGE; if (lpszSeedMessageID && (!*lpszSeedMessageID || !IsDigit(lpszSeedMessageID))) lpszSeedMessageID = NULL; if (IsBadWritePtr(lpszMessageID, 64)) return MAPI_E_INSUFFICIENT_MEMORY; // Validate parameters - end // We shouldn't need to show login UI, because the session must be valid // and a valid session would require a login ulRet = UlGetSession(lhSession, &pSession, ulUIParam, FALSE, TRUE); if (SUCCESS_SUCCESS != ulRet) return ulRet; hr = pSession->m_pfldrInbox->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset); if (FAILED(hr)) { ulRet = MAPI_E_NO_MESSAGES; goto exit; } hr = pSession->m_pfldrInbox->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL); if (FAILED(hr) || S_FALSE == hr) { ulRet = MAPI_E_NO_MESSAGES; goto exit; } if (lpszSeedMessageID) // If the seed is NULL { idMessage = (MESSAGEID)((UINT_PTR)StrToUint(lpszSeedMessageID)); while (1) { dwMsgIdPrev = MsgInfo.idMessage; pSession->m_pfldrInbox->FreeRecord(&MsgInfo); hr = pSession->m_pfldrInbox->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL); if (FAILED(hr) || S_FALSE == hr) { ulRet = MAPI_E_NO_MESSAGES; goto exit; } if (dwMsgIdPrev == idMessage) break; } } // Check for Read unread messages only flag if (flFlags & MAPI_UNREAD_ONLY) { while (ISFLAGSET(MsgInfo.dwFlags, ARF_READ)) { // Free MsgInfo pSession->m_pfldrInbox->FreeRecord(&MsgInfo); // Get the next message hr = pSession->m_pfldrInbox->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL); // Not Found if (FAILED(hr) || S_FALSE == hr) { ulRet = MAPI_E_NO_MESSAGES; goto exit; } } } wnsprintf(lpszMessageID, 64, "%lu", MsgInfo.idMessage); ulRet = SUCCESS_SUCCESS; exit: if (pSession && pSession->m_pfldrInbox) { pSession->m_pfldrInbox->CloseRowset(&hRowset); pSession->m_pfldrInbox->FreeRecord(&MsgInfo); } ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPIReadMail // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPIReadMail(LHANDLE lhSession, ULONG_PTR ulUIParam, LPSTR lpszMessageID, FLAGS flFlags, ULONG ulReserved, lpMapiMessage FAR *lppMessage) { ULONG ulRet = MAPI_E_FAILURE; HRESULT hr; lpMapiMessage rgMessage = NULL; PSESS pSession = NULL; // Validate parameters - Begin if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; if (!lpszMessageID) return MAPI_E_INVALID_MESSAGE; if (lpszMessageID && (!*lpszMessageID || !IsDigit(lpszMessageID))) return MAPI_E_INVALID_MESSAGE; if (IsBadWritePtr(lppMessage,(UINT)sizeof(lpMapiMessage))) return MAPI_E_FAILURE; // Validate parameters - End ulRet = UlGetSession(lhSession, &pSession, ulUIParam, FALSE, TRUE); if (SUCCESS_SUCCESS != ulRet) return ulRet; if (!HrReadMail(pSession->m_pfldrInbox, lpszMessageID, &rgMessage, flFlags)) goto exit; ulRet = SUCCESS_SUCCESS; *lppMessage = rgMessage; exit: if (ulRet != SUCCESS_SUCCESS) if (rgMessage) MAPIFreeBuffer(rgMessage); ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPISaveMail // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPISaveMail(LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiMessage lpMessage, FLAGS flFlags, ULONG ulReserved, LPSTR lpszMessageID) { ULONG ulRet = MAPI_E_FAILURE; HRESULT hr; IMimeMessage *pMsg = NULL; MESSAGEID msgid; PSESS pSession = NULL; HWND hwnd = (HWND)ulUIParam; BOOL fLogonUI; // Validate parameters - Begin if (ulUIParam && !IsWindow(hwnd)) hwnd = 0; if (!lpszMessageID) return MAPI_E_INVALID_MESSAGE; if (lpszMessageID && *lpszMessageID && !IsDigit(lpszMessageID)) return MAPI_E_INVALID_MESSAGE; if (IsBadReadPtr(lpMessage, (UINT)sizeof(lpMapiMessage))) return MAPI_E_FAILURE; fLogonUI = (0 != (flFlags & MAPI_LOGON_UI)); // Services shouldn't ask for UI if (!IsProcessInteractive() && fLogonUI) return MAPI_E_LOGIN_FAILURE; // Validate parameters - End ulRet = UlGetSession(lhSession, &pSession, ulUIParam, fLogonUI, TRUE); if (SUCCESS_SUCCESS != ulRet) return ulRet; #pragma prefast(suppress:11, "noise") if (*lpszMessageID) { MESSAGEIDLIST List; msgid = (MESSAGEID)((UINT_PTR)StrToUint(lpszMessageID)); List.cMsgs = 1; List.prgidMsg = &msgid; if (FAILED(hr = pSession->m_pfldrInbox->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &List, NULL, NOSTORECALLBACK))) { ulRet = MAPI_E_INVALID_MESSAGE; goto exit; } } // Fill IMimeMessage with the lpMessage structure members ulRet = HrFillMessage(&pMsg, lpMessage, NULL, FALSE, TRUE); if (ulRet) goto exit; if (FAILED(hr = HrSaveMessageInFolder(hwnd, pSession->m_pfldrInbox, pMsg, 0, &msgid, TRUE))) { ulRet = MAPI_E_FAILURE; goto exit; } ulRet = SUCCESS_SUCCESS; wnsprintf(lpszMessageID, 64, "%lu", msgid); exit: if (pMsg) pMsg->Release(); ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // MAPIDeleteMail // /////////////////////////////////////////////////////////////////////// ULONG FAR PASCAL MAPIDeleteMail(LHANDLE lhSession, ULONG_PTR ulUIParam, LPSTR lpszMessageID, FLAGS flFlags, ULONG ulReserved) { ULONG ulRet = MAPI_E_FAILURE; MESSAGEID dwMsgID; HRESULT hr; PSESS pSession = NULL; MESSAGEIDLIST List; // Validate parameters - Begin if (ulUIParam && !IsWindow((HWND)ulUIParam)) ulUIParam = 0; if (!lpszMessageID) return MAPI_E_INVALID_MESSAGE; if (!*lpszMessageID || !IsDigit(lpszMessageID)) return MAPI_E_INVALID_MESSAGE; // Validate parameters - End // This function requires a valid session that must have been // logged in at some point so login UI is not allowed ulRet = UlGetSession(lhSession, &pSession, ulUIParam, FALSE, TRUE); if (SUCCESS_SUCCESS != ulRet) return ulRet; dwMsgID = (MESSAGEID)((UINT_PTR)StrToUint(lpszMessageID)); List.cMsgs = 1; List.prgidMsg = &dwMsgID; hr = DeleteMessagesProgress((HWND)ulUIParam, pSession->m_pfldrInbox, DELETE_MESSAGE_NOPROMPT, &List); if (FAILED(hr)) goto exit; ulRet = SUCCESS_SUCCESS; exit: ReleaseSession(pSession); return ulRet; } /////////////////////////////////////////////////////////////////////// // // INTERNAL FUNCTIONS // /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// // // SMAPIAllocateBuffer // // Purpose: // Allocates a memory buffer that must be freed with MAPIFreeBuffer(). // // Arguments: // ulSize in Size, in bytes, of the buffer to be allocated. // lppv out Pointer to variable where the address of the // allocated memory will be returned. // // Returns: // sc Indicating error if any (see below) // // Errors: // MAPI_E_INSUFFICIENT_MEMORY Allocation failed. // /////////////////////////////////////////////////////////////////////// SCODE SMAPIAllocateBuffer(ULONG ulSize, LPVOID * lppv) { SCODE sc = S_OK; LPBufInternal lpBufInt; // Don't allow allocation to wrap across 32 bits, or to exceed 64K // under win16. if (ulSize > INT_SIZE(ulSize)) { DOUT("SMAPIAllocateBuffer: ulSize %ld is way too big\n", ulSize); sc = MAPI_E_INSUFFICIENT_MEMORY; goto ret; } lpBufInt = (LPBufInternal)g_pMalloc->Alloc((UINT)INT_SIZE(ulSize)); if (lpBufInt) { lpBufInt->pLink = NULL; lpBufInt->ulAllocFlags = ALLOC_WITH_ALLOC; *lppv = (LPVOID)LPBufExtFromLPBufInt(lpBufInt); } else { DOUT("SMAPIAllocateBuffer: not enough memory for %ld\n", ulSize); sc = MAPI_E_INSUFFICIENT_MEMORY; } ret: return sc; } /////////////////////////////////////////////////////////////////////// // // SMAPIAllocateMore // // Purpose: // Allocates a linked memory buffer in such a way that it can be freed // with one call to MAPIFreeBuffer (passing the buffer the client // originally allocated with SMAPIAllocateBuffer). // // Arguments: // ulSize in Size, in bytes, of the buffer to be allocated. // lpv in Pointer to a buffer allocated with SMAPIAllocateBuffer. // lppv out Pointer to variable where the address of the // allocated memory will be returned. // // Assumes: // Validates that lpBufOrig and lppv point to writable memory, // and that lpBufOrig was allocated with SMAPIAllocateBuffer. // // Returns: // sc Indicating error if any (see below) // // Side effects: // None // // Errors: // MAPI_E_INSUFFICIENT_MEMORY Allocation failed. // /////////////////////////////////////////////////////////////////////// SCODE SMAPIAllocateMore(ULONG ulSize, LPVOID lpv, LPVOID * lppv) { SCODE sc = S_OK; LPBufInternal lpBufInt; LPBufInternal lpBufOrig; lpBufOrig = LPBufIntFromLPBufExt(lpv); #ifdef DEBUG if (!FValidAllocChain(lpBufOrig)) { sc = MAPI_E_FAILURE; goto ret; } #endif // Don't allow allocation to wrap across 32 bits, or to be // greater than 64K under win16. if (ulSize > INT_SIZE(ulSize)) { DOUT("SMAPIAllocateMore: ulSize %ld is way too big\n", ulSize); sc = MAPI_E_INSUFFICIENT_MEMORY; goto ret; } // Allocate the chained block and hook it to the head of the chain. lpBufInt = (LPBufInternal)g_pMalloc->Alloc((UINT)INT_SIZE(ulSize)); if (lpBufInt) { lpBufInt->ulAllocFlags = ALLOC_WITH_ALLOC_MORE; // EnterCriticalSection(&csHeap); lpBufInt->pLink = lpBufOrig->pLink; lpBufOrig->pLink = lpBufInt; // LeaveCriticalSection(&csHeap); *lppv = (LPVOID)LPBufExtFromLPBufInt(lpBufInt); } else { DOUT("SMAPIAllocateMore: not enough memory for %ld\n", ulSize); sc = MAPI_E_INSUFFICIENT_MEMORY; } ret: return sc; } #ifdef DEBUG BOOL FValidAllocChain(LPBufInternal lpBuf) { LPBufInternal lpBufTemp; if (IsBadWritePtr(lpBuf, sizeof(BufInternal))) { TellBadBlockInt(lpBuf, "fails address check"); return FALSE; } if (GetFlags(lpBuf->ulAllocFlags) != ALLOC_WITH_ALLOC) { TellBadBlockInt(lpBuf, "has invalid flags"); return FALSE; } for (lpBufTemp = lpBuf->pLink; lpBufTemp; lpBufTemp = lpBufTemp->pLink) { if (IsBadWritePtr(lpBufTemp, sizeof(BufInternal))) { TellBadBlockInt(lpBufTemp, "(linked block) fails address check"); return FALSE; } if (GetFlags(lpBufTemp->ulAllocFlags) != ALLOC_WITH_ALLOC_MORE) { TellBadBlockInt(lpBufTemp, "(linked block) has invalid flags"); return FALSE; } } return TRUE; } #endif // DEBUG /* - HrAdrentryFromPrecip - * Purpose: * Copies data from a MapiRecipDesc structure to the property * value array on an ADRENTRY structure. * * Arguments: * precip in the input structure * padrentry out the output structure * * Returns: * HRESULT * * Errors: * MAPI_E_INVALID_RECIPS * MAPI_E_BAD_RECIPTYPE * others passed through */ HRESULT HrAdrentryFromPrecip(lpMapiRecipDesc precip, ADRENTRY *padrentry) { HRESULT hr; LPSPropValue pprop; LPSTR pszAddress = NULL; // Validate lpMapiRecipDesc, ie, ensure that if there isn't an EntryID or // an Address there had better be a Display Name, otherwise we fail // like MAPI 0 with MAPI_E_FAILURE. if ((!precip->lpszAddress || !precip->lpszAddress[0]) && (!precip->ulEIDSize || !precip->lpEntryID) && (!precip->lpszName || !precip->lpszName[0])) { hr = MAPI_E_INVALID_RECIPS; goto ret; } if (NULL == s_lpWabObject) { hr = MAPI_E_FAILURE; goto ret; } hr = s_lpWabObject->AllocateBuffer(4 * sizeof(SPropValue), (LPVOID*)&padrentry->rgPropVals); if (hr) goto ret; pprop = padrentry->rgPropVals; // Recipient type switch ((short)precip->ulRecipClass) { case MAPI_TO: case MAPI_CC: case MAPI_BCC: pprop->ulPropTag = PR_RECIPIENT_TYPE; pprop->Value.ul = precip->ulRecipClass; pprop++; break; default: hr = MAPI_E_BAD_RECIPTYPE; goto ret; } // Display Name if (precip->lpszName && *precip->lpszName) { ULONG cchName = lstrlen(precip->lpszName)+1; hr = s_lpWabObject->AllocateMore(cchName, padrentry->rgPropVals, (LPVOID*)&pprop->Value.lpszA); if (hr) goto ret; pprop->ulPropTag = PR_DISPLAY_NAME; StrCpyN(pprop->Value.lpszA, precip->lpszName, cchName); pprop++; } // Email Address if (precip->lpszAddress && *precip->lpszAddress) { ParseEmailAddress(precip->lpszAddress, NULL, &pszAddress); ULONG cchAddress = lstrlen(pszAddress)+1; hr = s_lpWabObject->AllocateMore(cchAddress, padrentry->rgPropVals, (LPVOID*)&pprop->Value.lpszA); if (hr) goto ret; pprop->ulPropTag = PR_EMAIL_ADDRESS; StrCpyN(pprop->Value.lpszA, pszAddress, cchAddress); pprop++; } // EntryID if (precip->ulEIDSize && precip->lpEntryID) { hr = s_lpWabObject->AllocateMore(precip->ulEIDSize, padrentry->rgPropVals, (LPVOID*)&pprop->Value.bin.lpb); if (hr) goto ret; pprop->ulPropTag = PR_ENTRYID; pprop->Value.bin.cb = precip->ulEIDSize; CopyMemory(pprop->Value.bin.lpb, precip->lpEntryID, precip->ulEIDSize); pprop++; } padrentry->cValues = (ULONG) (pprop - padrentry->rgPropVals); Assert(padrentry->cValues <= 4); ret: if (pszAddress) MemFree(pszAddress); if ((hr) && (NULL != s_lpWabObject)) { s_lpWabObject->FreeBuffer(padrentry->rgPropVals); padrentry->rgPropVals = NULL; } return hr; } /* - HrAdrlistFromRgrecip - * Purpose: * Copies a list of simple MAPI recipients to a list of * extended MAPI recipients. * * Arguments: * nRecips in count of recipient in input list * lpRecips in list of recipients to be converted * ppadrlist out output list * * Returns: * HRESULT * */ HRESULT HrAdrlistFromRgrecip(ULONG nRecips, lpMapiRecipDesc lpRecips, LPADRLIST *ppadrlist) { HRESULT hr; LPADRLIST padrlist = NULL; lpMapiRecipDesc precip; ULONG i; LPADRENTRY padrentry = NULL; ULONG cbAdrList = sizeof(ADRLIST) + nRecips * sizeof(ADRENTRY); *ppadrlist = NULL; if (NULL == s_lpWabObject) { hr = E_FAIL; goto exit; } hr = s_lpWabObject->AllocateBuffer(cbAdrList, (LPVOID*)&padrlist); if (hr) goto exit; ZeroMemory(padrlist, cbAdrList); // Copy each entry. // Note that the memory for each recipient's properties must // be linked so that Address() can free it using MAPIFreeBuffer. for (i = 0, padrentry = padrlist->aEntries, precip = lpRecips; i < nRecips; i++, precip++, padrentry++) { // Copy the entry. Unresolved names will not be resolved. hr = HrAdrentryFromPrecip(precip, padrentry); if (hr) goto exit; // increment count so we can effectively blow away the list if a failure // occurs. padrlist->cEntries++; } *ppadrlist = padrlist; exit: Assert( !hr || (ULONG)hr > 26 ); if (hr) FreePadrlist(padrlist); return hr; } HRESULT HrRgrecipFromAdrlist(LPADRLIST lpAdrList, lpMapiRecipDesc * lppRecips) { HRESULT hr = S_OK; lpMapiRecipDesc rgRecips = NULL; lpMapiRecipDesc pRecip; LPADRENTRY pAdrEntry; LPSPropValue pProp; ULONG ul, ulProp; if (lpAdrList && lpAdrList->cEntries) { DWORD dwSize = sizeof(MapiRecipDesc) * lpAdrList->cEntries; hr = SMAPIAllocateBuffer(dwSize, (LPVOID*)&rgRecips); if (FAILED (hr)) goto exit; ZeroMemory(rgRecips, dwSize); // Initialize the Padding for (ul = 0, pAdrEntry = lpAdrList->aEntries, pRecip = rgRecips; ulcEntries; ul++, pAdrEntry++, pRecip++) { for (ulProp = 0, pProp = pAdrEntry->rgPropVals; ulProp < pAdrEntry->cValues; ulProp++, pProp++) { ULONG cch; switch (PROP_ID(pProp->ulPropTag)) { case PROP_ID(PR_ENTRYID): hr = SMAPIAllocateMore(pProp->Value.bin.cb, rgRecips, (LPVOID*)(&(pRecip->lpEntryID))); if (FAILED (hr)) goto exit; pRecip->ulEIDSize = pProp->Value.bin.cb; CopyMemory(pRecip->lpEntryID, pProp->Value.bin.lpb, pProp->Value.bin.cb); break; case PROP_ID(PR_EMAIL_ADDRESS): cch = lstrlen(pProp->Value.lpszA)+1; hr = SMAPIAllocateMore(cch, rgRecips, (LPVOID*)(&(pRecip->lpszAddress))); if (FAILED (hr)) goto exit; StrCpyN(pRecip->lpszAddress, pProp->Value.lpszA, cch); break; case PROP_ID(PR_DISPLAY_NAME): cch = lstrlen(pProp->Value.lpszA)+1; hr = SMAPIAllocateMore(cch, rgRecips,(LPVOID*)(&(pRecip->lpszName))); if (FAILED (hr)) goto exit; StrCpyN(pRecip->lpszName, pProp->Value.lpszA, cch); break; case PROP_ID(PR_RECIPIENT_TYPE): pRecip->ulRecipClass = pProp->Value.l; break; default: break; } } } } exit: if (hr) { MAPIFreeBuffer(rgRecips); rgRecips = NULL; } *lppRecips = rgRecips; return hr; } void ParseEmailAddress(LPSTR pszEmail, LPSTR *ppszAddrType, LPSTR *ppszAddress) { CStringParser spAddress; char chToken; Assert(ppszAddress); spAddress.Init(pszEmail, lstrlen(pszEmail), 0); // Parse the address for the delimiter chToken = spAddress.ChParse(":"); if (chToken == ':') { if (ppszAddrType) *ppszAddrType = PszDup(spAddress.PszValue()); spAddress.ChParse(c_szEmpty); *ppszAddress = PszDup(spAddress.PszValue()); } else { if (ppszAddrType) *ppszAddrType = PszDup(c_szSMTP); *ppszAddress = PszDup(pszEmail); } } void FreePadrlist(LPADRLIST lpAdrList) { if ((lpAdrList) && (NULL != s_lpWabObject)) { for (ULONG ul = 0; ul < lpAdrList->cEntries; ul++) s_lpWabObject->FreeBuffer(lpAdrList->aEntries[ul].rgPropVals); s_lpWabObject->FreeBuffer(lpAdrList); } } ULONG AddMapiRecip(LPMIMEADDRESSTABLE pAddrTable, lpMapiRecipDesc lpRecip, BOOL bValidateRecips) { LPSTR pszName = NULL, pszAddress = NULL; LPSTR pszNameFree = NULL, pszAddrFree = NULL; LPADRLIST pAdrList = NULL; ULONG ulPropCount; ULONG ulRet = MAPI_E_FAILURE; HRESULT hr; if (lpRecip->ulRecipClass > 3 || lpRecip->ulRecipClass < 1) return MAPI_E_BAD_RECIPTYPE; if (lpRecip->ulEIDSize && lpRecip->lpEntryID && SUCCEEDED(HrFromIDToNameAndAddress(&pszName, &pszAddress, lpRecip->ulEIDSize, (ENTRYID*)lpRecip->lpEntryID))) { pszNameFree = pszName; pszAddrFree = pszAddress; } else if (lpRecip->lpszAddress && *lpRecip->lpszAddress) { // we have an email address ParseEmailAddress(lpRecip->lpszAddress, NULL, &pszAddress); pszAddrFree = pszAddress; if (lpRecip->lpszName && *lpRecip->lpszName) pszName = lpRecip->lpszName; else // no name, so make it the same as the address pszName = pszAddress; } else if (lpRecip->lpszName && *lpRecip->lpszName) { if (bValidateRecips) { // we have a name, but no address, so resolve it hr = HrAdrlistFromRgrecip(1, lpRecip, &pAdrList); if (FAILED(hr)) goto exit; // Call ResolveName of IAddrBook if (NULL == s_lpAddrBook) { ulRet = MAPI_E_FAILURE; goto exit; } hr = s_lpAddrBook->ResolveName(NULL, NULL, NULL, pAdrList); if (hr) { if (hr == MAPI_E_NOT_FOUND) ulRet = MAPI_E_UNKNOWN_RECIPIENT; else if (hr == MAPI_E_AMBIGUOUS_RECIP) ulRet = MAPI_E_AMBIGUOUS_RECIPIENT; else ulRet = MAPI_E_FAILURE; goto exit; } else if ((pAdrList->cEntries != 1) || !pAdrList->aEntries->cValues) { ulRet = MAPI_E_AMBIGUOUS_RECIPIENT; goto exit; } for (ulPropCount = 0; ulPropCount < pAdrList->aEntries->cValues; ulPropCount++) { switch (pAdrList->aEntries->rgPropVals[ulPropCount].ulPropTag) { case PR_EMAIL_ADDRESS: pszAddress = pAdrList->aEntries->rgPropVals[ulPropCount].Value.lpszA; break; case PR_DISPLAY_NAME: pszName = pAdrList->aEntries->rgPropVals[ulPropCount].Value.lpszA; break; default: break; } } } else pszName = lpRecip->lpszName; } else { return MAPI_E_INVALID_RECIPS; } hr = pAddrTable->Append(MapiRecipToMimeOle(lpRecip->ulRecipClass), IET_DECODED, pszName, pszAddress, NULL); if (SUCCEEDED(hr)) ulRet = SUCCESS_SUCCESS; exit: if (pszNameFree) MemFree(pszNameFree); if (pszAddrFree) MemFree(pszAddrFree); if (pAdrList) FreePadrlist(pAdrList); return ulRet; } /////////////////////////////////////////////////////////////////////////////////// // Given a MapiMessage structure, this function creates a IMimeMessage object and // fills it with the appropriate structure members // // Arguments: // pMsg out IMimeMessage pointer // ppStream out Stream pointer // lpMessage in Message structure // nc in/out NCINFO structure // // Result // BOOL - TRUE if successful FALSE if failed //////////////////////////////////////////////////////////////////////////////////// ULONG HrFillMessage(LPMIMEMESSAGE *pMsg, lpMapiMessage lpMessage, BOOL *pfWebPage, BOOL bValidateRecips, BOOL fOriginator) { BOOL bRet = FALSE; LPSTREAM pStream = NULL; LPMIMEADDRESSTABLE pAddrTable = NULL; IImnAccount *pAccount = NULL; HRESULT hr; LPSTR pszAddress; ULONG ulRet = MAPI_E_FAILURE; if (pfWebPage) *pfWebPage = FALSE; // create an empty message hr = HrCreateMessage(pMsg); if (FAILED(hr)) goto error; // set the subject on the message if (lpMessage->lpszSubject) { hr = MimeOleSetBodyPropA(*pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, lpMessage->lpszSubject); if (FAILED(hr)) goto error; } // set the body on the message if (lpMessage->lpszNoteText && *(lpMessage->lpszNoteText)) { hr = MimeOleCreateVirtualStream(&pStream); if (FAILED(hr)) goto error; hr = pStream->Write(lpMessage->lpszNoteText, lstrlen(lpMessage->lpszNoteText), NULL); if (FAILED(hr)) goto error; hr = (*pMsg)->SetTextBody(TXT_PLAIN, IET_DECODED, NULL, pStream, NULL); if (FAILED(hr)) goto error; } // ignore lpMessage->lpszMessageType // ignore lpMessage->lpszDateReceived // ignore lpMessage->lpszConversationID // ignore lpMessage->flFlags // ignore lpMessage->lpOriginator // set the recipients on the message if (lpMessage->nRecipCount || fOriginator) { ULONG ulRecipRet; hr = (*pMsg)->GetAddressTable(&pAddrTable); if (FAILED(hr)) goto error; for (ULONG i = 0; i < lpMessage->nRecipCount; i++) { ulRecipRet = AddMapiRecip(pAddrTable, &lpMessage->lpRecips[i], bValidateRecips); if (ulRecipRet != SUCCESS_SUCCESS) { ulRet = ulRecipRet; goto error; } } } // set the attachments on the message if (lpMessage->nFileCount) { // special case: no body & one .HTM file - inline the HTML if ((!lpMessage->lpszNoteText || !*(lpMessage->lpszNoteText)) && lpMessage->nFileCount == 1 && !(lpMessage->lpFiles->flFlags & MAPI_OLE) && !(lpMessage->lpFiles->flFlags & MAPI_OLE_STATIC) && FIsHTMLFile(lpMessage->lpFiles->lpszPathName)) { #if 0 DWORD dwByteOrder; DWORD cbRead; #endif Assert(NULL == pStream); hr = CreateStreamOnHFile(lpMessage->lpFiles->lpszPathName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL, &pStream); if (FAILED(hr)) goto error; #if 0 // SBAILEY: Raid-75400 - Try to detect the byte order mark in the html.... if (SUCCEEDED(pStream->Read(&dwByteOrder, sizeof(DWORD), &cbRead)) && cbRead == sizeof(DWORD)) { // Byte Order if (dwByteOrder == 0xfffe) { // Create a new stream IStream *pStmTemp=NULL; // Create it if (SUCCEEDED(MimeOleCreateVirtualStream(&pStmTemp))) { // Copy pStream into pStmTemp if (SUCCEEDED(HrCopyStream(pStream, pStmTemp, NULL))) { // Release pStream pStream->Release(); // Assume pStmTemp pStream = pStmTemp; // Don't Release pStmTemp pStmTemp = NULL; // Should already be unicode Assert(1200 == lpMessage->lpFiles->ulReserved); // Make sure ulReserved is set to 1200 lpMessage->lpFiles->ulReserved = 1200; } } // Cleanup SafeRelease(pStmTemp); } } // Rewind HrRewindStream(pStream); #endif // intl hack. If the shell is calling us, then lpFiles->ulReserved contains the codepage of // the webpage they're attaching. All other mapi clients should call us with 0 as this param. // if ulReserved is a valid CP, we'll convert to a HCHARSET and use that for the message. if (lpMessage->lpFiles->ulReserved) HrSetMsgCodePage((*pMsg), lpMessage->lpFiles->ulReserved); hr = (*pMsg)->SetTextBody(TXT_HTML, (1200 == lpMessage->lpFiles->ulReserved ? IET_UNICODE : IET_INETCSET), NULL, pStream, NULL); if (FAILED(hr)) goto error; // we're sending this as a web page if (pfWebPage) *pfWebPage = TRUE; } else { lpMapiFileDesc pFile; LPSTREAM pStreamFile; LPTSTR pszFileName; for (ULONG i = 0; i < lpMessage->nFileCount; i++) { pFile = &lpMessage->lpFiles[i]; if (pFile->lpszPathName && *(pFile->lpszPathName)) { hr = CreateStreamOnHFile(pFile->lpszPathName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL, &pStreamFile); if (FAILED(hr)) goto error; if (pFile->lpszFileName && *pFile->lpszFileName) pszFileName = pFile->lpszFileName; else pszFileName = pFile->lpszPathName; hr = (*pMsg)->AttachFile(pszFileName, pStreamFile, NULL); pStreamFile->Release(); if (FAILED(hr)) goto error; } } } } if (fOriginator) { TCHAR szDisplayName[CCHMAX_DISPLAY_NAME]; TCHAR szEmailAddress[CCHMAX_EMAIL_ADDRESS]; TCHAR szAccountName[CCHMAX_DISPLAY_NAME]; // Get the default account if (FAILED(hr = g_pAcctMan->GetDefaultAccount(ACCT_MAIL, &pAccount))) goto error; // Get Originator Display Name if (FAILED(hr = pAccount->GetPropSz(AP_SMTP_DISPLAY_NAME, szDisplayName, ARRAYSIZE(szDisplayName)))) goto error; // Get Originator Email Name if (FAILED(hr = pAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szEmailAddress, ARRAYSIZE(szEmailAddress)))) goto error; // Get the account Name if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_NAME, szAccountName, ARRAYSIZE(szAccountName)))) goto error; // Append Sender if (FAILED(hr = pAddrTable->Append(IAT_FROM, IET_DECODED, szDisplayName, szEmailAddress, NULL))) goto error; if (FAILED(hr = HrSetAccount(*pMsg, szAccountName))) goto error; } ulRet = SUCCESS_SUCCESS; // If you're not a web page (whose charset can be sniffed), set the default charset... if((NULL == pfWebPage) || (!(*pfWebPage))) { if (g_hDefaultCharsetForMail==NULL) ReadSendMailDefaultCharset(); (*pMsg)->SetCharset(g_hDefaultCharsetForMail, CSET_APPLY_ALL); } error: SafeRelease(pStream); SafeRelease(pAddrTable); SafeRelease(pAccount); return ulRet; } HRESULT AddRecipient(lpMapiMessage pMessage, lpMapiRecipDesc pRecip, ADDRESSPROPS *pAddress, ULONG ulRecipType) { HRESULT hr; ULONG cbEntryID; LPENTRYID lpEntryID = NULL; LPSTR pszAddrType = NULL; LPSTR pszAddress = NULL; ULONG cch; cch = lstrlen(pAddress->pszFriendly)+1; if (FAILED(hr = SMAPIAllocateMore(cch, pMessage, (LPVOID*)&(pRecip->lpszName)))) goto exit; StrCpyN(pRecip->lpszName, pAddress->pszFriendly, cch); cch = lstrlen(pAddress->pszEmail)+1; if (FAILED(hr = SMAPIAllocateMore(cch, pMessage, (LPVOID*)&(pRecip->lpszAddress)))) goto exit; StrCpyN(pRecip->lpszAddress, pAddress->pszEmail, cch); pRecip->ulReserved = 0; pRecip->ulRecipClass = ulRecipType; ParseEmailAddress(pRecip->lpszAddress, &pszAddrType, &pszAddress); if (NULL == s_lpAddrBook) { hr = E_FAIL; goto exit; } if (FAILED(hr = s_lpAddrBook->CreateOneOff(pRecip->lpszName, pszAddrType, pszAddress, 0, &cbEntryID, &lpEntryID))) goto exit; if (FAILED(hr = SMAPIAllocateMore(cbEntryID, pMessage, (LPVOID*)&(pRecip->lpEntryID)))) goto exit; pRecip->ulEIDSize = cbEntryID; CopyMemory(pRecip->lpEntryID, lpEntryID, cbEntryID); exit: if ((lpEntryID) && (NULL != s_lpWabObject)) s_lpWabObject->FreeBuffer(lpEntryID); if (pszAddrType) MemFree(pszAddrType); if (pszAddress) MemFree(pszAddress); return hr; } HRESULT AddRecipientType(lpMapiMessage pMessage, LPMIMEADDRESSTABLE pAddrTable, DWORD dwAdrType, ULONG ulRecipType) { IMimeEnumAddressTypes *pEnum; ADDRESSPROPS rAddress; HRESULT hr = S_OK; if (FAILED(hr = pAddrTable->EnumTypes(dwAdrType, IAP_FRIENDLY|IAP_EMAIL, &pEnum))) return MAPI_E_FAILURE; while (S_OK == pEnum->Next(1, &rAddress, NULL)) { if (SUCCEEDED(hr = AddRecipient(pMessage, &pMessage->lpRecips[pMessage->nRecipCount], &rAddress, ulRecipType))) pMessage->nRecipCount++; g_pMoleAlloc->FreeAddressProps(&rAddress); } pEnum->Release(); return hr; } HRESULT AddOriginator(lpMapiMessage pMessage, LPMIMEADDRESSTABLE pAddrTable) { IMimeEnumAddressTypes *pEnum; ADDRESSPROPS rAddress; HRESULT hr = S_OK; if (FAILED(hr = pAddrTable->EnumTypes(IAT_FROM, IAP_FRIENDLY|IAP_EMAIL, &pEnum))) return MAPI_E_FAILURE; if (S_OK == pEnum->Next(1, &rAddress, NULL)) { hr = AddRecipient(pMessage, pMessage->lpOriginator, &rAddress, MAPI_ORIG); g_pMoleAlloc->FreeAddressProps(&rAddress); } else { if (SUCCEEDED(hr = SMAPIAllocateMore(1, pMessage, (LPVOID*)&(pMessage->lpOriginator->lpszName)))) { pMessage->lpOriginator->lpszAddress = pMessage->lpOriginator->lpszName; *pMessage->lpOriginator->lpszName = 0; } } pEnum->Release(); return hr; } /////////////////////////////////////////////////////////////////////////////////// // Called by MAPIReadMail to Read an existing message in the store // and copy in a MapiMessage structure // // Arguments: // pFolder in pointer to IMessageFolder // lpszMessageID in Message ID // lppMessage out pointer to lpMapiMessage structure/ // // Result // BOOL - TRUE if successful FALSE if failed //////////////////////////////////////////////////////////////////////////////////// BOOL HrReadMail(IMessageFolder *pFolder, LPSTR lpszMessageID, lpMapiMessage FAR *lppMessage, FLAGS flFlags) { ULONG nRecipCount=0, ulCount=0, nBody=0; HRESULT hr; MESSAGEID dwMsgID; MESSAGEINFO MsgInfo={0}; IStream *pStream = NULL; IStream *pStreamHTML = NULL; IMimeMessage *pMsg = NULL; LPMIMEADDRESSTABLE pAddrTable = NULL; LPSTR pszTemp = 0; lpMapiMessage rgMessage; ULONG ulSize,ulRead; PROPVARIANT rVariant; FILETIME localfiletime; SYSTEMTIME systemtime; ULONG cAttach=0; LPHBODY rghAttach = 0; lpMapiFileDesc rgFiles = NULL; BOOL bRet=FALSE; dwMsgID = (MESSAGEID)((UINT_PTR)StrToUint(lpszMessageID)); MsgInfo.idMessage = dwMsgID; if (FAILED(pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &MsgInfo, NULL))) goto exit; if (FAILED(hr = pFolder->OpenMessage(dwMsgID, OPEN_MESSAGE_SECURE, &pMsg, NOSTORECALLBACK))) goto exit; // Allocate memory for rgMessage if (FAILED(hr = SMAPIAllocateBuffer(sizeof(MapiMessage), (LPVOID*)&rgMessage))) goto exit; ZeroMemory(rgMessage, sizeof(MapiMessage)); // Get the subject if (SUCCEEDED(hr = MimeOleGetBodyPropA(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &pszTemp))) { ULONG cchTemp = lstrlen(pszTemp)+1; if (FAILED(hr = SMAPIAllocateMore(cchTemp, rgMessage, (LPVOID*)&(rgMessage->lpszSubject)))) goto exit; StrCpyN(rgMessage->lpszSubject, pszTemp, cchTemp); SafeMimeOleFree(pszTemp); } // Get the body text if (!(flFlags & MAPI_ENVELOPE_ONLY)) { if (FAILED(hr = pMsg->GetTextBody(TXT_PLAIN, IET_DECODED, &pStream, NULL))) { if (SUCCEEDED(hr = pMsg->GetTextBody(TXT_HTML, IET_INETCSET, &pStreamHTML, NULL))) { if (FAILED(hr = HrConvertHTMLToPlainText(pStreamHTML, &pStream, CF_TEXT))) goto exit; } } if (pStream) { if (FAILED(hr = HrGetStreamSize(pStream, &ulSize))) goto exit; if (FAILED(hr = HrRewindStream(pStream))) goto exit; if (ulSize>0) { if (FAILED(hr = SMAPIAllocateMore(ulSize + 1, rgMessage, (LPVOID*)&(rgMessage->lpszNoteText)))) goto exit; if (FAILED(hr = pStream->Read((LPVOID)rgMessage->lpszNoteText, ulSize, &ulRead))) goto exit; rgMessage->lpszNoteText[ulRead] = 0; } } } else { // if we don't call GetTextBody, then the GetAttachments call will consider the bodies as attachments if (FAILED(pMsg->GetTextBody(TXT_PLAIN, IET_DECODED, NULL, NULL))) pMsg->GetTextBody(TXT_HTML, IET_INETCSET, NULL, NULL); } // Set the message date / Received Time... rVariant.vt = VT_FILETIME; if (SUCCEEDED(hr = pMsg->GetProp(PIDTOSTR(PID_ATT_RECVTIME),0,&rVariant))) { if (!FileTimeToLocalFileTime(&rVariant.filetime, &localfiletime)) goto exit; if (!FileTimeToSystemTime(&localfiletime, &systemtime)) goto exit; if (FAILED(hr = SMAPIAllocateMore(20, rgMessage, (LPVOID*)&(rgMessage->lpszDateReceived)))) goto exit; wnsprintf(rgMessage->lpszDateReceived,20,"%04.4d/%02.2d/%02.2d %02.2d:%02.2d", systemtime.wYear, systemtime.wMonth, systemtime.wDay, systemtime.wHour, systemtime.wMinute); } // Set the flags if (ISFLAGSET(MsgInfo.dwFlags, ARF_READ)) rgMessage->flFlags = 0; else rgMessage->flFlags = MAPI_UNREAD; // Get Address Table CHECKHR(hr = pMsg->GetAddressTable(&pAddrTable)); if (FAILED(hr = SMAPIAllocateMore(sizeof(MapiRecipDesc), rgMessage, (LPVOID*)&(rgMessage->lpOriginator)))) goto exit; ZeroMemory(rgMessage->lpOriginator, sizeof(MapiRecipDesc)); if (FAILED(AddOriginator(rgMessage, pAddrTable))) goto exit; // Allocate space for the recipients if (FAILED(pAddrTable->CountTypes(IAT_RECIPS, &nRecipCount))) goto exit; if (nRecipCount) { if (FAILED(hr = SMAPIAllocateMore(nRecipCount * sizeof(MapiRecipDesc), rgMessage, (LPVOID*)&(rgMessage->lpRecips)))) goto exit; ZeroMemory(rgMessage->lpRecips, nRecipCount * sizeof(MapiRecipDesc)); // Add the To if (FAILED(AddRecipientType(rgMessage, pAddrTable, IAT_TO, MAPI_TO))) goto exit; // Add the Cc if (FAILED(AddRecipientType(rgMessage, pAddrTable, IAT_CC, MAPI_CC))) goto exit; // Add the Bcc if (FAILED(AddRecipientType(rgMessage, pAddrTable, IAT_BCC, MAPI_BCC))) goto exit; } // Fill the lpFiles structure if (FAILED(hr = pMsg->GetAttachments(&cAttach, &rghAttach))) goto exit; if (!(flFlags & (MAPI_SUPPRESS_ATTACH|MAPI_ENVELOPE_ONLY))) { if (flFlags & MAPI_BODY_AS_FILE) nBody = 1; if (cAttach + nBody) { if (FAILED(hr = SMAPIAllocateMore((cAttach + nBody) * sizeof(MapiFileDesc), rgMessage, (LPVOID*)&rgFiles))) goto exit; } // Check if MAPI_BODY_AS_FILE is set in flFlags if (flFlags & MAPI_BODY_AS_FILE) { TCHAR lpszPath[MAX_PATH]; // Create a temporary file if (!FBuildTempPath ("msoenote.txt", lpszPath, MAX_PATH, FALSE)) goto exit; if FAILED(hr = WriteStreamToFile(pStream, lpszPath, CREATE_ALWAYS, GENERIC_WRITE)) goto exit; // Reset the body back to NULL if (rgMessage->lpszNoteText) rgMessage->lpszNoteText[0] = '\0'; // Make this file as the first attachment ULONG cchPath = lstrlen(lpszPath) + 1; if (FAILED(hr = SMAPIAllocateMore(cchPath, rgMessage, (LPVOID*)&(rgFiles[0].lpszFileName)))) goto exit; if (FAILED(hr = SMAPIAllocateMore(cchPath, rgMessage, (LPVOID*)&(rgFiles[0].lpszPathName)))) goto exit; StrCpyN(rgFiles[0].lpszPathName, lpszPath, cchPath); StrCpyN(rgFiles[0].lpszFileName, lpszPath, cchPath); rgFiles[0].ulReserved = 0; rgFiles[0].flFlags = 0; rgFiles[0].nPosition = 0; rgFiles[0].lpFileType = NULL; } for (ulCount = 0; ulCount < cAttach; ulCount++) { LPMIMEBODY pBody=0; TCHAR lpszPath[MAX_PATH]; LPTSTR lpszFileName; // The file doesn't contain anything. Fill the file // from the stream if (FAILED(hr = MimeOleGetBodyPropA(pMsg, rghAttach[ulCount], PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &lpszFileName))) goto exit; if (!FBuildTempPath (lpszFileName, lpszPath, MAX_PATH, FALSE)) goto exit; SafeMimeOleFree(lpszFileName); hr=pMsg->BindToObject(rghAttach[ulCount], IID_IMimeBody, (LPVOID *)&pBody); if (FAILED(hr)) goto exit; hr=pBody->SaveToFile(IET_INETCSET, lpszPath); if (FAILED(hr)) goto exit; ULONG cchPath = lstrlen(lpszPath) + 1; if (FAILED(hr = SMAPIAllocateMore(cchPath, rgMessage, (LPVOID*)&rgFiles[ulCount+nBody].lpszPathName))) goto exit; if (FAILED(hr = SMAPIAllocateMore(cchPath, rgMessage, (LPVOID*)&rgFiles[ulCount+nBody].lpszFileName))) goto exit; StrCpyN(rgFiles[ulCount+nBody].lpszPathName, lpszPath, cchPath); StrCpyN(rgFiles[ulCount+nBody].lpszFileName, lpszPath, cchPath); rgFiles[ulCount+nBody].ulReserved = 0; rgFiles[ulCount+nBody].flFlags = 0; rgFiles[ulCount+nBody].nPosition = 0; rgFiles[ulCount+nBody].lpFileType = NULL; ReleaseObj(pBody); } } else { // else condition added in response to bug# 2716 (v-snatar) if ((flFlags & MAPI_SUPPRESS_ATTACH) && !(flFlags & MAPI_ENVELOPE_ONLY)) { if (cAttach) { // Allocate memory for rgFiles if (FAILED(hr = SMAPIAllocateMore(cAttach * sizeof(MapiFileDesc), rgMessage, (LPVOID*)&rgFiles))) goto exit; // This is important as we don't fill any other structure // member apart from lpszFileName ZeroMemory((LPVOID)rgFiles,cAttach * sizeof(MapiFileDesc)); for (ulCount = 0; ulCount < cAttach; ulCount++) { LPTSTR lpszFileName; if (FAILED(hr = MimeOleGetBodyPropA(pMsg, rghAttach[ulCount], PIDTOSTR(PID_ATT_GENFNAME), NOFLAGS, &lpszFileName))) goto exit; // Allocate memory for the filename ULONG cchFileName = lstrlen(lpszFileName)+1; if (FAILED(hr = SMAPIAllocateMore(cchFileName, rgMessage, (LPVOID*)&rgFiles[ulCount].lpszFileName))) goto exit; StrCpyN(rgFiles[ulCount].lpszFileName, lpszFileName, cchFileName); SafeMimeOleFree(lpszFileName); } } } } // Set other parameters of MapiMessage rgMessage->ulReserved = 0; rgMessage->lpszMessageType = NULL; rgMessage->lpszConversationID = NULL; rgMessage->lpFiles = rgFiles; rgMessage->nFileCount = rgFiles ? cAttach + nBody : 0; bRet = TRUE; *lppMessage = rgMessage; // Mark the message as Read if (!(flFlags & MAPI_PEEK)) { MESSAGEIDLIST List; ADJUSTFLAGS Flags; List.cMsgs = 1; List.prgidMsg = &MsgInfo.idMessage; Flags.dwAdd = ARF_READ; Flags.dwRemove = 0; pFolder->SetMessageFlags(&List, &Flags, NULL, NOSTORECALLBACK); } exit: SafeRelease(pStreamHTML); if (pFolder) pFolder->FreeRecord(&MsgInfo); SafeMimeOleFree(rghAttach); if (FAILED(hr)) SafeRelease((pMsg)); if (pMsg) pMsg->Release(); if (pAddrTable) pAddrTable->Release(); return bRet; } /////////////////////////////////////////////////////////////////////////////////// // This function is used to check if the parameters in the MapiMessage structure // are sufficient to Send a mail using the structure details. // // Parameters: // lpMessage in pointer to MapiMessage structure/ // // Result // BOOL - 0 if successful or appropriate error message /////////////////////////////////////////////////////////////////////////////////// ULONG HrValidateMessage(lpMapiMessage lpMessage) { ULONG ulCount=0; if (lpMessage->lpszSubject && IsBadStringPtr(lpMessage->lpszSubject, (UINT)0xFFFF)) return MAPI_E_FAILURE; if (lpMessage->lpszNoteText && IsBadStringPtr(lpMessage->lpszNoteText, (UINT)0xFFFF)) return MAPI_E_FAILURE; if (lpMessage->nFileCount > 0) { for (ulCount=0; ulCountnFileCount; ulCount++) { if (!lpMessage->lpFiles[ulCount].lpszPathName) return MAPI_E_FAILURE; } } return SUCCESS_SUCCESS; } BOOL HrSMAPISend(HWND hWnd, IMimeMessage *pMsg) { BOOL bRet = FALSE; HRESULT hr; ISpoolerEngine *pSpooler = NULL; BOOL fSendImmediate = FALSE; if (!g_pSpooler) { if (FAILED(hr = CreateThreadedSpooler(NULL, &pSpooler, TRUE))) goto error; g_pSpooler = pSpooler; } fSendImmediate = DwGetOption(OPT_SENDIMMEDIATE); if (FAILED(hr = HrSendMailToOutBox(hWnd, pMsg, fSendImmediate, TRUE))) goto error; if (pSpooler) { CloseThreadedSpooler(pSpooler); pSpooler = NULL; g_pSpooler = NULL; } bRet = TRUE; error: if (pSpooler) pSpooler->Release(); return bRet; } HRESULT HrFromIDToNameAndAddress(LPTSTR *ppszLocalName, LPTSTR *ppszLocalAddress, ULONG cbEID, LPENTRYID lpEID) { ULONG ulObjType = 0, ulcValues; IMailUser *lpMailUser = NULL; HRESULT hr = NOERROR; SizedSPropTagArray(2, ptaEid) = { 2, {PR_DISPLAY_NAME, PR_EMAIL_ADDRESS} }; SPropValue *spValue = NULL; // Validate the parameters - Begin if (0 == cbEID || NULL == lpEID || ppszLocalName == NULL || ppszLocalAddress == NULL) return E_INVALIDARG; *ppszLocalName = NULL; *ppszLocalAddress = NULL; // Validate the parameters - End if (NULL == s_lpAddrBook) { hr = MAPI_E_FAILURE; goto error; } if FAILED(hr = s_lpAddrBook->OpenEntry(cbEID, (LPENTRYID)lpEID, NULL, 0, &ulObjType, (LPUNKNOWN *)&lpMailUser)) goto error; if FAILED(hr = lpMailUser->GetProps((LPSPropTagArray)&ptaEid, NULL, &ulcValues, &spValue)) goto error; if (ulcValues != 2) { hr = MAPI_E_FAILURE; goto error; } if (spValue[0].ulPropTag == PR_DISPLAY_NAME) { ULONG cchLocalName = lstrlen(spValue[0].Value.lpszA) + 1; if (!MemAlloc((LPVOID*)ppszLocalName, cchLocalName)) goto error; StrCpyN(*ppszLocalName, spValue[0].Value.lpszA, cchLocalName); } if (spValue[1].ulPropTag == PR_EMAIL_ADDRESS) { ULONG cchLocalAddress = lstrlen(spValue[1].Value.lpszA) + 1; if (!MemAlloc((LPVOID*)ppszLocalAddress, cchLocalAddress)) goto error; StrCpyN(*ppszLocalAddress, spValue[1].Value.lpszA, cchLocalAddress); } hr = NOERROR; error: ReleaseObj(lpMailUser); if ((spValue) && (NULL != s_lpWabObject)) s_lpWabObject->FreeBuffer(spValue); return hr; } // Fix for Bug #62129 (v-snatar) HRESULT HrSendAndRecv() { HRESULT hr=E_FAIL; ISpoolerEngine *pSpooler = NULL; if (!g_pSpooler) { if (FAILED(hr = CreateThreadedSpooler(NULL, &pSpooler, TRUE))) goto error; g_pSpooler = pSpooler; } g_pSpooler->StartDelivery(NULL, NULL, FOLDERID_INVALID, DELIVER_MAIL_RECV | DELIVER_NOUI ); WaitForSingleObject(hSmapiEvent, INFINITE); if (pSpooler) { CloseThreadedSpooler(pSpooler); pSpooler = NULL; g_pSpooler = NULL; } error: if (pSpooler) pSpooler->Release(); return hr; } /* This code was taken from OLK2000's RTM source code Debug code was removed, the allocator was switched over to g_pMalloc, the name was changed from IsServiceAnExe, and the static s_isState was added. */ BOOL WINAPI IsProcessInteractive( VOID ) { static INTERACTIVESTATE s_isState = IS_UNINIT; HANDLE hProcessToken = NULL; HANDLE hThreadToken = NULL; DWORD groupLength = 50; DWORD dw; PTOKEN_GROUPS groupInfo = NULL; SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; PSID InteractiveSid = NULL; PSID ServiceSid = NULL; DWORD i; BOOL fServiceSID = FALSE; BOOL fInteractiveSID = FALSE; // Start with assumption that process is a Service, not an EXE. // This is the conservative assumption. If there's an error, we // have to return an answer based on incomplete information. The // consequences are less grave if we assume we're in a service: // an interactive app might fail instead of putting up UI, but if // a service mistakenly tries to put up UI it will hang. BOOL fExe = FALSE; // Bail out early if we have already been here if (s_isState != IS_UNINIT) { return (IS_INTERACTIVE == s_isState); } // If we're not running on NT, the high bit of the version is set. // In this case, it's always an EXE. DWORD dwVersion = GetVersion(); if (dwVersion >= 0x80000000) { fExe = TRUE; goto ret; } // The information we need is on the process token. // If we're impersonating, we probably won't be able to open the process token. // Revert now; we'll re-impersonate when we're done. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hThreadToken)) { RevertToSelf(); } else { dw = GetLastError(); if (dw != ERROR_NO_TOKEN) { goto ret; } } // Now open the process token. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken)) { goto ret; } groupInfo = (PTOKEN_GROUPS)g_pMalloc->Alloc(groupLength); if (groupInfo == NULL) goto ret; if (!GetTokenInformation(hProcessToken, TokenGroups, groupInfo, groupLength, &groupLength)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { goto ret; } g_pMalloc->Free(groupInfo); groupInfo = NULL; groupInfo = (PTOKEN_GROUPS)g_pMalloc->Alloc(groupLength); if (groupInfo == NULL) goto ret; if (!GetTokenInformation(hProcessToken, TokenGroups, groupInfo, groupLength, &groupLength)) { goto ret; } } // // We now know the groups associated with this token. We want to look to see if // the interactive group is active in the token, and if so, we know that // this is an interactive process. // // We also look for the "service" SID, and if it's present, we know we're a service. // // The service SID will be present iff the service is running in a // user account (and was invoked by the service controller). // if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &InteractiveSid)) { goto ret; } if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &ServiceSid)) { goto ret; } for (i = 0; i < groupInfo->GroupCount ; i += 1) { SID_AND_ATTRIBUTES sanda = groupInfo->Groups[i]; PSID Sid = sanda.Sid; // // Check to see if the group we're looking at is one of // the 2 groups we're interested in. // We should never see both groups. // if (EqualSid(Sid, InteractiveSid)) { // // This process has the Interactive SID in its // token. This means that the process is running as // an EXE. // fInteractiveSID = TRUE; break; } else if (EqualSid(Sid, ServiceSid)) { // // This process has the Service SID in its // token. This means that the process is running as // a service running in a user account. // fServiceSID = TRUE; break; } } /////// Truth table /////// // // 1. fServiceSID && !fInteractiveSID // This process has the Service SID in its token. // This means that the process is running as a service running in // a user account. // // 2. !fServiceSID && fInteractiveSID // This process has the Interactive SID in its token. // This means that the process is running as an EXE. // // 3. !fServiceSID && !fInteractiveSID // Neither Interactive or Service was present in the current users token, // This implies that the process is running as a service, most likely // running as LocalSystem. // // 4. fServiceSID && fInteractiveSID // This shouldn't happen. // if (fServiceSID) { if (fInteractiveSID) { AssertSz(FALSE, "IsServiceAnExe: fServiceSID && fInteractiveSID - wha?"); } fExe = FALSE; } else if (fInteractiveSID) { fExe = TRUE; } else // !fServiceSID && !fInteractiveSID { fExe = FALSE; } ret: if (InteractiveSid) FreeSid(InteractiveSid); if (ServiceSid) FreeSid(ServiceSid); if (groupInfo) g_pMalloc->Free(groupInfo); if (hThreadToken) { if (!ImpersonateLoggedOnUser(hThreadToken)) { AssertSz(FALSE, "ImpersonateLoggedOnUser failed!"); } CloseHandle(hThreadToken); } if (hProcessToken) CloseHandle(hProcessToken); // Avoid the overhead for future calls s_isState = (fExe) ? IS_INTERACTIVE : IS_NOTINTERACTIVE; return(fExe); } const static HELPMAP g_rgCtxVirus[] = { {IDC_TO_TEXT, IDH_MAIL_VIRUS_TO}, {IDC_SUBJECT_TEXT, IDH_MAIL_VIRUS_SUBJECT}, {IDOK, IDH_MAIL_VIRUS_SEND}, {IDCANCEL, IDH_MAIL_VIRUS_DONT_SEND}, {idcStatic1, IDH_NEWS_COMM_GROUPBOX}, {idcStatic2, IDH_NEWS_COMM_GROUPBOX}, {idcStatic3, IDH_NEWS_COMM_GROUPBOX}, {0, 0} }; INT_PTR CALLBACK WarnSendMailDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { lpMapiMessage lpMessage = (lpMapiMessage)lParam; if (lpMessage) { if (lpMessage->lpszSubject) SetDlgItemText(hwnd, IDC_SUBJECT_TEXT, lpMessage->lpszSubject); if (lpMessage->nRecipCount) { TCHAR *szTo = NULL; int cTo = MAX_PATH; int cch = 0; if (MemAlloc((void**)&szTo, cTo*sizeof(TCHAR))) { szTo[0] = (TCHAR)0; for (ULONG i = 0; i < lpMessage->nRecipCount; i++) { int cLen = lstrlen(lpMessage->lpRecips[i].lpszAddress); if ((cch + cLen + 1) > cTo) { cTo += cLen + MAX_PATH; if (!MemRealloc((void **)&szTo, cTo*sizeof(TCHAR))) break; } if (i > 0) { StrCatBuff(szTo, ";", cTo); cch++; } StrCatBuff(szTo, lpMessage->lpRecips[i].lpszAddress, cTo); cch += cLen; } SetDlgItemText(hwnd, IDC_TO_TEXT, szTo); MemFree(szTo); } } } SetFocus(GetDlgItem(hwnd, IDOK)); CenterDialog(hwnd); return(FALSE); } case WM_HELP: case WM_CONTEXTMENU: return OnContextHelp(hwnd, msg, wParam, lParam, g_rgCtxVirus); case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: // fall through... case IDCANCEL: EndDialog(hwnd, LOWORD(wParam)); return(TRUE); } break; // wm_command } // message switch return(FALSE); }