// -------------------------------------------------------------------------------- // Smtptask.cpp // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved // Steven J. Bailey // -------------------------------------------------------------------------------- #include "pch.hxx" #include "smtptask.h" #include "mimeutil.h" #include "goptions.h" #include "strconst.h" #include "xputil.h" #include "resource.h" #include "shlwapip.h" #include "spoolui.h" #include "thormsgs.h" #include "flagconv.h" #include "ourguid.h" #include "msgfldr.h" #include "storecb.h" #include "demand.h" #include "taskutil.h" // -------------------------------------------------------------------------------- // Data Types // -------------------------------------------------------------------------------- typedef enum tagSMTPEVENTTYPE { EVENT_SMTP, EVENT_IMAPUPLOAD } SMTPEVENTTYPE; // -------------------------------------------------------------------------------- // CURRENTSMTPEVENT // -------------------------------------------------------------------------------- #define CURRENTSMTPEVENT(_rTable) (&_rTable.prgEvent[_rTable.iEvent]) // -------------------------------------------------------------------------------- // CMessageIdStream::CMessageIdStream // -------------------------------------------------------------------------------- CMessageIdStream::CMessageIdStream(IStream *pStream) : m_pStream(pStream) { // Reference count m_cRef = 1; // Addref the source stream m_pStream->AddRef(); // Format the string ULONG cchPrefix = wnsprintf(m_szMessageId, ARRAYSIZE(m_szMessageId), "%s: ", STR_HDR_MESSAGEID); // Generate a message Id if (FAILED(MimeOleGenerateMID(m_szMessageId + cchPrefix, sizeof(m_szMessageId) - cchPrefix, FALSE))) { Assert(FALSE); *m_szMessageId = '\0'; m_cchMessageId = 0; } // Otherwise, fixup the message id so that \r\n else { StrCatBuff(m_szMessageId, "\r\n", ARRAYSIZE(m_szMessageId)); m_cchMessageId = lstrlen(m_szMessageId); } // Iniailize Index m_cbIndex = 0; } // -------------------------------------------------------------------------------- // CMessageIdStream::Seek // -------------------------------------------------------------------------------- STDMETHODIMP CMessageIdStream::Seek(LARGE_INTEGER liMove, DWORD dwOrigin, ULARGE_INTEGER *pulNew) { // Only supported case if (STREAM_SEEK_SET != dwOrigin && 0 != liMove.LowPart && 0 != liMove.HighPart && 0 != m_cbIndex) { Assert(FALSE); return E_NOTIMPL; } // Otheriwse, set new position else if (pulNew) { pulNew->HighPart = 0; pulNew->LowPart = 0; } // Done return S_OK; } // -------------------------------------------------------------------------------- // CMessageIdStream::Read // -------------------------------------------------------------------------------- STDMETHODIMP CMessageIdStream::Read(LPVOID pv, ULONG cbWanted, ULONG *pcbRead) { // Locals HRESULT hr=S_OK; // Reading from m_szMessageId ? if (m_cbIndex < m_cchMessageId) { // Compute amount I can read from m_cchMessageId ULONG cbReadMsgId = min(m_cchMessageId - m_cbIndex, cbWanted); // Init pcbRead if (pcbRead) *pcbRead = 0; // If we have some ? if (cbReadMsgId) { // Copy memory CopyMemory(pv, m_szMessageId + m_cbIndex, cbReadMsgId); // Increment index m_cbIndex += cbReadMsgId; } // If there is still some left to read... if (cbReadMsgId < cbWanted) hr = m_pStream->Read(((LPBYTE)pv + cbReadMsgId), cbWanted - cbReadMsgId, pcbRead); // Fixup pcbRead if (pcbRead) (*pcbRead) += cbReadMsgId; } // Otherwise, read from source stream else hr = m_pStream->Read(pv, cbWanted, pcbRead); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::CSmtpTask // -------------------------------------------------------------------------------- CSmtpTask::CSmtpTask(void) { m_cRef = 1; m_dwFlags = 0; m_pSpoolCtx = NULL; m_pAccount = NULL; m_pTransport = NULL; m_pOutbox = NULL; m_pSentItems = NULL; m_cbTotal = 0; m_cbSent = 0; m_wProgress = 0; m_idEvent = INVALID_EVENT; m_idEventUpload = INVALID_EVENT; m_pUI = NULL; m_dwState = 0; m_pAdrEnum = NULL; m_hwndTimeout = NULL; m_pLogFile = NULL; ZeroMemory(&m_rServer, sizeof(INETSERVER)); ZeroMemory(&m_rTable, sizeof(SMTPEVENTTABLE)); ZeroMemory(&m_rList, sizeof(MESSAGEIDLIST)); m_pCancel = NULL; m_tyOperation = SOT_INVALID; InitializeCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CSmtpTask::~CSmtpTask // -------------------------------------------------------------------------------- CSmtpTask::~CSmtpTask(void) { // Reset the Object _ResetObject(TRUE); // Kill the critical section DeleteCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CSmtpTask::_ResetObject // -------------------------------------------------------------------------------- void CSmtpTask::_ResetObject(BOOL fDeconstruct) { // Make sure the transport is disconnect if (m_pTransport) { m_pTransport->Release(); m_pTransport = NULL; } // Release the Outbox SafeRelease(m_pAccount); SafeRelease(m_pOutbox); SafeRelease(m_pSentItems); SafeRelease(m_pAdrEnum); SafeRelease(m_pSpoolCtx); SafeRelease(m_pUI); SafeRelease(m_pLogFile); SafeRelease(m_pCancel); SafeMemFree(m_rList.prgidMsg); ZeroMemory(&m_rList, sizeof(MESSAGEIDLIST)); // Free the event table elements _FreeEventTableElements(); // Deconstructing if (fDeconstruct) { // Free Event Table SafeMemFree(m_rTable.prgEvent); } // Otherwise, reset some vars else { // Reset total byte count m_cbTotal = 0; m_idEvent = INVALID_EVENT; m_cbSent = 0; m_wProgress = 0; ZeroMemory(&m_rServer, sizeof(INETSERVER)); } } // -------------------------------------------------------------------------------- // CSmtpTask::Init // -------------------------------------------------------------------------------- void CSmtpTask::_FreeEventTableElements(void) { // Loop the table of events for (ULONG i=0; iAddRef(); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::CSmtpTask // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CSmtpTask::AddRef(void) { EnterCriticalSection(&m_cs); ULONG cRef = ++m_cRef; LeaveCriticalSection(&m_cs); return cRef; } // -------------------------------------------------------------------------------- // CSmtpTask::CSmtpTask // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CSmtpTask::Release(void) { EnterCriticalSection(&m_cs); ULONG cRef = --m_cRef; LeaveCriticalSection(&m_cs); if (0 != cRef) return cRef; delete this; return 0; } // -------------------------------------------------------------------------------- // CSmtpTask::Init // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::Init(DWORD dwFlags, ISpoolerBindContext *pBindCtx) { // Invalid Arg if (NULL == pBindCtx) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Reset this object _ResetObject(FALSE); // Save the Activity Flags - DELIVER_xxx m_dwFlags = dwFlags; // Hold onto the bind context Assert(NULL == m_pSpoolCtx); m_pSpoolCtx = pBindCtx; m_pSpoolCtx->AddRef(); // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::BuildEvents // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::BuildEvents(ISpoolerUI *pSpoolerUI, IImnAccount *pAccount, FOLDERID idFolder) { // Locals HRESULT hr=S_OK; DWORD fSplitMsgs; FOLDERINFO Folder; FOLDERID id; DWORD cbMaxPart; CHAR szRes[255]; CHAR szMessage[255]; CHAR szAccount[CCHMAX_ACCOUNT_NAME]; CHAR szDefault[CCHMAX_ACCOUNT_NAME]; MESSAGEINFO MsgInfo={0}; HROWSET hRowset=NULL; // Invalid Arg if (NULL == pSpoolerUI || NULL == pAccount) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Validate State Assert(NULL == m_pTransport && NULL == m_pAccount && NULL == m_pOutbox && 0 == m_rTable.cEvents); Assert(NULL == m_pSentItems); // Save the UI Object m_pUI = pSpoolerUI; m_pUI->AddRef(); // Release current Account m_pAccount = pAccount; m_pAccount->AddRef(); // Get the Account Name CHECKHR(hr = m_pAccount->GetPropSz(AP_ACCOUNT_NAME, szAccount, ARRAYSIZE(szAccount))); // Split Messages ? if (FAILED(m_pAccount->GetPropDw(AP_SMTP_SPLIT_MESSAGES, &fSplitMsgs))) fSplitMsgs = FALSE; // Split Size if (FAILED(m_pAccount->GetPropDw(AP_SMTP_SPLIT_SIZE, &cbMaxPart))) { fSplitMsgs = FALSE; cbMaxPart = 0; } // Else, convert cbMaxPart for kilobytes to bytes else cbMaxPart *= 1024; // Get the default Account if (SUCCEEDED(g_pAcctMan->GetDefaultAccountName(ACCT_MAIL, szDefault, ARRAYSIZE(szDefault)))) { if (lstrcmpi(szDefault, szAccount) == 0) FLAGSET(m_dwState, SMTPSTATE_DEFAULT); } // Get the outbox CHECKHR(hr = m_pSpoolCtx->BindToObject(IID_CLocalStoreOutbox, (LPVOID *)&m_pOutbox)); // Create a Rowset CHECKHR(hr = m_pOutbox->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset)); // Loop while (S_OK == m_pOutbox->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL)) { // Process Store Header CHECKHR(hr = _HrAppendOutboxMessage(szAccount, &MsgInfo, fSplitMsgs, cbMaxPart)); // Free Current m_pOutbox->FreeRecord(&MsgInfo); } // If no messages, were done if (0 == m_rTable.cEvents) goto exit; // Get a SMTP log file m_pSpoolCtx->BindToObject(IID_CSmtpLogFile, (LPVOID *)&m_pLogFile); // Format the string LOADSTRING(IDS_SPS_SMTPEVENT, szRes); // Build the message wnsprintf(szMessage, ARRAYSIZE(szMessage), szRes, m_rTable.cEvents, szAccount); // Register the event CHECKHR(hr = m_pSpoolCtx->RegisterEvent(szMessage, (ISpoolerTask *)this, EVENT_SMTP, m_pAccount, &m_idEvent)); // Get SentItems if (DwGetOption(OPT_SAVESENTMSGS)) { // Get Sent Items Folder CHECKHR(hr = TaskUtil_OpenSentItemsFolder(m_pAccount, &m_pSentItems)); // Get the Folder Id m_pSentItems->GetFolderId(&id); // Get the folder information CHECKHR(hr = g_pStore->GetFolderInfo(id, &Folder)); // If not a local folder, then we will need to do an upload if (Folder.tyFolder != FOLDER_LOCAL) { // Get event string LOADSTRING(IDS_SPS_MOVEEVENT, szRes); // Format the message wnsprintf(szMessage, ARRAYSIZE(szMessage), szRes, szAccount); // Register Upload Event CHECKHR(hr = m_pSpoolCtx->RegisterEvent(szMessage, (ISpoolerTask *)this, EVENT_IMAPUPLOAD, m_pAccount, &m_idEventUpload)); } // Free the Record g_pStore->FreeRecord(&Folder); } exit: // Cleanup if (m_pOutbox) { m_pOutbox->CloseRowset(&hRowset); m_pOutbox->FreeRecord(&MsgInfo); } // Failure if (FAILED(hr)) { _CatchResult(hr, IXP_SMTP); _ResetObject(FALSE); } // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::_HrAppendEventTable // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrAppendEventTable(LPSMTPEVENTINFO *ppEvent) { // Locals HRESULT hr=S_OK; // Add an item to the event list if (m_rTable.cEvents + 1 > m_rTable.cAlloc) { // Realloc the table CHECKHR(hr = HrRealloc((LPVOID *)&m_rTable.prgEvent, sizeof(SMTPEVENTINFO) * (m_rTable.cAlloc + 10))); // Increment cAlloc m_rTable.cAlloc += 10; } // Readability *ppEvent = &m_rTable.prgEvent[m_rTable.cEvents]; // ZeroInit ZeroMemory(*ppEvent, sizeof(SMTPEVENTINFO)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::_HrAppendOutboxMessage // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrAppendOutboxMessage(LPCSTR pszAccount, LPMESSAGEINFO pMsgInfo, BOOL fSplitMsgs, DWORD cbMaxPart) { // Locals HRESULT hr=S_OK; LPSMTPEVENTINFO pEvent; IImnAccount *pAccount=NULL; // Invalid Arg Assert(pszAccount && pMsgInfo); // Has this message been submitted and is it a mail message if((pMsgInfo->dwFlags & (ARF_SUBMITTED | ARF_NEWSMSG)) != ARF_SUBMITTED) goto exit; // Empty Account Name if (NULL == pMsgInfo->pszAcctId || FAILED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pMsgInfo->pszAcctId, &pAccount))) { // Not the Default Account if (!ISFLAGSET(m_dwState, SMTPSTATE_DEFAULT) || ISFLAGSET(m_dwFlags, DELIVER_NOUI)) goto exit; // Have I asked the user if they want to use the default account if (!ISFLAGSET(m_dwState, SMTPSTATE_ASKEDDEFAULT)) { // Get hwndParent HWND hwndParent; if (FAILED(m_pUI->GetWindow(&hwndParent))) hwndParent = NULL; // Message if (AthMessageBoxW(hwndParent, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(IDS_SPS_SMTPUSEDEFAULT), NULL, MB_YESNO|MB_ICONEXCLAMATION ) == IDYES) FLAGSET(m_dwState, SMTPSTATE_USEDEFAULT); else goto exit; // I've Asked, don't ask again FLAGSET(m_dwState, SMTPSTATE_ASKEDDEFAULT); } // Not using default else if (!ISFLAGSET(m_dwState, SMTPSTATE_USEDEFAULT)) goto exit; } // Use this account else if (lstrcmpi(pMsgInfo->pszAcctName, pszAccount) != 0) goto exit; // Split the Message ? if (TRUE == fSplitMsgs && pMsgInfo->cbMessage >= cbMaxPart) { // Make sure the total number of messages is less than 100 if (100 <= (pMsgInfo->cbMessage / cbMaxPart)) { HWND hwndParent; CHAR rgchBuff[CCHMAX_STRINGRES + 20]; CHAR rgchRes[CCHMAX_STRINGRES]; DWORD cbMaxPartCount; // Figure out the new message part size cbMaxPartCount = pMsgInfo->cbMessage / 100; // Round the new message part size to the // closest power of 2 if (0x80000000 <= cbMaxPartCount) { // Can't round up any higher cbMaxPart = cbMaxPartCount; } else { cbMaxPart = 1; do { cbMaxPart *= 2; } while ( 0 != (cbMaxPartCount /= 2)); } // Get the UI window if (FAILED(m_pUI->GetWindow(&hwndParent))) hwndParent = NULL; if (NULL == AthLoadString(idsErrTooManySplitMsgs, rgchRes, sizeof(rgchRes))) { hr = E_OUTOFMEMORY; goto exit; } // Display the warning string wnsprintf(rgchBuff, ARRAYSIZE(rgchBuff), rgchRes, cbMaxPart / 1024); if (AthMessageBox(hwndParent, MAKEINTRESOURCE(idsAthenaMail), rgchBuff, NULL, MB_YESNO|MB_ICONEXCLAMATION ) != IDYES) goto exit; } // Split and Add the message CHECKHR(hr = _HrAppendSplitMessage(pMsgInfo, cbMaxPart)); } // Otherwise, simply add the message as normal else { // Append the Table CHECKHR(hr = _HrAppendEventTable(&pEvent)); // Save the store message id pEvent->idMessage = pMsgInfo->idMessage; // Save Message Size + 100 which is the average MID that is added on pEvent->cbEvent = pMsgInfo->cbMessage; // Increment total send byte count m_cbTotal += pEvent->cbEvent; // Running Sent Total pEvent->cbSentTotal = m_cbTotal; // Increment Number of event m_rTable.cEvents++; } exit: // Cleanup SafeRelease(pAccount); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::_HrAppendSplitMessage // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrAppendSplitMessage(LPMESSAGEINFO pMsgInfo, DWORD cbMaxPart) { // Locals HRESULT hr=S_OK; ULONG c; ULONG iPart=1; ULONG cParts; IMimeMessage *pMessage=NULL; IMimeMessage *pMsgPart=NULL; IMimeMessageParts *pParts=NULL; IMimeEnumMessageParts *pEnum=NULL; LPSMTPEVENTINFO pEvent; // Invalid Arg Assert(pMsgInfo); // Lets open the message from the Outbox CHECKHR(hr = _HrOpenMessage(pMsgInfo->idMessage, &pMessage)); // Split the message CHECKHR(hr = pMessage->SplitMessage(cbMaxPart, &pParts)); // Get Total Parts CHECKHR(hr = pParts->CountParts(&cParts)); // Walk the list of messages CHECKHR(hr = pParts->EnumParts(&pEnum)); // Walk the parts and add them into the event list while(SUCCEEDED(pEnum->Next(1, &pMsgPart, &c)) && 1 == c) { // Append the Table CHECKHR(hr = _HrAppendEventTable(&pEvent)); // Event Type FLAGSET(pEvent->dwFlags, SMTPEVENT_SPLITPART); // Split Information pEvent->iPart = iPart; pEvent->cParts = cParts; pEvent->cbParts = pMsgInfo->cbMessage; // Save the message part pEvent->pMessage = pMsgPart; pMsgPart = NULL; // Save Message Size pEvent->pMessage->GetMessageSize(&pEvent->cbEvent, 0); // Increment total send byte count m_cbTotal += pEvent->cbEvent; // Running Sent Total pEvent->cbSentTotal = m_cbTotal; // Increment Number of event m_rTable.cEvents++; // Increment iPart iPart++; } // Have the last split message free the header info pEvent->idMessage = pMsgInfo->idMessage; exit: // Cleanup SafeRelease(pMessage); SafeRelease(pParts); SafeRelease(pMsgPart); SafeRelease(pEnum); // Done return hr; } // ------------------------------------------------------------------------------------ // CSmtpTask::_HrOpenMessage // ------------------------------------------------------------------------------------ HRESULT CSmtpTask::_HrOpenMessage(MESSAGEID idMessage, IMimeMessage **ppMessage) { // Locals HRESULT hr=S_OK; // Check Params Assert(ppMessage && m_pOutbox); // Init *ppMessage = NULL; // Stream in message CHECKHR(hr = m_pOutbox->OpenMessage(idMessage, OPEN_MESSAGE_SECURE , ppMessage, NOSTORECALLBACK)); // remove an X-Unsent headers before sending (*ppMessage)->DeleteBodyProp(HBODY_ROOT, PIDTOSTR(PID_HDR_XUNSENT)); exit: // Failure if (FAILED(hr)) SafeRelease((*ppMessage)); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::Execute // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::Execute(EVENTID eid, DWORD_PTR dwTwinkie) { HRESULT hr = E_FAIL; // Thread Safety EnterCriticalSection(&m_cs); // What is the event type if (EVENT_SMTP == dwTwinkie) { // Better not have a transport yet... Assert(NULL == m_pTransport); // Create the Transport Object CHECKHR(hr = CreateSMTPTransport(&m_pTransport)); // Init the Transport CHECKHR(hr = m_pTransport->InitNew(NULL, (ISMTPCallback *)this)); // Fill an INETSERVER structure from the account object CHECKHR(hr = m_pTransport->InetServerFromAccount(m_pAccount, &m_rServer)); // Use IP Address for HELO command ? if (DwGetOption(OPT_SMTPUSEIPFORHELO)) FLAGSET(m_rServer.dwFlags, ISF_SMTP_USEIPFORHELO); // If this account is set to always prompt for password and password isn't already cached, show UI so we can prompt user for password if (ISFLAGSET(m_rServer.dwFlags, ISF_ALWAYSPROMPTFORPASSWORD) && FAILED(GetPassword(m_rServer.dwPort, m_rServer.szServerName, m_rServer.szUserName, NULL, 0))) m_pUI->ShowWindow(SW_SHOW); // Execute SMTP Event hr = _ExecuteSMTP(eid, dwTwinkie); } // Otherwise, do IMAP Event else if (EVENT_IMAPUPLOAD == dwTwinkie) hr = _ExecuteUpload(eid, dwTwinkie); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } STDMETHODIMP CSmtpTask::CancelEvent(EVENTID eid, DWORD_PTR dwTwinkie) { return(S_OK); } // -------------------------------------------------------------------------------- // CSmtpTask::_ExecuteSMTP // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_ExecuteSMTP(EVENTID eid, DWORD_PTR dwTwinkie) { // Locals HRESULT hr=S_OK; CHAR szRes[CCHMAX_RES]; CHAR szBuf[CCHMAX_RES + CCHMAX_SERVER_NAME]; DWORD cb; // I only handle on event Assert(m_pAccount && m_idEvent == eid && m_pUI && m_pTransport && m_rTable.cEvents > 0); // Set the animation m_pUI->SetAnimation(idanOutbox, TRUE); // Setup Progress Meter m_pUI->SetProgressRange(100); // Connecting to ... LoadString(g_hLocRes, idsInetMailConnectingHost, szRes, ARRAYSIZE(szRes)); wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, m_rServer.szAccount); m_pUI->SetGeneralProgress(szBuf); // Notify m_pSpoolCtx->Notify(DELIVERY_NOTIFY_CONNECTING, 0); // Connect CHECKHR(hr = m_pTransport->Connect(&m_rServer, TRUE, TRUE)); exit: // Failure if (FAILED(hr)) { FLAGSET(m_dwState, SMTPSTATE_EXECUTEFAILED); _CatchResult(hr, IXP_SMTP); // Hands Off my callback: otherwise we leak like a stuck pig SideAssert(m_pTransport->HandsOffCallback() == S_OK); } return hr; } // _ExecuteSMTP HRESULT CSmtpTask::_ExecuteUpload(EVENTID eid, DWORD_PTR dwTwinkie) { // Locals HRESULT hr=S_OK; ADJUSTFLAGS Flags; // I only handle on event Assert(m_pAccount && m_idEventUpload == eid && m_pUI && m_pTransport && m_rTable.cEvents > 0); // Invalid State Assert(m_pOutbox); Assert(m_pSentItems != NULL); // Are the Ids if (m_rList.cMsgs) { // Setup Flags Flags.dwAdd = ARF_READ; Flags.dwRemove = ARF_SUBMITTED | ARF_UNSENT; // Move the message from the sent items folder hr = m_pOutbox->CopyMessages(m_pSentItems, COPY_MESSAGE_MOVE, &m_rList, &Flags, NULL, this); Assert(FAILED(hr)); if (hr == E_PENDING) { hr = S_OK; } else { IXPTYPE ixpType; FLAGSET(m_dwState, SMTPSTATE_EXECUTEFAILED); // Remap the Error Result hr = TrapError(SP_E_CANT_MOVETO_SENTITEMS); // Show an error in the spooler dialog ixpType = m_pTransport->GetIXPType(); _CatchResult(hr, ixpType); } } else { hr = E_FAIL; } return hr; } // _ExecuteSMTP // -------------------------------------------------------------------------------- // CSmtpTask::OnTimeout // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnTimeout(DWORD *pdwTimeout, IInternetTransport *pTransport) { // Locals HRESULT hr=S_OK; // Thread Safety EnterCriticalSection(&m_cs); // Is there currently a timeout dialog if (m_hwndTimeout) { // Set foreground SetForegroundWindow(m_hwndTimeout); } else { // Not suppose to be showing UI ? if (ISFLAGSET(m_dwFlags, DELIVER_NOUI)) { hr = S_FALSE; goto exit; } // Do Timeout Dialog m_hwndTimeout = TaskUtil_HwndOnTimeout(m_rServer.szServerName, m_rServer.szAccount, "SMTP", m_rServer.dwTimeout, (ITimeoutCallback *) this); // Couldn't create the dialog if (NULL == m_hwndTimeout) { hr = S_FALSE; goto exit; } } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Always tell the transport to keep on trucking return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::OnLogonPrompt // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnLogonPrompt(LPINETSERVER pInetServer, IInternetTransport *pTransport) { // Locals HRESULT hr=S_FALSE; SMTPAUTHTYPE authtype; char szPassword[CCHMAX_PASSWORD]; // Check if we have a cached password that's different from current password hr = GetPassword(pInetServer->dwPort, pInetServer->szServerName, pInetServer->szUserName, szPassword, sizeof(szPassword)); if (SUCCEEDED(hr) && 0 != lstrcmp(szPassword, pInetServer->szPassword)) { StrCpyN(pInetServer->szPassword, szPassword, ARRAYSIZE(pInetServer->szPassword)); ZeroMemory(szPassword, sizeof(szPassword)); // Done for security. return S_OK; } hr = S_FALSE; // Re-initialize // Thread Safety EnterCriticalSection(&m_cs); // NOERRORS... if (ISFLAGSET(m_dwFlags, DELIVER_NOUI)) goto exit; // TaskUtil_OnLogonPrompt hr = TaskUtil_OnLogonPrompt(m_pAccount, m_pUI, NULL, pInetServer, AP_SMTP_USERNAME, AP_SMTP_PASSWORD, AP_SMTP_PROMPT_PASSWORD, FALSE); // Cache the password if the user slected ok if (S_OK == hr) { // Save the password SavePassword(pInetServer->dwPort, pInetServer->szServerName, pInetServer->szUserName, pInetServer->szPassword); // Lets switch the account to using the logon information... authtype = SMTP_AUTH_USE_SMTP_SETTINGS; m_pAccount->SetPropDw(AP_SMTP_USE_SICILY, (DWORD)authtype); // Save the changes m_pAccount->SaveChanges(); } exit: // Thread Safety LeaveCriticalSection(&m_cs); ZeroMemory(szPassword, sizeof(szPassword)); // Done for security. // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::OnPrompt // -------------------------------------------------------------------------------- STDMETHODIMP_(INT) CSmtpTask::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, IInternetTransport *pTransport) { // Locals HWND hwnd; INT nAnswer; // Thread Safety EnterCriticalSection(&m_cs); // Raid 55082 - SPOOLER: SPA/SSL auth to NNTP does not display cert warning and fails. #if 0 if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND))) return(0); #endif // Invalid State Assert(m_pUI); // Get Window if (FAILED(m_pUI->GetWindow(&hwnd))) hwnd = NULL; // I assume this is a critical prompt, so I will not check for no UI mode nAnswer = MessageBox(hwnd, pszText, pszCaption, uType); // Thread Safety LeaveCriticalSection(&m_cs); // Done return nAnswer; } // -------------------------------------------------------------------------------- // CSmtpTask::OnStatus // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnStatus(IXPSTATUS ixpstatus, IInternetTransport *pTransport) { // Locals EVENTCOMPLETEDSTATUS tyEventStatus=EVENT_SUCCEEDED; // Invalid State Assert(m_pUI && m_pSpoolCtx); if (!m_pUI || !m_pSpoolCtx) { return E_FAIL; } // Thread Safety EnterCriticalSection(&m_cs); // Feed the the IXP status to the UI object m_pUI->SetSpecificProgress(MAKEINTRESOURCE(XPUtil_StatusToString(ixpstatus))); // Disconnected if (ixpstatus == IXP_DISCONNECTED) { // Kill the timeout dialog if (m_hwndTimeout) { DestroyWindow(m_hwndTimeout); m_hwndTimeout = NULL; } // _OnDisconnectComplete HRESULT hrDisconnect = _OnDisconnectComplete(); // Reset the progress // m_pUI->SetProgressRange(100); // Set the animation m_pUI->SetAnimation(idanOutbox, FALSE); // Determine if (ISFLAGSET(m_dwState, SMTPSTATE_CANCELED)) tyEventStatus = EVENT_CANCELED; else if (m_rTable.cCompleted == 0 && m_rTable.cEvents > 0) tyEventStatus = EVENT_FAILED; else if (m_rTable.cCompleted && m_rTable.cEvents && m_rTable.cCompleted < m_rTable.cEvents) tyEventStatus = EVENT_WARNINGS; else if (FAILED(hrDisconnect)) tyEventStatus = EVENT_WARNINGS; // Result m_pSpoolCtx->Notify(DELIVERY_NOTIFY_RESULT, tyEventStatus); // Result m_pSpoolCtx->Notify(DELIVERY_NOTIFY_COMPLETE, 0); // Hands Off my callback if (m_pTransport) SideAssert(m_pTransport->HandsOffCallback() == S_OK); // This task is complete if (!ISFLAGSET(m_dwState, SMTPSTATE_EXECUTEFAILED)) m_pSpoolCtx->EventDone(m_idEvent, tyEventStatus); } // Authorizing else if (ixpstatus == IXP_AUTHORIZING) m_pSpoolCtx->Notify(DELIVERY_NOTIFY_AUTHORIZING, 0); // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::OnError // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnError(IXPSTATUS ixpstatus, LPIXPRESULT pResult, IInternetTransport *pTransport) { INETSERVER rServer; HRESULT hrResult; // Thread Safety EnterCriticalSection(&m_cs); // Invalid State Assert(m_pUI); // Insert Error Into UI if (m_pTransport) { hrResult = pTransport->GetServerInfo(&rServer); if (FAILED(hrResult)) CopyMemory(&rServer, &m_rServer, sizeof(rServer)); } _CatchResult(pResult, &rServer, IXP_SMTP); // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::OnCommand // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnCommand(CMDTYPE cmdtype, LPSTR pszLine, HRESULT hrResponse, IInternetTransport *pTransport) { // Logging if (m_pLogFile && pszLine) { // Response if (CMD_RESP == cmdtype) m_pLogFile->WriteLog(LOGFILE_RX, pszLine); // Send else if (CMD_SEND == cmdtype) m_pLogFile->WriteLog(LOGFILE_TX, pszLine); } // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::_CatchResult // -------------------------------------------------------------------------------- TASKRESULTTYPE CSmtpTask::_CatchResult(HRESULT hr, IXPTYPE ixpType) { // Locals IXPRESULT rResult; // Build an IXPRESULT ZeroMemory(&rResult, sizeof(IXPRESULT)); rResult.hrResult = hr; // Get the SMTP Result Type return _CatchResult(&rResult, &m_rServer, ixpType); } // -------------------------------------------------------------------------------- // CSmtpTask::_CatchResult // -------------------------------------------------------------------------------- TASKRESULTTYPE CSmtpTask::_CatchResult(LPIXPRESULT pResult, INETSERVER *pServer, IXPTYPE ixpType) { // Locals HWND hwndParent; TASKRESULTTYPE tyTaskResult=TASKRESULT_FAILURE; LPSTR pszSubject=NULL; // If Succeeded if (SUCCEEDED(pResult->hrResult)) return TASKRESULT_SUCCESS; // Is there is a current event, get the subject if (m_rTable.prgEvent && m_rTable.prgEvent[m_rTable.iEvent].pMessage) { // Get the subject if (FAILED(MimeOleGetBodyPropA(m_rTable.prgEvent[m_rTable.iEvent].pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &pszSubject))) pszSubject = NULL; } // Get Window if (NULL == m_pUI || FAILED(m_pUI->GetWindow(&hwndParent))) hwndParent = NULL; // Process generic protocol errro tyTaskResult = TaskUtil_FBaseTransportError(ixpType, m_idEvent, pResult, pServer, pszSubject, m_pUI, !ISFLAGSET(m_dwFlags, DELIVER_NOUI), hwndParent); // Have a Transport if (m_pTransport) { // If Task Failure, drop the connection if (TASKRESULT_FAILURE == tyTaskResult) { // Roast the current connection m_pTransport->DropConnection(); } // If Event Failure... else if (TASKRESULT_EVENTFAILED == tyTaskResult) { // Goto Next Event if (FAILED(_HrFinishCurrentEvent(pResult->hrResult))) { // Roast the current connection m_pTransport->DropConnection(); } } } // Cleanup SafeMemFree(pszSubject); // Return Result return tyTaskResult; } // -------------------------------------------------------------------------------- // CSmtpTask::_DoProgress // -------------------------------------------------------------------------------- void CSmtpTask::_DoProgress(void) { // Locals WORD wProgress; WORD wDelta; LPSMTPEVENTINFO pEvent; // Invalid Arg Assert(m_cbTotal > 0 && m_pUI); // Compute Current Progress Index wProgress = (WORD)((m_cbSent * 100) / m_cbTotal); // Compute Delta wDelta = wProgress - m_wProgress; // Progress Delta if (wDelta > 0) { // Incremenet Progress m_pUI->IncrementProgress(wDelta); // Increment my wProgress m_wProgress += wDelta; // Don't go over 100 percent Assert(m_wProgress <= 100); } } // -------------------------------------------------------------------------------- // CSmtpTask::OnResponse // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnResponse(LPSMTPRESPONSE pResponse) { // Thread Safety EnterCriticalSection(&m_cs); if (pResponse) { // Handle the Error if (TASKRESULT_SUCCESS != _CatchResult(&pResponse->rIxpResult, &m_rServer, IXP_SMTP)) goto exit; // Handle Command Type switch(pResponse->command) { case SMTP_CONNECTED: // CommandRSET _CatchResult(_HrOnConnected(), IXP_SMTP); // Done break; case SMTP_RSET: // Progress _DoProgress(); // Send the current message _CatchResult(_HrStartCurrentEvent(), IXP_SMTP); // Done break; case SMTP_MAIL: // Reset the address enumerator Assert(m_pAdrEnum); m_pAdrEnum->Reset(); // CommandRCPT _CatchResult(_HrCommandRCPT(), IXP_SMTP); // Done break; case SMTP_RCPT: // CommandRCPT -> CommandDATA _CatchResult(_HrCommandRCPT(), IXP_SMTP); // Done break; case SMTP_DATA: // Send the data stream _CatchResult(_HrSendDataStream(), IXP_SMTP); // Done break; case SMTP_SEND_STREAM: // Increment Current Progress _OnStreamProgress(&pResponse->rStreamInfo); // Done break; case SMTP_DOT: // Finish the Current Event _CatchResult(_HrFinishCurrentEvent(S_OK), IXP_SMTP); // Done break; } } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::_HrOnConnected // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrOnConnected(void) { // Locals CHAR szRes[CCHMAX_RES]; CHAR szMsg[CCHMAX_RES+CCHMAX_RES]; // Progress LOADSTRING(IDS_SPS_SMTPPROGGEN, szRes); wnsprintf(szMsg, ARRAYSIZE(szMsg), szRes, m_rServer.szAccount); // Set General Progress m_pUI->SetGeneralProgress(szMsg); // Progress _DoProgress(); // Notify m_pSpoolCtx->Notify(DELIVERY_NOTIFY_SENDING, 0); // Send the current message _CatchResult(_HrStartCurrentEvent(), IXP_SMTP); // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::_HrStartCurrentEvent // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrStartCurrentEvent(void) { // Locals HRESULT hr=S_OK; LPSMTPEVENTINFO pEvent; IMimeAddressTable *pAddrTable=NULL; CHAR szRes[CCHMAX_RES]; CHAR szMsg[CCHMAX_RES + CCHMAX_ACCOUNT_NAME]; // Invalid Arg Assert(m_rTable.iEvent < m_rTable.cEvents); // Get the current event pEvent = CURRENTSMTPEVENT(m_rTable); // Is this a partial message if (ISFLAGSET(pEvent->dwFlags, SMTPEVENT_SPLITPART)) { LOADSTRING(IDS_SPS_SMTPPROGRESS_SPLIT, szRes); wnsprintf(szMsg, ARRAYSIZE(szMsg), szRes, m_rTable.iEvent + 1, m_rTable.cEvents, pEvent->iPart, pEvent->cParts); } // Otherwise else { LOADSTRING(IDS_SPS_SMTPPROGRESS, szRes); wnsprintf(szMsg, ARRAYSIZE(szMsg), szRes, m_rTable.iEvent + 1, m_rTable.cEvents); } // Set Specific Progress m_pUI->SetSpecificProgress(szMsg); // If mail is coming from the outbox if (!ISFLAGSET(pEvent->dwFlags, SMTPEVENT_SPLITPART)) { // Open Store Message if (FAILED(_HrOpenMessage(pEvent->idMessage, &pEvent->pMessage))) { hr = TrapError(SP_E_SMTP_CANTOPENMESSAGE); goto exit; } } // We better have a message object at this point else if (NULL == pEvent->pMessage) { Assert(FALSE); hr = TrapError(E_FAIL); goto exit; } // Catch Result CHECKHR(hr = _HrCommandMAIL()); exit: // Cleanup SafeRelease(pAddrTable); // Done return hr; } // ------------------------------------------------------------------------------------ // CSmtpTask::_HrCommandMAIL // ------------------------------------------------------------------------------------ HRESULT CSmtpTask::_HrCommandMAIL(void) { // Locals HRESULT hr=S_OK; HRESULT hrFind; IMimeAddressTable *pAdrTable=NULL; ADDRESSPROPS rAddress; ULONG c; LPSMTPEVENTINFO pEvent; // Get the current smtp event pEvent = CURRENTSMTPEVENT(m_rTable); // Init ZeroMemory(&rAddress, sizeof(ADDRESSPROPS)); // Check State Assert(m_pTransport && pEvent->pMessage); // Release Current Enumerator SafeRelease(m_pAdrEnum); // Get the Sender... CHECKHR(hr = pEvent->pMessage->GetAddressTable(&pAdrTable)); // Get Enumerator CHECKHR(hr = pAdrTable->EnumTypes(IAT_KNOWN, IAP_ADRTYPE | IAP_EMAIL, &m_pAdrEnum)); // Loop Enumerator while (SUCCEEDED(m_pAdrEnum->Next(1, &rAddress, &c)) && c == 1) { // Not IAT_FROM if (NULL == rAddress.pszEmail || IAT_FROM != rAddress.dwAdrType) { g_pMoleAlloc->FreeAddressProps(&rAddress); continue; } // Send the command CHECKHR(hr = m_pTransport->CommandMAIL(rAddress.pszEmail)); // Done goto exit; } // No Sender hr = TrapError(IXP_E_SMTP_NO_SENDER); exit: // Cleanup SafeRelease(pAdrTable); g_pMoleAlloc->FreeAddressProps(&rAddress); // Done return hr; } // ------------------------------------------------------------------------------------ // CSmtpTask::_HrCommandRCPT // ------------------------------------------------------------------------------------ HRESULT CSmtpTask::_HrCommandRCPT(void) { // Locals HRESULT hr=S_OK; DWORD dwAdrType; DWORD c; LPSTR pszEmail=NULL; ADDRESSPROPS rAddress; LPSMTPEVENTINFO pEvent; // Get the current smtp event pEvent = CURRENTSMTPEVENT(m_rTable); // Init ZeroMemory(&rAddress, sizeof(ADDRESSPROPS)); // Check State Assert(m_pAdrEnum && m_pTransport && pEvent->pMessage); // Walk the enumerator for the next recipient while (SUCCEEDED(m_pAdrEnum->Next(1, &rAddress, &c)) && c == 1) { // Get Type if (rAddress.pszEmail && ISFLAGSET(IAT_RECIPS, rAddress.dwAdrType)) { // Send the command CHECKHR(hr = m_pTransport->CommandRCPT(rAddress.pszEmail)); // Count Recipients pEvent->cRecipients++; // Done goto exit; } // Release g_pMoleAlloc->FreeAddressProps(&rAddress); } // Release the Enumerator SafeRelease(m_pAdrEnum); // No Recipients if (0 == pEvent->cRecipients) { hr = TrapError(IXP_E_SMTP_NO_RECIPIENTS); goto exit; } // Send the Data Command CHECKHR(hr = m_pTransport->CommandDATA()); exit: // Cleanup g_pMoleAlloc->FreeAddressProps(&rAddress); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::_HrSendDataStream // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrSendDataStream(void) { // Locals HRESULT hr=S_OK; LPSTREAM pStream=NULL; LPSTREAM pStmActual; LPSTR pszBCC=NULL; LPSTR pszTo=NULL; LPSTR pszMessageId=NULL; LPSMTPEVENTINFO pEvent; CMessageIdStream *pStmWrapper=NULL; // Get the current smtp event pEvent = CURRENTSMTPEVENT(m_rTable); // Check State Assert(m_pTransport && pEvent->pMessage); // See if BCC is set if (SUCCEEDED(MimeOleGetBodyPropA(pEvent->pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_BCC), NOFLAGS, &pszBCC))) { // Locals LPSTR pszToAppend=NULL; // RAID-20750 - If the to line is not set, then we will set it to "Undisclosed Recipient" // or the SMTP gateways will put the BCC into the to line. if (FAILED(MimeOleGetBodyPropA(pEvent->pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_TO), NOFLAGS, &pszTo))) { // Raid-9691: We were just putting , which was an illegal email address (bad for Exchange Server) pszToAppend = "To: \r\n"; } // Raid-2705: If this fails, just get the message source if (FAILED(MimeOleStripHeaders(pEvent->pMessage, HBODY_ROOT, STR_HDR_BCC, pszToAppend, &pStream))) { // Get Message Stream CHECKHR(hr = pEvent->pMessage->GetMessageSource(&pStream, 0)); } } // Otherwise, just get the message source else { // Get Message Stream CHECKHR(hr = pEvent->pMessage->GetMessageSource(&pStream, 0)); } // Lets see if the message has a message-id already if (FAILED(MimeOleGetBodyPropA(pEvent->pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_MESSAGEID), NOFLAGS, &pszMessageId))) { // Create a wrapper for this stream that will output the messageid CHECKALLOC(pStmWrapper = new CMessageIdStream(pStream)); // Adjust pEvent->cbEvent pEvent->cbEvent += pStmWrapper->CchMessageId(); // Increment total m_cbTotal += pStmWrapper->CchMessageId(); // Increment pEvent->cbSentTotal pEvent->cbSentTotal += pStmWrapper->CchMessageId(); // Reset pStream pStmActual = (IStream *)pStmWrapper; } else pStmActual = pStream; // Send the stream CHECKHR(hr = m_pTransport->SendDataStream(pStmActual, pEvent->cbEvent)); exit: // Cleanup SafeRelease(pStream); SafeRelease(pStmWrapper); SafeMemFree(pszBCC); SafeMemFree(pszTo); SafeMemFree(pszMessageId); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::_OnStreamProgress // -------------------------------------------------------------------------------- void CSmtpTask::_OnStreamProgress(LPSMTPSTREAM pInfo) { // Locals LPSMTPEVENTINFO pEvent; // Get the current smtp event pEvent = CURRENTSMTPEVENT(m_rTable); // Increment Status pEvent->cbEventSent += pInfo->cbIncrement; Assert(pEvent->cbEventSent == pInfo->cbCurrent); // Increment total sent m_cbSent += pInfo->cbIncrement; // Do Progress _DoProgress(); } // -------------------------------------------------------------------------------- // CSmtpTask::_HrFinishCurrentEvent // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrFinishCurrentEvent(HRESULT hrResult) { // Locals HRESULT hr=S_OK; LPSMTPEVENTINFO pEvent; // Get the current smtp event pEvent = CURRENTSMTPEVENT(m_rTable); // Save the Event Result pEvent->hrResult = hrResult; // If the Event Failed... if (FAILED(pEvent->hrResult)) { // If this message was part of a split group, skip all pars in this group if (ISFLAGSET(pEvent->dwFlags, SMTPEVENT_SPLITPART)) { // Compute Next Event ULONG iNextEvent = m_rTable.iEvent + (pEvent->cParts - pEvent->iPart) + 1; // Increment to last part while(m_rTable.iEvent < iNextEvent && m_rTable.iEvent < m_rTable.cEvents) { // Goto next event m_rTable.iEvent++; // Fail this event _CatchResult(SP_E_SENDINGSPLITGROUP, IXP_SMTP); // Fixup m_cbSent to be correct m_cbSent = m_rTable.prgEvent[m_rTable.iEvent].cbSentTotal; // Update progress _DoProgress(); } } } // Otherwise else { // Mark the event as complete FLAGSET(pEvent->dwFlags, SMTPEVENT_COMPLETE); // Increment number of completed events m_rTable.cCompleted++; } // Go to next message CHECKHR(hr = _HrStartNextEvent()); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::_HrStartNextEvent // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_HrStartNextEvent(void) { // Locals HRESULT hr=S_OK; // Fixup m_cbSent to be correct m_cbSent = m_rTable.prgEvent[m_rTable.iEvent].cbSentTotal; // Are we done yet ? if (m_rTable.iEvent + 1 == m_rTable.cEvents) { // Update progress _DoProgress(); // Disconnect from the server CHECKHR(hr = m_pTransport->Disconnect()); } // Oterhwise, Increment Event and send rset else { // Next Event m_rTable.iEvent++; // Update progress _DoProgress(); // Send Reset Command CHECKHR(hr = m_pTransport->CommandRSET()); } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::_OnDisconnectComplete // -------------------------------------------------------------------------------- HRESULT CSmtpTask::_OnDisconnectComplete(void) { // Locals HRESULT hr=S_OK; PDWORD_PTR prgdwIds=NULL; DWORD cIds=0; DWORD cIdsAlloc=0; DWORD i; LPSMTPEVENTINFO pEvent; ADJUSTFLAGS Flags; // Invalid State Assert(m_pOutbox); // Walk through the list of events for (i=0; iidMessage && ISFLAGSET(pEvent->dwFlags, SMTPEVENT_COMPLETE)) { // Insert into my array of message ids if (cIds + 1 > cIdsAlloc) { // Realloc CHECKHR(hr = HrRealloc((LPVOID *)&prgdwIds, sizeof(DWORD) * (cIdsAlloc + 10))); // Increment cIdsAlloc cIdsAlloc += 10; } // Set Message Id prgdwIds[cIds++] = (DWORD_PTR)pEvent->idMessage; } } // Setup List m_rList.cMsgs = cIds; m_rList.prgidMsg = (LPMESSAGEID)prgdwIds; prgdwIds = NULL; if (m_rList.cMsgs) { Flags.dwAdd = ARF_READ; Flags.dwRemove = ARF_SUBMITTED | ARF_UNSENT; if (m_idEventUpload == INVALID_EVENT) { if (DwGetOption(OPT_SAVESENTMSGS)) { Assert(m_pSentItems != NULL); // Move the message from the sent items folder CHECKHR(hr = m_pOutbox->CopyMessages(m_pSentItems, COPY_MESSAGE_MOVE, &m_rList, &Flags, NULL, NOSTORECALLBACK)); } else { // Delete the messages CHECKHR(hr = m_pOutbox->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &m_rList, NULL, NOSTORECALLBACK)); } } else { // Raid-7639: OE sends message over and over when runs out of disk space. m_pOutbox->SetMessageFlags(&m_rList, &Flags, NULL, NOSTORECALLBACK); } } exit: // Cleanup SafeMemFree(prgdwIds); // Done return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::Cancel // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::Cancel(void) { // Thread Safety EnterCriticalSection(&m_cs); // Cancelled FLAGSET(m_dwState, SMTPSTATE_CANCELED); // Simply drop the connection if (m_pTransport) m_pTransport->DropConnection(); if (m_pCancel != NULL) m_pCancel->Cancel(CT_ABORT); // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::OnTimeoutResponse // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnTimeoutResponse(TIMEOUTRESPONSE eResponse) { // Thread Safety EnterCriticalSection(&m_cs); // Should have a handle to the timeout window Assert(m_hwndTimeout); // No timeout window handle m_hwndTimeout = NULL; // Stop ? if (TIMEOUT_RESPONSE_STOP == eResponse) { // Cancelled FLAGSET(m_dwState, SMTPSTATE_CANCELED); // Report error and drop connection _CatchResult(IXP_E_TIMEOUT, IXP_SMTP); } // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CSmtpTask::IsDialogMessage // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::IsDialogMessage(LPMSG pMsg) { HRESULT hr=S_FALSE; EnterCriticalSection(&m_cs); if (m_hwndTimeout && IsWindow(m_hwndTimeout)) hr = (TRUE == ::IsDialogMessage(m_hwndTimeout, pMsg)) ? S_OK : S_FALSE; LeaveCriticalSection(&m_cs); return hr; } // -------------------------------------------------------------------------------- // CSmtpTask::OnFlagsChanged // -------------------------------------------------------------------------------- STDMETHODIMP CSmtpTask::OnFlagsChanged(DWORD dwFlags) { EnterCriticalSection(&m_cs); m_dwFlags = dwFlags; LeaveCriticalSection(&m_cs); return (S_OK); } STDMETHODIMP CSmtpTask::OnBegin(STOREOPERATIONTYPE tyOperation, STOREOPERATIONINFO *pOpInfo, IOperationCancel *pCancel) { char szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES]; // Hold onto this Assert(m_tyOperation == SOT_INVALID); if (pCancel) { m_pCancel = pCancel; m_pCancel->AddRef(); } m_tyOperation = tyOperation; // Set the animation m_pUI->SetAnimation(idanOutbox, TRUE); // Setup Progress Meter m_pUI->SetProgressRange(100); m_wProgress = 0; LOADSTRING(IDS_SPS_MOVEPROGRESS, szRes); wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, 1, m_rList.cMsgs); m_pUI->SetSpecificProgress(szBuf); // Party On return(S_OK); } STDMETHODIMP CSmtpTask::OnProgress(STOREOPERATIONTYPE tyOperation, DWORD dwCurrent, DWORD dwMax, LPCSTR pszStatus) { char szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES]; WORD wProgress, wDelta; // NOTE: that you can get more than one type of value for tyOperation. // Most likely, you will get SOT_CONNECTION_STATUS and then the // operation that you might expect. See HotStore.idl and look for // the STOREOPERATION enumeration type for more info. if (tyOperation == SOT_CONNECTION_STATUS) { // Connecting to ... LoadString(g_hLocRes, idsInetMailConnectingHost, szRes, ARRAYSIZE(szRes)); wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, m_rServer.szAccount); m_pUI->SetGeneralProgress(szBuf); } else if (tyOperation == SOT_COPYMOVE_MESSAGE) { // Compute Current Progress Index wProgress = (WORD)((dwCurrent * 100) / dwMax); // Compute Delta wDelta = wProgress - m_wProgress; // Progress Delta if (wDelta > 0) { // Incremenet Progress m_pUI->IncrementProgress(wDelta); // Increment my wProgress m_wProgress += wDelta; // Don't go over 100 percent Assert(m_wProgress <= 100); } if (dwCurrent < dwMax) { LOADSTRING(IDS_SPS_MOVEPROGRESS, szRes); wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, dwCurrent + 1, dwMax); // Set Specific Progress m_pUI->SetSpecificProgress(szBuf); } } // Done return(S_OK); } STDMETHODIMP CSmtpTask::OnTimeout(LPINETSERVER pServer, LPDWORD pdwTimeout, IXPTYPE ixpServerType) { // Is there currently a timeout dialog if (m_hwndTimeout) { // Set foreground SetForegroundWindow(m_hwndTimeout); } else { LPCSTR pszProtocol; // Not suppose to be showing UI ? if (ISFLAGSET(m_dwFlags, DELIVER_NOUI)) return(S_FALSE); // Do Timeout Dialog GetProtocolString(&pszProtocol, ixpServerType); if (pServer) { m_hwndTimeout = TaskUtil_HwndOnTimeout(pServer->szServerName, pServer->szAccount, pszProtocol, pServer->dwTimeout, (ITimeoutCallback *) this); // Couldn't create the dialog if (NULL == m_hwndTimeout) return(S_FALSE); } } return(S_OK); } STDMETHODIMP CSmtpTask::CanConnect(LPCSTR pszAccountId, DWORD dwFlags) { HWND hwnd; BOOL fPrompt = TRUE; if (m_pUI) m_pUI->GetWindow(&hwnd); else hwnd = NULL; // Call into general CanConnect Utility if ((m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)) || (dwFlags & CC_FLAG_DONTPROMPT)) fPrompt = FALSE; return CallbackCanConnect(pszAccountId, hwnd, fPrompt); } STDMETHODIMP CSmtpTask::OnLogonPrompt(LPINETSERVER pServer, IXPTYPE ixpServerType) { HWND hwnd; if (m_hwndTimeout) { DestroyWindow(m_hwndTimeout); m_hwndTimeout = NULL; } if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND))) return(E_FAIL); if (m_pUI) m_pUI->GetWindow(&hwnd); else hwnd = NULL; // Call into general OnLogonPrompt Utility return CallbackOnLogonPrompt(hwnd, pServer, ixpServerType); } STDMETHODIMP CSmtpTask::OnComplete(STOREOPERATIONTYPE tyOperation, HRESULT hrComplete, LPSTOREOPERATIONINFO pOpInfo, LPSTOREERROR pErrorInfo) { HRESULT hr; char szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES * 2], szSubject[64]; EVENTCOMPLETEDSTATUS tyEventStatus; if (m_hwndTimeout) { DestroyWindow(m_hwndTimeout); m_hwndTimeout = NULL; } IxpAssert(m_tyOperation != SOT_INVALID); if (m_tyOperation != tyOperation) return(S_OK); Assert(tyOperation == SOT_COPYMOVE_MESSAGE); // Figure out if we succeeded or failed if (FAILED(hrComplete)) { Assert(m_pUI); if (NULL != pErrorInfo) { IXPRESULT ixpResult; INETSERVER rServer; char szProblem[CCHMAX_STRINGRES]; int iLen; // Prepend sent items text error text to supplied problem Assert(tyOperation == SOT_COPYMOVE_MESSAGE); iLen = LoadString(g_hLocRes, IDS_SP_E_CANT_MOVETO_SENTITEMS, szProblem, sizeof(szProblem)); if (iLen < sizeof(szProblem) - 1) { szProblem[iLen] = ' '; iLen += 1; szProblem[iLen] = '\0'; } if (NULL != pErrorInfo->pszProblem) StrCpyN(szProblem + iLen, pErrorInfo->pszProblem, ARRAYSIZE(szProblem) - iLen); TaskUtil_SplitStoreError(&ixpResult, &rServer, pErrorInfo); ixpResult.pszProblem = szProblem; _CatchResult(&ixpResult, &rServer, pErrorInfo->ixpType); } else { // Remap the Error Result hr = TrapError(SP_E_CANT_MOVETO_SENTITEMS); // Show an error in the spooler dialog _CatchResult(hr, IXP_IMAP); // Without a STOREERROR, we just have to guess } } m_pUI->SetAnimation(idanOutbox, FALSE); if (ISFLAGSET(m_dwState, SMTPSTATE_CANCELED)) tyEventStatus = EVENT_CANCELED; else if (SUCCEEDED(hrComplete)) tyEventStatus = EVENT_SUCCEEDED; else tyEventStatus = EVENT_FAILED; // Result m_pSpoolCtx->Notify(DELIVERY_NOTIFY_RESULT, tyEventStatus); // Result m_pSpoolCtx->Notify(DELIVERY_NOTIFY_COMPLETE, 0); m_pSpoolCtx->EventDone(m_idEventUpload, tyEventStatus); // Release your cancel object SafeRelease(m_pCancel); m_tyOperation = SOT_INVALID; // Done return(S_OK); } STDMETHODIMP CSmtpTask::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, INT *piUserResponse) { HWND hwnd; // Close any timeout dialog, if present if (m_hwndTimeout) { DestroyWindow(m_hwndTimeout); m_hwndTimeout = NULL; } // Raid 55082 - SPOOLER: SPA/SSL auth to NNTP does not display cert warning and fails. #if 0 if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND))) return(E_FAIL); #endif if (m_pUI) m_pUI->GetWindow(&hwnd); else hwnd = NULL; // Call into my swanky utility return CallbackOnPrompt(hwnd, hrError, pszText, pszCaption, uType, piUserResponse); }