// -------------------------------------------------------------------------------- // 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; } }