1667 lines
45 KiB
C++
1667 lines
45 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// MODULE: ontask.cpp
|
|
//
|
|
// PURPOSE: Implements the offline news task.
|
|
//
|
|
|
|
#include "pch.hxx"
|
|
#include "resource.h"
|
|
#include "ontask.h"
|
|
#include "thormsgs.h"
|
|
#include "xputil.h"
|
|
#include "mimeutil.h"
|
|
#include <stdio.h>
|
|
#include "strconst.h"
|
|
#include <newsstor.h>
|
|
#include "ourguid.h"
|
|
#include "taskutil.h"
|
|
|
|
ASSERTDATA
|
|
|
|
const static char c_szThis[] = "this";
|
|
|
|
const PFNONSTATEFUNC COfflineTask::m_rgpfnState[ONTS_MAX] =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
&COfflineTask::Download_Init,
|
|
NULL,
|
|
&COfflineTask::Download_AllMsgs,
|
|
&COfflineTask::Download_NewMsgs,
|
|
&COfflineTask::Download_MarkedMsgs,
|
|
&COfflineTask::Download_Done,
|
|
};
|
|
|
|
const PFNARTICLEFUNC COfflineTask::m_rgpfnArticle[ARTICLE_MAX] =
|
|
{
|
|
&COfflineTask::Article_GetNext,
|
|
NULL,
|
|
&COfflineTask::Article_Done
|
|
};
|
|
|
|
#define GROUP_DOWNLOAD_FLAGS(flag) (((flag) & FOLDER_DOWNLOADHEADERS) || \
|
|
((flag) & FOLDER_DOWNLOADNEW) || \
|
|
((flag) & FOLDER_DOWNLOADALL))
|
|
|
|
#define CMSGIDALLOC 512
|
|
//
|
|
// FUNCTION: COfflineTask::COfflineTask()
|
|
//
|
|
// PURPOSE: Initializes the member variables of the object.
|
|
//
|
|
COfflineTask::COfflineTask()
|
|
{
|
|
m_cRef = 1;
|
|
|
|
m_fInited = FALSE;
|
|
m_dwFlags = 0;
|
|
m_state = ONTS_IDLE;
|
|
m_eidCur = 0;
|
|
m_pInfo = NULL;
|
|
m_szAccount[0] = 0;
|
|
m_cEvents = 0;
|
|
m_fDownloadErrors = FALSE;
|
|
m_fFailed = FALSE;
|
|
m_fNewHeaders = FALSE;
|
|
m_fCancel = FALSE;
|
|
|
|
m_pBindCtx = NULL;
|
|
m_pUI = NULL;
|
|
|
|
m_pFolder = NULL;
|
|
|
|
m_hwnd = 0;
|
|
|
|
m_dwLast = 0;
|
|
m_dwPrev = 0;
|
|
m_cDownloaded = 0;
|
|
m_dwPrevHigh = 0;
|
|
m_dwNewInboxMsgs = 0;
|
|
m_pList = NULL;
|
|
|
|
m_pCancel = NULL;
|
|
m_hTimeout = NULL;
|
|
m_tyOperation = SOT_INVALID;
|
|
}
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::~COfflineTask()
|
|
//
|
|
// PURPOSE: Frees any resources allocated during the life of the class.
|
|
//
|
|
COfflineTask::~COfflineTask()
|
|
{
|
|
DestroyWindow(m_hwnd);
|
|
|
|
SafeMemFree(m_pInfo);
|
|
SafeMemFree(m_pList);
|
|
|
|
SafeRelease(m_pBindCtx);
|
|
SafeRelease(m_pUI);
|
|
|
|
CallbackCloseTimeout(&m_hTimeout);
|
|
SafeRelease(m_pCancel);
|
|
|
|
if (m_pFolder)
|
|
{
|
|
m_pFolder->Close();
|
|
SideAssert(0 == m_pFolder->Release());
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT COfflineTask::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
|
|
{
|
|
if (NULL == *ppvObj)
|
|
return (E_INVALIDARG);
|
|
|
|
*ppvObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
*ppvObj = (LPVOID)(ISpoolerTask *) this;
|
|
else if (IsEqualIID(riid, IID_ISpoolerTask))
|
|
*ppvObj = (LPVOID)(ISpoolerTask *) this;
|
|
|
|
if (NULL == *ppvObj)
|
|
return (E_NOINTERFACE);
|
|
|
|
AddRef();
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
ULONG COfflineTask::AddRef(void)
|
|
{
|
|
ULONG cRefT;
|
|
|
|
cRefT = ++m_cRef;
|
|
|
|
return (cRefT);
|
|
}
|
|
|
|
|
|
ULONG COfflineTask::Release(void)
|
|
{
|
|
ULONG cRefT;
|
|
|
|
cRefT = --m_cRef;
|
|
|
|
if (0 == cRefT)
|
|
delete this;
|
|
|
|
return (cRefT);
|
|
}
|
|
|
|
static const char c_szOfflineTask[] = "Offline Task";
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Init()
|
|
//
|
|
// PURPOSE: Called by the spooler engine to tell us what type of task to
|
|
// execute and to provide us with a pointer to our bind context.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> dwFlags - Flags to tell us what types of things to do
|
|
// <in> pBindCtx - Pointer to the bind context interface we are to use
|
|
//
|
|
// RETURN VALUE:
|
|
// E_INVALIDARG
|
|
// SP_E_ALREADYINITIALIZED
|
|
// S_OK
|
|
// E_OUTOFMEMORY
|
|
//
|
|
HRESULT COfflineTask::Init(DWORD dwFlags, ISpoolerBindContext *pBindCtx)
|
|
{
|
|
// Validate the arguments
|
|
Assert(pBindCtx != NULL);
|
|
|
|
// Check to see if we've been initialzed already
|
|
Assert(!m_fInited);
|
|
|
|
// Copy the flags
|
|
m_dwFlags = dwFlags;
|
|
|
|
// Copy the bind context pointer
|
|
m_pBindCtx = pBindCtx;
|
|
m_pBindCtx->AddRef();
|
|
|
|
// Create the window
|
|
WNDCLASSEX wc;
|
|
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
if (!GetClassInfoEx(g_hInst, c_szOfflineTask, &wc))
|
|
{
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = TaskWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = g_hInst;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = c_szOfflineTask;
|
|
wc.hIcon = NULL;
|
|
wc.hIconSm = NULL;
|
|
|
|
RegisterClassEx(&wc);
|
|
}
|
|
|
|
m_hwnd = CreateWindow(c_szOfflineTask, NULL, WS_POPUP, 10, 10, 10, 10,
|
|
GetDesktopWindow(), NULL, g_hInst, this);
|
|
if (!m_hwnd)
|
|
return(E_OUTOFMEMORY);
|
|
|
|
m_fInited = TRUE;
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::BuildEvents()
|
|
//
|
|
// PURPOSE: This method is called by the spooler engine telling us to create
|
|
// and event list for the account specified.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> pAccount - Account object to build the event list for
|
|
//
|
|
// RETURN VALUE:
|
|
// SP_E_UNINITALIZED
|
|
// E_INVALIDARG
|
|
// S_OK
|
|
//
|
|
HRESULT COfflineTask::BuildEvents(ISpoolerUI *pSpoolerUI, IImnAccount *pAccount, FOLDERID idFolder)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Validate the arguments
|
|
Assert(pAccount != NULL);
|
|
Assert(pSpoolerUI != NULL);
|
|
|
|
// Check to see if we've been initalized
|
|
Assert(m_fInited);
|
|
|
|
// Get the account name from the account object
|
|
if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_NAME, m_szAccount, ARRAYSIZE(m_szAccount))))
|
|
return(hr);
|
|
|
|
// Get the account name from the account object
|
|
if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_ID, m_szAccountId, ARRAYSIZE(m_szAccountId))))
|
|
return(hr);
|
|
|
|
if (FAILED(hr = g_pStore->FindServerId(m_szAccountId, &m_idAccount)))
|
|
return(hr);
|
|
|
|
// Copy the UI object
|
|
m_pUI = pSpoolerUI;
|
|
m_pUI->AddRef();
|
|
|
|
hr = InsertGroups(pAccount, idFolder);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::InsertGroups()
|
|
//
|
|
// PURPOSE: Scans the specified account for groups that have an update
|
|
// property or marked messages.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> szAccount - Name of the account to check
|
|
// <in> pAccount - Pointer to the IImnAccount object for szAccount
|
|
//
|
|
// RETURN VALUE:
|
|
// S_OK
|
|
// E_OUTOFMEMORY
|
|
//
|
|
HRESULT COfflineTask::InsertGroups(IImnAccount *pAccount, FOLDERID idFolder)
|
|
{
|
|
FOLDERINFO info = { 0 };
|
|
HRESULT hr = S_OK;
|
|
DWORD dwFlags = 0;
|
|
DWORD ids;
|
|
TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
|
|
EVENTID eid;
|
|
ONEVENTINFO *pei = NULL;
|
|
BOOL fIMAP = FALSE;
|
|
DWORD dwServerFlags;
|
|
|
|
|
|
// Figure out if this is NNTP or IMAP
|
|
if (SUCCEEDED(pAccount->GetServerTypes(&dwServerFlags)) && (dwServerFlags & (SRV_IMAP | SRV_HTTPMAIL)))
|
|
fIMAP = TRUE;
|
|
|
|
if (FOLDERID_INVALID != idFolder)
|
|
{
|
|
// Fill Folder
|
|
hr = g_pStore->GetFolderInfo(idFolder, &info);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Figure out what we're downloading
|
|
ids = 0;
|
|
if (m_dwFlags & DELIVER_OFFLINE_HEADERS)
|
|
{
|
|
dwFlags = FOLDER_DOWNLOADHEADERS;
|
|
if (m_dwFlags & DELIVER_OFFLINE_MARKED)
|
|
ids = idsDLHeadersAndMarked;
|
|
else
|
|
ids = idsDLHeaders;
|
|
}
|
|
else if (m_dwFlags & DELIVER_OFFLINE_NEW)
|
|
{
|
|
dwFlags = FOLDER_DOWNLOADNEW;
|
|
if (m_dwFlags & DELIVER_OFFLINE_MARKED)
|
|
ids = idsDLNewMsgsAndMarked;
|
|
else
|
|
ids = idsDLNewMsgs;
|
|
}
|
|
else if (m_dwFlags & DELIVER_OFFLINE_ALL)
|
|
{
|
|
dwFlags = FOLDER_DOWNLOADALL;
|
|
ids = idsDLAllMsgs;
|
|
}
|
|
else if (m_dwFlags & DELIVER_OFFLINE_MARKED)
|
|
{
|
|
ids = idsDLMarkedMsgs;
|
|
}
|
|
|
|
// Create the event description
|
|
Assert(ids);
|
|
AthLoadString(ids, szRes, ARRAYSIZE(szRes));
|
|
wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, info.pszName);
|
|
|
|
// Allocate a structure to save as our twinkie
|
|
if (!MemAlloc((LPVOID *) &pei, sizeof(ONEVENTINFO)))
|
|
{
|
|
g_pStore->FreeRecord(&info);
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
StrCpyN(pei->szGroup, info.pszName, ARRAYSIZE(pei->szGroup));
|
|
pei->idGroup = info.idFolder;
|
|
pei->dwFlags = dwFlags;
|
|
pei->fMarked = m_dwFlags & DELIVER_OFFLINE_MARKED;
|
|
pei->fIMAP = fIMAP;
|
|
|
|
// Insert the event into the spooler
|
|
hr = m_pBindCtx->RegisterEvent(szBuf, this, (DWORD_PTR) pei, pAccount, &eid);
|
|
if (SUCCEEDED(hr))
|
|
m_cEvents++;
|
|
|
|
g_pStore->FreeRecord(&info);
|
|
}
|
|
else
|
|
{
|
|
//Either Sync All or Send & Receive
|
|
|
|
Assert(m_idAccount != FOLDERID_INVALID);
|
|
|
|
BOOL fInclude = FALSE;
|
|
|
|
if (!(m_dwFlags & DELIVER_OFFLINE_SYNC) && !(m_dwFlags & DELIVER_NOSKIP))
|
|
{
|
|
DWORD dw;
|
|
if (dwServerFlags & SRV_IMAP)
|
|
{
|
|
if (SUCCEEDED(pAccount->GetPropDw(AP_IMAP_POLL, &dw)) && dw)
|
|
{
|
|
fInclude = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dwServerFlags & SRV_HTTPMAIL)
|
|
{
|
|
if (SUCCEEDED(pAccount->GetPropDw(AP_HTTPMAIL_POLL, &dw)) && dw)
|
|
{
|
|
fInclude = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
fInclude = TRUE;
|
|
|
|
if (fInclude)
|
|
hr = InsertAllGroups(m_idAccount, pAccount, fIMAP);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
HRESULT COfflineTask::InsertAllGroups(FOLDERID idParent, IImnAccount *pAccount, BOOL fIMAP)
|
|
{
|
|
FOLDERINFO info = { 0 };
|
|
IEnumerateFolders *pEnum = NULL;
|
|
HRESULT hr = S_OK;
|
|
DWORD dwFlags = 0;
|
|
BOOL fMarked;
|
|
DWORD ids;
|
|
TCHAR szRes[CCHMAX_STRINGRES], szBuf[CCHMAX_STRINGRES];
|
|
EVENTID eid;
|
|
ONEVENTINFO *pei = NULL;
|
|
BOOL fSubscribedOnly = TRUE;
|
|
|
|
if (fIMAP)
|
|
fSubscribedOnly = FALSE;
|
|
|
|
Assert(idParent != FOLDERID_INVALID);
|
|
hr = g_pStore->EnumChildren(idParent, fSubscribedOnly, &pEnum);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
// Walk the list of groups and add them to the queue as necessary
|
|
while (S_OK == pEnum->Next(1, &info, NULL))
|
|
{
|
|
// If the download flags are set for this group, insert it
|
|
dwFlags = info.dwFlags;
|
|
|
|
HasMarkedMsgs(info.idFolder, &fMarked);
|
|
|
|
if (GROUP_DOWNLOAD_FLAGS(dwFlags) || fMarked)
|
|
{
|
|
// Figure out what we're downloading
|
|
ids = 0;
|
|
if (dwFlags & FOLDER_DOWNLOADHEADERS)
|
|
{
|
|
if (fMarked)
|
|
ids = idsDLHeadersAndMarked;
|
|
else
|
|
ids = idsDLHeaders;
|
|
}
|
|
else if (dwFlags & FOLDER_DOWNLOADNEW)
|
|
{
|
|
if (fMarked)
|
|
ids = idsDLNewMsgsAndMarked;
|
|
else
|
|
ids = idsDLNewMsgs;
|
|
}
|
|
else if (dwFlags & FOLDER_DOWNLOADALL)
|
|
{
|
|
ids = idsDLAllMsgs;
|
|
}
|
|
else if (fMarked)
|
|
{
|
|
ids = idsDLMarkedMsgs;
|
|
}
|
|
|
|
// Create the event description
|
|
Assert(ids);
|
|
AthLoadString(ids, szRes, ARRAYSIZE(szRes));
|
|
wnsprintf(szBuf, ARRAYSIZE(szBuf), szRes, info.pszName);
|
|
|
|
// Allocate a structure to save as our twinkie
|
|
if (!MemAlloc((LPVOID *) &pei, sizeof(ONEVENTINFO)))
|
|
{
|
|
g_pStore->FreeRecord(&info);
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
StrCpyN(pei->szGroup, info.pszName, ARRAYSIZE(pei->szGroup));
|
|
pei->idGroup = info.idFolder;
|
|
pei->dwFlags = dwFlags;
|
|
pei->fMarked = fMarked;
|
|
pei->fIMAP = fIMAP;
|
|
|
|
// Insert the event into the spooler
|
|
hr = m_pBindCtx->RegisterEvent(szBuf, this, (DWORD_PTR) pei, pAccount, &eid);
|
|
if (FAILED(hr))
|
|
{
|
|
g_pStore->FreeRecord(&info);
|
|
break;
|
|
}
|
|
|
|
m_cEvents++;
|
|
}
|
|
|
|
// Recurse on any children
|
|
if (info.dwFlags & FOLDER_HASCHILDREN)
|
|
{
|
|
hr = InsertAllGroups(info.idFolder, pAccount, fIMAP);
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
g_pStore->FreeRecord(&info);
|
|
}
|
|
|
|
pEnum->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Execute()
|
|
//
|
|
// PURPOSE: This signals our task to start executing an event.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> pSpoolerUI - Pointer of the UI object we'll display progress through
|
|
// <in> eid - ID of the event to execute
|
|
// <in> dwTwinkie - Our extra information we associated with the event
|
|
//
|
|
// RETURN VALUE:
|
|
// SP_E_EXECUTING
|
|
// S_OK
|
|
// E_INVALIDARG
|
|
// SP_E_UNINITIALIZED
|
|
//
|
|
HRESULT COfflineTask::Execute(EVENTID eid, DWORD_PTR dwTwinkie)
|
|
{
|
|
// Make sure we're already idle
|
|
Assert(m_state == ONTS_IDLE)
|
|
|
|
// Make sure we're initialized
|
|
Assert(m_fInited);
|
|
Assert(m_pInfo == NULL);
|
|
|
|
// Copy the event id and event info
|
|
m_eidCur = eid;
|
|
m_pInfo = (ONEVENTINFO *) dwTwinkie;
|
|
|
|
// Forget UI stuff if we're just going to cancel everything
|
|
if (FALSE == m_fCancel)
|
|
{
|
|
// Update the event UI to an executing state
|
|
Assert(m_pUI);
|
|
m_pUI->UpdateEventState(m_eidCur, -1, NULL, MAKEINTRESOURCE(idsStateExecuting));
|
|
m_pUI->SetProgressRange(1);
|
|
|
|
// Set up the progress
|
|
SetGeneralProgress((LPSTR)idsInetMailConnectingHost, m_szAccount);
|
|
if (m_pInfo->fIMAP)
|
|
m_pUI->SetAnimation(idanInbox, TRUE);
|
|
else
|
|
m_pUI->SetAnimation(idanDownloadNews, TRUE);
|
|
}
|
|
|
|
m_state = ONTS_INIT;
|
|
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
HRESULT COfflineTask::CancelEvent(EVENTID eid, DWORD_PTR dwTwinkie)
|
|
{
|
|
// Make sure we're initialized
|
|
Assert(m_fInited);
|
|
|
|
Assert(dwTwinkie != 0);
|
|
MemFree((ONEVENTINFO *)dwTwinkie);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: <???>
|
|
//
|
|
// PURPOSE: <???>
|
|
//
|
|
// PARAMETERS:
|
|
// <???>
|
|
//
|
|
// RETURN VALUE:
|
|
// <???>
|
|
//
|
|
// COMMENTS:
|
|
// <???>
|
|
//
|
|
HRESULT COfflineTask::ShowProperties(HWND hwndParent, EVENTID eid, DWORD_PTR dwTwinkie)
|
|
{
|
|
return (E_NOTIMPL);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: <???>
|
|
//
|
|
// PURPOSE: <???>
|
|
//
|
|
// PARAMETERS:
|
|
// <???>
|
|
//
|
|
// RETURN VALUE:
|
|
// <???>
|
|
//
|
|
// COMMENTS:
|
|
// <???>
|
|
//
|
|
HRESULT COfflineTask::GetExtendedDetails(EVENTID eid, DWORD_PTR dwTwinkie,
|
|
LPSTR *ppszDetails)
|
|
{
|
|
return (E_NOTIMPL);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: <???>
|
|
//
|
|
// PURPOSE: <???>
|
|
//
|
|
// PARAMETERS:
|
|
// <???>
|
|
//
|
|
// RETURN VALUE:
|
|
// <???>
|
|
//
|
|
// COMMENTS:
|
|
// <???>
|
|
//
|
|
HRESULT COfflineTask::Cancel(void)
|
|
{
|
|
Assert(m_state != ONTS_IDLE);
|
|
|
|
m_fCancel = TRUE;
|
|
|
|
m_state = ONTS_END;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::TaskWndProc()
|
|
//
|
|
// PURPOSE: Hidden window that processes messages for this task.
|
|
//
|
|
LRESULT CALLBACK COfflineTask::TaskWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
COfflineTask *pThis = (COfflineTask *) GetProp(hwnd, c_szThis);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
LPCREATESTRUCT pcs = (LPCREATESTRUCT) lParam;
|
|
pThis = (COfflineTask *) pcs->lpCreateParams;
|
|
SetProp(hwnd, c_szThis, (LPVOID) pThis);
|
|
return (0);
|
|
}
|
|
|
|
case NTM_NEXTSTATE:
|
|
if (pThis)
|
|
{
|
|
pThis->AddRef();
|
|
pThis->NextState();
|
|
pThis->Release();
|
|
}
|
|
return (0);
|
|
|
|
case NTM_NEXTARTICLESTATE:
|
|
if (pThis)
|
|
{
|
|
pThis->AddRef();
|
|
if (m_rgpfnArticle[pThis->m_as])
|
|
(pThis->*(m_rgpfnArticle[pThis->m_as]))();
|
|
pThis->Release();
|
|
}
|
|
return (0);
|
|
|
|
case WM_DESTROY:
|
|
RemoveProp(hwnd, c_szThis);
|
|
break;
|
|
}
|
|
|
|
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::NextState()
|
|
//
|
|
// PURPOSE: Executes the function for the current state
|
|
//
|
|
void COfflineTask::NextState(void)
|
|
{
|
|
if (m_fCancel)
|
|
m_state = ONTS_END;
|
|
|
|
if (NULL != m_rgpfnState[m_state])
|
|
(this->*(m_rgpfnState[m_state]))();
|
|
}
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Download_Init()
|
|
//
|
|
// PURPOSE: Does the initialization needed to download headers and messages
|
|
// for a particular newsgroup.
|
|
//
|
|
HRESULT COfflineTask::Download_Init(void)
|
|
{
|
|
HRESULT hr;
|
|
SYNCFOLDERFLAGS flags = SYNC_FOLDER_DEFAULT;
|
|
FOLDERINFO info;
|
|
|
|
Assert(m_pFolder == NULL);
|
|
Assert(0 == flags); // If this isn't 0, please verify correctness
|
|
|
|
hr = g_pStore->OpenFolder(m_pInfo->idGroup, NULL, NOFLAGS, &m_pFolder);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
Assert(m_pFolder != NULL);
|
|
|
|
hr = g_pStore->GetFolderInfo(m_pInfo->idGroup, &info);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
if (m_pInfo->fIMAP)
|
|
{
|
|
// Get highest Msg ID the brute-force way (IMAP doesn't set dwClientHigh)
|
|
GetHighestCachedMsgID(m_pFolder, &m_dwPrevHigh);
|
|
}
|
|
else
|
|
m_dwPrevHigh = info.dwClientHigh;
|
|
|
|
g_pStore->FreeRecord(&info);
|
|
|
|
// Update the UI to an executing state
|
|
Assert(m_pUI);
|
|
m_pUI->UpdateEventState(m_eidCur, -1, NULL, MAKEINTRESOURCE(idsStateExecuting));
|
|
m_fDownloadErrors = FALSE;
|
|
|
|
// Check to see if the user wants us to download new headers
|
|
if (GROUP_DOWNLOAD_FLAGS(m_pInfo->dwFlags))
|
|
{
|
|
if (!(m_pInfo->dwFlags & FOLDER_DOWNLOADALL) || m_pInfo->fIMAP)
|
|
flags = SYNC_FOLDER_NEW_HEADERS | SYNC_FOLDER_CACHED_HEADERS;
|
|
else
|
|
flags = SYNC_FOLDER_ALLFLAGS;
|
|
|
|
// Update Progress
|
|
SetGeneralProgress((LPSTR)idsLogCheckingNewMessages, m_pInfo->szGroup);
|
|
}
|
|
else
|
|
{
|
|
m_state = ONTS_ALLMSGS;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
// Before we download any headers, we need to make a note of what the current
|
|
// server high is so we know which articles are new.
|
|
|
|
hr = m_pFolder->Synchronize(flags, 0, (IStoreCallback *)this);
|
|
Assert(hr != S_OK);
|
|
|
|
if (hr == E_PENDING)
|
|
hr = S_OK;
|
|
|
|
if (m_pInfo->fIMAP)
|
|
{
|
|
m_pUI->SetAnimation(idanInbox, TRUE);
|
|
m_pBindCtx->Notify(DELIVERY_NOTIFY_RECEIVING, 0);
|
|
}
|
|
else
|
|
{
|
|
m_pUI->SetAnimation(idanDownloadNews, TRUE);
|
|
m_pBindCtx->Notify(DELIVERY_NOTIFY_RECEIVING_NEWS, 0);
|
|
}
|
|
|
|
Failure:
|
|
if (FAILED(hr))
|
|
{
|
|
// $$$$BUGBUG$$$$
|
|
InsertError((LPSTR)idsLogErrorSwitchGroup, m_pInfo->szGroup, m_szAccount);
|
|
m_fFailed = TRUE;
|
|
|
|
m_state = ONTS_END;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Download_AllMsgs()
|
|
//
|
|
// PURPOSE:
|
|
//
|
|
//
|
|
HRESULT COfflineTask::Download_AllMsgs(void)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cMsgs, cMsgsBuf;
|
|
LPMESSAGEID pMsgId;
|
|
MESSAGEIDLIST list;
|
|
MESSAGEINFO MsgInfo = {0};
|
|
HROWSET hRowset = NULL;
|
|
|
|
// Check to see if we even want to download all messages
|
|
if (!(m_pInfo->dwFlags & FOLDER_DOWNLOADALL))
|
|
{
|
|
m_state = ONTS_NEWMSGS;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
return(S_OK);
|
|
}
|
|
|
|
// We need to determine a list of messages to download. What we're looking
|
|
// to do is download all of the messages that we know about which are unread.
|
|
// To do this, we need to find the intersection of the unread range list and
|
|
// the known range list.
|
|
|
|
// Create a Rowset
|
|
hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, 0, &hRowset);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
cMsgs = 0;
|
|
cMsgsBuf = 0;
|
|
pMsgId = NULL;
|
|
|
|
// Get the first message
|
|
while (S_OK == m_pFolder->QueryRowset(hRowset, 1, (void **)&MsgInfo, NULL))
|
|
{
|
|
if (0 == (MsgInfo.dwFlags & ARF_HASBODY) && 0 == (MsgInfo.dwFlags & ARF_IGNORE))
|
|
{
|
|
if (cMsgs == cMsgsBuf)
|
|
{
|
|
if (!MemRealloc((void **)&pMsgId, (cMsgsBuf + CMSGIDALLOC) * sizeof(MESSAGEID)))
|
|
{
|
|
m_pFolder->FreeRecord(&MsgInfo);
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
cMsgsBuf += CMSGIDALLOC;
|
|
}
|
|
|
|
pMsgId[cMsgs] = MsgInfo.idMessage;
|
|
cMsgs++;
|
|
}
|
|
|
|
// Free the header info
|
|
m_pFolder->FreeRecord(&MsgInfo);
|
|
}
|
|
|
|
// Release Lock
|
|
m_pFolder->CloseRowset(&hRowset);
|
|
|
|
// TODO: error handling
|
|
Assert(!FAILED(hr));
|
|
|
|
// Check to see if we found anything
|
|
if (cMsgs == 0)
|
|
{
|
|
// Nothing to download. We should move on to the marked download
|
|
// state.
|
|
Assert(pMsgId == NULL);
|
|
|
|
m_state = ONTS_MARKEDMSGS;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
return(S_OK);
|
|
}
|
|
|
|
// Update the general progress
|
|
SetGeneralProgress((LPSTR)idsLogStartDownloadAll, m_pInfo->szGroup);
|
|
|
|
list.cAllocated = 0;
|
|
list.cMsgs = cMsgs;
|
|
list.prgidMsg = pMsgId;
|
|
|
|
// Ask for the first article
|
|
hr = Article_Init(&list);
|
|
|
|
if (pMsgId != NULL)
|
|
MemFree(pMsgId);
|
|
|
|
Failure:
|
|
if (FAILED(hr))
|
|
{
|
|
// $$$$BUGBUG$$$$
|
|
InsertError((LPSTR)idsLogErrorSwitchGroup, m_pInfo->szGroup, m_szAccount);
|
|
m_fFailed = TRUE;
|
|
|
|
m_state = ONTS_END;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Download_NewMsgs()
|
|
//
|
|
// PURPOSE: This function determines if there are any new messages to be
|
|
// downloaded. If so, it creates a list of message numbers that
|
|
// need to be downloaded.
|
|
//
|
|
HRESULT COfflineTask::Download_NewMsgs(void)
|
|
{
|
|
HRESULT hr;
|
|
ROWORDINAL iRow = 0;
|
|
BOOL fFound;
|
|
HROWSET hRowset;
|
|
DWORD cMsgs, cMsgsBuf;
|
|
LPMESSAGEID pMsgId;
|
|
MESSAGEIDLIST list;
|
|
MESSAGEINFO Message = {0};
|
|
|
|
// Check to see if there are even new messages to download
|
|
// Check to see if we even want to download all messages
|
|
if (!(m_pInfo->dwFlags & FOLDER_DOWNLOADNEW) || !m_fNewHeaders)
|
|
{
|
|
// Move the next state
|
|
m_state = ONTS_MARKEDMSGS;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
return(S_OK);
|
|
}
|
|
|
|
// We've got new messages, build a range list of those message numbers.
|
|
// This range list is essentially every number in the known range above
|
|
// m_dwPrevHigh.
|
|
|
|
hr = S_OK;
|
|
|
|
cMsgs = 0;
|
|
cMsgsBuf = 0;
|
|
pMsgId = NULL;
|
|
fFound = FALSE;
|
|
|
|
// TODO: this method of figuring out if there are new msgs isn't going to work all
|
|
// the time. if the previous high is removed from the store during syncing (cancelled
|
|
// news post, deleted msg, expired news post, etc) and new headers are downloaded,
|
|
// we won't pull down the new msgs. we need a better way of detecting new hdrs and
|
|
// pulling down there bodies
|
|
|
|
if (m_dwPrevHigh > 0)
|
|
{
|
|
Message.idMessage = (MESSAGEID)m_dwPrevHigh;
|
|
|
|
// Find This Record. If this fails, we go ahead and do a full scan which is less
|
|
// efficient, but OK.
|
|
if (DB_S_FOUND == m_pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, &iRow))
|
|
{
|
|
m_pFolder->FreeRecord(&Message);
|
|
}
|
|
}
|
|
|
|
hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, 0, &hRowset);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (SUCCEEDED(m_pFolder->SeekRowset(hRowset, SEEK_ROWSET_BEGIN, iRow, NULL)))
|
|
{
|
|
// Get the first message
|
|
while (S_OK == m_pFolder->QueryRowset(hRowset, 1, (void **)&Message, NULL))
|
|
{
|
|
if (cMsgs == cMsgsBuf)
|
|
{
|
|
if (!MemRealloc((void **)&pMsgId, (cMsgsBuf + CMSGIDALLOC) * sizeof(MESSAGEID)))
|
|
{
|
|
m_pFolder->FreeRecord(&Message);
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
cMsgsBuf += CMSGIDALLOC;
|
|
}
|
|
|
|
// It's possible to have already downloaded the body if the message was
|
|
// watched. It's also possible for the message to be part of an ignored
|
|
// thread.
|
|
if (0 == (Message.dwFlags & ARF_HASBODY) && 0 == (Message.dwFlags & ARF_IGNORE) && (Message.idMessage >= (MESSAGEID) m_dwPrevHigh))
|
|
{
|
|
pMsgId[cMsgs] = Message.idMessage;
|
|
cMsgs++;
|
|
}
|
|
|
|
// Free the header info
|
|
m_pFolder->FreeRecord(&Message);
|
|
}
|
|
|
|
}
|
|
|
|
// Release Lock
|
|
m_pFolder->CloseRowset(&hRowset);
|
|
}
|
|
|
|
// TODO: error handling
|
|
Assert(!FAILED(hr));
|
|
|
|
// Check to see if there was anything added
|
|
if (cMsgs == 0)
|
|
{
|
|
// Nothing to download. We should move on to the marked download
|
|
// state.
|
|
|
|
m_state = ONTS_MARKEDMSGS;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
return(S_OK);
|
|
}
|
|
|
|
// Update the general progress
|
|
SetGeneralProgress((LPSTR)idsLogStartDownloadAll, m_pInfo->szGroup);
|
|
|
|
list.cAllocated = 0;
|
|
list.cMsgs = cMsgs;
|
|
list.prgidMsg = pMsgId;
|
|
|
|
// Ask for the first article
|
|
hr = Article_Init(&list);
|
|
|
|
if (pMsgId != NULL)
|
|
MemFree(pMsgId);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Download_MarkedMsgs()
|
|
//
|
|
// PURPOSE:
|
|
//
|
|
//
|
|
HRESULT COfflineTask::Download_MarkedMsgs(void)
|
|
{
|
|
HRESULT hr;
|
|
HROWSET hRowset;
|
|
DWORD cMsgs, cMsgsBuf;
|
|
LPMESSAGEID pMsgId;
|
|
MESSAGEIDLIST list;
|
|
MESSAGEINFO MsgInfo;
|
|
|
|
// Check to see if we even want to download marked messages
|
|
if (!m_pInfo->fMarked)
|
|
{
|
|
// Move on to the next state
|
|
m_state = ONTS_END;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
return(S_OK);
|
|
}
|
|
|
|
// We need to determine a list of messages to download. What we're looking
|
|
// to do is download all of the messages that are marked which are unread.
|
|
// To do this, we need to find the intersection of the unread range list and
|
|
// the marked range list.
|
|
|
|
// Create a Rowset
|
|
hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, 0, &hRowset);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
cMsgs = 0;
|
|
cMsgsBuf = 0;
|
|
pMsgId = NULL;
|
|
|
|
// Get the first message
|
|
while (S_OK == m_pFolder->QueryRowset(hRowset, 1, (void **)&MsgInfo, NULL))
|
|
{
|
|
if (((MsgInfo.dwFlags & ARF_DOWNLOAD) || (MsgInfo.dwFlags & ARF_WATCH)) && 0 == (MsgInfo.dwFlags & ARF_HASBODY))
|
|
{
|
|
if (cMsgs == cMsgsBuf)
|
|
{
|
|
if (!MemRealloc((void **)&pMsgId, (cMsgsBuf + CMSGIDALLOC) * sizeof(MESSAGEID)))
|
|
{
|
|
m_pFolder->FreeRecord(&MsgInfo);
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
cMsgsBuf += CMSGIDALLOC;
|
|
}
|
|
|
|
pMsgId[cMsgs] = MsgInfo.idMessage;
|
|
cMsgs++;
|
|
}
|
|
|
|
// Free the header info
|
|
m_pFolder->FreeRecord(&MsgInfo);
|
|
}
|
|
|
|
// Release Lock
|
|
m_pFolder->CloseRowset(&hRowset);
|
|
|
|
// TODO: error handling
|
|
Assert(!FAILED(hr));
|
|
|
|
// Check to see if we found anything
|
|
if (cMsgs == 0)
|
|
{
|
|
// Nothing to download. We should move on to next state.
|
|
|
|
m_state = ONTS_END;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
return(S_OK);
|
|
}
|
|
|
|
// Update the general progress
|
|
SetGeneralProgress((LPSTR)idsLogStartDownloadAll, m_pInfo->szGroup);
|
|
|
|
list.cAllocated = 0;
|
|
list.cMsgs = cMsgs;
|
|
list.prgidMsg = pMsgId;
|
|
|
|
// Ask for the first article
|
|
hr = Article_Init(&list);
|
|
|
|
if (pMsgId != NULL)
|
|
MemFree(pMsgId);
|
|
|
|
Failure:
|
|
if (FAILED(hr))
|
|
{
|
|
// $$$$BUGBUG$$$$
|
|
InsertError((LPSTR)idsLogErrorSwitchGroup, m_pInfo->szGroup, m_szAccount);
|
|
m_fFailed = TRUE;
|
|
|
|
m_state = ONTS_END;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Download_Done()
|
|
//
|
|
// PURPOSE:
|
|
//
|
|
//
|
|
HRESULT COfflineTask::Download_Done(void)
|
|
{
|
|
// Make sure we don't get freed before we can clean up
|
|
AddRef();
|
|
|
|
// Tell the spooler we're done
|
|
Assert(m_pBindCtx);
|
|
m_pBindCtx->Notify(DELIVERY_NOTIFY_COMPLETE, m_dwNewInboxMsgs);
|
|
|
|
if (m_fCancel)
|
|
m_pBindCtx->EventDone(m_eidCur, EVENT_CANCELED);
|
|
else if (m_fFailed)
|
|
m_pBindCtx->EventDone(m_eidCur, EVENT_FAILED);
|
|
else if (m_fDownloadErrors)
|
|
m_pBindCtx->EventDone(m_eidCur, EVENT_WARNINGS);
|
|
else
|
|
m_pBindCtx->EventDone(m_eidCur, EVENT_SUCCEEDED);
|
|
|
|
m_cEvents--;
|
|
|
|
if (m_pFolder != NULL)
|
|
{
|
|
m_pFolder->Close();
|
|
m_pFolder->Release();
|
|
m_pFolder = NULL;
|
|
}
|
|
|
|
m_state = ONTS_IDLE;
|
|
|
|
SafeMemFree(m_pInfo);
|
|
|
|
Release();
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::InsertError()
|
|
//
|
|
// PURPOSE: This function is a wrapper for the ISpoolerUI::InsertError()
|
|
// that takes the responsibility of loading the string resource
|
|
// and constructing the error message.
|
|
//
|
|
void COfflineTask::InsertError(const TCHAR *pFmt, ...)
|
|
{
|
|
int i;
|
|
va_list pArgs;
|
|
LPCTSTR pszT;
|
|
TCHAR szFmt[CCHMAX_STRINGRES];
|
|
DWORD cbWritten;
|
|
TCHAR szBuf[2 * CCHMAX_STRINGRES];
|
|
|
|
// If we were passed a string resource ID, then we need to load it
|
|
if (IS_INTRESOURCE(pFmt))
|
|
{
|
|
AthLoadString(PtrToUlong(pFmt), szFmt, ARRAYSIZE(szFmt));
|
|
pszT = szFmt;
|
|
}
|
|
else
|
|
pszT = pFmt;
|
|
|
|
// Format the string
|
|
va_start(pArgs, pFmt);
|
|
i = wvnsprintf(szBuf, ARRAYSIZE(szBuf), pszT, pArgs);
|
|
va_end(pArgs);
|
|
|
|
// Send the string to the UI
|
|
m_pUI->InsertError(m_eidCur, szBuf);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::SetSpecificProgress()
|
|
//
|
|
// PURPOSE: This function is a wrapper for the ISpoolerUI::SetSpecificProgress()
|
|
// that takes the responsibility of loading the string resource
|
|
// and constructing the error message.
|
|
//
|
|
void COfflineTask::SetSpecificProgress(const TCHAR *pFmt, ...)
|
|
{
|
|
int i;
|
|
va_list pArgs;
|
|
LPCTSTR pszT;
|
|
TCHAR szFmt[CCHMAX_STRINGRES];
|
|
DWORD cbWritten;
|
|
TCHAR szBuf[2 * CCHMAX_STRINGRES];
|
|
|
|
// If we were passed a string resource ID, then we need to load it
|
|
if (IS_INTRESOURCE(pFmt))
|
|
{
|
|
AthLoadString(PtrToUlong(pFmt), szFmt, ARRAYSIZE(szFmt));
|
|
pszT = szFmt;
|
|
}
|
|
else
|
|
pszT = pFmt;
|
|
|
|
// Format the string
|
|
va_start(pArgs, pFmt);
|
|
i = wvnsprintf(szBuf, ARRAYSIZE(szBuf), pszT, pArgs);
|
|
va_end(pArgs);
|
|
|
|
// Send the string to the UI
|
|
m_pUI->SetSpecificProgress(szBuf);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::SetGeneralProgress()
|
|
//
|
|
// PURPOSE: This function is a wrapper for the ISpoolerUI::SetGeneralProgress()
|
|
// that takes the responsibility of loading the string resource
|
|
// and constructing the error message.
|
|
//
|
|
void COfflineTask::SetGeneralProgress(const TCHAR *pFmt, ...)
|
|
{
|
|
int i;
|
|
va_list pArgs;
|
|
LPCTSTR pszT;
|
|
TCHAR szFmt[CCHMAX_STRINGRES];
|
|
DWORD cbWritten;
|
|
TCHAR szBuf[2 * CCHMAX_STRINGRES];
|
|
|
|
// If we were passed a string resource ID, then we need to load it
|
|
if (IS_INTRESOURCE(pFmt))
|
|
{
|
|
AthLoadString(PtrToUlong(pFmt), szFmt, ARRAYSIZE(szFmt));
|
|
pszT = szFmt;
|
|
}
|
|
else
|
|
pszT = pFmt;
|
|
|
|
// Format the string
|
|
va_start(pArgs, pFmt);
|
|
i = wvnsprintf(szBuf, ARRAYSIZE(szBuf), pszT, pArgs);
|
|
va_end(pArgs);
|
|
|
|
// Send the string to the UI
|
|
m_pUI->SetGeneralProgress(szBuf);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Article_Init()
|
|
//
|
|
// PURPOSE: Initializes the article download substate machine.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> pRange - Range list of articles to download.
|
|
//
|
|
HRESULT COfflineTask::Article_Init(MESSAGEIDLIST *pList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(pList != NULL);
|
|
Assert(pList->cMsgs > 0);
|
|
Assert(m_pList == NULL);
|
|
|
|
hr = CloneMessageIDList(pList, &m_pList);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
// Determine the first and the size
|
|
m_cDownloaded = 0;
|
|
m_cCur = 0;
|
|
m_dwNewInboxMsgs = 0;
|
|
|
|
// Set up the UI
|
|
SetSpecificProgress((LPSTR)idsIMAPDnldProgressFmt, 0, m_pList->cMsgs);
|
|
m_pUI->SetProgressRange((WORD)m_pList->cMsgs);
|
|
|
|
// Request the first one
|
|
m_as = ARTICLE_GETNEXT;
|
|
PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Article_GetNext()
|
|
//
|
|
// PURPOSE: Determines the next article in the range of articles to
|
|
// download and requests that article from the server.
|
|
//
|
|
HRESULT COfflineTask::Article_GetNext(void)
|
|
{
|
|
HRESULT hr;
|
|
LPMIMEMESSAGE pMsg = NULL;
|
|
|
|
if (NULL == m_pFolder)
|
|
return(S_OK);
|
|
|
|
// Find out the next article number
|
|
if (m_cCur == m_pList->cMsgs)
|
|
{
|
|
// We're done. Exit.
|
|
m_as = ARTICLE_END;
|
|
PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
|
|
return(S_OK);
|
|
}
|
|
|
|
m_cDownloaded++;
|
|
// (YST) Bug 97397 We should send notification message from here too, because this is
|
|
// only one availble place for HTTP (fIMAP is set for HTTP).
|
|
if(m_pInfo->fIMAP)
|
|
OnProgress(SOT_NEW_MAIL_NOTIFICATION, 1, 0, NULL);
|
|
|
|
// Update the progress UI
|
|
SetSpecificProgress((LPSTR)idsIMAPDnldProgressFmt, m_cDownloaded, m_pList->cMsgs);
|
|
m_pUI->IncrementProgress(1);
|
|
|
|
// Ask for the article
|
|
hr = m_pFolder->OpenMessage(m_pList->prgidMsg[m_cCur], 0, &pMsg, (IStoreCallback *)this);
|
|
|
|
if (pMsg != NULL)
|
|
pMsg->Release();
|
|
m_cCur++;
|
|
|
|
if (hr == E_PENDING)
|
|
{
|
|
m_as = ARTICLE_ONRESP;
|
|
}
|
|
else
|
|
{
|
|
// Whatever happened, we should move on to the next article.
|
|
m_as = ARTICLE_GETNEXT;
|
|
PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: COfflineTask::Article_Done()
|
|
//
|
|
// PURPOSE: When we've downloaded the last article, this function cleans
|
|
// up and moves us to the next state.
|
|
//
|
|
HRESULT COfflineTask::Article_Done(void)
|
|
{
|
|
// Free the range list we were working off of
|
|
MemFree(m_pList);
|
|
m_pList = NULL;
|
|
|
|
// Move to the next state. The next state is either get marked or done.
|
|
if (m_state == ONTS_MARKEDMSGS)
|
|
m_state = ONTS_END;
|
|
else
|
|
m_state = ONTS_MARKEDMSGS;
|
|
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::IsDialogMessage(LPMSG pMsg)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnFlagsChanged(DWORD dwFlags)
|
|
{
|
|
m_dwFlags = dwFlags;
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnBegin(STOREOPERATIONTYPE tyOperation, STOREOPERATIONINFO *pOpInfo, IOperationCancel *pCancel)
|
|
{
|
|
// Hold onto this
|
|
Assert(m_tyOperation == SOT_INVALID);
|
|
|
|
if (pCancel)
|
|
{
|
|
m_pCancel = pCancel;
|
|
m_pCancel->AddRef();
|
|
}
|
|
m_tyOperation = tyOperation;
|
|
|
|
m_dwPrev = 0;
|
|
m_dwLast = 0;
|
|
|
|
// Party On
|
|
return(S_OK);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnProgress(STOREOPERATIONTYPE tyOperation, DWORD dwCurrent, DWORD dwMax, LPCSTR pszStatus)
|
|
{
|
|
// Close any timeout dialog, if present
|
|
CallbackCloseTimeout(&m_hTimeout);
|
|
|
|
// NOTE: that you can get more than one type of value for tyOperation.
|
|
// Most likely, you will get SOT_CONNECTION_STATUS and then the
|
|
// operation that you might expect. See HotStore.idl and look for
|
|
// the STOREOPERATION enumeration type for more info.
|
|
|
|
switch (tyOperation)
|
|
{
|
|
case SOT_CONNECTION_STATUS:
|
|
break;
|
|
|
|
case SOT_NEW_MAIL_NOTIFICATION:
|
|
m_dwNewInboxMsgs += dwCurrent;
|
|
break;
|
|
|
|
default:
|
|
if (m_state == ONTS_INIT)
|
|
{
|
|
// Update UI
|
|
if (dwMax > m_dwLast)
|
|
{
|
|
m_dwLast = dwMax;
|
|
m_pUI->SetProgressRange((WORD)m_dwLast);
|
|
}
|
|
|
|
SetSpecificProgress((LPSTR)idsDownloadingHeaders, dwCurrent, m_dwLast);
|
|
m_pUI->IncrementProgress((WORD) (dwCurrent - m_dwPrev));
|
|
m_dwPrev = dwCurrent;
|
|
}
|
|
} // switch
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnTimeout(LPINETSERVER pServer, LPDWORD pdwTimeout, IXPTYPE ixpServerType)
|
|
{
|
|
if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
|
|
return(E_FAIL);
|
|
|
|
// Display a timeout dialog
|
|
return CallbackOnTimeout(pServer, ixpServerType, *pdwTimeout, (ITimeoutCallback *)this, &m_hTimeout);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::CanConnect(LPCSTR pszAccountId, DWORD dwFlags)
|
|
{
|
|
HWND hwnd;
|
|
BOOL fPrompt = TRUE;
|
|
|
|
if (m_pUI)
|
|
m_pUI->GetWindow(&hwnd);
|
|
else
|
|
hwnd = NULL;
|
|
|
|
// Call into general CanConnect Utility
|
|
if ((m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)) || (dwFlags & CC_FLAG_DONTPROMPT))
|
|
fPrompt = FALSE;
|
|
|
|
return CallbackCanConnect(pszAccountId, hwnd, fPrompt);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnLogonPrompt(LPINETSERVER pServer, IXPTYPE ixpServerType)
|
|
{
|
|
HWND hwnd;
|
|
|
|
// Close any timeout dialog, if present
|
|
CallbackCloseTimeout(&m_hTimeout);
|
|
|
|
if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)) &&
|
|
!(ISFLAGSET(pServer->dwFlags, ISF_ALWAYSPROMPTFORPASSWORD) &&
|
|
'\0' == pServer->szPassword[0]))
|
|
return(S_FALSE);
|
|
|
|
if (m_pUI)
|
|
m_pUI->GetWindow(&hwnd);
|
|
else
|
|
hwnd = NULL;
|
|
|
|
// Call into general OnLogonPrompt Utility
|
|
return CallbackOnLogonPrompt(hwnd, pServer, ixpServerType);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnComplete(STOREOPERATIONTYPE tyOperation, HRESULT hrComplete,
|
|
LPSTOREOPERATIONINFO pOpInfo, LPSTOREERROR pErrorInfo)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dw;
|
|
BOOL fUserCancel = FALSE;
|
|
|
|
// Close any timeout dialog, if present
|
|
CallbackCloseTimeout(&m_hTimeout);
|
|
|
|
Assert(m_tyOperation != SOT_INVALID);
|
|
if (m_tyOperation != tyOperation)
|
|
return(S_OK);
|
|
|
|
switch (hrComplete)
|
|
{
|
|
case STORE_E_EXPIRED:
|
|
case IXP_E_HTTP_NOT_MODIFIED:
|
|
// Completely ignore errors due to expired/deleted messages
|
|
hrComplete = S_OK;
|
|
break;
|
|
|
|
case STORE_E_OPERATION_CANCELED:
|
|
case HR_E_USER_CANCEL_CONNECT:
|
|
case IXP_E_USER_CANCEL:
|
|
fUserCancel = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (FAILED(hrComplete))
|
|
{
|
|
LPSTR pszOpDescription = NULL;
|
|
LPSTR pszSubject = NULL;
|
|
MESSAGEINFO Message;
|
|
BOOL fFreeMsgInfo = FALSE;
|
|
char szBuf[CCHMAX_STRINGRES], szFmt[CCHMAX_STRINGRES];
|
|
|
|
switch (tyOperation)
|
|
{
|
|
case SOT_GET_MESSAGE:
|
|
// we've already incremented m_cCur by the time we get this
|
|
Assert((m_cCur - 1) < m_pList->cMsgs);
|
|
Message.idMessage = m_pList->prgidMsg[m_cCur - 1];
|
|
|
|
pszOpDescription = MAKEINTRESOURCE(idsNewsTaskArticleError);
|
|
if (DB_S_FOUND == m_pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL))
|
|
{
|
|
fFreeMsgInfo = TRUE;
|
|
pszSubject = Message.pszSubject;
|
|
}
|
|
|
|
break; // case SOT_GET_MESSAGE
|
|
|
|
case SOT_SYNC_FOLDER:
|
|
LoadString(g_hLocRes, idsHeaderDownloadFailureFmt, szFmt, sizeof(szFmt));
|
|
wnsprintf(szBuf, ARRAYSIZE(szBuf), szFmt, (NULL == m_pInfo) ? c_szEmpty : m_pInfo->szGroup);
|
|
pszOpDescription = szBuf;
|
|
break;
|
|
|
|
default:
|
|
LoadString(g_hLocRes, idsMessageSyncFailureFmt, szFmt, sizeof(szFmt));
|
|
wnsprintf(szBuf, ARRAYSIZE(szBuf), szFmt, (NULL == m_pInfo) ? c_szEmpty : m_pInfo->szGroup);
|
|
pszOpDescription = szBuf;
|
|
break; // default case
|
|
} // switch
|
|
|
|
m_fDownloadErrors = TRUE;
|
|
if (NULL != pErrorInfo)
|
|
{
|
|
Assert(pErrorInfo->hrResult == hrComplete); // These two should not be different
|
|
TaskUtil_InsertTransportError(ISFLAGCLEAR(m_dwFlags, DELIVER_NOUI), m_pUI, m_eidCur,
|
|
pErrorInfo, pszOpDescription, pszSubject);
|
|
}
|
|
|
|
if (fFreeMsgInfo)
|
|
m_pFolder->FreeRecord(&Message);
|
|
}
|
|
|
|
if (fUserCancel)
|
|
{
|
|
// User has cancelled the OnLogonPrompt dialog, so abort EVERYTHING
|
|
Cancel();
|
|
}
|
|
else if (m_state == ONTS_INIT)
|
|
{
|
|
SetSpecificProgress((LPSTR)idsDownloadingHeaders, m_dwLast, m_dwLast);
|
|
m_pUI->IncrementProgress((WORD) (m_dwLast - m_dwPrev));
|
|
|
|
// Set a flag if we actually downloaded new headers
|
|
m_fNewHeaders = (m_dwLast > 0);
|
|
|
|
// Move to the next state
|
|
m_state = ONTS_ALLMSGS;
|
|
PostMessage(m_hwnd, NTM_NEXTSTATE, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
m_as = ARTICLE_GETNEXT;
|
|
PostMessage(m_hwnd, NTM_NEXTARTICLESTATE, 0, 0);
|
|
}
|
|
|
|
// Release your cancel object
|
|
SafeRelease(m_pCancel);
|
|
m_tyOperation = SOT_INVALID;
|
|
|
|
// Done
|
|
return(S_OK);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, INT *piUserResponse)
|
|
{
|
|
HWND hwnd;
|
|
|
|
// Close any timeout dialog, if present
|
|
CallbackCloseTimeout(&m_hTimeout);
|
|
|
|
// Raid 55082 - SPOOLER: SPA/SSL auth to NNTP does not display cert warning and fails.
|
|
#if 0
|
|
if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
|
|
return(E_FAIL);
|
|
#endif
|
|
|
|
if (m_pUI)
|
|
m_pUI->GetWindow(&hwnd);
|
|
else
|
|
hwnd = NULL;
|
|
|
|
// Call into my swanky utility
|
|
return CallbackOnPrompt(hwnd, hrError, pszText, pszCaption, uType, piUserResponse);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::OnTimeoutResponse(TIMEOUTRESPONSE eResponse)
|
|
{
|
|
// Call into general timeout response utility
|
|
return CallbackOnTimeoutResponse(eResponse, m_pCancel, &m_hTimeout);
|
|
}
|
|
|
|
STDMETHODIMP COfflineTask::GetParentWindow(DWORD dwReserved, HWND *phwndParent)
|
|
{
|
|
if (!!(m_dwFlags & (DELIVER_NOUI | DELIVER_BACKGROUND)))
|
|
return(E_FAIL);
|
|
|
|
if (m_pUI)
|
|
{
|
|
return m_pUI->GetWindow(phwndParent);
|
|
}
|
|
else
|
|
{
|
|
*phwndParent = NULL;
|
|
return E_FAIL;
|
|
}
|
|
}
|