Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3334 lines
91 KiB

  1. /*
  2. * n e w s s t o r . c p p
  3. *
  4. * Purpose:
  5. * Derives from IMessageServer to implement news specific store communication
  6. *
  7. * Owner:
  8. * cevans.
  9. *
  10. * History:
  11. * May '98: Created
  12. * June '98 Rewrote
  13. *
  14. * Copyright (C) Microsoft Corp. 1998.
  15. */
  16. #include "pch.hxx"
  17. #include "newsstor.h"
  18. #include "xpcomm.h"
  19. #include "xputil.h"
  20. #include "conman.h"
  21. #include "IMsgSite.h"
  22. #include "note.h"
  23. #include "storutil.h"
  24. #include "storfldr.h"
  25. #include "oerules.h"
  26. #include "ruleutil.h"
  27. #include <rulesmgr.h>
  28. #include <serverq.h>
  29. #include "newsutil.h"
  30. #include "range.h"
  31. #define AssertSingleThreaded AssertSz(m_dwThreadId == GetCurrentThreadId(), "Multi-threading make me sad.")
  32. #define WM_NNTP_BEGIN_OP (WM_USER + 69)
  33. static const char s_szNewsStoreWndClass[] = "Outlook Express NewsStore";
  34. // Get XX Header consts
  35. const BYTE MAXOPS = 3; // maxnumber of HEADER commands to issue
  36. const BYTE DLOVERKILL = 10; // percent to grab more than user's desired chunk [10,..)
  37. const BYTE FRACNEEDED = 8; // percent needed to satisfy user's amount [1,10]
  38. void AddRequestedRange(FOLDERINFO *pInfo, DWORD dwLow, DWORD dwHigh, BOOL *pfReq, BOOL *pfRead);
  39. // SOT_SYNC_FOLDER
  40. static const PFNOPFUNC c_rgpfnSyncFolder[] =
  41. {
  42. &CNewsStore::Connect,
  43. &CNewsStore::Group,
  44. &CNewsStore::ExpireHeaders,
  45. &CNewsStore::Headers
  46. };
  47. // SOT_GET_MESSAGE
  48. static const PFNOPFUNC c_rgpfnGetMessage[] =
  49. {
  50. &CNewsStore::Connect,
  51. &CNewsStore::GroupIfNecessary, // only issue group command if necessary
  52. &CNewsStore::Article
  53. };
  54. // SOT_PUT_MESSAGE
  55. static const PFNOPFUNC c_rgpfnPutMessage[] =
  56. {
  57. &CNewsStore::Connect,
  58. &CNewsStore::Post
  59. };
  60. // SOT_SYNCING_STORE
  61. static const PFNOPFUNC c_rgpfnSyncStore[] =
  62. {
  63. &CNewsStore::Connect,
  64. &CNewsStore::List,
  65. &CNewsStore::DeleteDeadGroups,
  66. &CNewsStore::Descriptions
  67. };
  68. // SOT_GET_NEW_GROUPS
  69. static const PFNOPFUNC c_rgpfnGetNewGroups[] =
  70. {
  71. &CNewsStore::Connect,
  72. &CNewsStore::NewGroups
  73. };
  74. // SOT_UPDATE_FOLDER
  75. static const PFNOPFUNC c_rgpfnUpdateFolder[] =
  76. {
  77. &CNewsStore::Connect,
  78. &CNewsStore::Group
  79. };
  80. // SOT_GET_WATCH_INFO
  81. static const PFNOPFUNC c_rgpfnGetWatchInfo[] =
  82. {
  83. &CNewsStore::Connect,
  84. &CNewsStore::Group,
  85. &CNewsStore::XHdrReferences,
  86. &CNewsStore::XHdrSubject,
  87. &CNewsStore::WatchedArticles
  88. };
  89. //
  90. // FUNCTION: CreateNewsStore()
  91. //
  92. // PURPOSE: Creates the CNewsStore object and returns it's IUnknown
  93. // pointer.
  94. //
  95. // PARAMETERS:
  96. // [in] pUnkOuter - Pointer to the IUnknown that this object should
  97. // aggregate with.
  98. // [out] ppUnknown - Returns the pointer to the newly created object.
  99. //
  100. HRESULT CreateNewsStore(IUnknown *pUnkOuter, IUnknown **ppUnknown)
  101. {
  102. HRESULT hr;
  103. IMessageServer *pServer;
  104. // Trace
  105. TraceCall("CreateNewsStore");
  106. // Invalid Args
  107. Assert(ppUnknown);
  108. // Initialize
  109. *ppUnknown = NULL;
  110. // Create me
  111. CNewsStore *pNew = new CNewsStore();
  112. if (NULL == pNew)
  113. return TraceResult(E_OUTOFMEMORY);
  114. hr = CreateServerQueue((IMessageServer *)pNew, &pServer);
  115. pNew->Release();
  116. if (FAILED(hr))
  117. return(hr);
  118. // Cast to unknown
  119. *ppUnknown = SAFECAST(pServer, IMessageServer *);
  120. // Done
  121. return S_OK;
  122. }
  123. //----------------------------------------------------------------------
  124. // CNewsStore
  125. //----------------------------------------------------------------------
  126. //
  127. //
  128. // FUNCTION: CNewsStore::CNewsStore()
  129. //
  130. // PURPOSE: Constructor
  131. //
  132. CNewsStore::CNewsStore()
  133. {
  134. m_cRef = 1;
  135. m_hwnd = NULL;
  136. m_pStore = NULL;
  137. m_pFolder = NULL;
  138. m_idFolder = FOLDERID_INVALID;
  139. m_idParent = FOLDERID_INVALID;
  140. m_szGroup[0] = 0;
  141. m_szAccountId[0] = 0;
  142. ZeroMemory(&m_op, sizeof(m_op));
  143. m_pROP = NULL;
  144. m_pTransport = NULL;
  145. m_ixpStatus = IXP_DISCONNECTED;
  146. m_dwLastStatusTicks = 0;
  147. ZeroMemory(&m_rInetServerInfo, sizeof(INETSERVER));
  148. m_dwWatchLow = 0;
  149. m_dwWatchHigh = 0;
  150. m_rgpszWatchInfo = 0;
  151. m_fXhdrSubject = 0;
  152. m_cRange.Clear();
  153. m_pTable = NULL;
  154. #ifdef DEBUG
  155. m_dwThreadId = GetCurrentThreadId();
  156. #endif // DEBUG
  157. }
  158. //
  159. //
  160. // FUNCTION: CNewsStore::~CNewsStore()
  161. //
  162. // PURPOSE: Destructor
  163. //
  164. CNewsStore::~CNewsStore()
  165. {
  166. AssertSingleThreaded;
  167. if (m_hwnd != NULL)
  168. DestroyWindow(m_hwnd);
  169. if (m_pTransport)
  170. {
  171. // If we're still connected, drop the connection and then release
  172. if (_FConnected())
  173. m_pTransport->DropConnection();
  174. SideAssert(m_pTransport->Release() == 0);
  175. m_pTransport = NULL;
  176. }
  177. _FreeOperation();
  178. if (m_pROP != NULL)
  179. MemFree(m_pROP);
  180. SafeRelease(m_pStore);
  181. SafeRelease(m_pFolder);
  182. SafeRelease(m_pTable);
  183. }
  184. //
  185. // FUNCTION: CNewsStore::QueryInterface()
  186. //
  187. STDMETHODIMP CNewsStore::QueryInterface(REFIID riid, LPVOID *ppv)
  188. {
  189. // Locals
  190. HRESULT hr=S_OK;
  191. // Stack
  192. TraceCall("CNewsStore::QueryInterface");
  193. AssertSingleThreaded;
  194. // Find IID
  195. if (IID_IUnknown == riid)
  196. *ppv = (IMessageServer *)this;
  197. else if (IID_IMessageServer == riid)
  198. *ppv = (IMessageServer *)this;
  199. else if (IID_ITransportCallback == riid)
  200. *ppv = (ITransportCallback *)this;
  201. else if (IID_ITransportCallbackService == riid)
  202. *ppv = (ITransportCallbackService *)this;
  203. else if (IID_INNTPCallback == riid)
  204. *ppv = (INNTPCallback *)this;
  205. else if (IID_INewsStore == riid)
  206. *ppv = (INewsStore *)this;
  207. else
  208. {
  209. *ppv = NULL;
  210. hr = E_NOINTERFACE;
  211. goto exit;
  212. }
  213. // AddRef It
  214. ((IUnknown *)*ppv)->AddRef();
  215. exit:
  216. // Done
  217. return hr;
  218. }
  219. //
  220. // FUNCTION: CNewsStore::AddRef()
  221. //
  222. STDMETHODIMP_(ULONG) CNewsStore::AddRef(void)
  223. {
  224. TraceCall("CNewsStore::AddRef");
  225. AssertSingleThreaded;
  226. return InterlockedIncrement(&m_cRef);
  227. }
  228. //
  229. // FUNCTION: CNewsStore::Release()
  230. //
  231. STDMETHODIMP_(ULONG) CNewsStore::Release(void)
  232. {
  233. TraceCall("CNewsStore::Release");
  234. AssertSingleThreaded;
  235. LONG cRef = InterlockedDecrement(&m_cRef);
  236. Assert(cRef >= 0);
  237. if (0 == cRef)
  238. delete this;
  239. return (ULONG)cRef;
  240. }
  241. HRESULT CNewsStore::Initialize(IMessageStore *pStore, FOLDERID idStoreRoot, IMessageFolder *pFolder, FOLDERID idFolder)
  242. {
  243. HRESULT hr;
  244. FOLDERINFO info;
  245. AssertSingleThreaded;
  246. if (pStore == NULL || idStoreRoot == FOLDERID_INVALID)
  247. return(E_INVALIDARG);
  248. if (!_CreateWnd())
  249. return(E_FAIL);
  250. m_idParent = idStoreRoot;
  251. m_idFolder = idFolder;
  252. ReplaceInterface(m_pStore, pStore);
  253. ReplaceInterface(m_pFolder, pFolder);
  254. hr = m_pStore->GetFolderInfo(idStoreRoot, &info);
  255. if (FAILED(hr))
  256. return(hr);
  257. Assert(!!(info.dwFlags & FOLDER_SERVER));
  258. StrCpyN(m_szAccountId, info.pszAccountId, ARRAYSIZE(m_szAccountId));
  259. m_pStore->FreeRecord(&info);
  260. return(S_OK);
  261. }
  262. HRESULT CNewsStore::ResetFolder(IMessageFolder *pFolder, FOLDERID idFolder)
  263. {
  264. AssertSingleThreaded;
  265. if (pFolder == NULL || idFolder == FOLDERID_INVALID)
  266. return(E_INVALIDARG);
  267. m_idFolder = idFolder;
  268. ReplaceInterface(m_pFolder, pFolder);
  269. return(S_OK);
  270. }
  271. HRESULT CNewsStore::Initialize(FOLDERID idStoreRoot, LPCSTR pszAccountId)
  272. {
  273. AssertSingleThreaded;
  274. if (idStoreRoot == FOLDERID_INVALID || pszAccountId == NULL)
  275. return(E_INVALIDARG);
  276. if (!_CreateWnd())
  277. return(E_FAIL);
  278. m_idParent = idStoreRoot;
  279. m_idFolder = FOLDERID_INVALID;
  280. #pragma prefast(suppress:282, "this macro uses the assignment as part of a test for NULL")
  281. ReplaceInterface(m_pStore, NULL);
  282. #pragma prefast(suppress:282, "this macro uses the assignment as part of a test for NULL")
  283. ReplaceInterface(m_pFolder, NULL);
  284. StrCpyN(m_szAccountId, pszAccountId, ARRAYSIZE(m_szAccountId));
  285. return(S_OK);
  286. }
  287. BOOL CNewsStore::_CreateWnd()
  288. {
  289. WNDCLASS wc;
  290. Assert(m_hwnd == NULL);
  291. if (!GetClassInfo(g_hInst, s_szNewsStoreWndClass, &wc))
  292. {
  293. wc.style = 0;
  294. wc.lpfnWndProc = CNewsStore::NewsStoreWndProc;
  295. wc.cbClsExtra = 0;
  296. wc.cbWndExtra = 0;
  297. wc.hInstance = g_hInst;
  298. wc.hIcon = NULL;
  299. wc.hCursor = NULL;
  300. wc.hbrBackground = NULL;
  301. wc.lpszMenuName = NULL;
  302. wc.lpszClassName = s_szNewsStoreWndClass;
  303. if (RegisterClass(&wc) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
  304. return E_FAIL;
  305. }
  306. m_hwnd = CreateWindowEx(WS_EX_TOPMOST,
  307. s_szNewsStoreWndClass,
  308. s_szNewsStoreWndClass,
  309. WS_POPUP,
  310. CW_USEDEFAULT,
  311. CW_USEDEFAULT,
  312. CW_USEDEFAULT,
  313. CW_USEDEFAULT,
  314. NULL,
  315. NULL,
  316. g_hInst,
  317. (LPVOID)this);
  318. return (NULL != m_hwnd);
  319. }
  320. // --------------------------------------------------------------------------------
  321. // CHTTPMailServer::_WndProc
  322. // --------------------------------------------------------------------------------
  323. LRESULT CALLBACK CNewsStore::NewsStoreWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  324. {
  325. CNewsStore *pThis = (CNewsStore *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  326. switch (msg)
  327. {
  328. case WM_NCCREATE:
  329. Assert(pThis == NULL);
  330. pThis = (CNewsStore *)((LPCREATESTRUCT)lParam)->lpCreateParams;
  331. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pThis);
  332. break;
  333. case WM_NNTP_BEGIN_OP:
  334. Assert(pThis != NULL);
  335. pThis->_DoOperation();
  336. break;
  337. }
  338. return(DefWindowProc(hwnd, msg, wParam, lParam));
  339. }
  340. HRESULT CNewsStore::_BeginDeferredOperation(void)
  341. {
  342. return (PostMessage(m_hwnd, WM_NNTP_BEGIN_OP, 0, 0) ? E_PENDING : E_FAIL);
  343. }
  344. HRESULT CNewsStore::Close(DWORD dwFlags)
  345. {
  346. AssertSingleThreaded;
  347. // let go of the transport, so that it let's go of us
  348. if (m_op.tyOperation != SOT_INVALID)
  349. m_op.fCancel = TRUE;
  350. if (dwFlags & MSGSVRF_DROP_CONNECTION || dwFlags & MSGSVRF_HANDS_OFF_SERVER)
  351. {
  352. if (_FConnected())
  353. m_pTransport->DropConnection();
  354. }
  355. if (dwFlags & MSGSVRF_HANDS_OFF_SERVER)
  356. {
  357. if (m_pTransport)
  358. {
  359. m_pTransport->HandsOffCallback();
  360. m_pTransport->Release();
  361. m_pTransport = NULL;
  362. }
  363. }
  364. return(S_OK);
  365. }
  366. void CNewsStore::_FreeOperation()
  367. {
  368. FILEADDRESS faStream;
  369. if (m_op.pCallback != NULL)
  370. m_op.pCallback->Release();
  371. if (m_pFolder != NULL && m_op.faStream != 0)
  372. m_pFolder->DeleteStream(m_op.faStream);
  373. if (m_op.pStream != NULL)
  374. m_op.pStream->Release();
  375. if (m_op.pPrevFolders != NULL)
  376. MemFree(m_op.pPrevFolders);
  377. if (m_op.pszGroup != NULL)
  378. MemFree(m_op.pszGroup);
  379. if (m_op.pszArticleId != NULL)
  380. MemFree(m_op.pszArticleId);
  381. ZeroMemory(&m_op, sizeof(OPERATION));
  382. m_op.tyOperation = SOT_INVALID;
  383. }
  384. HRESULT CNewsStore::Connect()
  385. {
  386. INETSERVER rInetServerInfo;
  387. HRESULT hr;
  388. BOOL fInetInit;
  389. IImnAccount *pAccount = NULL;
  390. char szAccountName[CCHMAX_ACCOUNT_NAME];
  391. char szLogFile[MAX_PATH];
  392. DWORD dwLog;
  393. AssertSingleThreaded;
  394. Assert(m_op.pCallback != NULL);
  395. //Bug# 68339
  396. if (g_pAcctMan)
  397. {
  398. hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, m_szAccountId, &pAccount);
  399. if (FAILED(hr))
  400. return(hr);
  401. fInetInit = FALSE;
  402. if (_FConnected())
  403. {
  404. Assert(m_pTransport != NULL);
  405. hr = m_pTransport->InetServerFromAccount(pAccount, &rInetServerInfo);
  406. if (FAILED(hr))
  407. goto exit;
  408. Assert(m_rInetServerInfo.szServerName[0] != 0);
  409. if (m_rInetServerInfo.rasconntype == rInetServerInfo.rasconntype &&
  410. m_rInetServerInfo.dwPort == rInetServerInfo.dwPort &&
  411. m_rInetServerInfo.fSSL == rInetServerInfo.fSSL &&
  412. m_rInetServerInfo.fTrySicily == rInetServerInfo.fTrySicily &&
  413. m_rInetServerInfo.dwTimeout == rInetServerInfo.dwTimeout &&
  414. 0 == lstrcmp(m_rInetServerInfo.szUserName, rInetServerInfo.szUserName) &&
  415. ('\0' == rInetServerInfo.szPassword[0] ||
  416. 0 == lstrcmp(m_rInetServerInfo.szPassword, rInetServerInfo.szPassword)) &&
  417. 0 == lstrcmp(m_rInetServerInfo.szServerName, rInetServerInfo.szServerName) &&
  418. 0 == lstrcmp(m_rInetServerInfo.szConnectoid, rInetServerInfo.szConnectoid))
  419. {
  420. hr = S_OK;
  421. goto exit;
  422. }
  423. fInetInit = TRUE;
  424. m_pTransport->DropConnection();
  425. }
  426. hr = m_op.pCallback->CanConnect(m_szAccountId, NOFLAGS);
  427. if (hr != S_OK)
  428. {
  429. if (hr == S_FALSE)
  430. hr = HR_E_USER_CANCEL_CONNECT;
  431. goto exit;
  432. }
  433. if (!m_pTransport)
  434. {
  435. *szLogFile = 0;
  436. dwLog = DwGetOption(OPT_NEWS_XPORT_LOG);
  437. if (dwLog)
  438. {
  439. hr = pAccount->GetPropSz(AP_ACCOUNT_NAME, szAccountName, ARRAYSIZE(szAccountName));
  440. if (FAILED(hr))
  441. goto exit;
  442. _CreateDataFilePath(m_szAccountId, szAccountName, szLogFile, ARRAYSIZE(szLogFile));
  443. }
  444. hr = CreateNNTPTransport(&m_pTransport);
  445. if (FAILED(hr))
  446. goto exit;
  447. hr = m_pTransport->InitNew(*szLogFile ? szLogFile : NULL, this);
  448. if (FAILED(hr))
  449. goto exit;
  450. }
  451. // Convert the account name to an INETSERVER struct that can be passed to Connect()
  452. if (fInetInit)
  453. {
  454. CopyMemory(&m_rInetServerInfo, &rInetServerInfo, sizeof(INETSERVER));
  455. }
  456. else
  457. {
  458. hr = m_pTransport->InetServerFromAccount(pAccount, &m_rInetServerInfo);
  459. if (FAILED(hr))
  460. goto exit;
  461. }
  462. // Always connect using the most recently supplied password from the user
  463. GetPassword(m_rInetServerInfo.dwPort, m_rInetServerInfo.szServerName,
  464. m_rInetServerInfo.szUserName, m_rInetServerInfo.szPassword,
  465. sizeof(m_rInetServerInfo.szPassword));
  466. if (m_pTransport)
  467. {
  468. hr = m_pTransport->Connect(&m_rInetServerInfo, TRUE, TRUE);
  469. if (hr == S_OK)
  470. {
  471. m_op.nsPending = NS_CONNECT;
  472. hr = E_PENDING;
  473. }
  474. }
  475. exit:
  476. if (pAccount)
  477. pAccount->Release();
  478. }
  479. else
  480. hr = E_FAIL;
  481. return hr;
  482. }
  483. HRESULT CNewsStore::Group()
  484. {
  485. HRESULT hr;
  486. FOLDERINFO info;
  487. AssertSingleThreaded;
  488. Assert(m_pTransport != NULL);
  489. hr = m_pStore->GetFolderInfo(m_op.idFolder, &info);
  490. if (SUCCEEDED(hr))
  491. {
  492. hr = m_pTransport->CommandGROUP(info.pszName);
  493. if (hr == S_OK)
  494. {
  495. m_op.pszGroup = PszDup(info.pszName);
  496. m_op.nsPending = NS_GROUP;
  497. hr = E_PENDING;
  498. }
  499. m_pStore->FreeRecord(&info);
  500. }
  501. return hr;
  502. }
  503. HRESULT CNewsStore::GroupIfNecessary()
  504. {
  505. FOLDERINFO info;
  506. HRESULT hr = S_OK;
  507. AssertSingleThreaded;
  508. Assert(m_pTransport != NULL);
  509. if (0 == (m_op.dwFlags & OPFLAG_NOGROUPCMD))
  510. {
  511. hr = m_pStore->GetFolderInfo(m_op.idFolder, &info);
  512. if (SUCCEEDED(hr))
  513. {
  514. if (0 != lstrcmpi(m_szGroup, info.pszName))
  515. {
  516. hr = m_pTransport->CommandGROUP(info.pszName);
  517. if (hr == S_OK)
  518. {
  519. m_op.nsPending = NS_GROUP;
  520. hr = E_PENDING;
  521. }
  522. }
  523. m_pStore->FreeRecord(&info);
  524. }
  525. }
  526. return hr;
  527. }
  528. HRESULT CNewsStore::ExpireHeaders()
  529. {
  530. HRESULT hr;
  531. FOLDERINFO info;
  532. MESSAGEINFO Message;
  533. DWORD dwLow, cid, cidBuf;
  534. MESSAGEIDLIST idList;
  535. HROWSET hRowset;
  536. HLOCK hNotify;
  537. hr = m_pStore->GetFolderInfo(m_op.idFolder, &info);
  538. if (FAILED(hr))
  539. return(hr);
  540. dwLow = min(info.dwServerLow - 1, info.dwClientHigh);
  541. m_pStore->FreeRecord(&info);
  542. hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset);
  543. if (FAILED(hr))
  544. return(hr);
  545. cid = 0;
  546. cidBuf = 0;
  547. idList.cAllocated = 0;
  548. idList.prgidMsg = NULL;
  549. hr = m_pFolder->LockNotify(NOFLAGS, &hNotify);
  550. if (SUCCEEDED(hr))
  551. {
  552. while (TRUE)
  553. {
  554. hr = m_pFolder->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL);
  555. if (FAILED(hr))
  556. break;
  557. // Done
  558. if (S_FALSE == hr)
  559. {
  560. hr = S_OK;
  561. break;
  562. }
  563. if ((DWORD_PTR)Message.idMessage <= dwLow ||
  564. !!(Message.dwFlags & ARF_ARTICLE_EXPIRED))
  565. {
  566. if (cid == cidBuf)
  567. {
  568. cidBuf += 512;
  569. if (!MemRealloc((void **)&idList.prgidMsg, cidBuf * sizeof(MESSAGEID)))
  570. {
  571. m_pFolder->FreeRecord(&Message);
  572. hr = E_OUTOFMEMORY;
  573. break;
  574. }
  575. }
  576. idList.prgidMsg[cid] = Message.idMessage;
  577. cid++;
  578. }
  579. m_pFolder->FreeRecord(&Message);
  580. }
  581. m_pFolder->UnlockNotify(&hNotify);
  582. }
  583. m_pFolder->CloseRowset(&hRowset);
  584. // if it fails, its no big deal, they'll just have some stale headers until next time
  585. if (cid > 0)
  586. {
  587. Assert(idList.prgidMsg != NULL);
  588. idList.cMsgs = cid;
  589. // Delete the messages from the folder without a trashcan (after all, this is news)
  590. if (SUCCEEDED(m_pFolder->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &idList, NULL, NULL)) &&
  591. SUCCEEDED(m_pStore->GetFolderInfo(m_op.idFolder, &info)))
  592. {
  593. info.dwClientLow = dwLow + 1;
  594. m_pStore->UpdateRecord(&info);
  595. m_pStore->FreeRecord(&info);
  596. }
  597. MemFree(idList.prgidMsg);
  598. }
  599. return(hr);
  600. }
  601. HRESULT CNewsStore::Headers(void)
  602. {
  603. FOLDERINFO FolderInfo;
  604. HRESULT hr;
  605. RANGE rRange;
  606. BOOL fNew;
  607. hr = m_pStore->GetFolderInfo(m_op.idFolder, &FolderInfo);
  608. if (FAILED(hr))
  609. return(hr);
  610. Assert(0 == lstrcmpi(m_szGroup, FolderInfo.pszName));
  611. hr = _ComputeHeaderRange(m_op.dwSyncFlags, m_op.cHeaders, &FolderInfo, &rRange);
  612. if (hr == S_OK)
  613. {
  614. // Transport will not allow dwFirst to be 0.
  615. // In this case, there are no messages to be received.
  616. Assert(rRange.dwFirst > 0);
  617. Assert(rRange.dwFirst <= rRange.dwLast);
  618. hr = m_pTransport->GetHeaders(&rRange);
  619. if (hr == S_OK)
  620. {
  621. m_op.nsPending = NS_HEADERS;
  622. hr = E_PENDING;
  623. }
  624. }
  625. if (hr != E_PENDING)
  626. {
  627. if (m_pROP != NULL)
  628. {
  629. MemFree(m_pROP);
  630. m_pROP = NULL;
  631. }
  632. }
  633. if (hr == S_FALSE)
  634. hr = S_OK;
  635. m_pStore->FreeRecord(&FolderInfo);
  636. return(hr);
  637. }
  638. HRESULT CNewsStore::_ComputeHeaderRange(SYNCFOLDERFLAGS dwFlags, DWORD cHeaders, FOLDERINFO *pInfo, RANGE *pRange)
  639. {
  640. HRESULT hr;
  641. UINT uLeftToGet;
  642. ULONG ulMaxReq;
  643. BOOL fFullScan;
  644. DWORD dwDownload;
  645. CRangeList *pRequested;
  646. Assert(pInfo != NULL);
  647. Assert(pRange != NULL);
  648. // Bail if there are no messages to be gotten
  649. if (0 == pInfo->dwServerCount ||
  650. pInfo->dwServerLow > pInfo->dwServerHigh)
  651. {
  652. Assert(!m_pROP);
  653. return(S_FALSE);
  654. }
  655. pRequested = new CRangeList;
  656. if (pRequested == NULL)
  657. {
  658. hr = E_OUTOFMEMORY;
  659. goto error;
  660. }
  661. if (pInfo->Requested.cbSize > 0)
  662. pRequested->Load(pInfo->Requested.pBlobData, pInfo->Requested.cbSize);
  663. ulMaxReq = pRequested->Max();
  664. Assert(0 == pRequested->Min());
  665. fFullScan = (0 == pRequested->MinOfRange(ulMaxReq));
  666. // Bail if we've scanned the whole group
  667. if (fFullScan && (pRequested->Max() == pInfo->dwServerHigh))
  668. goto endit;
  669. if (m_pROP != NULL)
  670. {
  671. // Bail if we've gotten all the user wants
  672. if (m_pROP->uObtained >= ((FRACNEEDED * m_pROP->dwChunk) / 10))
  673. goto endit;
  674. // Bail if this has gone on for too many calls
  675. if (m_pROP->cOps > m_pROP->MaxOps)
  676. goto endit;
  677. }
  678. else
  679. {
  680. m_op.dwProgress = 0;
  681. // Do setup
  682. if (!MemAlloc((LPVOID*)&m_pROP, sizeof(SREFRESHOP)))
  683. {
  684. hr = E_OUTOFMEMORY;
  685. goto error;
  686. }
  687. ZeroMemory(m_pROP, sizeof(SREFRESHOP));
  688. m_pROP->fOnlyNewHeaders = !!(dwFlags & SYNC_FOLDER_NEW_HEADERS);
  689. if (!!(dwFlags & SYNC_FOLDER_XXX_HEADERS))
  690. {
  691. Assert(cHeaders > 0);
  692. m_pROP->dwChunk = cHeaders;
  693. m_pROP->dwDlSize = (DWORD)((m_pROP->dwChunk * DLOVERKILL) / 10);
  694. m_pROP->MaxOps = MAXOPS;
  695. m_pROP->fEnabled = TRUE;
  696. m_op.dwTotal = m_pROP->dwDlSize;
  697. }
  698. else
  699. {
  700. // user has turned off the X headers option
  701. // so we need to get all of the newest headers, but then also
  702. // grab any old headers on this refresh
  703. // we have to do all that here and now because there is no
  704. // UI available to the user
  705. // don't want to quit except on a full scan
  706. // m_pROP->fOnlyNewHeaders = FALSE;
  707. m_pROP->MaxOps = m_pROP->dwChunk = m_pROP->dwDlSize = pInfo->dwServerHigh;
  708. Assert(!m_pROP->fEnabled);
  709. m_op.dwTotal = pInfo->dwNotDownloaded;
  710. }
  711. }
  712. uLeftToGet = m_pROP->dwDlSize - m_pROP->uObtained;
  713. if (RANGE_ERROR == ulMaxReq)
  714. {
  715. AssertSz(0, TEXT("shouldn't be here, but you can ignore."));
  716. ulMaxReq = pInfo->dwServerLow - 1;
  717. }
  718. Assert(ulMaxReq <= pInfo->dwServerHigh);
  719. Assert(pRequested->IsInRange(pInfo->dwServerLow - 1));
  720. ///////////////////////////////////
  721. /// Compute begin and end numbers
  722. if (ulMaxReq < pInfo->dwServerHigh)
  723. {
  724. // get the newest headers
  725. Assert(0 == m_pROP->cOps); // EricAn said this assert might not be valid
  726. Assert(ulMaxReq + 1 >= pInfo->dwServerLow);
  727. m_pROP->dwLast = pInfo->dwServerHigh;
  728. if (!m_pROP->fEnabled || (m_pROP->dwChunk - 1 > m_pROP->dwLast))
  729. {
  730. m_pROP->dwFirst = ulMaxReq + 1;
  731. }
  732. else
  733. {
  734. // we use dwChunk here b/c headers will be nearly dense
  735. m_pROP->dwFirst = max(m_pROP->dwLast - (m_pROP->dwChunk - 1), ulMaxReq + 1);
  736. }
  737. m_pROP->dwFirstNew = ulMaxReq + 1;
  738. }
  739. else if (m_pROP->dwFirst > m_pROP->dwFirstNew) // if init to zero, won't be true
  740. {
  741. // still new headers user hasn't seen
  742. Assert(m_pROP->cOps); // can't happen at first
  743. Assert(m_pROP->dwFirstNew >= pInfo->dwServerLow); // better be valid
  744. Assert(m_pROP->fEnabled); // should have gotten them all
  745. m_pROP->dwLast = m_pROP->dwFirst - 1; // since cOps is pos, dwFirst is valid
  746. if (uLeftToGet - 1 > m_pROP->dwLast)
  747. m_pROP->dwFirst = m_pROP->dwFirstNew;
  748. else
  749. m_pROP->dwFirst = max(m_pROP->dwLast - (uLeftToGet - 1), m_pROP->dwFirstNew);
  750. }
  751. else if (!m_pROP->fOnlyNewHeaders)
  752. {
  753. RangeType rt;
  754. // want to find the highest num header we've never requested
  755. m_pROP->dwFirstNew = pInfo->dwServerHigh; // no new mesgs in this session
  756. if (!pRequested->HighestAntiRange(&rt))
  757. {
  758. AssertSz(0, TEXT("You can ignore if you want, but we shouldn't be here."));
  759. rt.low = max(pRequested->Max() + 1, pInfo->dwServerLow);
  760. rt.high = pInfo->dwServerHigh;
  761. if (rt.low == rt.high)
  762. goto endit;
  763. }
  764. m_pROP->dwLast = rt.high;
  765. if (!m_pROP->fEnabled || ((uLeftToGet - 1) > rt.high))
  766. m_pROP->dwFirst = rt.low;
  767. else
  768. m_pROP->dwFirst = max(rt.low, rt.high - (uLeftToGet - 1));
  769. }
  770. else
  771. {
  772. goto endit;
  773. }
  774. // check our math and logic about download range
  775. Assert(m_pROP->dwLast <= pInfo->dwServerHigh);
  776. Assert(m_pROP->dwFirst >= pInfo->dwServerLow);
  777. Assert(!pRequested->IsInRange(m_pROP->dwLast));
  778. Assert(!pRequested->IsInRange(m_pROP->dwFirst));
  779. Assert(!m_pROP->fEnabled || ((m_pROP->dwLast - m_pROP->dwFirst) < m_pROP->dwDlSize));
  780. if (!m_pROP->dwLast || (m_pROP->dwFirst > m_pROP->dwLast))
  781. {
  782. AssertSz(0, TEXT("You would have made a zero size HEADER call"));
  783. goto endit;
  784. }
  785. pRequested->Release();
  786. pRange->idType = RT_RANGE;
  787. pRange->dwFirst = m_pROP->dwFirst;
  788. pRange->dwLast = m_pROP->dwLast;
  789. dwDownload = pRange->dwLast - pRange->dwFirst + 1;
  790. if (dwDownload + m_op.dwProgress > m_op.dwTotal)
  791. m_op.dwTotal = dwDownload + m_op.dwProgress;
  792. return(S_OK);
  793. endit:
  794. hr = S_FALSE;
  795. error:
  796. if (m_pROP != NULL)
  797. {
  798. MemFree(m_pROP);
  799. m_pROP = NULL;
  800. }
  801. if(pRequested) pRequested->Release();
  802. return(hr);
  803. }
  804. HRESULT CNewsStore::Article()
  805. {
  806. HRESULT hr;
  807. MESSAGEINFO info;
  808. DWORD dwTotalLines;
  809. ARTICLEID rArticleId;
  810. AssertSingleThreaded;
  811. Assert(m_pTransport != NULL);
  812. dwTotalLines = 0;
  813. if (m_op.pszArticleId != NULL)
  814. {
  815. rArticleId.idType = AID_MSGID;
  816. rArticleId.pszMessageId = m_op.pszArticleId;
  817. m_op.pszArticleId = NULL;
  818. }
  819. else if (m_op.idMessage)
  820. {
  821. ZeroMemory(&info, sizeof(info));
  822. info.idMessage = m_op.idMessage;
  823. hr = m_pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &info, NULL);
  824. if (DB_S_FOUND == hr)
  825. {
  826. dwTotalLines = info.cLines;
  827. m_pFolder->FreeRecord(&info);
  828. }
  829. rArticleId.idType = AID_ARTICLENUM;
  830. rArticleId.dwArticleNum = (DWORD_PTR)m_op.idMessage;
  831. }
  832. else
  833. {
  834. Assert(m_op.idServerMessage);
  835. rArticleId.idType = AID_ARTICLENUM;
  836. rArticleId.dwArticleNum = m_op.idServerMessage;
  837. }
  838. m_op.dwProgress = 0;
  839. m_op.dwTotal = dwTotalLines;
  840. hr = m_pTransport->CommandARTICLE(&rArticleId);
  841. if (hr == S_OK)
  842. {
  843. m_op.nsPending = NS_ARTICLE;
  844. hr = E_PENDING;
  845. }
  846. return(hr);
  847. }
  848. HRESULT CNewsStore::Post()
  849. {
  850. HRESULT hr;
  851. NNTPMESSAGE rMsg;
  852. AssertSingleThreaded;
  853. Assert(m_pTransport != NULL);
  854. rMsg.pstmMsg = m_op.pStream;
  855. rMsg.cbSize = 0;
  856. hr = m_pTransport->CommandPOST(&rMsg);
  857. if (SUCCEEDED(hr))
  858. {
  859. m_op.nsPending = NS_POST;
  860. hr = E_PENDING;
  861. }
  862. return(hr);
  863. }
  864. HRESULT CNewsStore::NewGroups()
  865. {
  866. HRESULT hr;
  867. NNTPMESSAGE rMsg;
  868. AssertSingleThreaded;
  869. Assert(m_pTransport != NULL);
  870. hr = m_pTransport->CommandNEWGROUPS(&m_op.st, NULL);
  871. if (SUCCEEDED(hr))
  872. {
  873. m_op.nsPending = NS_NEWGROUPS;
  874. hr = E_PENDING;
  875. }
  876. return(hr);
  877. }
  878. int __cdecl CompareFolderIds(const void *elem1, const void *elem2)
  879. {
  880. return(*((DWORD *)elem1) - *((DWORD *)elem2));
  881. }
  882. HRESULT CNewsStore::List()
  883. {
  884. HRESULT hr;
  885. ULONG cFolders;
  886. FOLDERINFO info;
  887. IEnumerateFolders *pEnum;
  888. Assert(0 == m_op.dwFlags);
  889. Assert(m_op.pPrevFolders == NULL);
  890. m_op.cPrevFolders = 0;
  891. hr = m_pStore->EnumChildren(m_idParent, FALSE, &pEnum);
  892. if (SUCCEEDED(hr))
  893. {
  894. hr = pEnum->Count(&cFolders);
  895. if (SUCCEEDED(hr) && cFolders > 0)
  896. {
  897. if (!MemAlloc((void **)&m_op.pPrevFolders, cFolders * sizeof(FOLDERID)))
  898. {
  899. hr = E_OUTOFMEMORY;
  900. }
  901. else
  902. {
  903. while (S_OK == pEnum->Next(1, &info, NULL))
  904. {
  905. m_op.pPrevFolders[m_op.cPrevFolders] = info.idFolder;
  906. m_op.cPrevFolders++;
  907. m_pStore->FreeRecord(&info);
  908. }
  909. Assert(m_op.cPrevFolders == cFolders);
  910. qsort(m_op.pPrevFolders, m_op.cPrevFolders, sizeof(FOLDERID), CompareFolderIds);
  911. }
  912. }
  913. pEnum->Release();
  914. }
  915. if (SUCCEEDED(hr))
  916. hr = _List(NULL);
  917. return(hr);
  918. }
  919. HRESULT CNewsStore::DeleteDeadGroups()
  920. {
  921. ULONG i;
  922. HRESULT hr;
  923. FOLDERID *pId;
  924. if (m_op.pPrevFolders != NULL)
  925. {
  926. Assert(m_op.cPrevFolders > 0);
  927. for (i = 0, pId = m_op.pPrevFolders; i < m_op.cPrevFolders; i++, pId++)
  928. {
  929. if (*pId != 0)
  930. {
  931. hr = m_pStore->DeleteFolder(*pId, DELETE_FOLDER_NOTRASHCAN, NULL);
  932. Assert(SUCCEEDED(hr));
  933. }
  934. }
  935. MemFree(m_op.pPrevFolders);
  936. m_op.pPrevFolders = NULL;
  937. }
  938. return(S_OK);
  939. }
  940. static const char c_szNewsgroups[] = "NEWSGROUPS";
  941. HRESULT CNewsStore::Descriptions()
  942. {
  943. HRESULT hr;
  944. m_op.dwFlags = OPFLAG_DESCRIPTIONS;
  945. hr = _List(c_szNewsgroups);
  946. return(hr);
  947. }
  948. HRESULT CNewsStore::_List(LPCSTR pszCommand)
  949. {
  950. HRESULT hr;
  951. AssertSingleThreaded;
  952. Assert(m_pTransport != NULL);
  953. m_op.dwProgress = 0;
  954. m_op.dwTotal = 0;
  955. hr = m_pTransport->CommandLIST((LPSTR)pszCommand);
  956. if (hr == S_OK)
  957. {
  958. m_op.nsPending = NS_LIST;
  959. hr = E_PENDING;
  960. }
  961. return(hr);
  962. }
  963. HRESULT CNewsStore::_DoOperation()
  964. {
  965. HRESULT hr;
  966. STOREOPERATIONINFO soi;
  967. STOREOPERATIONINFO *psoi;
  968. Assert(m_op.tyOperation != SOT_INVALID);
  969. Assert(m_op.pfnState != NULL);
  970. Assert(m_op.cState > 0);
  971. Assert(m_op.iState <= m_op.cState);
  972. hr = S_OK;
  973. if (m_op.iState == 0)
  974. {
  975. if (m_op.tyOperation == SOT_GET_MESSAGE)
  976. {
  977. // provide message id on get message start
  978. soi.cbSize = sizeof(STOREOPERATIONINFO);
  979. soi.idMessage = m_op.idMessage;
  980. psoi = &soi;
  981. }
  982. else
  983. {
  984. psoi = NULL;
  985. }
  986. m_op.pCallback->OnBegin(m_op.tyOperation, psoi, (IOperationCancel *)this);
  987. }
  988. while (m_op.iState < m_op.cState)
  989. {
  990. hr = (this->*(m_op.pfnState[m_op.iState]))();
  991. if (FAILED(hr))
  992. break;
  993. m_op.iState++;
  994. }
  995. if ((m_op.iState == m_op.cState) ||
  996. (FAILED(hr) && hr != E_PENDING))
  997. {
  998. if (hr == HR_E_USER_CANCEL_CONNECT)
  999. {
  1000. // if operation is canceled, add the flush flag
  1001. m_op.error.dwFlags |= SE_FLAG_FLUSHALL;
  1002. }
  1003. if (FAILED(hr))
  1004. {
  1005. IXPRESULT rIxpResult;
  1006. // Fake an IXPRESULT
  1007. ZeroMemory(&rIxpResult, sizeof(rIxpResult));
  1008. rIxpResult.hrResult = hr;
  1009. // Return meaningful error information
  1010. _FillStoreError(&m_op.error, &rIxpResult);
  1011. Assert(m_op.error.hrResult == hr);
  1012. }
  1013. else
  1014. m_op.error.hrResult = hr;
  1015. m_op.pCallback->OnComplete(m_op.tyOperation, hr, NULL, &m_op.error);
  1016. _FreeOperation();
  1017. }
  1018. return(hr);
  1019. }
  1020. //
  1021. // FUNCTION: CNewsStore::SynchronizeFolder()
  1022. //
  1023. // PURPOSE: Load all of the new messages headers for this folder
  1024. // as appropriate based on the flags
  1025. //
  1026. // PARAMETERS:
  1027. // [in] dwFlags -
  1028. //
  1029. HRESULT CNewsStore::SynchronizeFolder(SYNCFOLDERFLAGS dwFlags, DWORD cHeaders,
  1030. IStoreCallback *pCallback)
  1031. {
  1032. HRESULT hr;
  1033. // Stack
  1034. TraceCall("CNewsStore::SynchronizeFolder");
  1035. AssertSingleThreaded;
  1036. Assert(pCallback != NULL);
  1037. Assert(m_op.tyOperation == SOT_INVALID);
  1038. Assert(m_pROP == NULL);
  1039. m_op.tyOperation = SOT_SYNC_FOLDER;
  1040. m_op.pfnState = c_rgpfnSyncFolder;
  1041. m_op.iState = 0;
  1042. m_op.cState = ARRAYSIZE(c_rgpfnSyncFolder);
  1043. m_op.pCallback = pCallback;
  1044. m_op.pCallback->AddRef();
  1045. m_op.idFolder = m_idFolder;
  1046. m_op.dwSyncFlags = dwFlags;
  1047. m_op.cHeaders = cHeaders;
  1048. hr = _BeginDeferredOperation();
  1049. return hr;
  1050. }
  1051. //
  1052. // FUNCTION: CNewsStore::GetMessage()
  1053. //
  1054. // PURPOSE: Start the retrieval of a single message as specified
  1055. // by idMessage.
  1056. //
  1057. // PARAMETERS:
  1058. // [in] idFolder -
  1059. // [in] idMessage -
  1060. // [in] pStream -
  1061. // [in] pCallback - callbacks in case we need to present ui, progress,
  1062. //
  1063. HRESULT CNewsStore::GetMessage(MESSAGEID idMessage, IStoreCallback *pCallback)
  1064. {
  1065. HRESULT hr;
  1066. // Stack
  1067. TraceCall("CNewsStore::GetMessage");
  1068. AssertSingleThreaded;
  1069. Assert(pCallback != NULL);
  1070. Assert(m_op.tyOperation == SOT_INVALID);
  1071. // create a persistent stream
  1072. if (FAILED(hr = CreatePersistentWriteStream(m_pFolder, &m_op.pStream, &m_op.faStream)))
  1073. goto exit;
  1074. m_op.tyOperation = SOT_GET_MESSAGE;
  1075. m_op.pfnState = c_rgpfnGetMessage;
  1076. m_op.iState = 0;
  1077. m_op.cState = ARRAYSIZE(c_rgpfnGetMessage);
  1078. m_op.dwFlags = 0;
  1079. m_op.pCallback = pCallback;
  1080. m_op.pCallback->AddRef();
  1081. m_op.idFolder = m_idFolder;
  1082. m_op.idMessage = idMessage;
  1083. hr = _BeginDeferredOperation();
  1084. exit:
  1085. return hr;
  1086. }
  1087. HRESULT CNewsStore::GetArticle(LPCSTR pszArticleId, IStream *pStream,
  1088. IStoreCallback *pCallback)
  1089. {
  1090. HRESULT hr;
  1091. // Stack
  1092. TraceCall("CNewsStore::GetArticle");
  1093. AssertSingleThreaded;
  1094. Assert(pStream != NULL);
  1095. Assert(pCallback != NULL);
  1096. Assert(m_op.tyOperation == SOT_INVALID);
  1097. m_op.pszArticleId = PszDup(pszArticleId);
  1098. if (m_op.pszArticleId == NULL)
  1099. return(E_OUTOFMEMORY);
  1100. m_op.tyOperation = SOT_GET_MESSAGE;
  1101. m_op.pfnState = c_rgpfnGetMessage;
  1102. m_op.iState = 0;
  1103. m_op.cState = ARRAYSIZE(c_rgpfnGetMessage);
  1104. m_op.dwFlags = OPFLAG_NOGROUPCMD;
  1105. m_op.pCallback = pCallback;
  1106. m_op.pCallback->AddRef();
  1107. m_op.idFolder = m_idFolder;
  1108. m_op.idMessage = 0;
  1109. m_op.pStream = pStream;
  1110. m_op.pStream->AddRef();
  1111. hr = _BeginDeferredOperation();
  1112. return hr;
  1113. }
  1114. //
  1115. // FUNCTION: CNewsStore::PutMessage()
  1116. //
  1117. // PURPOSE: Posting news messages
  1118. //
  1119. // PARAMETERS:
  1120. // [in] idFolder -
  1121. // [in] dwFlags -
  1122. // [in] pftReceived -
  1123. // [in] pStream -
  1124. // [in] pCallback - callbacks in case we need to present ui, progress,
  1125. //
  1126. HRESULT CNewsStore::PutMessage(FOLDERID idFolder, MESSAGEFLAGS dwFlags,
  1127. LPFILETIME pftReceived, IStream *pStream,
  1128. IStoreCallback *pCallback)
  1129. {
  1130. HRESULT hr;
  1131. // Stack
  1132. TraceCall("CNewsStore::GetMessage");
  1133. AssertSingleThreaded;
  1134. Assert(pStream != NULL);
  1135. Assert(pCallback != NULL);
  1136. Assert(m_op.tyOperation == SOT_INVALID);
  1137. m_op.tyOperation = SOT_PUT_MESSAGE;
  1138. m_op.pfnState = c_rgpfnPutMessage;
  1139. m_op.iState = 0;
  1140. m_op.cState = ARRAYSIZE(c_rgpfnPutMessage);
  1141. m_op.pCallback = pCallback;
  1142. m_op.pCallback->AddRef();
  1143. m_op.idFolder = idFolder;
  1144. m_op.dwMsgFlags = dwFlags;
  1145. m_op.pStream = pStream;
  1146. m_op.pStream->AddRef();
  1147. hr = _BeginDeferredOperation();
  1148. return hr;
  1149. }
  1150. //
  1151. // FUNCTION: CNewsStore::SynchronizeStore()
  1152. //
  1153. // PURPOSE: Synchronize the list of mail groups
  1154. //
  1155. // PARAMETERS:
  1156. // [in] idParent -
  1157. // [in] dwFlags -
  1158. // [in] pCallback - callbacks in case we need to present ui, progress,
  1159. //
  1160. HRESULT CNewsStore::SynchronizeStore(FOLDERID idParent, SYNCSTOREFLAGS dwFlags, IStoreCallback *pCallback)
  1161. {
  1162. HRESULT hr;
  1163. AssertSingleThreaded;
  1164. Assert(pCallback != NULL);
  1165. Assert(m_op.tyOperation == SOT_INVALID);
  1166. m_op.tyOperation = SOT_SYNCING_STORE;
  1167. m_op.pfnState = c_rgpfnSyncStore;
  1168. m_op.iState = 0;
  1169. m_op.cState = ARRAYSIZE(c_rgpfnSyncStore);
  1170. if (0 == (dwFlags & SYNC_STORE_GET_DESCRIPTIONS))
  1171. {
  1172. // we don't need to do the descriptions command
  1173. m_op.cState -= 1;
  1174. }
  1175. m_op.pCallback = pCallback;
  1176. m_op.pCallback->AddRef();
  1177. m_op.idFolder = idParent;
  1178. hr = _BeginDeferredOperation();
  1179. return(hr);
  1180. }
  1181. HRESULT CNewsStore::GetNewGroups(LPSYSTEMTIME pSysTime, IStoreCallback *pCallback)
  1182. {
  1183. HRESULT hr;
  1184. AssertSingleThreaded;
  1185. Assert(pSysTime != NULL);
  1186. Assert(pCallback != NULL);
  1187. Assert(m_op.tyOperation == SOT_INVALID);
  1188. m_op.tyOperation = SOT_GET_NEW_GROUPS;
  1189. m_op.pfnState = c_rgpfnGetNewGroups;
  1190. m_op.iState = 0;
  1191. m_op.cState = ARRAYSIZE(c_rgpfnGetNewGroups);
  1192. m_op.pCallback = pCallback;
  1193. m_op.pCallback->AddRef();
  1194. m_op.st = *pSysTime;
  1195. m_op.idFolder = m_idParent;
  1196. hr = _BeginDeferredOperation();
  1197. return(hr);
  1198. }
  1199. //
  1200. // FUNCTION: CNewsStore::GetFolderCounts()
  1201. //
  1202. // PURPOSE: Update folder statistics for the passed in folder
  1203. //
  1204. // PARAMETERS:
  1205. // [in] idFolder - folder id associated with the newsgroup
  1206. // [in] pCallback - callbacks to send OnComplete to.
  1207. //
  1208. HRESULT CNewsStore::GetFolderCounts(FOLDERID idFolder, IStoreCallback *pCallback)
  1209. {
  1210. HRESULT hr;
  1211. // Stack
  1212. TraceCall("CNewsStore::GetFolderCounts");
  1213. AssertSingleThreaded;
  1214. Assert(pCallback != NULL);
  1215. Assert(m_op.tyOperation == SOT_INVALID);
  1216. m_op.tyOperation = SOT_UPDATE_FOLDER;
  1217. m_op.pfnState = c_rgpfnUpdateFolder;
  1218. m_op.iState = 0;
  1219. m_op.cState = ARRAYSIZE(c_rgpfnUpdateFolder);
  1220. m_op.pCallback = pCallback;
  1221. m_op.pCallback->AddRef();
  1222. m_op.idFolder = idFolder;
  1223. hr = _BeginDeferredOperation();
  1224. return hr;
  1225. }
  1226. HRESULT CNewsStore::SetIdleCallback(IStoreCallback *pDefaultCallback)
  1227. {
  1228. // Stack
  1229. TraceCall("CNewsStore::SetIdleCallback");
  1230. return E_NOTIMPL;
  1231. }
  1232. HRESULT CNewsStore::CopyMessages(IMessageFolder *pDest, COPYMESSAGEFLAGS dwOptions,
  1233. LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags,
  1234. IStoreCallback *pCallback)
  1235. {
  1236. // Stack
  1237. TraceCall("CNewsStore::CopyMessages");
  1238. return E_NOTIMPL;
  1239. }
  1240. HRESULT CNewsStore::DeleteMessages(DELETEMESSAGEFLAGS dwOptions,
  1241. LPMESSAGEIDLIST pList, IStoreCallback *pCallback)
  1242. {
  1243. // Stack
  1244. TraceCall("CNewsStore::DeleteMessages");
  1245. AssertSingleThreaded;
  1246. Assert(pList != NULL);
  1247. Assert(pCallback != NULL);
  1248. Assert(m_pFolder != NULL);
  1249. return(m_pFolder->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | dwOptions, pList, NULL, NULL));
  1250. }
  1251. HRESULT CNewsStore::SetMessageFlags(LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags, SETMESSAGEFLAGSFLAGS dwFlags,
  1252. IStoreCallback *pCallback)
  1253. {
  1254. // Stack
  1255. TraceCall("CNewsStore::SetMessageFlags");
  1256. Assert(NULL == pList || pList->cMsgs > 0);
  1257. return E_NOTIMPL;
  1258. }
  1259. HRESULT CNewsStore::GetServerMessageFlags(MESSAGEFLAGS *pFlags)
  1260. {
  1261. return S_FALSE;
  1262. }
  1263. HRESULT CNewsStore::CreateFolder(FOLDERID idParent, SPECIALFOLDER tySpecial,
  1264. LPCSTR pszName, FLDRFLAGS dwFlags,
  1265. IStoreCallback *pCallback)
  1266. {
  1267. // Stack
  1268. TraceCall("CNewsStore::CreateFolder");
  1269. return E_NOTIMPL;
  1270. }
  1271. HRESULT CNewsStore::MoveFolder(FOLDERID idFolder, FOLDERID idParentNew,
  1272. IStoreCallback *pCallback)
  1273. {
  1274. // Stack
  1275. TraceCall("CNewsStore::MoveFolder");
  1276. return E_NOTIMPL;
  1277. }
  1278. HRESULT CNewsStore::RenameFolder(FOLDERID idFolder, LPCSTR pszName, IStoreCallback *pCallback)
  1279. {
  1280. // Stack
  1281. TraceCall("CNewsStore::RenameFolder");
  1282. return E_NOTIMPL;
  1283. }
  1284. HRESULT CNewsStore::DeleteFolder(FOLDERID idFolder, DELETEFOLDERFLAGS dwFlags, IStoreCallback *pCallback)
  1285. {
  1286. // Stack
  1287. TraceCall("CNewsStore::DeleteFolder");
  1288. return E_NOTIMPL;
  1289. }
  1290. HRESULT CNewsStore::SubscribeToFolder(FOLDERID idFolder, BOOL fSubscribe,
  1291. IStoreCallback *pCallback)
  1292. {
  1293. // Stack
  1294. TraceCall("CNewsStore::SubscribeToFolder");
  1295. return E_NOTIMPL;
  1296. }
  1297. //
  1298. // FUNCTION: CNewsStore::Cancel()
  1299. //
  1300. // PURPOSE: Cancel the operation
  1301. //
  1302. // PARAMETERS:
  1303. // [in] tyCancel - The way that the operation was canceled.
  1304. // Generally CT_ABORT or CT_CANCEL
  1305. //
  1306. HRESULT CNewsStore::Cancel(CANCELTYPE tyCancel)
  1307. {
  1308. if (m_op.tyOperation != SOT_INVALID)
  1309. {
  1310. m_op.fCancel = TRUE;
  1311. if (_FConnected())
  1312. m_pTransport->DropConnection();
  1313. }
  1314. return(S_OK);
  1315. }
  1316. //
  1317. // FUNCTION: CNewsStore::OnTimeout()
  1318. //
  1319. // PURPOSE:
  1320. //
  1321. // PARAMETERS:
  1322. // [in] pdwTimeout -
  1323. // [in] pTransport -
  1324. //
  1325. HRESULT CNewsStore::OnTimeout(DWORD *pdwTimeout, IInternetTransport *pTransport)
  1326. {
  1327. // Stack
  1328. TraceCall("CNewsStore::OnTimeout");
  1329. AssertSingleThreaded;
  1330. Assert(m_op.tyOperation != SOT_INVALID);
  1331. Assert(m_op.pCallback != NULL);
  1332. m_op.pCallback->OnTimeout(&m_rInetServerInfo, pdwTimeout, IXP_NNTP);
  1333. return(S_OK);
  1334. }
  1335. //
  1336. // FUNCTION: CNewsStore::OnLogonPrompt()
  1337. //
  1338. // PURPOSE:
  1339. //
  1340. // PARAMETERS:
  1341. // [in] pInetServer -
  1342. // [in] pTransport -
  1343. //
  1344. HRESULT CNewsStore::OnLogonPrompt(LPINETSERVER pInetServer, IInternetTransport *pTransport)
  1345. {
  1346. HRESULT hr;
  1347. char szPassword[CCHMAX_PASSWORD];
  1348. // Stack
  1349. TraceCall("CNewsStore::OnLogonPrompt");
  1350. AssertSingleThreaded;
  1351. Assert(pInetServer != NULL);
  1352. Assert(pTransport != NULL);
  1353. Assert(m_op.tyOperation != SOT_INVALID);
  1354. Assert(m_op.pCallback != NULL);
  1355. // Check if we have a cached password that's different from current password
  1356. hr = GetPassword(pInetServer->dwPort, pInetServer->szServerName, pInetServer->szUserName,
  1357. szPassword, sizeof(szPassword));
  1358. if (SUCCEEDED(hr) && 0 != lstrcmp(szPassword, pInetServer->szPassword))
  1359. {
  1360. StrCpyN(pInetServer->szPassword, szPassword, ARRAYSIZE(pInetServer->szPassword));
  1361. return S_OK;
  1362. }
  1363. hr = m_op.pCallback->OnLogonPrompt(pInetServer, IXP_NNTP);
  1364. // Cache the password for this session
  1365. if (S_OK == hr)
  1366. {
  1367. SavePassword(pInetServer->dwPort, pInetServer->szServerName, pInetServer->szUserName, pInetServer->szPassword);
  1368. // copy the password/user name into our local inetserver
  1369. StrCpyN(m_rInetServerInfo.szPassword, pInetServer->szPassword, ARRAYSIZE(m_rInetServerInfo.szPassword));
  1370. StrCpyN(m_rInetServerInfo.szUserName, pInetServer->szUserName, ARRAYSIZE(m_rInetServerInfo.szUserName));
  1371. }
  1372. return(hr);
  1373. }
  1374. //
  1375. // FUNCTION: CNewsStore::OnPrompt()
  1376. //
  1377. // PURPOSE:
  1378. //
  1379. // PARAMETERS:
  1380. // [in] hrError -
  1381. // [in] pszText -
  1382. // [in] pszCaption -
  1383. // [in] uType -
  1384. // [in] pTransport -
  1385. //
  1386. int CNewsStore::OnPrompt(HRESULT hrError, LPCSTR pszText, LPCSTR pszCaption,
  1387. UINT uType, IInternetTransport *pTransport)
  1388. {
  1389. int iResponse = 0;
  1390. // Stack
  1391. TraceCall("CNewsStore::OnPrompt");
  1392. AssertSingleThreaded;
  1393. Assert(m_op.tyOperation != SOT_INVALID);
  1394. Assert(m_op.pCallback != NULL);
  1395. m_op.pCallback->OnPrompt(hrError, pszText, pszCaption, uType, &iResponse);
  1396. return(iResponse);
  1397. }
  1398. //
  1399. // FUNCTION: CNewsStore::OnStatus()
  1400. //
  1401. // PURPOSE:
  1402. //
  1403. // PARAMETERS:
  1404. // [in] ixpstatus - status code passed in from the transport
  1405. // [in] pTransport - The NNTP transport that is calling us
  1406. //
  1407. HRESULT CNewsStore::OnStatus(IXPSTATUS ixpstatus, IInternetTransport *pTransport)
  1408. {
  1409. HRESULT hr;
  1410. // Stack
  1411. TraceCall("CNewsStore::OnStatus");
  1412. AssertSingleThreaded;
  1413. m_ixpStatus = ixpstatus;
  1414. if (m_op.pCallback != NULL)
  1415. m_op.pCallback->OnProgress(SOT_CONNECTION_STATUS, ixpstatus, 0, m_rInetServerInfo.szServerName);
  1416. // If we were disconnected, then clean up some internal state.
  1417. if (IXP_DISCONNECTED == ixpstatus)
  1418. {
  1419. // Reset the group name so we know to reissue it later.
  1420. m_szGroup[0] = 0;
  1421. if (m_op.tyOperation != SOT_INVALID)
  1422. {
  1423. Assert(m_op.pCallback != NULL);
  1424. if (m_op.fCancel)
  1425. {
  1426. IXPRESULT rIxpResult;
  1427. // if operation is canceled, add the flush flag
  1428. m_op.error.dwFlags |= SE_FLAG_FLUSHALL;
  1429. // Fake the IXPRESULT
  1430. ZeroMemory(&rIxpResult, sizeof(rIxpResult));
  1431. rIxpResult.hrResult = STORE_E_OPERATION_CANCELED;
  1432. // Return meaningful error information
  1433. _FillStoreError(&m_op.error, &rIxpResult);
  1434. Assert(STORE_E_OPERATION_CANCELED == m_op.error.hrResult);
  1435. m_op.pCallback->OnComplete(m_op.tyOperation, m_op.error.hrResult, NULL, &m_op.error);
  1436. _FreeOperation();
  1437. }
  1438. }
  1439. }
  1440. return(S_OK);
  1441. }
  1442. //
  1443. // FUNCTION: CNewsStore::OnError()
  1444. //
  1445. // PURPOSE:
  1446. //
  1447. // PARAMETERS:
  1448. // [in] ixpstatus -
  1449. // [in] pResult -
  1450. // [in] pTransport -
  1451. //
  1452. HRESULT CNewsStore::OnError(IXPSTATUS ixpstatus, LPIXPRESULT pResult,
  1453. IInternetTransport *pTransport)
  1454. {
  1455. // Stack
  1456. TraceCall("CNewsStore::OnError");
  1457. return(S_OK);
  1458. }
  1459. //
  1460. // FUNCTION: CNewsStore::OnCommand()
  1461. //
  1462. // PURPOSE:
  1463. //
  1464. // PARAMETERS:
  1465. // [in] cmdtype -
  1466. // [in] pszLine -
  1467. // [in] hrResponse -
  1468. // [in] pTransport -
  1469. //
  1470. HRESULT CNewsStore::OnCommand(CMDTYPE cmdtype, LPSTR pszLine, HRESULT hrResponse,
  1471. IInternetTransport *pTransport)
  1472. {
  1473. // Stack
  1474. TraceCall("CNewsStore::OnCommand");
  1475. return E_NOTIMPL;
  1476. }
  1477. HRESULT CNewsStore::GetParentWindow(DWORD dwReserved, HWND *phwndParent)
  1478. {
  1479. HRESULT hr;
  1480. AssertSingleThreaded;
  1481. Assert(m_op.pCallback != NULL);
  1482. hr = m_op.pCallback->GetParentWindow(dwReserved, phwndParent);
  1483. return hr;
  1484. }
  1485. HRESULT CNewsStore::GetAccount(LPDWORD pdwServerType, IImnAccount **ppAccount)
  1486. {
  1487. // Locals
  1488. HRESULT hr=S_OK;
  1489. // Invalid Args
  1490. Assert(ppAccount);
  1491. Assert(g_pAcctMan);
  1492. // Initialize
  1493. *ppAccount = NULL;
  1494. // Find the Account
  1495. IF_FAILEXIT(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, m_szAccountId, ppAccount));
  1496. // Set server type
  1497. *pdwServerType = SRV_NNTP;
  1498. exit:
  1499. // Done
  1500. return(hr);
  1501. }
  1502. //
  1503. // FUNCTION: CNewsStore::OnResponse()
  1504. //
  1505. // PURPOSE:
  1506. //
  1507. // PARAMETERS:
  1508. // [in] pResponse - response data from the query
  1509. //
  1510. HRESULT CNewsStore::OnResponse(LPNNTPRESPONSE pResponse)
  1511. {
  1512. HRESULT hr, hrResult;
  1513. AssertSingleThreaded;
  1514. // If we got disconnected etc while there was still socket activity pending
  1515. // this can happen.
  1516. if (m_op.tyOperation == SOT_INVALID)
  1517. return(S_OK);
  1518. Assert(m_op.pCallback != NULL);
  1519. // Here's a little special something. If the caller is waiting for a connect
  1520. // response, and the connect fails the transport's returns a response with the
  1521. // state set to NS_DISCONNECTED. If this is the case, we coerce it a bit to
  1522. // make the states happy.
  1523. if (m_op.nsPending == NS_CONNECT && pResponse->state == NS_DISCONNECTED)
  1524. pResponse->state = NS_CONNECT;
  1525. if (pResponse->state == NS_IDLE)
  1526. return(S_OK);
  1527. // Check to see if we're in the right state. If we're out of sync, good
  1528. // luck trying to recover without disconnecting.
  1529. Assert(pResponse->state == m_op.nsPending);
  1530. hr = S_OK;
  1531. hrResult = pResponse->rIxpResult.hrResult;
  1532. // If this is a GROUP command, we need to update our internal state to show
  1533. // what group we're now in if we need to switch later. Also update the
  1534. // folderinfo with current stats from the server.
  1535. if (pResponse->state == NS_GROUP)
  1536. hr = _HandleGroupResponse(pResponse);
  1537. // We need to handle the article response to copy the lines to the caller's
  1538. // stream.
  1539. else if (pResponse->state == NS_ARTICLE)
  1540. hr = _HandleArticleResponse(pResponse);
  1541. //pump the data into the store
  1542. else if (pResponse->state == NS_LIST)
  1543. hr = _HandleListResponse(pResponse, FALSE);
  1544. //pump the headers into the folder
  1545. else if (pResponse->state == NS_HEADERS)
  1546. hr = _HandleHeadResponse(pResponse);
  1547. //callback to the poster with result
  1548. else if (pResponse->state == NS_POST)
  1549. hr = _HandlePostResponse(pResponse);
  1550. else if (pResponse->state == NS_NEWGROUPS)
  1551. hr = _HandleListResponse(pResponse, TRUE);
  1552. else if (pResponse->state == NS_XHDR)
  1553. {
  1554. if (m_fXhdrSubject)
  1555. hr = _HandleXHdrSubjectResponse(pResponse);
  1556. else
  1557. hr = _HandleXHdrReferencesResponse(pResponse);
  1558. }
  1559. else if (FAILED(pResponse->rIxpResult.hrResult))
  1560. {
  1561. Assert(pResponse->fDone);
  1562. _FillStoreError(&m_op.error, &pResponse->rIxpResult);
  1563. if (pResponse->state == NS_CONNECT)
  1564. {
  1565. // if connection fails, then add the flush-flag
  1566. m_op.error.dwFlags |= SE_FLAG_FLUSHALL;
  1567. }
  1568. m_op.pCallback->OnComplete(m_op.tyOperation, pResponse->rIxpResult.hrResult, NULL, &m_op.error);
  1569. }
  1570. pResponse->pTransport->ReleaseResponse(pResponse);
  1571. if (FAILED(hrResult))
  1572. {
  1573. _FreeOperation();
  1574. return(S_OK);
  1575. }
  1576. if (FAILED(hr))
  1577. {
  1578. m_op.error.hrResult = hr;
  1579. if (_FConnected())
  1580. m_pTransport->DropConnection();
  1581. m_op.pCallback->OnComplete(m_op.tyOperation, hr, NULL, NULL);
  1582. _FreeOperation();
  1583. return (S_OK);
  1584. }
  1585. // Check to see if we can issue the next command
  1586. else if (pResponse->fDone)
  1587. {
  1588. m_op.iState++;
  1589. _DoOperation();
  1590. }
  1591. return(S_OK);
  1592. }
  1593. //
  1594. // FUNCTION: CNewsStore::HandleHeadResponse
  1595. //
  1596. // PURPOSE: Stuff the headers into the message store
  1597. //
  1598. // PARAMETERS:
  1599. // pResp - Pointer to an NNTPResp from the server.
  1600. //
  1601. // RETURN VALUE:
  1602. // ignored
  1603. //
  1604. HRESULT CNewsStore::_HandleHeadResponse(LPNNTPRESPONSE pResp)
  1605. {
  1606. DWORD dwLow, dwHigh;
  1607. BOOL fFreeReq, fFreeRead;
  1608. HRESULT hr;
  1609. CRangeList *pRange;
  1610. LPSTR lpsz;
  1611. ADDRESSLIST addrList;
  1612. PROPVARIANT rDecoded;
  1613. NNTPHEADER *pHdrOld;
  1614. FOLDERINFO FolderInfo;
  1615. MESSAGEINFO rMessageInfo;
  1616. MESSAGEINFO *pHdrNew = &rMessageInfo;
  1617. IOERule *pIRuleSender = NULL;
  1618. BOOL fDontSave = FALSE;
  1619. HLOCK hNotifyLock = NULL;
  1620. ACT_ITEM * pActions = NULL;
  1621. ULONG cActions = 0;
  1622. IOEExecRules * pIExecRules = NULL;
  1623. Assert(m_pFolder);
  1624. Assert(pResp);
  1625. Assert(m_pROP != NULL);
  1626. if (FAILED(pResp->rIxpResult.hrResult))
  1627. {
  1628. Assert(pResp->fDone);
  1629. _FillStoreError(&m_op.error, &pResp->rIxpResult);
  1630. m_op.pCallback->OnComplete(m_op.tyOperation, pResp->rIxpResult.hrResult, NULL, &m_op.error);
  1631. if (m_pROP != NULL)
  1632. {
  1633. MemFree(m_pROP);
  1634. m_pROP = NULL;
  1635. }
  1636. return(S_OK);
  1637. }
  1638. if (pResp->rHeaders.cHeaders == 0)
  1639. {
  1640. Assert(pResp->fDone);
  1641. if (SUCCEEDED(m_pStore->GetFolderInfo(m_idFolder, &FolderInfo)))
  1642. {
  1643. AddRequestedRange(&FolderInfo, m_pROP->dwFirst, m_pROP->dwLast, &fFreeReq, &fFreeRead);
  1644. FolderInfo.dwNotDownloaded = NewsUtil_GetNotDownloadCount(&FolderInfo);
  1645. m_pStore->UpdateRecord(&FolderInfo);
  1646. if (fFreeReq)
  1647. MemFree(FolderInfo.Requested.pBlobData);
  1648. if (fFreeRead)
  1649. MemFree(FolderInfo.Read.pBlobData);
  1650. m_pROP->cOps++;
  1651. m_op.iState--;
  1652. m_pStore->FreeRecord(&FolderInfo);
  1653. }
  1654. return(S_OK);
  1655. }
  1656. pRange = NULL;
  1657. if (SUCCEEDED(m_pStore->GetFolderInfo(m_idFolder, &FolderInfo)))
  1658. {
  1659. if (FolderInfo.Read.cbSize > 0)
  1660. {
  1661. pRange = new CRangeList;
  1662. if (pRange != NULL)
  1663. pRange->Load(FolderInfo.Read.pBlobData, FolderInfo.Read.cbSize);
  1664. }
  1665. m_pStore->FreeRecord(&FolderInfo);
  1666. }
  1667. m_pROP->uObtained += pResp->rHeaders.cHeaders;
  1668. // Get the block sender rule if it exists
  1669. Assert(NULL != g_pRulesMan);
  1670. (VOID) g_pRulesMan->GetRule(RULEID_SENDERS, RULE_TYPE_NEWS, 0, &pIRuleSender);
  1671. m_pFolder->LockNotify(NOFLAGS, &hNotifyLock);
  1672. // Loop through the headers in pResp and convert each to a MESSAGEINFO
  1673. // and write it to the store
  1674. for (UINT i = 0; i < pResp->rHeaders.cHeaders; i++)
  1675. {
  1676. m_op.dwProgress++;
  1677. pHdrOld = &(pResp->rHeaders.rgHeaders[i]);
  1678. ZeroMemory(&rMessageInfo, sizeof(rMessageInfo));
  1679. fDontSave = FALSE;
  1680. // Article ID
  1681. pHdrNew->idMessage = (MESSAGEID)((DWORD_PTR)pHdrOld->dwArticleNum);
  1682. if (DB_S_FOUND == m_pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &rMessageInfo, NULL))
  1683. {
  1684. m_pFolder->FreeRecord(&rMessageInfo);
  1685. m_op.dwProgress++;
  1686. continue;
  1687. }
  1688. // Account ID
  1689. pHdrNew->pszAcctId = m_szAccountId;
  1690. pHdrNew->pszAcctName = m_rInetServerInfo.szAccount;
  1691. // Subject
  1692. rDecoded.vt = VT_LPSTR;
  1693. if (FAILED(MimeOleDecodeHeader(NULL, pHdrOld->pszSubject, &rDecoded, NULL)))
  1694. pHdrNew->pszSubject = PszDup(pHdrOld->pszSubject);
  1695. else
  1696. pHdrNew->pszSubject = rDecoded.pszVal;
  1697. // Strip trailing whitespace from the subject
  1698. ULONG cb = 0;
  1699. UlStripWhitespace(pHdrNew->pszSubject, FALSE, TRUE, &cb);
  1700. // Normalize the subject
  1701. pHdrNew->pszNormalSubj = SzNormalizeSubject(pHdrNew->pszSubject);
  1702. // From
  1703. pHdrNew->pszFromHeader = pHdrOld->pszFrom;
  1704. if (S_OK == MimeOleParseRfc822Address(IAT_FROM, IET_ENCODED, pHdrNew->pszFromHeader, &addrList))
  1705. {
  1706. if (addrList.cAdrs > 0)
  1707. {
  1708. pHdrNew->pszDisplayFrom = addrList.prgAdr[0].pszFriendly;
  1709. addrList.prgAdr[0].pszFriendly = NULL;
  1710. pHdrNew->pszEmailFrom = addrList.prgAdr[0].pszEmail;
  1711. addrList.prgAdr[0].pszEmail = NULL;
  1712. }
  1713. g_pMoleAlloc->FreeAddressList(&addrList);
  1714. }
  1715. // Date
  1716. MimeOleInetDateToFileTime(pHdrOld->pszDate, &pHdrNew->ftSent);
  1717. // Set the Reveived date (this will get set right when we download the message)
  1718. pHdrNew->ftReceived = pHdrNew->ftSent;
  1719. // Message-ID
  1720. pHdrNew->pszMessageId = pHdrOld->pszMessageId;
  1721. // References
  1722. pHdrNew->pszReferences = pHdrOld->pszReferences;
  1723. // Article Size in bytes
  1724. pHdrNew->cbMessage = pHdrOld->dwBytes;
  1725. // Lines
  1726. pHdrNew->cLines = pHdrOld->dwLines;
  1727. // XRef
  1728. if (pHdrOld->pszXref)
  1729. pHdrNew->pszXref = pHdrOld->pszXref;
  1730. else
  1731. pHdrNew->pszXref = NULL;
  1732. // Its a news message
  1733. FLAGSET(pHdrNew->dwFlags, ARF_NEWSMSG);
  1734. if (NULL != pIRuleSender)
  1735. {
  1736. pIRuleSender->Evaluate(pHdrNew->pszAcctId, pHdrNew, m_pFolder,
  1737. NULL, NULL, pHdrOld->dwBytes, &pActions, &cActions);
  1738. if ((1 == cActions) && (ACT_TYPE_DELETE == pActions[0].type))
  1739. {
  1740. fDontSave = TRUE;
  1741. }
  1742. }
  1743. //Add it to the database
  1744. hr = S_OK;
  1745. if (FALSE == fDontSave)
  1746. {
  1747. if (pRange != NULL)
  1748. {
  1749. if (pRange->IsInRange(pHdrOld->dwArticleNum))
  1750. FLAGSET(pHdrNew->dwFlags, ARF_READ);
  1751. }
  1752. hr = m_pFolder->InsertRecord(pHdrNew);
  1753. if (SUCCEEDED(hr))
  1754. {
  1755. if (NULL == pIExecRules)
  1756. {
  1757. CExecRules * pExecRules;
  1758. pExecRules = new CExecRules;
  1759. if (NULL != pExecRules)
  1760. {
  1761. hr = pExecRules->QueryInterface(IID_IOEExecRules, (void **) &pIExecRules);
  1762. if (FAILED(hr))
  1763. {
  1764. delete pExecRules;
  1765. }
  1766. }
  1767. }
  1768. g_pRulesMan->ExecuteRules(RULE_TYPE_NEWS, NOFLAGS, NULL, pIExecRules, pHdrNew, m_pFolder, NULL);
  1769. }
  1770. }
  1771. // Free the memory in rMessageInfo so we can start anew with the next entry
  1772. SafeMemFree(pHdrNew->pszSubject);
  1773. SafeMemFree(pHdrNew->pszDisplayFrom);
  1774. SafeMemFree(pHdrNew->pszEmailFrom);
  1775. // Free up any actions done by rules
  1776. if (NULL != pActions)
  1777. {
  1778. RuleUtil_HrFreeActionsItem(pActions, cActions);
  1779. MemFree(pActions);
  1780. pActions = NULL;
  1781. }
  1782. if (FAILED(hr) && hr != DB_E_DUPLICATE)
  1783. {
  1784. SafeRelease(pRange);
  1785. SafeRelease(pIRuleSender);
  1786. SafeRelease(pIExecRules);
  1787. m_pFolder->UnlockNotify(&hNotifyLock);
  1788. return(hr);
  1789. }
  1790. m_op.pCallback->OnProgress(SOT_SYNC_FOLDER, m_op.dwProgress, m_op.dwTotal, m_szGroup);
  1791. }
  1792. m_pFolder->UnlockNotify(&hNotifyLock);
  1793. SafeRelease(pIRuleSender);
  1794. SafeRelease(pIExecRules);
  1795. SafeRelease(pRange);
  1796. Assert(m_op.dwProgress <= m_op.dwTotal);
  1797. if (m_op.pCallback)
  1798. {
  1799. m_op.pCallback->OnProgress(SOT_SYNC_FOLDER, m_op.dwProgress, m_op.dwTotal, m_szGroup);
  1800. // We have to re-fetch the folder info because m_pFolder->InsertRecord may have update this folder....
  1801. if (m_pStore && SUCCEEDED(m_pStore->GetFolderInfo(m_idFolder, &FolderInfo)))
  1802. {
  1803. dwLow = pResp->rHeaders.rgHeaders[0].dwArticleNum;
  1804. dwHigh = pResp->rHeaders.rgHeaders[pResp->rHeaders.cHeaders - 1].dwArticleNum;
  1805. AddRequestedRange(&FolderInfo, m_pROP->dwFirst, pResp->fDone ? m_pROP->dwLast : dwHigh, &fFreeReq, &fFreeRead);
  1806. if (FolderInfo.dwClientLow == 0 || dwLow < FolderInfo.dwClientLow)
  1807. FolderInfo.dwClientLow = dwLow;
  1808. if (dwHigh > FolderInfo.dwClientHigh)
  1809. FolderInfo.dwClientHigh = dwHigh;
  1810. FolderInfo.dwNotDownloaded = NewsUtil_GetNotDownloadCount(&FolderInfo);
  1811. m_pStore->UpdateRecord(&FolderInfo);
  1812. if (fFreeReq)
  1813. MemFree(FolderInfo.Requested.pBlobData);
  1814. if (fFreeRead)
  1815. MemFree(FolderInfo.Read.pBlobData);
  1816. if (pResp->fDone)
  1817. {
  1818. m_pROP->cOps++;
  1819. m_op.iState--;
  1820. }
  1821. m_pStore->FreeRecord(&FolderInfo);
  1822. }
  1823. }
  1824. return(S_OK);
  1825. }
  1826. void MarkExistingFolder(FOLDERID idFolder, FOLDERID *pId, ULONG cId)
  1827. {
  1828. // TODO: if this linear search is too slow, use a binary search
  1829. // (but we'll have to switch to a struct with folderid and bool)
  1830. ULONG i;
  1831. Assert(pId != NULL);
  1832. Assert(cId > 0);
  1833. for (i = 0; i < cId; i++, pId++)
  1834. {
  1835. if (idFolder == *pId)
  1836. {
  1837. *pId = 0;
  1838. break;
  1839. }
  1840. else if (idFolder < *pId)
  1841. {
  1842. break;
  1843. }
  1844. }
  1845. }
  1846. //
  1847. // FUNCTION: CNewsStore::HandleListResponse
  1848. //
  1849. // PURPOSE: Callback function used by the protocol to give us one line
  1850. // at a time in response to a "LIST" command. Add each line
  1851. // as a folder in the global folder store.
  1852. //
  1853. // PARAMETERS:
  1854. // pResp - Pointer to an NNTPResp from the server.
  1855. //
  1856. // RETURN VALUE:
  1857. // ignored
  1858. //
  1859. HRESULT CNewsStore::_HandleListResponse(LPNNTPRESPONSE pResp, BOOL fNew)
  1860. {
  1861. LPSTR psz, pszCount;
  1862. int nSize;
  1863. char szGroupName[CCHMAX_FOLDER_NAME], szNumber[15];
  1864. FLDRFLAGS fFolderFlags;
  1865. HRESULT hr;
  1866. BOOL fDescriptions;
  1867. UINT lFirst, lLast;
  1868. FOLDERINFO Folder;
  1869. STOREOPERATIONTYPE type;
  1870. LPNNTPLIST pnl = &pResp->rList;
  1871. Assert(pResp);
  1872. if (FAILED(pResp->rIxpResult.hrResult))
  1873. {
  1874. Assert(pResp->fDone);
  1875. _FillStoreError(&m_op.error, &pResp->rIxpResult);
  1876. m_op.pCallback->OnComplete(m_op.tyOperation, pResp->rIxpResult.hrResult, NULL, &m_op.error);
  1877. return(S_OK);
  1878. }
  1879. fDescriptions = !!(m_op.dwFlags & OPFLAG_DESCRIPTIONS);
  1880. if ((fNew && pnl->cLines > 0) ||
  1881. (!fNew && pResp->fDone))
  1882. {
  1883. if (SUCCEEDED(m_pStore->GetFolderInfo(m_idParent, &Folder)))
  1884. {
  1885. if (fNew ^ !!(Folder.dwFlags & FOLDER_HASNEWGROUPS))
  1886. {
  1887. Folder.dwFlags ^= FOLDER_HASNEWGROUPS;
  1888. m_pStore->UpdateRecord(&Folder);
  1889. }
  1890. m_pStore->FreeRecord(&Folder);
  1891. }
  1892. }
  1893. for (DWORD i = 0; i < pnl->cLines; i++, m_op.dwProgress++)
  1894. {
  1895. // Parse out just the group name.
  1896. psz = pnl->rgszLines[i];
  1897. Assert(*psz);
  1898. if (fDescriptions && *psz == '#')
  1899. continue;
  1900. while (*psz && !IsSpace(psz))
  1901. psz = CharNext(psz);
  1902. nSize = (int)(psz - pnl->rgszLines[i]);
  1903. if (nSize >= CCHMAX_FOLDER_NAME)
  1904. nSize = CCHMAX_FOLDER_NAME - 1;
  1905. CopyMemory(szGroupName, pnl->rgszLines[i], nSize);
  1906. szGroupName[nSize] = 0;
  1907. // this is the first article in the group
  1908. while (*psz && IsSpace(psz))
  1909. psz = CharNext(psz);
  1910. if (fDescriptions)
  1911. {
  1912. // psz now points to the description which should be
  1913. // null terminated in the response.
  1914. // Load the folder, if possible, and set the description
  1915. // on it.
  1916. ZeroMemory(&Folder, sizeof(FOLDERINFO));
  1917. Folder.pszName = szGroupName;
  1918. Folder.idParent = m_idParent;
  1919. if (DB_S_FOUND == m_pStore->FindRecord(IINDEX_ALL, COLUMNS_ALL, &Folder, NULL))
  1920. {
  1921. if (Folder.pszDescription == NULL ||
  1922. 0 != lstrcmp(psz, Folder.pszDescription))
  1923. {
  1924. Folder.pszDescription = psz;
  1925. m_pStore->UpdateRecord(&Folder);
  1926. }
  1927. m_pStore->FreeRecord(&Folder);
  1928. }
  1929. }
  1930. else
  1931. {
  1932. pszCount = psz;
  1933. while (*psz && !IsSpace(psz))
  1934. psz = CharNext(psz);
  1935. nSize = (int) (psz - pszCount);
  1936. CopyMemory(szNumber, pszCount, nSize);
  1937. szNumber[nSize] = 0;
  1938. lLast = StrToInt(szNumber);
  1939. // this is the last article in the group
  1940. while (*psz && IsSpace(psz))
  1941. psz = CharNext(psz);
  1942. pszCount = psz;
  1943. while (*psz && !IsSpace(psz))
  1944. psz = CharNext(psz);
  1945. nSize = (int)(psz - pszCount);
  1946. CopyMemory(szNumber, pszCount, nSize);
  1947. szNumber[nSize] = 0;
  1948. lFirst = StrToInt(szNumber);
  1949. // Now go see if the group allows posting or not.
  1950. while (*psz && IsSpace(psz))
  1951. psz = CharNext(psz);
  1952. #define FOLDER_LISTMASK (FOLDER_NEW | FOLDER_NOPOSTING | FOLDER_MODERATED | FOLDER_BLOCKED)
  1953. if (fNew)
  1954. fFolderFlags = FOLDER_NEW;
  1955. else
  1956. fFolderFlags = 0;
  1957. if (*psz == 'n')
  1958. fFolderFlags |= FOLDER_NOPOSTING;
  1959. else if (*psz == 'm')
  1960. fFolderFlags |= FOLDER_MODERATED;
  1961. else if (*psz == 'x')
  1962. fFolderFlags |= FOLDER_BLOCKED;
  1963. ZeroMemory(&Folder, sizeof(FOLDERINFO));
  1964. Folder.pszName = szGroupName;
  1965. Folder.idParent = m_idParent;
  1966. if (DB_S_FOUND == m_pStore->FindRecord(IINDEX_ALL, COLUMNS_ALL, &Folder, NULL))
  1967. {
  1968. if (m_op.pPrevFolders != NULL)
  1969. MarkExistingFolder(Folder.idFolder, m_op.pPrevFolders, m_op.cPrevFolders);
  1970. Assert(0 == (fFolderFlags & ~FOLDER_LISTMASK));
  1971. if ((Folder.dwFlags & FOLDER_LISTMASK) != fFolderFlags)
  1972. {
  1973. Folder.dwFlags = (Folder.dwFlags & ~FOLDER_LISTMASK);
  1974. Folder.dwFlags |= fFolderFlags;
  1975. m_pStore->UpdateRecord(&Folder);
  1976. }
  1977. // TODO: should we update server high, low and count here???
  1978. m_pStore->FreeRecord(&Folder);
  1979. }
  1980. else
  1981. {
  1982. // ZeroMemory(&Folder, sizeof(FOLDERINFO));
  1983. // Folder.idParent = m_idParent;
  1984. // Folder.pszName = szGroupName;
  1985. Folder.tySpecial = FOLDER_NOTSPECIAL;
  1986. Folder.dwFlags = fFolderFlags;
  1987. Folder.dwServerLow = lFirst;
  1988. Folder.dwServerHigh = lLast;
  1989. if (Folder.dwServerLow <= Folder.dwServerHigh)
  1990. {
  1991. Folder.dwServerCount = Folder.dwServerHigh - Folder.dwServerLow + 1;
  1992. Folder.dwNotDownloaded = Folder.dwServerCount;
  1993. }
  1994. hr = m_pStore->CreateFolder(NOFLAGS, &Folder, NULL);
  1995. Assert(hr != STORE_S_ALREADYEXISTS);
  1996. if (FAILED(hr))
  1997. return(hr);
  1998. }
  1999. }
  2000. }
  2001. // only send status every 1/2 second or so.
  2002. if (GetTickCount() > (m_dwLastStatusTicks + 500))
  2003. {
  2004. if (fNew)
  2005. type = SOT_GET_NEW_GROUPS;
  2006. else
  2007. type = fDescriptions ? SOT_SYNCING_DESCRIPTIONS : SOT_SYNCING_STORE;
  2008. m_op.pCallback->OnProgress(type, m_op.dwProgress, 0, m_rInetServerInfo.szServerName);
  2009. m_dwLastStatusTicks = GetTickCount();
  2010. }
  2011. if (!fNew &&
  2012. !fDescriptions &&
  2013. pResp->fDone &&
  2014. SUCCEEDED(pResp->rIxpResult.hrResult))
  2015. {
  2016. IImnAccount *pAcct;
  2017. SYSTEMTIME stNow;
  2018. FILETIME ftNow;
  2019. hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, m_szAccountId, &pAcct);
  2020. if (SUCCEEDED(hr))
  2021. {
  2022. GetSystemTime(&stNow);
  2023. SystemTimeToFileTime(&stNow, &ftNow);
  2024. pAcct->SetProp(AP_LAST_UPDATED, (LPBYTE)&ftNow, sizeof(ftNow));
  2025. pAcct->SaveChanges();
  2026. pAcct->Release();
  2027. }
  2028. }
  2029. return(S_OK);
  2030. }
  2031. //
  2032. // FUNCTION: CNewsStore::HandlePostResponse
  2033. //
  2034. // PURPOSE: Callback function used by the protocol to give us one line
  2035. // at a time in response to a "POST" command. Add each line
  2036. // as a folder in the global folder store.
  2037. //
  2038. // PARAMETERS:
  2039. // pResp - Pointer to an NNTPResp from the server.
  2040. //
  2041. // RETURN VALUE:
  2042. // ignored
  2043. //
  2044. HRESULT CNewsStore::_HandlePostResponse(LPNNTPRESPONSE pResp)
  2045. {
  2046. Assert(pResp != NULL);
  2047. if (FAILED(pResp->rIxpResult.hrResult))
  2048. {
  2049. Assert(pResp->fDone);
  2050. _FillStoreError(&m_op.error, &pResp->rIxpResult);
  2051. m_op.pCallback->OnComplete(m_op.tyOperation, pResp->rIxpResult.hrResult, NULL, &m_op.error);
  2052. return(S_OK);
  2053. }
  2054. return(S_OK);
  2055. }
  2056. //
  2057. // FUNCTION: CNewsStore::HandleGroupResponse
  2058. //
  2059. // PURPOSE: Callback function when a GROUP command completes
  2060. //
  2061. // PARAMETERS:
  2062. // pResp - Pointer to an NNTPResp from the server.
  2063. //
  2064. // RETURN VALUE:
  2065. // ignored
  2066. //
  2067. HRESULT CNewsStore::_HandleGroupResponse(LPNNTPRESPONSE pResp)
  2068. {
  2069. FOLDERINFO FolderInfo;
  2070. FOLDERID idFolder;
  2071. BOOL fFreeReq, fFreeRead;
  2072. Assert(pResp);
  2073. if (FAILED(pResp->rIxpResult.hrResult))
  2074. {
  2075. Assert(pResp->fDone);
  2076. Assert(m_op.pszGroup != NULL);
  2077. _FillStoreError(&m_op.error, &pResp->rIxpResult, m_op.pszGroup);
  2078. m_op.pCallback->OnComplete(m_op.tyOperation, pResp->rIxpResult.hrResult, NULL, &m_op.error);
  2079. if (pResp->rIxpResult.uiServerError == IXP_NNTP_NO_SUCH_NEWSGROUP)
  2080. {
  2081. // HACK: this is so the treeview gets the notification that this folder is being deleted
  2082. m_pStore->SubscribeToFolder(m_op.idFolder, TRUE, NULL);
  2083. m_pStore->DeleteFolder(m_op.idFolder, DELETE_FOLDER_NOTRASHCAN, NULL);
  2084. }
  2085. return(S_OK);
  2086. }
  2087. IxpAssert(pResp->rGroup.pszGroup);
  2088. StrCpyN(m_szGroup, pResp->rGroup.pszGroup, ARRAYSIZE(m_szGroup));
  2089. if (SUCCEEDED(m_pStore->GetFolderInfo(m_op.idFolder, &FolderInfo)))
  2090. {
  2091. fFreeReq = FALSE;
  2092. fFreeRead = FALSE;
  2093. if (pResp->rGroup.dwFirst <= pResp->rGroup.dwLast)
  2094. {
  2095. FolderInfo.dwServerLow = pResp->rGroup.dwFirst;
  2096. FolderInfo.dwServerHigh = pResp->rGroup.dwLast;
  2097. FolderInfo.dwServerCount = pResp->rGroup.dwCount;
  2098. if (FolderInfo.dwServerLow > 0)
  2099. AddRequestedRange(&FolderInfo, 0, FolderInfo.dwServerLow - 1, &fFreeReq, &fFreeRead);
  2100. FolderInfo.dwNotDownloaded = NewsUtil_GetNotDownloadCount(&FolderInfo);
  2101. }
  2102. else
  2103. {
  2104. FolderInfo.dwServerLow = 0;
  2105. FolderInfo.dwServerHigh = 0;
  2106. FolderInfo.dwServerCount = 0;
  2107. FolderInfo.dwNotDownloaded = 0;
  2108. }
  2109. m_pStore->UpdateRecord(&FolderInfo);
  2110. if (fFreeReq)
  2111. MemFree(FolderInfo.Requested.pBlobData);
  2112. if (fFreeRead)
  2113. MemFree(FolderInfo.Read.pBlobData);
  2114. m_pStore->FreeRecord(&FolderInfo);
  2115. }
  2116. return(S_OK);
  2117. }
  2118. //
  2119. // FUNCTION: CNewsStore::HandleArticleResponse
  2120. //
  2121. // PURPOSE: Callback function used by the protocol write a message
  2122. // into the store.
  2123. //
  2124. // PARAMETERS:
  2125. // pResp - Pointer to an NNTPResp from the server.
  2126. //
  2127. // RETURN VALUE:
  2128. // ignored
  2129. //
  2130. HRESULT CNewsStore::_HandleArticleResponse(LPNNTPRESPONSE pResp)
  2131. {
  2132. HRESULT hr;
  2133. ADJUSTFLAGS flags;
  2134. MESSAGEIDLIST list;
  2135. ULONG cbWritten;
  2136. Assert(pResp);
  2137. if (FAILED(pResp->rIxpResult.hrResult))
  2138. {
  2139. Assert(pResp->fDone);
  2140. _FillStoreError(&m_op.error, &pResp->rIxpResult);
  2141. m_op.pCallback->OnComplete(m_op.tyOperation, pResp->rIxpResult.hrResult, NULL, &m_op.error);
  2142. if ((pResp->rIxpResult.uiServerError == IXP_NNTP_NO_SUCH_ARTICLE_NUM ||
  2143. pResp->rIxpResult.uiServerError == IXP_NNTP_NO_SUCH_ARTICLE_FOUND) &&
  2144. m_pFolder != NULL)
  2145. {
  2146. list.cAllocated = 0;
  2147. list.cMsgs = 1;
  2148. list.prgidMsg = &m_op.idMessage;
  2149. flags.dwAdd = ARF_ARTICLE_EXPIRED;
  2150. flags.dwRemove = ARF_DOWNLOAD;
  2151. m_pFolder->SetMessageFlags(&list, &flags, NULL, NULL);
  2152. m_pFolder->SetMessageStream(m_op.idMessage, m_op.pStream);
  2153. m_op.faStream = 0;
  2154. }
  2155. return(S_OK);
  2156. }
  2157. // We need to write the bytes that are returned on to the stream the
  2158. // caller provided
  2159. Assert(m_op.pStream);
  2160. hr = m_op.pStream->Write(pResp->rArticle.pszLines,
  2161. pResp->rArticle.cbLines, &cbWritten);
  2162. // if (FAILED(hr) || (pResp->rArticle.cbLines != cbWritten))
  2163. if (FAILED(hr))
  2164. return(hr);
  2165. Assert(pResp->rArticle.cbLines == cbWritten);
  2166. // The NNTPRESPONSE struct is going to get sent to the caller anyway,
  2167. // so we need to doctor the cLines member to be the total line count
  2168. // for the message.
  2169. m_op.dwProgress += pResp->rArticle.cLines;
  2170. m_op.pCallback->OnProgress(SOT_GET_MESSAGE, m_op.dwProgress, m_op.dwTotal, NULL);
  2171. // If we're done, then we also rewind the stream
  2172. if (pResp->fDone)
  2173. {
  2174. HrRewindStream(m_op.pStream);
  2175. // Articles coming in from news: article urls do not have an IMessageFolder associated with them.
  2176. if (m_pFolder)
  2177. {
  2178. flags.dwAdd = 0;
  2179. flags.dwRemove = ARF_DOWNLOAD;
  2180. if (m_op.idServerMessage)
  2181. _SaveMessageToStore(m_pFolder, m_op.idServerMessage, m_op.pStream);
  2182. else
  2183. CommitMessageToStore(m_pFolder, &flags, m_op.idMessage, m_op.pStream);
  2184. m_op.faStream = 0;
  2185. }
  2186. if (m_op.pStream != NULL)
  2187. {
  2188. m_op.pStream->Release();
  2189. m_op.pStream = NULL;
  2190. }
  2191. }
  2192. SafeMemFree(pResp->rArticle.pszLines);
  2193. return(S_OK);
  2194. }
  2195. void CNewsStore::_FillStoreError(LPSTOREERROR pErrorInfo, IXPRESULT *pResult, LPSTR pszGroup)
  2196. {
  2197. TraceCall("CNewsStore::FillStoreError");
  2198. Assert(m_cRef >= 0); // Can be called during destruction
  2199. Assert(NULL != pErrorInfo);
  2200. if (pszGroup == NULL)
  2201. pszGroup = m_szGroup;
  2202. // Fill out the STOREERROR structure
  2203. ZeroMemory(pErrorInfo, sizeof(*pErrorInfo));
  2204. pErrorInfo->hrResult = pResult->hrResult;
  2205. pErrorInfo->uiServerError = pResult->uiServerError;
  2206. pErrorInfo->hrServerError = pResult->hrServerError;
  2207. pErrorInfo->dwSocketError = pResult->dwSocketError;
  2208. pErrorInfo->pszProblem = pResult->pszProblem;
  2209. pErrorInfo->pszDetails = pResult->pszResponse;
  2210. pErrorInfo->pszAccount = m_rInetServerInfo.szAccount;
  2211. pErrorInfo->pszServer = m_rInetServerInfo.szServerName;
  2212. pErrorInfo->pszFolder = pszGroup;
  2213. pErrorInfo->pszUserName = m_rInetServerInfo.szUserName;
  2214. pErrorInfo->pszProtocol = "NNTP";
  2215. pErrorInfo->pszConnectoid = m_rInetServerInfo.szConnectoid;
  2216. pErrorInfo->rasconntype = m_rInetServerInfo.rasconntype;
  2217. pErrorInfo->ixpType = IXP_NNTP;
  2218. pErrorInfo->dwPort = m_rInetServerInfo.dwPort;
  2219. pErrorInfo->fSSL = m_rInetServerInfo.fSSL;
  2220. pErrorInfo->fTrySicily = m_rInetServerInfo.fTrySicily;
  2221. pErrorInfo->dwFlags = 0;
  2222. }
  2223. //
  2224. // FUNCTION: CNewsStore::_CreateDataFilePath()
  2225. //
  2226. // PURPOSE: Creates a full path to a data file based on an account and a filename
  2227. //
  2228. // PARAMETERS:
  2229. // <in> pszAccount - account name
  2230. // <in> pszFileName - file name to be appended
  2231. // <out> pszPath - full path to data file
  2232. //
  2233. HRESULT CNewsStore::_CreateDataFilePath(LPCTSTR pszAccountId, LPCTSTR pszFileName, LPTSTR pszPath, DWORD cchPathSize)
  2234. {
  2235. HRESULT hr = NOERROR;
  2236. TCHAR szDirectory[MAX_PATH];
  2237. Assert(pszAccountId && *pszAccountId);
  2238. Assert(pszFileName);
  2239. Assert(pszPath);
  2240. // Get the Store Root Directory
  2241. hr = GetStoreRootDirectory(szDirectory, ARRAYSIZE(szDirectory));
  2242. // Validate that I have room
  2243. if (lstrlen(szDirectory) + lstrlen((LPSTR)pszFileName) + 2 >= MAX_PATH)
  2244. {
  2245. Assert(FALSE);
  2246. hr = TraceResult(E_FAIL);
  2247. goto exit;
  2248. }
  2249. if (SUCCEEDED(hr))
  2250. hr = OpenDirectory(szDirectory);
  2251. // Format the filename
  2252. wnsprintf(pszPath, cchPathSize,"%s\\%s.log", szDirectory, pszFileName);
  2253. exit:
  2254. return hr;
  2255. }
  2256. void AddRequestedRange(FOLDERINFO *pInfo, DWORD dwLow, DWORD dwHigh, BOOL *pfReq, BOOL *pfRead)
  2257. {
  2258. CRangeList *pRange;
  2259. Assert(pInfo != NULL);
  2260. Assert(dwLow <= dwHigh);
  2261. Assert(pfReq != NULL);
  2262. Assert(pfRead != NULL);
  2263. *pfReq = FALSE;
  2264. *pfRead = FALSE;
  2265. pRange = new CRangeList;
  2266. if (pRange != NULL)
  2267. {
  2268. if (pInfo->Requested.cbSize > 0)
  2269. pRange->Load(pInfo->Requested.pBlobData, pInfo->Requested.cbSize);
  2270. pRange->AddRange(dwLow, dwHigh);
  2271. *pfReq = pRange->Save(&pInfo->Requested.pBlobData, &pInfo->Requested.cbSize);
  2272. pRange->Release();
  2273. }
  2274. if (pInfo->Read.cbSize > 0)
  2275. {
  2276. pRange = new CRangeList;
  2277. if (pRange != NULL)
  2278. {
  2279. pRange->Load(pInfo->Read.pBlobData, pInfo->Read.cbSize);
  2280. pRange->DeleteRange(dwLow, dwHigh);
  2281. *pfRead = pRange->Save(&pInfo->Read.pBlobData, &pInfo->Read.cbSize);
  2282. pRange->Release();
  2283. }
  2284. }
  2285. }
  2286. HRESULT CNewsStore::MarkCrossposts(LPMESSAGEIDLIST pList, BOOL fRead)
  2287. {
  2288. PROPVARIANT var;
  2289. IMimeMessage *pMimeMsg;
  2290. IStream *pStream;
  2291. DWORD i;
  2292. MESSAGEINFO Message;
  2293. HRESULT hr;
  2294. LPSTR psz;
  2295. HROWSET hRowset = NULL;
  2296. if (NULL == pList)
  2297. {
  2298. hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset);
  2299. if (FAILED(hr))
  2300. return(hr);
  2301. }
  2302. for (i = 0; ; i++)
  2303. {
  2304. if (pList != NULL)
  2305. {
  2306. if (i >= pList->cMsgs)
  2307. break;
  2308. Message.idMessage = pList->prgidMsg[i];
  2309. hr = m_pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL);
  2310. if (FAILED(hr))
  2311. break;
  2312. else if (hr != DB_S_FOUND)
  2313. continue;
  2314. }
  2315. else
  2316. {
  2317. hr = m_pFolder->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL);
  2318. if (S_FALSE == hr)
  2319. {
  2320. hr = S_OK;
  2321. break;
  2322. }
  2323. else if (FAILED(hr))
  2324. {
  2325. break;
  2326. }
  2327. }
  2328. psz = NULL;
  2329. if (Message.pszXref == NULL &&
  2330. !!(Message.dwFlags & ARF_HASBODY))
  2331. {
  2332. if (SUCCEEDED(MimeOleCreateMessage(NULL, &pMimeMsg)))
  2333. {
  2334. if (SUCCEEDED(m_pFolder->OpenStream(ACCESS_READ, Message.faStream, &pStream)))
  2335. {
  2336. if (SUCCEEDED(hr = pMimeMsg->Load(pStream)))
  2337. {
  2338. var.vt = VT_EMPTY;
  2339. if (SUCCEEDED(pMimeMsg->GetProp(PIDTOSTR(STR_HDR_XREF), NOFLAGS, &var)))
  2340. {
  2341. Message.pszXref = var.pszVal;
  2342. psz = var.pszVal;
  2343. m_pFolder->UpdateRecord(&Message);
  2344. }
  2345. }
  2346. pStream->Release();
  2347. }
  2348. pMimeMsg->Release();
  2349. }
  2350. }
  2351. if (Message.pszXref != NULL && *Message.pszXref != 0)
  2352. _MarkCrossposts(Message.pszXref, fRead);
  2353. if (psz != NULL)
  2354. MemFree(psz);
  2355. m_pFolder->FreeRecord(&Message);
  2356. }
  2357. if (hRowset != NULL)
  2358. m_pFolder->CloseRowset(&hRowset);
  2359. return(hr);
  2360. }
  2361. void CNewsStore::_MarkCrossposts(LPCSTR szXRefs, BOOL fRead)
  2362. {
  2363. HRESULT hr;
  2364. CRangeList *pRange;
  2365. BOOL fReq, fFree;
  2366. DWORD dwArtNum;
  2367. IMessageFolder *pFolder;
  2368. MESSAGEINFO Message;
  2369. FOLDERINFO info;
  2370. LPSTR szT = StringDup(szXRefs);
  2371. LPSTR psz = szT, pszNum;
  2372. if (!szT)
  2373. return;
  2374. // skip over the server field
  2375. // $BUGBUG - we should really verify that our server generated the XRef
  2376. while (*psz && *psz != ' ')
  2377. psz++;
  2378. while (1)
  2379. {
  2380. // skip whitespace
  2381. while (*psz && (*psz == ' ' || *psz == '\t'))
  2382. psz++;
  2383. if (!*psz)
  2384. break;
  2385. // find the article num
  2386. pszNum = psz;
  2387. while (*pszNum && *pszNum != ':')
  2388. pszNum++;
  2389. if (!*pszNum)
  2390. break;
  2391. *pszNum++ = 0;
  2392. // Bug #47253 - Don't pass NULL pointers to SHLWAPI.
  2393. if (!*pszNum)
  2394. break;
  2395. dwArtNum = StrToInt(pszNum);
  2396. if (lstrcmpi(psz, m_szGroup) != 0)
  2397. {
  2398. ZeroMemory(&info, sizeof(FOLDERINFO));
  2399. info.idParent = m_idParent;
  2400. info.pszName = psz;
  2401. if (DB_S_FOUND == m_pStore->FindRecord(IINDEX_ALL, COLUMNS_ALL, &info, NULL))
  2402. {
  2403. if (!!(info.dwFlags & FOLDER_SUBSCRIBED))
  2404. {
  2405. fReq = FALSE;
  2406. if (info.Requested.cbSize > 0)
  2407. {
  2408. pRange = new CRangeList;
  2409. if (pRange != NULL)
  2410. {
  2411. pRange->Load(info.Requested.pBlobData, info.Requested.cbSize);
  2412. fReq = pRange->IsInRange(dwArtNum);
  2413. pRange->Release();
  2414. }
  2415. }
  2416. if (fReq)
  2417. {
  2418. hr = m_pStore->OpenFolder(info.idFolder, NULL, NOFLAGS, &pFolder);
  2419. if (SUCCEEDED(hr))
  2420. {
  2421. ZeroMemory(&Message, sizeof(MESSAGEINFO));
  2422. Message.idMessage = (MESSAGEID)((DWORD_PTR)dwArtNum);
  2423. hr = pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL);
  2424. if (DB_S_FOUND == hr)
  2425. {
  2426. if (fRead ^ !!(Message.dwFlags & ARF_READ))
  2427. {
  2428. Message.dwFlags ^= ARF_READ;
  2429. if (fRead && !!(Message.dwFlags & ARF_DOWNLOAD))
  2430. Message.dwFlags ^= ARF_DOWNLOAD;
  2431. pFolder->UpdateRecord(&Message);
  2432. }
  2433. pFolder->FreeRecord(&Message);
  2434. }
  2435. pFolder->Release();
  2436. }
  2437. }
  2438. else
  2439. {
  2440. pRange = new CRangeList;
  2441. if (pRange != NULL)
  2442. {
  2443. if (info.Read.cbSize > 0)
  2444. pRange->Load(info.Read.pBlobData, info.Read.cbSize);
  2445. if (fRead)
  2446. pRange->AddRange(dwArtNum);
  2447. else
  2448. pRange->DeleteRange(dwArtNum);
  2449. fFree = pRange->Save(&info.Read.pBlobData, &info.Read.cbSize);
  2450. pRange->Release();
  2451. m_pStore->UpdateRecord(&info);
  2452. if (fFree)
  2453. MemFree(info.Read.pBlobData);
  2454. }
  2455. }
  2456. }
  2457. m_pStore->FreeRecord(&info);
  2458. }
  2459. }
  2460. // skip over digits
  2461. while (IsDigit(pszNum))
  2462. pszNum++;
  2463. psz = pszNum;
  2464. }
  2465. MemFree(szT);
  2466. }
  2467. HRESULT CNewsStore::GetWatchedInfo(FOLDERID idFolder, IStoreCallback *pCallback)
  2468. {
  2469. HRESULT hr;
  2470. // Stack
  2471. TraceCall("CNewsStore::GetWatchedInfo");
  2472. AssertSingleThreaded;
  2473. Assert(pCallback != NULL);
  2474. Assert(m_op.tyOperation == SOT_INVALID);
  2475. m_op.tyOperation = SOT_GET_WATCH_INFO;
  2476. m_op.pfnState = c_rgpfnGetWatchInfo;
  2477. m_op.iState = 0;
  2478. m_op.cState = ARRAYSIZE(c_rgpfnGetWatchInfo);
  2479. m_op.pCallback = pCallback;
  2480. m_op.pCallback->AddRef();
  2481. m_op.idFolder = idFolder;
  2482. hr = _BeginDeferredOperation();
  2483. return hr;
  2484. }
  2485. HRESULT CNewsStore::XHdrReferences(void)
  2486. {
  2487. HRESULT hr = S_OK;
  2488. FOLDERINFO fi;
  2489. AssertSingleThreaded;
  2490. Assert(m_pTransport != NULL);
  2491. if (SUCCEEDED(m_pStore->GetFolderInfo(m_op.idFolder, &fi)))
  2492. {
  2493. if (fi.dwClientWatchedHigh < fi.dwServerHigh)
  2494. {
  2495. m_dwWatchLow = max(fi.dwClientHigh + 1, fi.dwClientWatchedHigh + 1);
  2496. m_dwWatchHigh = fi.dwServerHigh;
  2497. // Save our new high value
  2498. fi.dwClientWatchedHigh = fi.dwServerHigh;
  2499. m_pStore->UpdateRecord(&fi);
  2500. // Check to see if we have any work to do
  2501. if (m_dwWatchLow <= m_dwWatchHigh)
  2502. {
  2503. // Allocate an array for the retreived data
  2504. if (!MemAlloc((LPVOID *) &m_rgpszWatchInfo, sizeof(LPTSTR) * (m_dwWatchHigh - m_dwWatchLow + 1)))
  2505. {
  2506. m_pStore->FreeRecord(&fi);
  2507. return (E_OUTOFMEMORY);
  2508. }
  2509. ZeroMemory(m_rgpszWatchInfo, sizeof(LPTSTR) * (m_dwWatchHigh - m_dwWatchLow + 1));
  2510. m_cRange.Clear();
  2511. m_cTotal = 0;
  2512. m_cCurrent = 0;
  2513. m_op.dwProgress = 0;
  2514. m_op.dwTotal = m_dwWatchHigh - m_dwWatchLow;
  2515. m_fXhdrSubject = FALSE;
  2516. RANGE range;
  2517. range.idType = RT_RANGE;
  2518. range.dwFirst = m_dwWatchLow;
  2519. range.dwLast = m_dwWatchHigh;
  2520. hr = m_pTransport->CommandXHDR("References", &range, NULL);
  2521. if (hr == S_OK)
  2522. {
  2523. m_op.nsPending = NS_XHDR;
  2524. hr = E_PENDING;
  2525. }
  2526. }
  2527. }
  2528. m_pStore->FreeRecord(&fi);
  2529. }
  2530. return(hr);
  2531. }
  2532. HRESULT CNewsStore::_HandleXHdrReferencesResponse(LPNNTPRESPONSE pResp)
  2533. {
  2534. NNTPXHDR *pHdr;
  2535. // Check for error
  2536. if (FAILED(pResp->rIxpResult.hrResult))
  2537. {
  2538. Assert(pResp->fDone);
  2539. _FillStoreError(&m_op.error, &pResp->rIxpResult);
  2540. m_op.pCallback->OnComplete(m_op.tyOperation, pResp->rIxpResult.hrResult, NULL, &m_op.error);
  2541. return(S_OK);
  2542. }
  2543. // Loop through the returned data and insert those values into our array
  2544. for (DWORD i = 0; i < pResp->rXhdr.cHeaders; i++)
  2545. {
  2546. pHdr = &(pResp->rXhdr.rgHeaders[i]);
  2547. Assert(pHdr->dwArticleNum <= m_dwWatchHigh);
  2548. // Some servers return "(none)" for articles that don't have that
  2549. // header. Smart servers just don't return anything.
  2550. if (0 != lstrcmpi(pHdr->pszHeader, "(none)"))
  2551. {
  2552. m_rgpszWatchInfo[pHdr->dwArticleNum - m_dwWatchLow] = PszDupA(pHdr->pszHeader);
  2553. }
  2554. }
  2555. // Show a little progress here. This is actually a little complicated. The
  2556. // data returned might have a single line for each header, or might be sparse.
  2557. // we need to show progress proportional to how far we are through the headers.
  2558. m_op.dwProgress = (pResp->rXhdr.rgHeaders[pResp->rXhdr.cHeaders - 1].dwArticleNum - m_dwWatchLow);
  2559. m_op.pCallback->OnProgress(SOT_GET_WATCH_INFO, m_op.dwProgress, m_op.dwTotal,
  2560. m_rInetServerInfo.szServerName);
  2561. return (S_OK);
  2562. }
  2563. HRESULT CNewsStore::XHdrSubject(void)
  2564. {
  2565. HRESULT hr = S_OK;
  2566. FOLDERINFO fi;
  2567. AssertSingleThreaded;
  2568. Assert(m_pTransport != NULL);
  2569. // Check to see if we have any work to do
  2570. if ((m_dwWatchLow > m_dwWatchHigh) || (m_dwWatchLow == 0 && m_dwWatchHigh == 0))
  2571. return (S_OK);
  2572. m_op.dwProgress = 0;
  2573. m_op.dwTotal = m_dwWatchHigh - m_dwWatchLow;
  2574. RANGE range;
  2575. range.idType = RT_RANGE;
  2576. range.dwFirst = m_dwWatchLow;
  2577. range.dwLast = m_dwWatchHigh;
  2578. m_fXhdrSubject = TRUE;
  2579. hr = m_pTransport->CommandXHDR("Subject", &range, NULL);
  2580. if (hr == S_OK)
  2581. {
  2582. m_op.nsPending = NS_XHDR;
  2583. hr = E_PENDING;
  2584. }
  2585. return(hr);
  2586. }
  2587. HRESULT CNewsStore::_HandleXHdrSubjectResponse(LPNNTPRESPONSE pResp)
  2588. {
  2589. NNTPXHDR *pHdr;
  2590. // Check for error
  2591. if (FAILED(pResp->rIxpResult.hrResult))
  2592. {
  2593. Assert(pResp->fDone);
  2594. _FillStoreError(&m_op.error, &pResp->rIxpResult);
  2595. m_op.pCallback->OnComplete(m_op.tyOperation, pResp->rIxpResult.hrResult, NULL, &m_op.error);
  2596. return(S_OK);
  2597. }
  2598. // Loop through the returned data see which ones are watched
  2599. for (DWORD i = 0; i < pResp->rXhdr.cHeaders; i++)
  2600. {
  2601. pHdr = &(pResp->rXhdr.rgHeaders[i]);
  2602. Assert(pHdr->dwArticleNum <= m_dwWatchHigh);
  2603. // Check to see if this is part of a watched thread
  2604. if (_IsWatchedThread(m_rgpszWatchInfo[pHdr->dwArticleNum - m_dwWatchLow], pHdr->pszHeader))
  2605. {
  2606. m_cRange.AddRange(pHdr->dwArticleNum);
  2607. m_cTotal++;
  2608. }
  2609. }
  2610. // Show a little progress here.
  2611. m_op.dwProgress += pResp->rXhdr.cHeaders;
  2612. m_op.pCallback->OnProgress(SOT_GET_WATCH_INFO, m_op.dwProgress, m_op.dwTotal,
  2613. m_rInetServerInfo.szServerName);
  2614. // If this is the end of the xhdr data, we can free our array of references
  2615. if (pResp->fDone)
  2616. {
  2617. for (UINT i = 0; i < (m_dwWatchHigh - m_dwWatchLow + 1); i++)
  2618. {
  2619. if (m_rgpszWatchInfo[i])
  2620. MemFree(m_rgpszWatchInfo[i]);
  2621. }
  2622. MemFree(m_rgpszWatchInfo);
  2623. m_rgpszWatchInfo = 0;
  2624. m_dwWatchLow = 0;
  2625. m_dwWatchHigh = 0;
  2626. }
  2627. return (S_OK);
  2628. }
  2629. BOOL CNewsStore::_IsWatchedThread(LPSTR pszRef, LPSTR pszSubject)
  2630. {
  2631. // Get the Parent
  2632. Assert(m_pFolder);
  2633. return(S_OK == m_pFolder->IsWatched(pszRef, pszSubject) ? TRUE : FALSE);
  2634. }
  2635. HRESULT CNewsStore::WatchedArticles(void)
  2636. {
  2637. HRESULT hr = S_OK;
  2638. ARTICLEID rArticleId;
  2639. AssertSingleThreaded;
  2640. Assert(m_pTransport != NULL);
  2641. // Check to see if we have any work to do
  2642. if (m_cRange.Cardinality() == 0)
  2643. return (S_OK);
  2644. m_op.pCallback->OnProgress(SOT_GET_WATCH_INFO, ++m_cCurrent, m_cTotal, NULL);
  2645. m_op.idServerMessage = m_cRange.Min();
  2646. m_op.idMessage = 0;
  2647. m_cRange.DeleteRange(m_cRange.Min());
  2648. // Create a stream
  2649. if (FAILED(hr = CreatePersistentWriteStream(m_pFolder, &m_op.pStream, &m_op.faStream)))
  2650. return (E_OUTOFMEMORY);
  2651. hr = Article();
  2652. if (hr == E_PENDING)
  2653. m_op.iState--;
  2654. return (hr);
  2655. }
  2656. HRESULT CNewsStore::_SaveMessageToStore(IMessageFolder *pFolder, DWORD id, LPSTREAM pstm)
  2657. {
  2658. FOLDERINFO info;
  2659. BOOL fFreeReq, fFreeRead;
  2660. IMimeMessage *pMsg = 0;
  2661. HRESULT hr;
  2662. MESSAGEID idMessage = (MESSAGEID)((DWORD_PTR)id);
  2663. // Create a new message
  2664. if (SUCCEEDED(hr = MimeOleCreateMessage(NULL, &pMsg)))
  2665. {
  2666. if (SUCCEEDED(hr = pMsg->Load(pstm)))
  2667. {
  2668. if (SUCCEEDED(m_pStore->GetFolderInfo(m_op.idFolder, &info)))
  2669. {
  2670. fFreeReq = FALSE;
  2671. fFreeRead = FALSE;
  2672. AddRequestedRange(&info, id, id, &fFreeReq, &fFreeRead);
  2673. info.dwNotDownloaded = NewsUtil_GetNotDownloadCount(&info);
  2674. m_pStore->UpdateRecord(&info);
  2675. if (fFreeReq)
  2676. MemFree(info.Requested.pBlobData);
  2677. if (fFreeRead)
  2678. MemFree(info.Read.pBlobData);
  2679. }
  2680. hr = m_pFolder->SaveMessage(&idMessage, 0, 0, m_op.pStream, pMsg, NOSTORECALLBACK);
  2681. }
  2682. pMsg->Release();
  2683. }
  2684. return (hr);
  2685. }