Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2207 lines
63 KiB

// --------------------------------------------------------------------------------
// 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 <mid>\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; i<m_rTable.cEvents; i++)
{
// Release the Message
SafeRelease(m_rTable.prgEvent[i].pMessage);
}
// No Events
m_rTable.cEvents = 0;
}
// --------------------------------------------------------------------------------
// CSmtpTask::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CSmtpTask::QueryInterface(REFIID riid, LPVOID *ppv)
{
// Locals
HRESULT hr=S_OK;
// check params
if (ppv == NULL)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Find IID
if (IID_IUnknown == riid)
*ppv = (IUnknown *)(ISpoolerTask *)this;
else if (IID_ISpoolerTask == riid)
*ppv = (ISpoolerTask *)this;
else if (IID_ITimeoutCallback == riid)
*ppv = (ITimeoutCallback *) this;
else if (IID_ITransportCallbackService == riid)
*ppv = (ITransportCallbackService *) this;
else
{
*ppv = NULL;
hr = TrapError(E_NOINTERFACE);
goto exit;
}
// AddRef It
((IUnknown *)*ppv)->AddRef();
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 <Undiscolsed Recipient>, which was an illegal email address (bad for Exchange Server)
pszToAppend = "To: <Undisclosed-Recipient:;>\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; i<m_rTable.cEvents; i++)
{
// Readability
pEvent = &m_rTable.prgEvent[i];
// If this event was in the outbox
if (0 != pEvent->idMessage && 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);
}