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