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

2342 lines
71 KiB
C++

// --------------------------------------------------------------------------------
// Spengine.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Steven J. Bailey
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "spengine.h"
#include "strconst.h"
#include "spoolui.h"
#include "thormsgs.h"
#include "newstask.h"
#include "goptions.h"
#include "conman.h"
#include "resource.h"
#include "ontask.h"
#include "smtptask.h"
#include "pop3task.h"
#include "instance.h"
#include "shlwapip.h"
#include "ourguid.h"
#include "demand.h"
#include "storutil.h"
#include "msgfldr.h"
#include "httptask.h"
#include "watchtsk.h"
#include "shared.h"
#include "util.h"
// --------------------------------------------------------------------------------
// Globals
// --------------------------------------------------------------------------------
BOOL g_fCheckOutboxOnShutdown=FALSE;
extern HANDLE hSmapiEvent; // Added for Bug# 62129 (v-snatar)
// --------------------------------------------------------------------------------
// ISSPOOLERTHREAD
// --------------------------------------------------------------------------------
#define ISSPOOLERTHREAD \
(m_dwThreadId == GetCurrentThreadId())
// --------------------------------------------------------------------------------
// CSpoolerEngine::CSpoolerEngine
// --------------------------------------------------------------------------------
CSpoolerEngine::CSpoolerEngine(void)
{
m_cRef = 1;
m_pUI = NULL;
m_dwState = 0;
m_dwFlags = 0;
m_dwQueued = 0;
m_pAcctMan = NULL;
m_pUidlCache = NULL;
m_hwndUI = NULL;
m_pszAcctID = NULL;
m_idFolder = FOLDERID_INVALID;
m_dwThreadId = GetCurrentThreadId();
m_hThread = GetCurrentThread();
ZeroMemory(&m_rViewRegister, sizeof(VIEWREGISTER));
ZeroMemory(&m_rEventTable, sizeof(SPOOLEREVENTTABLE));
m_fBackgroundPollPending = FALSE;
m_dwPollInterval = 0;
m_cCurEvent = FALSE;
m_hwndTray = NULL;
m_fRasSpooled = FALSE;
m_fOfflineWhenDone = FALSE;
m_pPop3LogFile = NULL;
m_pSmtpLogFile = NULL;
m_fIDialed = FALSE;
m_cSyncEvent = 0;
m_fNoSyncEvent = FALSE;
InitializeCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::~CSpoolerEngine
// --------------------------------------------------------------------------------
CSpoolerEngine::~CSpoolerEngine(void)
{
Assert(m_rEventTable.prgEvents == NULL);
Assert(ISSPOOLERTHREAD);
if (g_pConMan)
g_pConMan->Unadvise((IConnectionNotify *) this);
OptionUnadvise(m_hwndUI);
SafeRelease(m_pUI);
SafeRelease(m_pAcctMan);
SafeRelease(m_pUidlCache);
SafeRelease(m_pSmtpLogFile);
SafeRelease(m_pPop3LogFile);
SafeMemFree(m_pszAcctID);
ReleaseMem(m_rViewRegister.rghwndView);
DeleteCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::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 *)(ISpoolerEngine *)this;
else if (IID_ISpoolerEngine == riid)
*ppv = (ISpoolerEngine *)this;
else if (IID_ISpoolerBindContext == riid)
*ppv = (ISpoolerBindContext *)this;
else
{
*ppv = NULL;
hr = TrapError(E_NOINTERFACE);
goto exit;
}
// AddRef It
((IUnknown *)*ppv)->AddRef();
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpoolerEngine::AddRef(void)
{
EnterCriticalSection(&m_cs);
ULONG cRef = ++m_cRef;
LeaveCriticalSection(&m_cs);
return cRef;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpoolerEngine::Release(void)
{
EnterCriticalSection(&m_cs);
ULONG cRef = --m_cRef;
LeaveCriticalSection(&m_cs);
if (0 != cRef)
return cRef;
delete this;
return 0;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Init
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::Init(ISpoolerUI *pUI, BOOL fPoll)
{
// Locals
HRESULT hr=S_OK;
DWORD dw;
// Thread Safety
EnterCriticalSection(&m_cs);
// Already Inited
if (m_pAcctMan)
{
Assert(FALSE);
goto exit;
}
// Create Default Spooler UI Object
if (NULL == pUI)
{
// Create a Serdy UI object
CHECKALLOC(m_pUI = (ISpoolerUI *)new CSpoolerDlg);
// Create
CHECKHR(hr = m_pUI->Init(GetDesktopWindow()));
}
// Otherwise, assume pUI
else
{
m_pUI = pUI;
m_pUI->AddRef();
}
// Register SpoolerBindContext with the UI object
m_pUI->RegisterBindContext((ISpoolerBindContext *)this);
// Get the window handle of the spooler UI
m_pUI->GetWindow(&m_hwndUI);
// Get Me An Account Manager
Assert(NULL == m_pAcctMan);
CHECKHR(hr = HrCreateAccountManager(&m_pAcctMan));
// Advise on the connection status
Assert(g_pConMan);
g_pConMan->Advise((IConnectionNotify *) this);
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
HRESULT CSpoolerEngine::OnStartupFinished(void)
{
DWORD dw;
// Start Polling...
dw = DwGetOption(OPT_POLLFORMSGS);
if (dw != OPTION_OFF)
SetTimer(m_hwndUI, IMAIL_POOLFORMAIL, dw, NULL);
// Advise Options
OptionAdvise(m_hwndUI);
return (S_OK);
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::StartDelivery
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::StartDelivery(HWND hwnd, LPCSTR pszAcctID, FOLDERID idFolder, DWORD dwFlags)
{
// Locals
HRESULT hr=S_OK;
// No Flags
if (0 == dwFlags || (DELIVER_SHOW != dwFlags && 0 == (dwFlags & ~DELIVER_COMMON_MASK)))
return TrapError(E_INVALIDARG);
// Check to see if we're working offline
Assert(g_pConMan);
if (DELIVER_SHOW != dwFlags && g_pConMan->IsGlobalOffline())
{
if (IDNO == AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrWorkingOffline),
0, MB_YESNO | MB_ICONEXCLAMATION ))
{
return (S_OK);
}
else
{
g_pConMan->SetGlobalOffline(FALSE);
}
}
// Enter Critical Section
EnterCriticalSection(&m_cs);
// If were busy...
if (!ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
// Don't need this anymore
SafeMemFree(m_pszAcctID);
// Save the Account Name
if (pszAcctID)
CHECKALLOC(m_pszAcctID = PszDupA(pszAcctID));
// Save the folder ID
m_idFolder = idFolder;
// Lets enter the busy state
FLAGSET(m_dwState, SPSTATE_BUSY);
}
else
FLAGSET(dwFlags, DELIVER_REFRESH);
// Process the outbox
Assert(m_hwndUI && IsWindow(m_hwndUI));
PostMessage(m_hwndUI, IMAIL_DELIVERNOW, 0, dwFlags);
exit:
// Leave Critical Section
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::_HrStartDeliveryActual
// --------------------------------------------------------------------------------
HRESULT CSpoolerEngine::_HrStartDeliveryActual(DWORD dwFlags)
{
// Locals
HRESULT hr=S_OK;
IImnAccount *pAccount=NULL;
ACCOUNTTABLE rTable;
IImnEnumAccounts *pEnum=NULL;
DWORD dw;
ULONG c;
MSG msg;
ULONG iConnectoid;
ULONG i;
CHAR szConnectoid[CCHMAX_CONNECTOID];
// Thread Safety
EnterCriticalSection(&m_cs);
// Init
ZeroMemory(&rTable, sizeof(ACCOUNTTABLE));
m_cSyncEvent = 0;
m_fNoSyncEvent = FALSE;
// If we are currently busy...
if (ISFLAGSET(dwFlags, DELIVER_REFRESH))
{
// If we are currently with no UI, and new request is for ui
if (ISFLAGSET(m_dwFlags, DELIVER_NOUI) && !ISFLAGSET(dwFlags, DELIVER_NOUI))
FLAGCLEAR(m_dwFlags, DELIVER_NOUI);
// If we are currently doing a background poll
if (ISFLAGSET(m_dwFlags, DELIVER_BACKGROUND) && !ISFLAGSET(dwFlags, DELIVER_BACKGROUND))
FLAGCLEAR(m_dwFlags, DELIVER_BACKGROUND);
// If not running with now ui, set to foreground
if (!ISFLAGSET(m_dwFlags, DELIVER_NOUI) && !ISFLAGSET(m_dwFlags, DELIVER_BACKGROUND))
{
m_pUI->ShowWindow(SW_SHOW);
SetForegroundWindow(m_hwndUI);
}
// Should I queue an outbox delivery?
if (0 == m_dwQueued && ISFLAGSET(dwFlags, DELIVER_QUEUE))
{
m_dwQueued = dwFlags;
FLAGCLEAR(m_dwQueued, DELIVER_REFRESH);
FLAGCLEAR(m_dwQueued, DELIVER_QUEUE);
}
// Done
goto exit;
}
// Simply show the ui ?
if (ISFLAGSET(dwFlags, DELIVER_SHOW))
{
m_pUI->ShowWindow(SW_SHOW);
SetForegroundWindow(m_hwndUI);
FLAGCLEAR(m_dwState, SPSTATE_BUSY);
goto exit;
}
// Reset
m_pUI->ClearEvents();
m_pUI->SetTaskCounts(0, 0);
m_pUI->StartDelivery();
// Save these flags
m_dwFlags = dwFlags;
// Show the UI if necessary
if (!ISFLAGSET(m_dwFlags, DELIVER_BACKGROUND))
{
m_pUI->ShowWindow(SW_SHOW);
SetForegroundWindow(m_hwndUI);
}
else
{
// If the invoker called for background, but the UI is already visible,
// then remove the flags
if (IsWindowVisible(m_hwndUI))
{
FLAGCLEAR(m_dwFlags, DELIVER_BACKGROUND);
FLAGCLEAR(m_dwFlags, DELIVER_NOUI);
}
}
#if 0
// Raid 43695: Spooler: News post with a CC causes an SMTP error
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#endif
// Single Account Polling...
if (m_pszAcctID)
{
// Add the Account into the Account Table
CHECKHR(hr = _HrAppendAccountTable(&rTable, m_pszAcctID, ALL_ACCT_SERVERS));
}
// Otherwise, polling all accounts
else
{
// Determine the types of servers we're going to queue up
DWORD dwServers = 0, dw;
if (m_dwFlags & DELIVER_SMTP_TYPE)
dwServers |= SRV_SMTP;
if (m_dwFlags & DELIVER_NEWS_TYPE && !(g_dwAthenaMode & MODE_MAILONLY))
dwServers |= SRV_NNTP;
if (m_dwFlags & DELIVER_HTTP_TYPE && !(g_dwAthenaMode & MODE_NEWSONLY))
dwServers |= SRV_HTTPMAIL;
if (m_dwFlags & DELIVER_IMAP_TYPE && !(g_dwAthenaMode & MODE_NEWSONLY))
dwServers |= SRV_IMAP;
if ((m_dwFlags & DELIVER_MAIL_RECV) && !(g_dwAthenaMode & MODE_NEWSONLY))
dwServers |= SRV_POP3;
// Enumerate the accounts
CHECKHR(hr = m_pAcctMan->Enumerate(dwServers, &pEnum));
// Sort by Account Name
pEnum->SortByAccountName();
// Add all the accounts into the Account Table
while (SUCCEEDED(pEnum->GetNext(&pAccount)))
{
// Add Account Into the Account Table
CHECKHR(hr = _HrAppendAccountTable(&rTable, pAccount, dwServers));
// Release
SafeRelease(pAccount);
}
}
// No Accounts...
if (0 == rTable.cAccounts)
goto exit;
// Sort the Account Table by Connnection Name
if (rTable.cRasAccts)
{
Assert(rTable.prgRasAcct);
_SortAccountTableByConnName(0, rTable.cRasAccts - 1, rTable.prgRasAcct);
}
m_fRasSpooled = FALSE;
m_fIDialed = FALSE;
// Raid-46334: MAIL: Time to build a Task List. First loop through the LAN list and build tasks from those accounts.
for (dw = 0; dw < rTable.cLanAccts; dw++)
{
if (ISFLAGSET(rTable.prgLanAcct[dw].dwServers, SRV_POP3) ||
ISFLAGSET(rTable.prgLanAcct[dw].dwServers, SRV_SMTP) ||
ISFLAGSET(rTable.prgLanAcct[dw].dwServers, SRV_IMAP) ||
ISFLAGSET(rTable.prgLanAcct[dw].dwServers, SRV_HTTPMAIL))
{
Assert(rTable.prgLanAcct[dw].pAccount);
_HrCreateTaskObject(&(rTable.prgLanAcct[dw]));
SafeRelease(rTable.prgLanAcct[dw].pAccount);
}
}
// Raid-46334: NEWS: Time to build a Task List. First loop through the LAN/news list and build tasks from those accounts.
for (dw = 0; dw < rTable.cLanAccts; dw++)
{
if (ISFLAGSET(rTable.prgLanAcct[dw].dwServers, SRV_NNTP))
{
Assert(rTable.prgLanAcct[dw].pAccount);
_HrCreateTaskObject(&(rTable.prgLanAcct[dw]));
SafeRelease(rTable.prgLanAcct[dw].pAccount);
}
else
Assert(NULL == rTable.prgLanAcct[dw].pAccount);
}
// Now walk the list of RAS accounts and add those to the task list
iConnectoid = 0;
while(iConnectoid < rTable.cRasAccts)
{
// Indirect Sort
i = rTable.prgRasAcct[iConnectoid].dwSort;
// Save current connectoid
StrCpyN(szConnectoid, rTable.prgRasAcct[i].szConnectoid, ARRAYSIZE(szConnectoid));
// Insert Ras Accounts
// TODO Add HTTP accounts to it too.
_InsertRasAccounts(&rTable, szConnectoid, SRV_POP3 | SRV_SMTP | SRV_IMAP | SRV_HTTPMAIL);
// Insert Ras Accounts
_InsertRasAccounts(&rTable, szConnectoid, SRV_NNTP);
// Move iConnectoid to next unique connectoid
while(1)
{
// Increment iConnectoid
iConnectoid++;
// Done
if (iConnectoid >= rTable.cRasAccts)
break;
// Indirect Sort
i = rTable.prgRasAcct[iConnectoid].dwSort;
// Next connectoid
if (lstrcmpi(szConnectoid, rTable.prgRasAcct[i].szConnectoid) != 0)
break;
}
}
// Execute the first task
m_cCurEvent = -1;
m_fNoSyncEvent = (ISFLAGSET(m_dwFlags, DELIVER_OFFLINE_SYNC) && m_pszAcctID != NULL && m_cSyncEvent == 0);
// Toggle Hangup When Done option
m_pUI->ChangeHangupOption(m_fRasSpooled, DwGetOption(OPT_DIALUP_HANGUP_DONE));
// $$HACK$$
EnableWindow(GetDlgItem(m_hwndUI, IDC_SP_STOP), TRUE);
// Notify
Notify(DELIVERY_NOTIFY_STARTING, 0);
// Start Next Task
PostMessage(m_hwndUI, IMAIL_NEXTTASK, 0, 0);
exit:
// Failure
if (FAILED(hr) || 0 == m_rEventTable.cEvents && !ISFLAGSET(dwFlags, DELIVER_SHOW))
{
// Not Busy
FLAGCLEAR(m_dwState, SPSTATE_BUSY);
// Forces a next task
PostMessage(m_hwndUI, IMAIL_NEXTTASK, 0, 0);
// No Flags
m_dwFlags = 0;
}
// Cleanup
SafeRelease(pEnum);
SafeRelease(pAccount);
SafeMemFree(m_pszAcctID);
SafeMemFree(rTable.prgLanAcct);
SafeMemFree(rTable.prgRasAcct);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::_InsertRasAccounts
// --------------------------------------------------------------------------------
void CSpoolerEngine::_InsertRasAccounts(LPACCOUNTTABLE pTable, LPCSTR pszConnectoid, DWORD dwSrvTypes)
{
// Locals
ULONG j;
ULONG i;
// Loop through the ras accounts and insert account on szConnetoid that are mail accounts
for (j=0; j<pTable->cRasAccts; j++)
{
// Indirect
i = pTable->prgRasAcct[j].dwSort;
// Is a mail account
if (pTable->prgRasAcct[i].dwServers & dwSrvTypes)
{
// On this connectoid
if (lstrcmpi(pszConnectoid, pTable->prgRasAcct[i].szConnectoid) == 0)
{
// We better have an account
Assert(pTable->prgRasAcct[i].pAccount);
// If dialog allowed or we can connect to the account
if (0 == (m_dwFlags & DELIVER_NODIAL) || S_OK == g_pConMan->CanConnect(pTable->prgRasAcct[i].pAccount))
{
_HrCreateTaskObject(&(pTable->prgRasAcct[i]));
}
// Release the account, we've added it
SafeRelease(pTable->prgRasAcct[i].pAccount);
}
}
}
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::_SortAccountTableByConnName
// --------------------------------------------------------------------------------
void CSpoolerEngine::_SortAccountTableByConnName(LONG left, LONG right, LPSPOOLERACCOUNT prgRasAcct)
{
// Locals
register long i, j;
DWORD k, temp;
i = left;
j = right;
k = prgRasAcct[(i + j) / 2].dwSort;
do
{
while(lstrcmpiA(prgRasAcct[prgRasAcct[i].dwSort].szConnectoid, prgRasAcct[k].szConnectoid) < 0 && i < right)
i++;
while (lstrcmpiA(prgRasAcct[prgRasAcct[j].dwSort].szConnectoid, prgRasAcct[k].szConnectoid) > 0 && j > left)
j--;
if (i <= j)
{
temp = prgRasAcct[i].dwSort;
prgRasAcct[i].dwSort = prgRasAcct[j].dwSort;
prgRasAcct[j].dwSort = temp;
i++; j--;
}
} while (i <= j);
if (left < j)
_SortAccountTableByConnName(left, j, prgRasAcct);
if (i < right)
_SortAccountTableByConnName(i, right, prgRasAcct);
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::_HrAppendAccountTable
// --------------------------------------------------------------------------------
HRESULT CSpoolerEngine::_HrAppendAccountTable(LPACCOUNTTABLE pTable, LPCSTR pszAcctID, DWORD dwServers)
{
// Locals
HRESULT hr=S_OK;
IImnAccount *pAccount=NULL;
// Invalid Arg
Assert(pTable && pszAcctID);
// Does the Account Exist...
CHECKHR(hr = m_pAcctMan->FindAccount(AP_ACCOUNT_ID, m_pszAcctID, &pAccount));
// Actual Append
CHECKHR(hr = _HrAppendAccountTable(pTable, pAccount, dwServers));
exit:
// Cleanup
SafeRelease(pAccount);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::_HrAppendAccountTable
// --------------------------------------------------------------------------------
HRESULT CSpoolerEngine::_HrAppendAccountTable(LPACCOUNTTABLE pTable, IImnAccount *pAccount, DWORD dwServers)
{
// Locals
HRESULT hr=S_OK;
LPSPOOLERACCOUNT pSpoolAcct;
DWORD dwConnType;
CHAR szConnectoid[CCHMAX_CONNECTOID];
// InvalidArg
Assert(pTable && pAccount);
// Init
*szConnectoid = '\0';
// Get the Account Connection Type
if (FAILED(pAccount->GetPropDw(AP_RAS_CONNECTION_TYPE, &dwConnType)))
{
// Default to Manual Connection
dwConnType = CONNECTION_TYPE_MANUAL;
}
// Otheriwse, get the connectoid if its a RAS connection
//else if (CONNECTION_TYPE_RAS == dwConnType || CONNECTION_TYPE_INETSETTINGS == dwConnType)
else if (CONNECTION_TYPE_RAS == dwConnType)
{
// AP_RAS_CONNECTOID
if (FAILED(pAccount->GetPropSz(AP_RAS_CONNECTOID, szConnectoid, ARRAYSIZE(szConnectoid))))
{
// Default to Lan Connection
dwConnType = CONNECTION_TYPE_MANUAL;
}
}
else if (CONNECTION_TYPE_INETSETTINGS == dwConnType)
{
DWORD dwFlags;
InternetGetConnectedStateExA(&dwFlags, szConnectoid, ARRAYSIZE(szConnectoid), 0);
if (!!(dwFlags & INTERNET_CONNECTION_MODEM))
{
dwConnType = CONNECTION_TYPE_RAS;
}
else
{
dwConnType = CONNECTION_TYPE_LAN;
}
}
// Which Table Should I insert into - LAN OR RAS
if (CONNECTION_TYPE_RAS == dwConnType)
{
// Better have a Connectoid
Assert(FIsEmptyA(szConnectoid) == FALSE);
// Grow the Table
if (pTable->cRasAccts + 1 > pTable->cRasAlloc)
{
// Temp
LPSPOOLERACCOUNT pRealloc=pTable->prgRasAcct;
// Realloc
CHECKALLOC(pTable->prgRasAcct = (LPSPOOLERACCOUNT)g_pMalloc->Realloc((LPVOID)pRealloc, (pTable->cRasAlloc + 5) * sizeof(SPOOLERACCOUNT)));
// Grow
pTable->cRasAlloc += 5;
}
// Readability
pSpoolAcct = &pTable->prgRasAcct[pTable->cRasAccts];
}
// Otherwise, LAN
else
{
// Grow the Table
if (pTable->cLanAccts + 1 > pTable->cLanAlloc)
{
// Temp
LPSPOOLERACCOUNT pRealloc=pTable->prgLanAcct;
// Realloc
CHECKALLOC(pTable->prgLanAcct = (LPSPOOLERACCOUNT)g_pMalloc->Realloc((LPVOID)pRealloc, (pTable->cLanAlloc + 5) * sizeof(SPOOLERACCOUNT)));
// Grow
pTable->cLanAlloc += 5;
}
// Readability
pSpoolAcct = &pTable->prgLanAcct[pTable->cLanAccts];
}
// Zero
ZeroMemory(pSpoolAcct, sizeof(SPOOLERACCOUNT));
// AddRef the Account
pSpoolAcct->pAccount = pAccount;
pSpoolAcct->pAccount->AddRef();
// Get the servers supported by the account
CHECKHR(hr = pAccount->GetServerTypes(&pSpoolAcct->dwServers));
//Mask the servers returned by the acctman with the servers we want to spool
pSpoolAcct->dwServers &= dwServers;
/*
if (pSpoolAcct->dwServers & (SRV_HTTPMAIL | SRV_IMAP))
{
//For each of these two servers, set the sync flags. See Bug# 51895
m_dwFlags |= (DELIVER_NEWSIMAP_OFFLINE | DELIVER_NEWSIMAP_OFFLINE_FLAGS);
}
*/
// Save Connection Type
pSpoolAcct->dwConnType = dwConnType;
// Save Connectoid
StrCpyN(pSpoolAcct->szConnectoid, szConnectoid, ARRAYSIZE(pSpoolAcct->szConnectoid));
// Increment Count and set the sort index
// if (CONNECTION_TYPE_RAS == dwConnType || dwConnType == CONNECTION_TYPE_INETSETTINGS)
if (CONNECTION_TYPE_RAS == dwConnType)
{
pSpoolAcct->dwSort = pTable->cRasAccts;
pTable->cRasAccts++;
}
else
{
pSpoolAcct->dwSort = pTable->cLanAccts;
pTable->cLanAccts++;
}
// Total Acount
pTable->cAccounts++;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Close
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::Close(void)
{
// Locals
HRESULT hr=S_OK;
// Thread Safety
EnterCriticalSection(&m_cs);
_StopPolling();
// Was I Threaded ?
if (NULL != m_hThread)
{
hr = TrapError(E_FAIL);
goto exit;
}
// Shutdown
CHECKHR(hr = Shutdown());
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Notify
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::Notify(DELIVERYNOTIFYTYPE notify, LPARAM lParam)
{
// Locals
ULONG i;
// Enter it
EnterCriticalSection(&m_cs);
// Same thread we were created on...
Assert(ISSPOOLERTHREAD);
// Loop through registered views
for (i=0; i<m_rViewRegister.cViewAlloc; i++)
if (m_rViewRegister.rghwndView[i] != 0 && IsWindow(m_rViewRegister.rghwndView[i]))
PostMessage(m_rViewRegister.rghwndView[i], MVM_SPOOLERDELIVERY, (WPARAM)notify, lParam);
// Enter it
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Advise
// --------------------------------------------------------------------------------
#define VIEW_TABLE_GROWSIZE 8
STDMETHODIMP CSpoolerEngine::Advise(HWND hwndView, BOOL fRegister)
{
// Locals
ULONG i;
HRESULT hr=S_OK;
// Enter it
EnterCriticalSection(&m_cs);
// If NULL view handle...
if (!hwndView)
{
hr = TrapError(E_FAIL);
goto exit;
}
// Are we registering the window
if (fRegister)
{
// Do we need to grow the register array
if (m_rViewRegister.cViewAlloc == m_rViewRegister.cView)
{
// Add some more
m_rViewRegister.cViewAlloc += VIEW_TABLE_GROWSIZE;
// Realloc the array
if (!MemRealloc((LPVOID *)&m_rViewRegister.rghwndView, sizeof(HWND) * m_rViewRegister.cViewAlloc))
{
m_rViewRegister.cViewAlloc -= VIEW_TABLE_GROWSIZE;
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
// Zeroinit the new items
ZeroMemory(&m_rViewRegister.rghwndView[m_rViewRegister.cView], sizeof(HWND) * (m_rViewRegister.cViewAlloc - m_rViewRegister.cView));
}
// Fill the first NULL item with the new view
for (i=0; i<m_rViewRegister.cViewAlloc; i++)
{
// If empty, lets fill it
if (!m_rViewRegister.rghwndView[i])
{
m_rViewRegister.rghwndView[i] = hwndView;
m_rViewRegister.cView++;
break;
}
}
// Did we insert it ?
AssertSz(i != m_rViewRegister.cViewAlloc, "didn't find a hole??");
}
// Otherwise, find and remove the view
else
{
// Look for hwndView
for (i=0; i<m_rViewRegister.cViewAlloc; i++)
{
// Is this it
if (m_rViewRegister.rghwndView[i] == hwndView)
{
m_rViewRegister.rghwndView[i] = NULL;
m_rViewRegister.cView--;
break;
}
}
}
exit:
// Leave CS
LeaveCriticalSection(&m_cs);
// If this is the first view to register, and there is a background poll pending, lets do it...
if (fRegister && m_rViewRegister.cView == 1 && m_fBackgroundPollPending)
{
StartDelivery(NULL, NULL, FOLDERID_INVALID, DELIVER_BACKGROUND_POLL);
m_fBackgroundPollPending = FALSE;
}
else if (m_rViewRegister.cView == 0)
{
// remove the notify icon if there aren't any views registered
UpdateTrayIcon(TRAYICON_REMOVE);
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::UpdateTrayIcon
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::UpdateTrayIcon(TRAYICONTYPE type)
{
// Locals
NOTIFYICONDATA nid;
HWND hwnd=NULL;
ULONG i;
// Enter it
EnterCriticalSection(&m_cs);
// Add the icon...
if (TRAYICON_ADD == type)
{
// Loop through registered views
for (i=0; i<m_rViewRegister.cViewAlloc; i++)
{
if (m_rViewRegister.rghwndView[i] && IsWindow(m_rViewRegister.rghwndView[i]))
{
hwnd = m_rViewRegister.rghwndView[i];
break;
}
}
// No window...
if (hwnd == NULL)
goto exit;
}
// Otherwise, if no notify window, were done
else if (m_hwndTray == NULL)
goto exit;
// Set Tray Notify Icon Data
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.uID = 0;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
nid.uCallbackMessage = MVM_NOTIFYICONEVENT;
nid.hIcon = LoadIcon(g_hLocRes, MAKEINTRESOURCE(idiNewMailNotify));
LoadString(g_hLocRes, idsNewMailNotify, nid.szTip, sizeof(nid.szTip));
// Hmmm
if (TRAYICON_REMOVE == type || (m_hwndTray != NULL && m_hwndTray != hwnd))
{
nid.hWnd = m_hwndTray;
Shell_NotifyIcon(NIM_DELETE, &nid);
m_hwndTray = NULL;
}
// Add
if (TRAYICON_ADD == type)
{
nid.hWnd = hwnd;
Shell_NotifyIcon(NIM_ADD, &nid);
m_hwndTray = hwnd;
}
exit:
// Leave CS
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::RegisterEvent
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::RegisterEvent(LPCSTR pszDescription, ISpoolerTask *pTask,
DWORD_PTR dwTwinkie, IImnAccount *pAccount,
LPEVENTID peid)
{
HRESULT hr = S_OK;
LPSPOOLEREVENT pEvent = NULL;
LPWSTR pwszConn = NULL;
WCHAR wsz[CCHMAX_STRINGRES];
// Verify the input parameters
if (FIsEmptyA(pszDescription) || pTask == NULL)
return (E_INVALIDARG);
EnterCriticalSection(&m_cs);
// Grow the Table
if (m_rEventTable.cEvents + 1 > m_rEventTable.cEventsAlloc)
{
// Temp
LPSPOOLEREVENT pRealloc = m_rEventTable.prgEvents;
// Realloc
CHECKALLOC(m_rEventTable.prgEvents = (LPSPOOLEREVENT) g_pMalloc->Realloc((LPVOID)pRealloc, (m_rEventTable.cEventsAlloc + 5) * sizeof(SPOOLEREVENT)));
// Grow
m_rEventTable.cEventsAlloc += 5;
}
pEvent = &m_rEventTable.prgEvents[m_rEventTable.cEvents];
// Insert the event
pEvent->eid = m_rEventTable.cEvents;
pEvent->pSpoolerTask = pTask;
pEvent->pSpoolerTask->AddRef();
pEvent->dwTwinkie = dwTwinkie;
pEvent->pAccount = pAccount;
pEvent->pAccount->AddRef();
// Get the Account Connection Type
if (FAILED(pAccount->GetPropDw(AP_RAS_CONNECTION_TYPE, &pEvent->dwConnType)))
{
// Default to Manual Connection
pEvent->dwConnType = CONNECTION_TYPE_MANUAL;
}
// Otheriwse, get the connectoid if its a RAS connection
//else if (CONNECTION_TYPE_RAS == pEvent->dwConnType || CONNECTION_TYPE_INETSETTINGS == pEvent->dwConnType)
else if (CONNECTION_TYPE_RAS == pEvent->dwConnType)
{
// AP_RAS_CONNECTOID
if (FAILED(pAccount->GetPropSz(AP_RAS_CONNECTOID, pEvent->szConnectoid, ARRAYSIZE(pEvent->szConnectoid))))
{
// Default to Lan Connection
pEvent->dwConnType = CONNECTION_TYPE_MANUAL;
}
}
else if (CONNECTION_TYPE_INETSETTINGS == pEvent->dwConnType)
{
DWORD dwFlags = 0;
InternetGetConnectedStateExA(&dwFlags, pEvent->szConnectoid, ARRAYSIZE(pEvent->szConnectoid), 0);
if (!!(dwFlags & INTERNET_CONNECTION_MODEM))
{
pEvent->dwConnType = CONNECTION_TYPE_RAS;
}
else
{
pEvent->dwConnType = CONNECTION_TYPE_LAN;
}
}
// Get the connection name to put in the task list
if (pEvent->dwConnType == CONNECTION_TYPE_LAN)
{
AthLoadStringW(idsConnectionLAN, wsz, ARRAYSIZE(wsz));
pwszConn = wsz;
}
else if (pEvent->dwConnType == CONNECTION_TYPE_MANUAL)
{
AthLoadStringW(idsConnectionManual, wsz, ARRAYSIZE(wsz));
pwszConn = wsz;
m_fRasSpooled = TRUE;
}
else
{
IF_NULLEXIT(pwszConn = PszToUnicode(CP_ACP, pEvent->szConnectoid));
m_fRasSpooled = TRUE;
}
// Add the event description to the UI
if (m_pUI)
{
m_pUI->InsertEvent(m_rEventTable.prgEvents[m_rEventTable.cEvents].eid, pszDescription, pwszConn);
m_pUI->SetTaskCounts(0, m_rEventTable.cEvents + 1);
}
// Check to see if the task cares about the event id
if (peid)
*peid = m_rEventTable.prgEvents[m_rEventTable.cEvents].eid;
m_rEventTable.cEvents++;
exit:
if (pwszConn != wsz)
MemFree(pwszConn);
LeaveCriticalSection(&m_cs);
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::EventDone
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::EventDone(EVENTID eid, EVENTCOMPLETEDSTATUS status)
{
LPSPOOLEREVENT pEvent;
// Update the UI
if (EVENT_SUCCEEDED == status)
{
m_rEventTable.cSucceeded++;
m_pUI->SetTaskCounts(m_rEventTable.cSucceeded, m_rEventTable.cEvents);
m_pUI->UpdateEventState(eid, IMAGE_CHECK, NULL, MAKEINTRESOURCE(idsStateCompleted));
}
else if (EVENT_WARNINGS == status)
{
m_pUI->UpdateEventState(eid, IMAGE_WARNING, NULL, MAKEINTRESOURCE(idsStateWarnings));
}
else if (EVENT_FAILED == status)
{
m_pUI->UpdateEventState(eid, IMAGE_ERROR, NULL, MAKEINTRESOURCE(idsStateFailed));
}
else if (EVENT_CANCELED == status)
{
m_pUI->UpdateEventState(eid, IMAGE_WARNING, NULL, MAKEINTRESOURCE(idsStateCanceled));
}
// When an event completes, we can move to the next item in the queue unless
// we're done.
if (!ISFLAGCLEAR(m_dwState, SPSTATE_CANCEL))
{
m_cCurEvent++;
pEvent = &m_rEventTable.prgEvents[m_cCurEvent];
for ( ; m_cCurEvent < m_rEventTable.cEvents; m_cCurEvent++, pEvent++)
{
pEvent->pSpoolerTask->CancelEvent(pEvent->eid, pEvent->dwTwinkie);
}
}
// Next Task
PostMessage(m_hwndUI, IMAIL_NEXTTASK, 0, 0);
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::_OpenMailLogFile
// --------------------------------------------------------------------------------
HRESULT CSpoolerEngine::_OpenMailLogFile(DWORD dwOptionId, LPCSTR pszPrefix,
LPCSTR pszFileName, ILogFile **ppLogFile)
{
// Locals
HRESULT hr=S_OK;
CHAR szLogFile[MAX_PATH];
CHAR szDirectory[MAX_PATH];
DWORD dw;
// Invalid Args
Assert(pszPrefix && ppLogFile);
// Log file path
dw = GetOption(dwOptionId, szLogFile, ARRAYSIZE(szLogFile));
// If we found a filepath and the file exists
if (0 == dw || FALSE == PathFileExists(szLogFile))
{
// Get the Store Root Directory
GetStoreRootDirectory(szDirectory, ARRAYSIZE(szDirectory));
// Ends with a backslash ?
IF_FAILEXIT(hr = MakeFilePath(szDirectory, pszFileName, c_szEmpty, szLogFile, ARRAYSIZE(szLogFile)));
// Reset the Option
SetOption(dwOptionId, szLogFile, lstrlen(szLogFile) + 1, NULL, 0);
}
// Create the log file
IF_FAILEXIT(hr = CreateLogFile(g_hInst, szLogFile, pszPrefix, DONT_TRUNCATE, ppLogFile,
FILE_SHARE_READ | FILE_SHARE_WRITE));
exit:
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::BindToObject
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::BindToObject(REFIID riid, void **ppvObject)
{
// Locals
HRESULT hr=S_OK;
// Invalid Arg
if (NULL == ppvObject)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// IID_ISpoolerBindContext
if (IID_ISpoolerBindContext == riid)
*ppvObject = (ISpoolerBindContext *)this;
// IID_CUidlCache
else if (IID_CUidlCache == riid)
{
// Doesn't Exist
if (NULL == m_pUidlCache)
{
// Open the Cache
CHECKHR(hr = OpenUidlCache(&m_pUidlCache));
}
// AddRef it
m_pUidlCache->AddRef();
// Return It
*ppvObject = (IDatabase *)m_pUidlCache;
}
// IImnAccountManager
else if (IID_IImnAccountManager == riid)
{
// Doesn't Exist
if (NULL == m_pAcctMan)
{
AssertSz(FALSE, "The Account Manager Could Not Be Created.");
hr = TrapError(E_FAIL);
goto exit;
}
// AddRef It
m_pAcctMan->AddRef();
// Return It
*ppvObject = (IImnAccountManager *)m_pAcctMan;
}
// ISpoolerUI
else if (IID_ISpoolerUI == riid)
{
// Doesn't Exist
if (NULL == m_pUI)
{
AssertSz(FALSE, "The Spooler UI Object Could Not Be Created.");
hr = TrapError(E_FAIL);
goto exit;
}
// AddRef It
m_pUI->AddRef();
// Return It
*ppvObject = (ISpoolerUI *)m_pUI;
}
// IID_CLocalStoreDeleted
else if (IID_CLocalStoreDeleted == riid)
{
// Open Special Folder
CHECKHR(hr = g_pStore->OpenSpecialFolder(FOLDERID_LOCAL_STORE, NULL, FOLDER_DELETED, (IMessageFolder **)ppvObject));
}
// IID_CLocalStoreInbox
else if (IID_CLocalStoreInbox == riid)
{
// Open Special Folder
CHECKHR(hr = g_pStore->OpenSpecialFolder(FOLDERID_LOCAL_STORE, NULL, FOLDER_INBOX, (IMessageFolder **)ppvObject));
}
// IID_CLocalStoreOutbox
else if (IID_CLocalStoreOutbox == riid)
{
// Open Special Folder
CHECKHR(hr = g_pStore->OpenSpecialFolder(FOLDERID_LOCAL_STORE, NULL, FOLDER_OUTBOX, (IMessageFolder **)ppvObject));
}
// IID_CLocalStoreSentItems
else if (IID_CLocalStoreSentItems == riid)
{
// Open Special Folder
CHECKHR(hr = g_pStore->OpenSpecialFolder(FOLDERID_LOCAL_STORE, NULL, FOLDER_SENT, (IMessageFolder **)ppvObject));
}
// IID_CPop3LogFile
else if (IID_CPop3LogFile == riid)
{
// Create logging objects
if (!DwGetOption(OPT_MAILLOG))
{
hr = TrapError(E_FAIL);
goto exit;
}
// Do I have the logfile yet ?
if (NULL == m_pPop3LogFile)
{
// Open the log file
CHECKHR(hr = _OpenMailLogFile(OPT_MAILPOP3LOGFILE, "POP3", c_szDefaultPop3Log, &m_pPop3LogFile));
}
// AddRef It
m_pPop3LogFile->AddRef();
// Return It
*ppvObject = (ILogFile *)m_pPop3LogFile;
}
// IID_CSmtpLogFile
else if (IID_CSmtpLogFile == riid)
{
// Create logging objects
if (!DwGetOption(OPT_MAILLOG))
{
hr = TrapError(E_FAIL);
goto exit;
}
// Do I have the logfile yet ?
if (NULL == m_pSmtpLogFile)
{
// Open the log file
CHECKHR(hr = _OpenMailLogFile(OPT_MAILSMTPLOGFILE, "SMTP", c_szDefaultSmtpLog, &m_pSmtpLogFile));
}
// AddRef It
m_pSmtpLogFile->AddRef();
// Return It
*ppvObject = (ILogFile *)m_pSmtpLogFile;
}
// E_NOTINTERFACE
else
{
hr = TrapError(E_NOINTERFACE);
goto exit;
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::TaskFromEventId
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::TaskFromEventId(EVENTID eid, ISpoolerTask *ppTask)
{
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Cancel
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::Cancel(void)
{
EnterCriticalSection(&m_cs);
if (ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
Assert(m_rEventTable.cEvents && m_rEventTable.prgEvents);
FLAGSET(m_dwState, SPSTATE_CANCEL);
if (m_rEventTable.prgEvents && m_rEventTable.prgEvents[m_cCurEvent].pSpoolerTask)
m_rEventTable.prgEvents[m_cCurEvent].pSpoolerTask->Cancel();
}
LeaveCriticalSection(&m_cs);
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::GetThreadInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::GetThreadInfo(LPDWORD pdwThreadId, HTHREAD* phThread)
{
// Invalid Arg
if (NULL == pdwThreadId || NULL == phThread)
return TrapError(E_INVALIDARG);
// Thread Safety
EnterCriticalSection(&m_cs);
// Return It
*pdwThreadId = m_dwThreadId;
*phThread = m_hThread;
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::QueryEndSession
// --------------------------------------------------------------------------------
STDMETHODIMP_(LRESULT) CSpoolerEngine::QueryEndSession(WPARAM wParam, LPARAM lParam)
{
if (ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
if (AthMessageBoxW(NULL, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsAbortDownload), NULL, MB_YESNO|MB_ICONEXCLAMATION ) == IDNO)
return FALSE;
}
Cancel();
return TRUE;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Shutdown
// --------------------------------------------------------------------------------
HRESULT CSpoolerEngine::Shutdown(void)
{
// Thread Safety
EnterCriticalSection(&m_cs);
// Better shutdown on the same thread we started on
Assert(ISSPOOLERTHREAD);
// Stop Polling
_StopPolling();
// Are we currently busy
_ShutdownTasks();
// If we're executing, then we need to stop and release all the tasks
for (UINT i = 0; i < m_rEventTable.cEvents; i++)
{
SafeRelease(m_rEventTable.prgEvents[i].pSpoolerTask);
SafeRelease(m_rEventTable.prgEvents[i].pAccount);
}
// Release Objects
SafeRelease(m_pUI);
SafeRelease(m_pAcctMan);
SafeRelease(m_pUidlCache);
SafeMemFree(m_pszAcctID);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::Shutdown
// --------------------------------------------------------------------------------
void CSpoolerEngine::_ShutdownTasks(void)
{
// Locals
HRESULT hr=S_OK;
MSG msg;
BOOL fFlushOutbox=FALSE;
IMessageFolder *pOutbox=NULL;
int ResId;
BOOL fOffline = FALSE;
// Clear queued events
m_dwQueued = 0;
// Check for unsent mail
if (g_fCheckOutboxOnShutdown)
{
// Open the Outbox
if (SUCCEEDED(BindToObject(IID_CLocalStoreOutbox, (LPVOID *)&pOutbox)))
{
// Locals
HROWSET hRowset=NULL;
MESSAGEINFO MsgInfo={0};
// Create a Rowset
if (SUCCEEDED(pOutbox->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset)))
{
// While
while (S_OK == pOutbox->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL))
{
// Has this message been submitted and is it a mail message
if (((MsgInfo.dwFlags & (ARF_SUBMITTED | ARF_NEWSMSG)) == ARF_SUBMITTED) &&
(!ISFLAGSET(m_dwState, SPSTATE_BUSY)))
{
fOffline = g_pConMan->IsGlobalOffline();
if (fOffline)
ResId = idsWarnUnsentMailOffline;
else
ResId = idsWarnUnsentMail;
// Prompt to flush the outbox
if (AthMessageBoxW(NULL, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(ResId), NULL, MB_YESNO|MB_ICONEXCLAMATION ) == IDYES)
{
// Go online
if (fOffline)
g_pConMan->SetGlobalOffline(FALSE);
// Flush on exit
fFlushOutbox = TRUE;
}
// Done
break;
}
// Free MsgInfo
pOutbox->FreeRecord(&MsgInfo);
}
// Free MsgInfo
pOutbox->FreeRecord(&MsgInfo);
// Close the Rowset
pOutbox->CloseRowset(&hRowset);
}
}
}
// Release outbox
SafeRelease(pOutbox);
// Set Shutdown state
FLAGSET(m_dwState, SPSTATE_SHUTDOWN);
// If not busy now, start the flush
if (!ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
// Flush the Outbox
if (fFlushOutbox)
{
// No need to flush again
fFlushOutbox = FALSE;
// Start the delivery
_HrStartDeliveryActual(DELIVER_SEND | DELIVER_SMTP_TYPE | DELIVER_HTTP_TYPE );
// We are busy
FLAGSET(m_dwState, SPSTATE_BUSY);
}
// Otheriwse, were done...
else
goto exit;
}
// We must wait for current cycle to finish
if (ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
// Lets show progress...
FLAGCLEAR(m_dwFlags, DELIVER_NOUI | DELIVER_BACKGROUND);
// Show the ui object
m_pUI->ShowWindow(SW_SHOW);
SetForegroundWindow(m_hwndUI);
// Here's a nice hack to disable the Hide button
EnableWindow(GetDlgItem(m_hwndUI, IDC_SP_MINIMIZE), FALSE);
// Set focus on the dialog
SetFocus(m_hwndUI);
// Set the focus onto the Stop Button
SetFocus(GetDlgItem(m_hwndUI, IDC_SP_STOP));
// Pump messages until current cycle is complete
while(GetMessage(&msg, NULL, 0, 0))
{
// Give the message to the UI object
if (m_pUI->IsDialogMessage(&msg) == S_FALSE && IsDialogMessage(&msg) == S_FALSE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// If no cycle, were done
if (!ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
// Do the Outbox
if (fFlushOutbox)
{
// Were the errors...
if (S_OK == m_pUI->AreThereErrors())
{
// Errors were encountered during the last Delivery Cycle. Would you still like to send the messages that are in your Outbox?
if (AthMessageBoxW(NULL, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsWarnErrorUnsentMail), NULL, MB_YESNO | MB_ICONEXCLAMATION ) == IDNO)
break;
}
// No need to flush again
fFlushOutbox = FALSE;
// Start the delivery
_HrStartDeliveryActual(DELIVER_SEND | DELIVER_SMTP_TYPE | DELIVER_HTTP_TYPE );
// We are busy
FLAGSET(m_dwState, SPSTATE_BUSY);
}
else
break;
}
}
}
// Were the errors...
if (S_OK == m_pUI->AreThereErrors() && !g_pInstance->SwitchingUsers())
{
// Tell the ui to go into Shutdown Mode
m_pUI->Shutdown();
// Show the ui object
m_pUI->ShowWindow(SW_SHOW);
SetForegroundWindow(m_hwndUI);
// We are busy
FLAGCLEAR(m_dwState, SPSTATE_UISHUTDOWN);
// Set the focus onto the Stop Button
SetFocus(GetDlgItem(m_hwndUI, IDC_SP_MINIMIZE));
// Pump messages until current cycle is complete
while(GetMessage(&msg, NULL, 0, 0))
{
// Give the message to the UI object
if (m_pUI->IsDialogMessage(&msg) == S_FALSE && IsDialogMessage(&msg) == S_FALSE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// User Pressed close yet ?
if (ISFLAGSET(m_dwState, SPSTATE_UISHUTDOWN))
break;
}
}
exit:
// Cleanup
SafeRelease(pOutbox);
// Done
return;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::UIShutdown
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::UIShutdown(void)
{
EnterCriticalSection(&m_cs);
FLAGSET(m_dwState, SPSTATE_UISHUTDOWN);
LeaveCriticalSection(&m_cs);
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::PumpMessages
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::PumpMessages(void)
{
// Locals
MSG msg;
BOOL fQuit=FALSE;
// Thread Safety
EnterCriticalSection(&m_cs);
// Pump messages until current cycle is complete
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// WM_QUIT
if (WM_QUIT == msg.message)
{
// Make a note that a quit was received
fQuit = TRUE;
// If not running with now ui, set to foreground
if (FALSE == IsWindowVisible(m_hwndUI))
{
m_pUI->ShowWindow(SW_SHOW);
SetForegroundWindow(m_hwndUI);
}
}
// Give the message to the UI object
if (m_pUI->IsDialogMessage(&msg) == S_FALSE && IsDialogMessage(&msg) == S_FALSE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Repost the quit message
if (fQuit)
PostThreadMessage(m_dwThreadId, WM_QUIT, 0, 0);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CSpoolerEngine::OnWindowMessage - S_OK (I Handled the message)
// --------------------------------------------------------------------------------
STDMETHODIMP CSpoolerEngine::OnWindowMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Locals
HRESULT hr=S_OK;
DWORD dw;
// Thread Safety
EnterCriticalSection(&m_cs);
// Handle the window message
switch(uMsg)
{
case IMAIL_DELIVERNOW:
_HrStartDeliveryActual((DWORD)lParam);
break;
case IMAIL_NEXTTASK:
_HrStartNextEvent();
break;
case WM_TIMER:
if (wParam == IMAIL_POOLFORMAIL)
{
KillTimer(hwnd, IMAIL_POOLFORMAIL);
_DoBackgroundPoll();
}
break;
case CM_OPTIONADVISE:
// Check to see if the polling option changed
if (wParam == OPT_POLLFORMSGS)
{
dw = DwGetOption(OPT_POLLFORMSGS);
if (dw != OPTION_OFF)
{
if (dw != m_dwPollInterval)
{
KillTimer(hwnd, IMAIL_POOLFORMAIL);
SetTimer(hwnd, IMAIL_POOLFORMAIL, dw, NULL);
m_dwPollInterval = dw;
}
}
else
{
KillTimer(hwnd, IMAIL_POOLFORMAIL);
m_dwPollInterval = 0;
}
}
// Check to see if the hang up option changed
if (wParam == OPT_DIALUP_HANGUP_DONE)
{
dw = DwGetOption(OPT_DIALUP_HANGUP_DONE);
m_pUI->ChangeHangupOption(m_fRasSpooled, dw);
}
break;
default:
hr = S_FALSE;
break;
}
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
HRESULT CSpoolerEngine::_HrCreateTaskObject(LPSPOOLERACCOUNT pSpoolerAcct)
{
DWORD cEvents, cEventsT;
HRESULT hr=S_OK;
// Let's try pumping messages to see if this get's any smoother
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Give the message to the UI object
if (m_pUI->IsDialogMessage(&msg) == S_FALSE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Create the appropriate task object. Start with SMTP
if (pSpoolerAcct->dwServers & SRV_SMTP && m_dwFlags & DELIVER_SEND)
{
CSmtpTask *pSmtpTask = new CSmtpTask();
if (pSmtpTask)
{
// Initialize the news task
if (SUCCEEDED(hr = pSmtpTask->Init(m_dwFlags, (ISpoolerBindContext *) this)))
{
// Tell the task to build it's event list
hr = pSmtpTask->BuildEvents(m_pUI, pSpoolerAcct->pAccount, 0);
}
pSmtpTask->Release();
}
}
// HTTPMail Servers
if ((!!(pSpoolerAcct->dwServers & SRV_HTTPMAIL)) && (!!(m_dwFlags & (DELIVER_SEND | DELIVER_POLL))))
{
/* DWORD dw;
if (SUCCEEDED(pSpoolerAcct->pAccount->GetPropDw(AP_HTTPMAIL_DOMAIN_MSN, &dw)) && dw)
{
if(HideHotmail())
return(hr);
}
*/
CHTTPTask *pHTTPTask = new CHTTPTask();
if (pHTTPTask)
{
// initialize the http mail task
if (SUCCEEDED(hr = pHTTPTask->Init(m_dwFlags, (ISpoolerBindContext *)this)))
hr = pHTTPTask->BuildEvents(m_pUI, pSpoolerAcct->pAccount, 0);
pHTTPTask->Release();
}
}
// POP3 Servers
if (pSpoolerAcct->dwServers & SRV_POP3 && m_dwFlags & DELIVER_MAIL_RECV)
{
// Skipping Marked Pop3 Accounts
DWORD dw=FALSE;
if (ISFLAGSET(m_dwFlags, DELIVER_NOSKIP) || FAILED(pSpoolerAcct->pAccount->GetPropDw(AP_POP3_SKIP, &dw)) || FALSE == dw)
{
CPop3Task *pPop3Task = new CPop3Task();
if (pPop3Task)
{
// Initialize the news task
if (SUCCEEDED(hr = pPop3Task->Init(m_dwFlags, (ISpoolerBindContext *) this)))
{
// Tell the task to build it's event list
hr = pPop3Task->BuildEvents(m_pUI, pSpoolerAcct->pAccount, 0);
}
pPop3Task->Release();
}
}
}
// Servers that support offline sync
if ((pSpoolerAcct->dwServers & (SRV_NNTP | SRV_IMAP | SRV_HTTPMAIL)))
{
if (!!((DELIVER_POLL | DELIVER_SEND) & m_dwFlags))
{
CNewsTask *pNewsTask = new CNewsTask();
if (pNewsTask)
{
// Initialize the news task
if (SUCCEEDED(hr = pNewsTask->Init(m_dwFlags, (ISpoolerBindContext *) this)))
{
// Tell the task to build it's event list
hr = pNewsTask->BuildEvents(m_pUI, pSpoolerAcct->pAccount, 0);
}
pNewsTask->Release();
}
}
if ((m_dwFlags & DELIVER_WATCH) && !(m_dwFlags & DELIVER_NO_NEWSPOLL)
&& (pSpoolerAcct->dwServers & SRV_NNTP))
{
CWatchTask *pWatchTask = new CWatchTask();
if (pWatchTask)
{
if (SUCCEEDED(hr = pWatchTask->Init(m_dwFlags, (ISpoolerBindContext *) this)))
{
hr = pWatchTask->BuildEvents(m_pUI, pSpoolerAcct->pAccount, m_idFolder);
}
pWatchTask->Release();
}
}
cEvents = m_rEventTable.cEvents;
if (m_dwFlags & DELIVER_OFFLINE_FLAGS)
{
COfflineTask *pOfflineTask = new COfflineTask();
if (pOfflineTask)
{
// Initialize the offline task
if (SUCCEEDED(hr = pOfflineTask->Init(m_dwFlags, (ISpoolerBindContext *) this)))
{
// Tell the task to build it's event list
hr = pOfflineTask->BuildEvents(m_pUI, pSpoolerAcct->pAccount, m_idFolder);
}
pOfflineTask->Release();
}
}
cEventsT = m_rEventTable.cEvents;
m_cSyncEvent += (cEventsT - cEvents);
}
return (hr);
}
STDMETHODIMP CSpoolerEngine::IsDialogMessage(LPMSG pMsg)
{
HRESULT hr=S_FALSE;
EnterCriticalSection(&m_cs);
if (ISFLAGSET(m_dwState, SPSTATE_BUSY) && (LONG)m_cCurEvent >= 0 && m_cCurEvent < m_rEventTable.cEvents && m_rEventTable.prgEvents[m_cCurEvent].pSpoolerTask)
hr = m_rEventTable.prgEvents[m_cCurEvent].pSpoolerTask->IsDialogMessage(pMsg);
LeaveCriticalSection(&m_cs);
return hr;
}
HRESULT CSpoolerEngine::_HrStartNextEvent(void)
{
HRESULT hr = S_OK;
TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
TCHAR szBuf2[CCHMAX_CONNECTOID + 2];
EnterCriticalSection(&m_cs);
// RAID-30804 Release the current task. This makes sure that objects like the pop3 object
// release it's locks on the store.
if ((LONG)m_cCurEvent >= 0 && m_cCurEvent < m_rEventTable.cEvents)
{
SafeRelease(m_rEventTable.prgEvents[m_cCurEvent].pSpoolerTask);
}
// Advance to the next event
m_cCurEvent++;
// Check to see if that pushes us over the edge
if (m_cCurEvent >= m_rEventTable.cEvents)
{
_HrGoIdle();
}
else
{
LPSPOOLEREVENT pEvent = &m_rEventTable.prgEvents[m_cCurEvent];
// Check to see if we need to connect first
if (pEvent->dwConnType == CONNECTION_TYPE_RAS)
{
// Check to see if we need to connect
if (m_cCurEvent == 0 || (0 != lstrcmpi(pEvent->szConnectoid, m_rEventTable.prgEvents[m_cCurEvent - 1].szConnectoid)) || S_OK != g_pConMan->CanConnect(pEvent->szConnectoid))
{
hr = _HrDoRasConnect(pEvent);
if (hr == HR_E_OFFLINE || hr == HR_E_USER_CANCEL_CONNECT || hr == HR_E_DIALING_INPROGRESS)
{
for (m_cCurEvent; m_cCurEvent < m_rEventTable.cEvents; m_cCurEvent++)
{
// Mark the event as cancelled
m_pUI->UpdateEventState(m_cCurEvent, IMAGE_WARNING, NULL, MAKEINTRESOURCE(idsStateCanceled));
//This is a hack to not show errors. In this case we just want to behave as though this
//operation succeeded.
m_rEventTable.cSucceeded++;
// Check to see if we've found a different connection yet
if ((m_cCurEvent == m_rEventTable.cEvents - 1) ||
0 != lstrcmpi(m_rEventTable.prgEvents[m_cCurEvent].szConnectoid, m_rEventTable.prgEvents[m_cCurEvent + 1].szConnectoid))
break;
}
}
else
if (FAILED(hr))
{
// We need to mark all the events for this connection as failed as
// well.
for (m_cCurEvent; m_cCurEvent < m_rEventTable.cEvents; m_cCurEvent++)
{
// Mark the event as failed
m_pUI->UpdateEventState(m_cCurEvent, IMAGE_ERROR, NULL, MAKEINTRESOURCE(idsStateFailed));
// Check to see if we've found a different connection yet
if ((m_cCurEvent == m_rEventTable.cEvents - 1) ||
0 != lstrcmpi(m_rEventTable.prgEvents[m_cCurEvent].szConnectoid, m_rEventTable.prgEvents[m_cCurEvent + 1].szConnectoid))
break;
}
// Insert an error for this failure
AthLoadString(idsRasErrorGeneralWithName, szRes, ARRAYSIZE(szRes));
wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, PszEscapeMenuStringA(m_rEventTable.prgEvents[m_cCurEvent].szConnectoid, szBuf2, ARRAYSIZE(szBuf2)));
m_pUI->InsertError(m_cCurEvent, szBuf);
hr = E_FAIL;
}
if (hr != S_OK)
{
// Move on to the next task
PostMessage(m_hwndUI, IMAIL_NEXTTASK, 0, 0);
goto exit;
}
}
}
if (FAILED(pEvent->pSpoolerTask->Execute(pEvent->eid, pEvent->dwTwinkie)))
{
m_pUI->UpdateEventState(pEvent->eid, IMAGE_ERROR, NULL, MAKEINTRESOURCE(idsStateFailed));
PostMessage(m_hwndUI, IMAIL_NEXTTASK, 0, 0);
}
else
m_pUI->UpdateEventState(pEvent->eid, IMAGE_EXECUTE, NULL, MAKEINTRESOURCE(idsStateExecuting));
}
exit:
LeaveCriticalSection(&m_cs);
//return (S_OK);
return hr;
}
HRESULT CSpoolerEngine::_HrDoRasConnect(const LPSPOOLEREVENT pEvent)
{
HRESULT hr;
HWND hwndParent = m_hwndUI;
// Check to see if we already can connect
hr = g_pConMan->CanConnect(pEvent->pAccount);
if (S_OK == hr)
return (S_OK);
// Check to see if we're allowed to dial
if (m_dwFlags & DELIVER_NODIAL)
return (E_FAIL);
if (m_dwFlags & DELIVER_DIAL_ALWAYS)
{
if (hr == HR_E_OFFLINE)
{
g_pConMan->SetGlobalOffline(FALSE);
m_fOfflineWhenDone = TRUE;
}
}
// Check to see if the parent window exists and is visible
if (!IsWindow(hwndParent) || !IsWindowVisible(hwndParent))
{
// Parent the UI to the browser window
hwndParent = FindWindowEx(NULL, NULL, c_szBrowserWndClass, 0);
}
// Try to connect
hr = g_pConMan->Connect(pEvent->pAccount, hwndParent, TRUE);
if (S_OK == hr)
{
m_fIDialed = TRUE;
return S_OK;
}
else
return hr;
}
HRESULT CSpoolerEngine::_HrGoIdle(void)
{
EnterCriticalSection(&m_cs);
// We need to hangup every time to be compatible with OE4. Bug# 75222
if (m_fRasSpooled && g_pConMan)
{
if (!!DwGetOption(OPT_DIALUP_HANGUP_DONE))
{
g_pConMan->Disconnect(m_hwndUI, FALSE, FALSE, FALSE);
}
}
// Check to see if we need to go offline now
// I'm disabling this for bug #17578.
if (m_fOfflineWhenDone)
{
g_pConMan->SetGlobalOffline(TRUE);
m_fOfflineWhenDone = FALSE;
}
// Tell the UI to idle
if (ISFLAGSET(m_dwState, SPSTATE_CANCEL))
m_pUI->GoIdle(m_dwState, ISFLAGSET(m_dwState, SPSTATE_SHUTDOWN), FALSE);
else
m_pUI->GoIdle(m_rEventTable.cSucceeded != m_rEventTable.cEvents, ISFLAGSET(m_dwState, SPSTATE_SHUTDOWN),
m_fNoSyncEvent && 0 == (m_dwFlags & DELIVER_BACKGROUND));
// If we're running background and there was errors, then we should show the UI
if (m_dwFlags & DELIVER_BACKGROUND && !(m_dwFlags & DELIVER_NOUI) &&
m_rEventTable.cSucceeded != m_rEventTable.cEvents)
{
m_pUI->ShowWindow(SW_SHOW);
SetForegroundWindow(m_hwndUI);
}
// Free the event table
for (UINT i = 0; i < m_rEventTable.cEvents; i++)
{
SafeRelease(m_rEventTable.prgEvents[i].pSpoolerTask);
SafeRelease(m_rEventTable.prgEvents[i].pAccount);
}
SafeMemFree(m_rEventTable.prgEvents);
ZeroMemory(&m_rEventTable, sizeof(SPOOLEREVENTTABLE));
// Leave the busy state
FLAGCLEAR(m_dwState, SPSTATE_CANCEL);
FLAGCLEAR(m_dwState, SPSTATE_BUSY);
// Notify
Notify(DELIVERY_NOTIFY_ALLDONE, 0);
// Is Something Queued, and the current poll was a success ?
if (!ISFLAGSET(m_dwState, SPSTATE_SHUTDOWN))
{
if (m_rEventTable.cSucceeded == m_rEventTable.cEvents && m_dwQueued)
StartDelivery(NULL, NULL, FOLDERID_INVALID, m_dwQueued);
else
_StartPolling();
}
// Nothing is queued now
m_dwQueued = 0;
// Thread Safety
LeaveCriticalSection(&m_cs);
// Added Bug# 62129 (v-snatar)
SetEvent(hSmapiEvent);
return (S_OK);
}
// ------------------------------------------------------------------------------------
// CSpoolerEngine::_DoBackgroundPoll
// ------------------------------------------------------------------------------------
void CSpoolerEngine::_DoBackgroundPoll(void)
{
BOOL fFound = FALSE;
ULONG i;
DWORD dw;
DWORD dwFlags = 0;
dw = DwGetOption(OPT_DIAL_DURING_POLL);
switch (dw)
{
case DIAL_ALWAYS:
{
//connect always
if (g_pConMan && g_pConMan->IsGlobalOffline())
{
g_pConMan->SetGlobalOffline(FALSE);
m_fOfflineWhenDone = TRUE;
}
dwFlags = DELIVER_BACKGROUND_POLL_DIAL_ALWAYS;
break;
}
case DIAL_IF_NOT_OFFLINE:
case DO_NOT_DIAL:
{
if (g_pConMan && g_pConMan->IsGlobalOffline())
{
_StartPolling();
return;
}
if (dw == DIAL_IF_NOT_OFFLINE)
{
dwFlags = DELIVER_BACKGROUND_POLL_DIAL;
}
else
{
dwFlags = DELIVER_BACKGROUND_POLL;
}
break;
}
default:
dwFlags = DELIVER_BACKGROUND_POLL_DIAL_ALWAYS;
}
//We need this flag to tell the spooler that this polling is triggered by the timer.
//In this case the spooler hangs up the phone if it dialed, irrespective of the option OPT_HANGUP_WHEN_DONE
dwFlags |= DELIVER_AT_INTERVALS | DELIVER_OFFLINE_FLAGS;
// Same thread we were created on...
Assert(ISSPOOLERTHREAD);
EnterCriticalSection(&m_cs);
// Are there any registered views...
for (i = 0; i < m_rViewRegister.cViewAlloc; i++)
{
// Is there a view handle
if (m_rViewRegister.rghwndView[i] && IsWindow(m_rViewRegister.rghwndView[i]))
{
fFound=TRUE;
break;
}
}
LeaveCriticalSection(&m_cs);
// If at least one view is registered we poll, otherwise we wait
if (fFound)
{
StartDelivery(NULL, NULL, FOLDERID_INVALID, dwFlags);
}
else
m_fBackgroundPollPending = TRUE;
}
void CSpoolerEngine::_StartPolling(void)
{
DWORD dw = DwGetOption(OPT_POLLFORMSGS);
if (dw != OPTION_OFF)
{
KillTimer(m_hwndUI, IMAIL_POOLFORMAIL);
SetTimer(m_hwndUI, IMAIL_POOLFORMAIL, dw, NULL);
}
}
void CSpoolerEngine::_StopPolling(void)
{
KillTimer(m_hwndUI, IMAIL_POOLFORMAIL);
}
STDMETHODIMP CSpoolerEngine::OnUIChange(BOOL fVisible)
{
EnterCriticalSection(&m_cs);
// Check to see if we need to notify the tasks
if (ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
// Check to see if our flags are up to date
if (fVisible)
{
FLAGCLEAR(m_dwFlags, DELIVER_NOUI);
FLAGCLEAR(m_dwFlags, DELIVER_BACKGROUND);
}
else
{
FLAGSET(m_dwFlags, DELIVER_BACKGROUND);
}
for (UINT i = m_cCurEvent; i < m_rEventTable.cEvents; i++)
{
if (m_rEventTable.prgEvents[i].pSpoolerTask)
{
m_rEventTable.prgEvents[i].pSpoolerTask->OnFlagsChanged(m_dwFlags);
}
}
}
LeaveCriticalSection(&m_cs);
return (S_OK);
}
STDMETHODIMP CSpoolerEngine::OnConnectionNotify(CONNNOTIFY nCode, LPVOID pvData,
CConnectionManager *pConMan)
{
// If we're not busy, and the user has background polling turned on, then
// we should fire a poll right now
/* Bug# 75222
if (nCode == CONNNOTIFY_CONNECTED && OPTION_OFF != DwGetOption(OPT_POLLFORMSGS))
{
if (!ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
SendMessage(m_hwndUI, WM_TIMER, IMAIL_POOLFORMAIL, 0);
}
}
*/
// If the user just chose "Work Offline", then we cancel anything that's in progress
if (nCode == CONNNOTIFY_WORKOFFLINE && !!pvData)
{
if (ISFLAGSET(m_dwState, SPSTATE_BUSY))
{
Cancel();
}
}
return (S_OK);
}