Windows2003-3790/inetcore/outlookexpress/mailnews/spooler/httptask.cpp
2020-09-30 16:53:55 +02:00

1158 lines
35 KiB
C++

// --------------------------------------------------------------------------------
// h t t p t a s k . h
// Copyright (c)1998 Microsoft Corporation, All Rights Reserved
// Greg S. Friedman
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "httptask.h"
#include "taskutil.h"
#include "acctcach.h"
#include "xputil.h"
#include "useragnt.h"
#include "..\http\httputil.h"
// --------------------------------------------------------------------------------
// Data Types
// --------------------------------------------------------------------------------
typedef enum tagHTTPEVENTTYPE
{
EVENT_HTTPSEND
} HTTPEVENTTYPE;
#define CURRENTHTTPEVENT ((LPHTTPEVENTINFO)m_psaEvents->GetItemAt(m_iEvent))
//----------------------------------------------------------------------
// FreeNewMessageInfo
//----------------------------------------------------------------------
static void __cdecl _FreeHTTPEventInfo(LPVOID pei)
{
Assert(NULL != pei);
SafeMemFree(pei);
}
// --------------------------------------------------------------------------------
// CHTTPTask::CHTTPTask
// --------------------------------------------------------------------------------
CHTTPTask::CHTTPTask(void) :
m_cRef(1),
m_dwFlags(NOFLAGS),
m_dwState(NOFLAGS),
m_cbTotal(0),
m_cbSent(0),
m_cbStart(0),
m_cCompleted(0),
m_wProgress(0),
m_pSpoolCtx(NULL),
m_pAccount(NULL),
m_pOutbox(NULL),
m_pSentItems(NULL),
m_psaEvents(NULL),
m_iEvent(0),
m_pszSubject(NULL),
m_pBody(NULL),
m_pTransport(NULL),
m_pUI(NULL),
m_idSendEvent(INVALID_EVENT),
m_pszAccountId(NULL),
m_pszSendMsgUrl(NULL)
{
InitializeCriticalSection(&m_cs);
ZeroMemory(&m_rServer, sizeof(m_rServer));
}
// --------------------------------------------------------------------------------
// CHTTPTask::~CHTTPTask
// --------------------------------------------------------------------------------
CHTTPTask::~CHTTPTask(void)
{
ZeroMemory(&m_rServer, sizeof(m_rServer)); // Done for security.
_Reset();
DeleteCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CHTTPTask::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::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
{
*ppv = NULL;
hr = TrapError(E_NOINTERFACE);
goto exit;
}
// AddRef It
((IUnknown *)*ppv)->AddRef();
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CHTTPTask::AddRef(void)
{
return InterlockedIncrement(&m_cRef);
}
// --------------------------------------------------------------------------------
// CHTTPTask::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CHTTPTask::Release(void)
{
LONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
delete this;
return (ULONG)cRef;
}
// ---------------------------------------------------------------------------
// ISpoolerTask Methods
// ---------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// CHTTPTask::Init
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::Init(DWORD dwFlags, ISpoolerBindContext *pBindCtx)
{
// Invalid Arg
if (NULL == pBindCtx)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// 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;
}
// --------------------------------------------------------------------------------
// CHTTPTask::BuildEvents
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::BuildEvents(ISpoolerUI *pSpoolerUI,
IImnAccount *pAccount,
FOLDERID idFolder)
{
HRESULT hr = S_OK;
HROWSET hRowset=NULL;
MESSAGEINFO mi = {0};
CHAR szAccount[CCHMAX_ACCOUNT_NAME];
CHAR szAccountId[CCHMAX_ACCOUNT_NAME];
CHAR szMessage[255];
CHAR szRes[255];
FOLDERINFO fi = {0};
LPFOLDERINFO pfiFree = NULL;
LPSTR pszUserAgent = GetOEUserAgentString();
LPSTR pszCachedPass = NULL;
FOLDERID idServer;
// Invalid Arg
if (NULL == pSpoolerUI || NULL == pAccount)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
m_pUI = pSpoolerUI;
m_pUI->AddRef();
m_pAccount = pAccount;
m_pAccount->AddRef();
// Get the Account Name
CHECKHR(hr = m_pAccount->GetPropSz(AP_ACCOUNT_NAME, szAccount, ARRAYSIZE(szAccount)));
// Get the account ID
CHECKHR(hr = m_pAccount->GetPropSz(AP_ACCOUNT_ID, szAccountId, ARRAYSIZE(szAccountId)));
m_pszAccountId = PszDupA(szAccountId);
// Get the outbox
CHECKHR(hr = m_pSpoolCtx->BindToObject(IID_CLocalStoreOutbox, (LPVOID *)&m_pOutbox));
// Get the sent items. Don't fail if it doesn't exist.
if (DwGetOption(OPT_SAVESENTMSGS))
{
if (SUCCEEDED(g_pStore->FindServerId(m_pszAccountId, &idServer)))
g_pStore->OpenSpecialFolder(idServer, NULL, FOLDER_SENT, &m_pSentItems);
}
// Create a Rowset
CHECKHR(hr = m_pOutbox->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
// Loop
while (S_OK == m_pOutbox->QueryRowset(hRowset, 1, (LPVOID *)&mi, NULL))
{
CHECKHR(hr = _HrAppendOutboxMessage(szAccount, &mi));
// Free Current
m_pOutbox->FreeRecord(&mi);
}
if (NULL == m_psaEvents || 0 == m_psaEvents->GetLength())
goto exit;
// create the transport object
CHECKHR(hr = CoCreateInstance(CLSID_IHTTPMailTransport, NULL, CLSCTX_INPROC_SERVER, IID_IHTTPMailTransport, (LPVOID *)&m_pTransport));
// Init the Transport
CHECKHR(hr = m_pTransport->InitNew(pszUserAgent, NULL, this));
// Fill an INETSERVER structure from the account object
CHECKHR(hr = m_pTransport->InetServerFromAccount(m_pAccount, &m_rServer));
// looked for a cached password and, if one exists, use it
GetAccountPropStrA(m_pszAccountId, CAP_PASSWORD, &pszCachedPass);
if (NULL != pszCachedPass)
StrCpyN(m_rServer.szPassword, pszCachedPass, sizeof(m_rServer.szPassword));
// connect to the server. the transport won't
// actually connect until a command is issued
CHECKHR(hr = m_pTransport->Connect(&m_rServer, TRUE, FALSE));
LOADSTRING(IDS_SPS_SMTPEVENT, szRes);
wnsprintf(szMessage, ARRAYSIZE(szMessage), szRes, m_psaEvents->GetLength(), m_rServer.szAccount);
CHECKHR(hr = m_pSpoolCtx->RegisterEvent(szMessage, (ISpoolerTask *)this, EVENT_HTTPSEND, m_pAccount, &m_idSendEvent));
// 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) && NULL == pszCachedPass)
{
m_pUI->ShowWindow(SW_SHOW);
}
exit:
// Cleanup
SafeMemFree(pszUserAgent);
SafeMemFree(pszCachedPass);
if (m_pOutbox)
{
m_pOutbox->CloseRowset(&hRowset);
m_pOutbox->FreeRecord(&mi);
}
LeaveCriticalSection(&m_cs);
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::Execute
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::Execute(EVENTID eid, DWORD_PTR dwTwinkie)
{
HRESULT hr = E_FAIL;
// Thread Safety
EnterCriticalSection(&m_cs);
if (EVENT_HTTPSEND == dwTwinkie)
hr = _HrExecuteSend(eid, dwTwinkie);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::CancelEvent
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::CancelEvent(EVENTID eid, DWORD_PTR dwTwinkie)
{
return S_OK;
}
// --------------------------------------------------------------------------------
// CHTTPTask::Cancel
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::Cancel(void)
{
// Thread Safety
EnterCriticalSection(&m_cs);
m_dwState |= HTTPSTATE_CANCELED;
// Simply drop the connection
if (m_pTransport)
m_pTransport->DropConnection();
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CHTTPTask::IsDialogMessage
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::IsDialogMessage(LPMSG pMsg)
{
return S_FALSE;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnFlagsChanged
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::OnFlagsChanged(DWORD dwFlags)
{
return S_OK;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnLogonPrompt
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::OnLogonPrompt(
LPINETSERVER pInetServer,
IInternetTransport *pTransport)
{
HRESULT hr = S_OK;
LPSTR pszCachedPass = NULL;
EnterCriticalSection(&m_cs);
// pull password out of the cache
GetAccountPropStrA(m_pszAccountId, CAP_PASSWORD, &pszCachedPass);
if (NULL != pszCachedPass && 0 != lstrcmp(pszCachedPass, pInetServer->szPassword))
{
StrCpyN(pInetServer->szPassword, pszCachedPass, ARRAYSIZE(pInetServer->szPassword));
goto exit;
}
if (ISFLAGSET(m_dwFlags, DELIVER_NOUI))
{
hr = S_FALSE;
goto exit;
}
hr = TaskUtil_OnLogonPrompt(m_pAccount, m_pUI, NULL, pInetServer, AP_HTTPMAIL_USERNAME,
AP_HTTPMAIL_PASSWORD, AP_HTTPMAIL_PROMPT_PASSWORD, TRUE);
// cache the password
if (S_OK == hr)
HrCacheAccountPropStrA(m_pszAccountId, CAP_PASSWORD, pInetServer->szPassword);
exit:
LeaveCriticalSection(&m_cs);
SafeMemFree(pszCachedPass);
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnPrompt
// --------------------------------------------------------------------------------
STDMETHODIMP_(INT) CHTTPTask::OnPrompt(
HRESULT hrError,
LPCTSTR pszText,
LPCTSTR pszCaption,
UINT uType,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnStatus
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::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)
{
HRESULT hrDisconnect = _OnDisconnectComplete();
// TODO: update progress state and deal with errors
// Set the animation
m_pUI->SetAnimation(idanOutbox, FALSE);
if (!ISFLAGSET(m_dwState, HTTPSTATE_EVENTSUCCESS) || (NULL != m_cCompleted && m_cCompleted < m_psaEvents->GetLength()))
{
if (ISFLAGSET(m_dwState, HTTPSTATE_CANCELED))
tyEventStatus = EVENT_CANCELED;
else if (ISFLAGSET(m_dwState, HTTPSTATE_EVENTSUCCESS))
tyEventStatus = EVENT_WARNINGS;
else
tyEventStatus = EVENT_FAILED;
}
// Result
m_pSpoolCtx->Notify(DELIVERY_NOTIFY_RESULT, tyEventStatus);
// Result
m_pSpoolCtx->Notify(DELIVERY_NOTIFY_COMPLETE, 0);
// Hands Off my callback
SideAssert(m_pTransport->HandsOffCallback() == S_OK);
// This task is complete
m_pSpoolCtx->EventDone(m_idSendEvent, tyEventStatus);
}
LeaveCriticalSection(&m_cs);
return S_OK;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnError
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::OnError(
IXPSTATUS ixpstatus,
LPIXPRESULT pIxpResult,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnProgress
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::OnProgress(
DWORD dwIncrement,
DWORD dwCurrent,
DWORD dwMaximum,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnCommand
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::OnCommand(
CMDTYPE cmdtype,
LPSTR pszLine,
HRESULT hrResponse,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
// --------------------------------------------------------------------------------
// CHTTPTask::OnTimeout
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::OnTimeout(
DWORD *pdwTimeout,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
// --------------------------------------------------------------------------------
// IHTTPMailCallback methods
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// CHTTPTask::OnResponse
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::OnResponse(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
HRESULT hrCatch;
// Thread Safety
EnterCriticalSection(&m_cs);
// if the send message url couldn't be retrieved, convert the error into
// a more informative error code.
if (SP_E_HTTP_SERVICEDOESNTWORK == pResponse->rIxpResult.hrResult)
{
_CatchResult(SP_E_HTTP_NOSENDMSGURL);
goto exit;
}
// Handle the Error
if (TASKRESULT_SUCCESS != _CatchResult(&pResponse->rIxpResult))
goto exit;
switch (pResponse->command)
{
case HTTPMAIL_GETPROP:
if (SUCCEEDED(_HrAdoptSendMsgUrl(pResponse->rGetPropInfo.pszProp)))
{
pResponse->rGetPropInfo.pszProp = NULL;
// build the message and post it
hr = _HrPostCurrentMessage();
}
break;
case HTTPMAIL_SENDMESSAGE:
_UpdateSendMessageProgress(pResponse);
if (pResponse->fDone)
_CatchResult(_HrFinishCurrentEvent(S_OK, pResponse->rSendMessageInfo.pszLocation));
break;
default:
Assert(FALSE);
break;
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::GetParentWindow
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPTask::GetParentWindow(HWND *phwndParent)
{
return E_NOTIMPL;
}
// --------------------------------------------------------------------------------
// Private Implementation
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// CHTTPTask::_Reset
// --------------------------------------------------------------------------------
void CHTTPTask::_Reset(void)
{
SafeRelease(m_pSpoolCtx);
SafeRelease(m_pAccount);
SafeRelease(m_pOutbox);
SafeRelease(m_pSentItems);
delete m_psaEvents;
m_iEvent = 0;
SafeMemFree(m_pszSubject);
SafeRelease(m_pBody);
SafeRelease(m_pTransport);
SafeRelease(m_pUI);
SafeMemFree(m_pszAccountId);
SafeMemFree(m_pszSendMsgUrl);
}
// --------------------------------------------------------------------------------
// CHTTPTask::_CatchResult
// --------------------------------------------------------------------------------
TASKRESULTTYPE CHTTPTask::_CatchResult(HRESULT hr)
{
// Locals
IXPRESULT rResult;
// Build an IXPRESULT
ZeroMemory(&rResult, sizeof(rResult));
rResult.hrResult = hr;
// Get the HTTPMail Result Type
return _CatchResult(&rResult);
}
// --------------------------------------------------------------------------------
// CHTTPTask::_CatchResult
// --------------------------------------------------------------------------------
TASKRESULTTYPE CHTTPTask::_CatchResult(LPIXPRESULT pResult)
{
HWND hwndParent;
TASKRESULTTYPE tyTaskResult = TASKRESULT_FAILURE;
// If Succeeded
if (SUCCEEDED(pResult->hrResult))
return TASKRESULT_SUCCESS;
// Get Window
if (NULL == m_pUI || FAILED(m_pUI->GetWindow(&hwndParent)))
hwndParent = NULL;
// Process generic protocol error
tyTaskResult = TaskUtil_FBaseTransportError(IXP_HTTPMail, m_idSendEvent, pResult, &m_rServer, m_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, NULL)))
{
// Roast the current connection
m_pTransport->DropConnection();
}
}
}
return tyTaskResult;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_HrAppendOutboxMessage
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_HrAppendOutboxMessage(LPCSTR pszAccount, LPMESSAGEINFO pmi)
{
HRESULT hr = S_OK;
IImnAccount *pAccount = NULL;
LPHTTPEVENTINFO pEvent = NULL;
Assert(NULL != pszAccount && NULL != pmi);
// handle empty account id
if (NULL == pmi->pszAcctId || FAILED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pmi->pszAcctId, &pAccount)))
{
// do some special junk with default accounts
goto exit;
}
else if (lstrcmpi(pmi->pszAcctName, pszAccount) != 0)
goto exit;
if (NULL == m_psaEvents)
CHECKHR(hr = CSortedArray::Create(NULL, _FreeHTTPEventInfo, &m_psaEvents));
// build the event object and append it into the event array
if (!MemAlloc((void **)&pEvent, sizeof(HTTPEVENTINFO)))
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
CHECKHR(hr = m_psaEvents->Add(pEvent));
pEvent->idMessage = pmi->idMessage;
pEvent->fComplete = FALSE;
m_cbTotal += (pmi->cbMessage + 175);
pEvent->cbSentTotal = m_cbTotal; // running total
pEvent = NULL;
exit:
SafeMemFree(pEvent);
SafeRelease(pAccount);
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_HrCreateSendProps
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_HrCreateSendProps(IMimeMessage *pMessage,
LPSTR *ppszFrom,
LPHTTPTARGETLIST *ppTargets)
{
HRESULT hr = S_OK;
IMimeAddressTable *pAdrTable = NULL;
IMimeEnumAddressTypes *pAdrEnum = NULL;
ADDRESSPROPS rAddress;
ULONG c;
LPSTR pszFrom = NULL;
LPSTR pszTemp = NULL;
LPHTTPTARGETLIST pTargets = NULL;
DWORD dwTargetCapacity = 0;
Assert(NULL != ppszFrom && NULL != ppTargets);
if (NULL == ppszFrom || NULL == ppTargets)
return E_INVALIDARG;
*ppszFrom = NULL;
*ppTargets = NULL;
// allocate a targetlist
if (!MemAlloc((void **)&pTargets, sizeof(HTTPTARGETLIST)))
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
pTargets->cTarget = 0;
pTargets->prgTarget = NULL;
ZeroMemory(&rAddress, sizeof(rAddress));
// Get the recipients...
CHECKHR(hr = pMessage->GetAddressTable(&pAdrTable));
// Get Enumerator
CHECKHR(hr = pAdrTable->EnumTypes(IAT_KNOWN, IAP_ADRTYPE | IAP_EMAIL, &pAdrEnum));
// Walk the enumerator
while (SUCCEEDED(pAdrEnum->Next(1, &rAddress, &c)) && c == 1)
{
if (NULL != rAddress.pszEmail)
{
// Get Type
if (ISFLAGSET(IAT_RECIPS, rAddress.dwAdrType))
{
// copy the address
pszTemp = PszDupA(rAddress.pszEmail);
if (NULL == pszTemp)
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
// add it to the collection of addresses we are building
if (pTargets->cTarget == dwTargetCapacity)
{
if (!MemRealloc((void **)&pTargets->prgTarget, (dwTargetCapacity + 4) * sizeof(LPSTR)))
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
dwTargetCapacity += 4;
}
pTargets->prgTarget[pTargets->cTarget++] = pszTemp;
pszTemp = NULL;
}
else if (NULL == pszFrom && IAT_FROM == rAddress.dwAdrType)
{
pszFrom = PszDupA(rAddress.pszEmail);
if (NULL == pszFrom)
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
}
}
// Release
g_pMoleAlloc->FreeAddressProps(&rAddress);
}
// success. transfer ownership of the params to the caller
*ppszFrom = pszFrom;
pszFrom = NULL;
*ppTargets = pTargets;
pTargets = NULL;
exit:
if (pTargets)
Http_FreeTargetList(pTargets);
SafeMemFree(pszTemp);
SafeRelease(pAdrTable);
SafeRelease(pAdrEnum);
SafeMemFree(pszFrom);
return hr;
}
// ------------------------------------------------------------------------------------
// CHTTPTask::_HrOpenMessage
// ------------------------------------------------------------------------------------
HRESULT CHTTPTask::_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;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_HrPostCurrentMessage
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_HrPostCurrentMessage(void)
{
HRESULT hr = S_OK;
IMimeMessage *pMessage = NULL;
LPHTTPEVENTINFO pEvent = NULL;
CHAR szRes[CCHMAX_RES];
CHAR szMsg[CCHMAX_RES + CCHMAX_ACCOUNT_NAME];
LPSTR pszFrom = NULL;
LPHTTPTARGETLIST pTargets = NULL;
Assert(NULL != m_psaEvents);
Assert(m_iEvent <= m_psaEvents->GetLength());
pEvent = CURRENTHTTPEVENT;
Assert(NULL != pEvent);
LOADSTRING(IDS_SPS_SMTPPROGRESS, szRes);
wnsprintf(szMsg, ARRAYSIZE(szMsg), szRes, m_iEvent + 1, m_psaEvents->GetLength());
// Set Specific Progress
m_pUI->SetSpecificProgress(szMsg);
// Open Store Message
if (FAILED(_HrOpenMessage(pEvent->idMessage, &pMessage)))
{
hr = TrapError(SP_E_SMTP_CANTOPENMESSAGE);
goto exit;
}
CHECKHR(hr = _HrCreateSendProps(pMessage, &pszFrom, &pTargets));
CHECKHR(hr = pMessage->GetMessageSource(&m_pBody, 0));
hr = m_pTransport->SendMessage(m_pszSendMsgUrl, pszFrom, pTargets, DwGetOption(OPT_SAVESENTMSGS), m_pBody, 0);
exit:
SafeRelease(pMessage);
SafeMemFree(pszFrom);
if (pTargets)
Http_FreeTargetList(pTargets);
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_HrExecuteSend
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_HrExecuteSend(EVENTID eid, DWORD_PTR dwTwinkie)
{
HRESULT hr = S_OK;
LPSTR pszSendMsgUrl = NULL;
CHAR szRes[CCHMAX_RES];
CHAR szBuf[CCHMAX_RES + CCHMAX_SERVER_NAME];
// 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);
Assert(NULL == m_pszSendMsgUrl);
// look for the sendmsg url in the cache
if (!GetAccountPropStrA(m_pszAccountId, CAP_HTTPMAILSENDMSG, &m_pszSendMsgUrl))
{
hr = m_pTransport->GetProperty(HTTPMAIL_PROP_SENDMSG, &pszSendMsgUrl);
if (E_PENDING == hr)
{
hr = S_OK;
goto exit;
}
CHECKHR(hr);
CHECKHR(hr = _HrAdoptSendMsgUrl(pszSendMsgUrl));
}
Assert(NULL != m_pszSendMsgUrl);
CHECKHR(hr = _HrPostCurrentMessage());
exit:
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_HrAdoptSendMsgUrl
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_HrAdoptSendMsgUrl(LPSTR pszSendMsgUrl)
{
Assert(NULL == m_pszSendMsgUrl);
if (NULL == pszSendMsgUrl)
return E_INVALIDARG;
m_pszSendMsgUrl = pszSendMsgUrl ;
// add it to the account data cache
HrCacheAccountPropStrA(m_pszAccountId, CAP_HTTPMAILSENDMSG, m_pszSendMsgUrl);
return S_OK;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_HrFinishCurrentEvent
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_HrFinishCurrentEvent(HRESULT hrResult, LPSTR pszLocationUrl)
{
// Locals
HRESULT hr = S_OK;
LPHTTPEVENTINFO pEvent;
MESSAGEID idMessage;
if (FAILED(hrResult))
goto exit;
// save in sent items
if (m_pSentItems && m_pBody && pszLocationUrl)
{
// add the message to the sent items folder
CHECKHR(hr = Http_AddMessageToFolder(m_pSentItems, m_pszAccountId, NULL, ARF_READ, pszLocationUrl, &idMessage));
// write the message body out
CHECKHR(hr = Http_SetMessageStream(m_pSentItems, idMessage, m_pBody, NULL, TRUE));
}
// Get the current http event
pEvent = CURRENTHTTPEVENT;
pEvent->fComplete = TRUE;
m_dwState |= HTTPSTATE_EVENTSUCCESS;
++m_cCompleted;
exit:
// go to the next event
hr = _HrStartNextEvent();
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_HrStartNextEvent
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_HrStartNextEvent(void)
{
// Locals
HRESULT hr = S_OK;
// free the previous subject
SafeMemFree(m_pszSubject);
// free the previous body
SafeRelease(m_pBody);
// Fixup m_cbSent to be correct
m_cbSent = (CURRENTHTTPEVENT)->cbSentTotal;
m_cbStart = m_cbSent;
// Are we done yet ?
if (m_iEvent + 1 == m_psaEvents->GetLength())
{
// Update progress
_DoProgress();
// Disconnect from the server
CHECKHR(hr = m_pTransport->Disconnect());
}
// otherwise, increment the event count and send the next message
else
{
// Next Event
m_iEvent++;
// Update progress
//_DoProgress();
// Send Reset Command
hr = _HrPostCurrentMessage();
if (hr == E_PENDING)
hr = S_OK;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_OnDisconnectComplete
// --------------------------------------------------------------------------------
HRESULT CHTTPTask::_OnDisconnectComplete(void)
{
// Locals
HRESULT hr = S_OK;
LPDWORD prgdwIds = NULL;
DWORD cIdsAlloc = 0;
DWORD i;
LPHTTPEVENTINFO pEvent;
ADJUSTFLAGS Flags;
DWORD cEvents;
MESSAGEIDLIST rList;
rList.cMsgs = 0;
rList.prgidMsg = NULL;
// Invalid State
Assert(m_pOutbox);
Assert(m_psaEvents);
cEvents = m_psaEvents->GetLength();
// Walk through the list of events
for (i=0; i < cEvents; i++)
{
// Readability
pEvent = (LPHTTPEVENTINFO)m_psaEvents->GetItemAt(i);
// If this event was in the outbox
if (0 != pEvent->idMessage && pEvent->fComplete)
{
// Insert into my array of message ids
if (rList.cMsgs + 1 > cIdsAlloc)
{
// Realloc
CHECKHR(hr = HrRealloc((LPVOID *)&rList.prgidMsg, sizeof(DWORD) * (cIdsAlloc + 10)));
// Increment cIdsAlloc
cIdsAlloc += 10;
}
// Set Message Id
rList.prgidMsg[rList.cMsgs++] = pEvent->idMessage;
}
}
if (rList.cMsgs)
{
Flags.dwAdd = ARF_READ;
Flags.dwRemove = ARF_SUBMITTED | ARF_UNSENT;
if (m_idSendEvent != INVALID_EVENT)
{
// Delete the messages.
// Messages are never copied to the local outbox for httpmail
CHECKHR(hr = m_pOutbox->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &rList, NULL, NOSTORECALLBACK));
}
else
{
// Raid-7639: OE sends message over and over when runs out of disk space.
m_pOutbox->SetMessageFlags(&rList, &Flags, NULL, NOSTORECALLBACK);
}
}
exit:
// Cleanup
SafeMemFree(rList.prgidMsg);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CHTTPTask::_UpdateSendMessageProgress
// --------------------------------------------------------------------------------
void CHTTPTask::_UpdateSendMessageProgress(LPHTTPMAILRESPONSE pResponse)
{
// the transport is attempting to resend the stream..
// reset our progress indicator
if (pResponse->rSendMessageInfo.fResend)
{
m_cbSent = m_cbStart;
}
else
{
// Increment Status
m_cbSent += pResponse->rSendMessageInfo.cbIncrement;
}
// Do Progress
_DoProgress();
}
// --------------------------------------------------------------------------------
// CHTTPTask::_DoProgress
// --------------------------------------------------------------------------------
void CHTTPTask::_DoProgress(void)
{
// Locals
WORD wProgress = 0;
WORD wDelta;
// Invalid Arg
Assert(m_cbTotal > 0 && m_pUI);
if (m_cbSent > 0)
{
// 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);
}
}
else if (m_wProgress != 0)
{
m_pUI->SetProgressPosition(0);
m_wProgress = 0;
}
}