Source code of Windows XP (NT5)
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.

1257 lines
30 KiB

  1. // implementation for app specific data
  2. #include <objbase.h>
  3. #include <Mapi.h>
  4. #include <Mapix.h>
  5. #include "MAPIDEFS.H"
  6. #include "EDKMDB.H"
  7. #include "mapispi.h"
  8. #include "mapiform.h"
  9. #include "mdbuix.h"
  10. #pragma data_seg(".text")
  11. #define INITGUID
  12. #include <initguid.h>
  13. #include <objbase.h>
  14. #include "SyncHndl.h"
  15. #include "priv.h"
  16. #include "base.h"
  17. #include "handler.h"
  18. #pragma data_seg()
  19. TCHAR szCLSIDDescription[] = TEXT("Outlook OneStop Handler");
  20. typedef struct _tagProfileItem
  21. {
  22. struct _tagProfileItem *pNextProfileItem;
  23. char *profileName; // pointer to profile name, only valid for lifetime of thread
  24. DWORD dwThreadId; // thread that owns the profile.
  25. } ProfileItem;
  26. // global list of Profile items so mail knows what profile it has already done.
  27. ProfileItem *g_pProfileItem = NULL; // pointer to first profile item
  28. extern HINSTANCE g_hmodThisDll; // Handle to this DLL itself.
  29. BOOL g_InCall = FALSE;
  30. // general routine for creating a working thread to make calls on.
  31. DWORD WINAPI WorkerThread( LPVOID lpArg );
  32. // thread creation for PrepareForSync and synchronize,
  33. // for simplification in Initialize we spin a worker thread and then
  34. // post all incoming requests to it so don't have to working about
  35. // api we are calling handling multiple threads.
  36. typedef struct _tagWorkerThreadArgs
  37. {
  38. HANDLE hEvent; // Event to wait on for new thread to be created.
  39. HRESULT hr; // return code of thread.
  40. CMailHandler *pThis;
  41. } WorkerThreadArgs;
  42. HRESULT CreateWorkerThread(DWORD *pdwThreadID,HANDLE *phThread,
  43. CMailHandler *pThis)
  44. {
  45. HRESULT hr = E_FAIL;
  46. HANDLE hNewThread = NULL;
  47. WorkerThreadArgs ThreadArgs;
  48. *phThread = NULL;
  49. ThreadArgs.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  50. ThreadArgs.pThis = pThis;
  51. if (ThreadArgs.hEvent)
  52. {
  53. hNewThread = CreateThread(NULL,0,WorkerThread,&ThreadArgs,0,pdwThreadID);
  54. if (hNewThread)
  55. {
  56. WaitForSingleObject(ThreadArgs.hEvent,INFINITE);
  57. if (NOERROR == ThreadArgs.hr)
  58. {
  59. *phThread = hNewThread;
  60. hr = NOERROR;
  61. }
  62. else
  63. {
  64. CloseHandle(hNewThread);
  65. hr = ThreadArgs.hr;
  66. }
  67. }
  68. else
  69. {
  70. hr = GetLastError();
  71. }
  72. CloseHandle(ThreadArgs.hEvent);
  73. }
  74. return hr;
  75. }
  76. typedef struct _tagArgs
  77. {
  78. } METHODARGS; // list of possible member for calling ThreadWndProc
  79. // definitions for handler messages
  80. #define WM_WORKERMSG_PREPFORSYNC (WM_USER+1)
  81. #define WM_WORKERMSG_SYNCHRONIZE (WM_USER+2)
  82. #define DWL_THREADWNDPROCCLASS 0 // window long offset to MsgService Hwnd this ptr.
  83. // WndProc for Worker thread
  84. LRESULT CALLBACK MsgThreadWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
  85. {
  86. CMailHandler *pThis = (CMailHandler *) GetWindowLong(hWnd, DWL_THREADWNDPROCCLASS);
  87. switch (msg)
  88. {
  89. case WM_CREATE :
  90. {
  91. CREATESTRUCT *pCreateStruct = (CREATESTRUCT *) lParam;
  92. SetWindowLong(hWnd, DWL_THREADWNDPROCCLASS,(LONG) pCreateStruct->lpCreateParams );
  93. pThis = (CMailHandler *) pCreateStruct->lpCreateParams ;
  94. }
  95. break;
  96. case WM_DESTROY:
  97. PostQuitMessage(0); // shut down this thread.
  98. break;
  99. case WM_WORKERMSG_PREPFORSYNC:
  100. pThis->PrepareForSyncCall();
  101. break;
  102. case WM_WORKERMSG_SYNCHRONIZE:
  103. pThis->SynchronizeCall();
  104. break;
  105. default:
  106. break;
  107. }
  108. return DefWindowProc(hWnd, msg, wParam, lParam);
  109. }
  110. DWORD WINAPI WorkerThread( LPVOID lpArg )
  111. {
  112. MSG msg;
  113. HRESULT hr;
  114. HRESULT hrCoInitialize;
  115. WorkerThreadArgs *pThreadArgs = (WorkerThreadArgs *) lpArg;
  116. pThreadArgs->hr = NOERROR;
  117. hrCoInitialize = CoInitialize(NULL);
  118. // need to do a PeekMessage and then set an event to make sure
  119. // a message loop is created before the first PostMessage is sent.
  120. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  121. // initialize the dialog box before returning to main thread.
  122. if (FAILED(hrCoInitialize) )
  123. {
  124. pThreadArgs->hr = E_OUTOFMEMORY;
  125. }
  126. else
  127. {
  128. ATOM aWndClass;
  129. WNDCLASS xClass;
  130. // Register windows class.we need for handling thread communication
  131. xClass.style = 0;
  132. xClass.lpfnWndProc = MsgThreadWndProc;
  133. xClass.cbClsExtra = 0;
  134. xClass.cbWndExtra = sizeof(DWORD); // room for class this ptr
  135. xClass.hInstance = g_hmodThisDll;
  136. xClass.hIcon = NULL;
  137. xClass.hCursor = NULL;
  138. xClass.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);
  139. xClass.lpszMenuName = NULL;
  140. xClass.lpszClassName = "OutlookHandlerClass";
  141. aWndClass = RegisterClass( &xClass );
  142. pThreadArgs->pThis->m_hwnd = CreateWindowEx(0,
  143. "OutlookHandlerClass",
  144. TEXT(""),
  145. // must use WS_POPUP so the window does not get
  146. // assigned a hot key by user.
  147. (WS_DISABLED | WS_POPUP),
  148. CW_USEDEFAULT,
  149. CW_USEDEFAULT,
  150. CW_USEDEFAULT,
  151. CW_USEDEFAULT,
  152. NULL, // REVIEW, can we give it a parent to not show up.
  153. NULL,
  154. g_hmodThisDll,
  155. pThreadArgs->pThis);
  156. pThreadArgs->hr = pThreadArgs->pThis->m_hwnd ? NOERROR : E_UNEXPECTED;
  157. }
  158. hr = pThreadArgs->hr;
  159. // let the caller know the thread is done initializing.
  160. if (pThreadArgs->hEvent)
  161. SetEvent(pThreadArgs->hEvent);
  162. if (NOERROR == hr)
  163. {
  164. // sit in loop receiving messages.
  165. while (GetMessage(&msg, NULL, 0, 0))
  166. {
  167. TranslateMessage(&msg);
  168. DispatchMessage(&msg);
  169. }
  170. }
  171. if (SUCCEEDED(hrCoInitialize))
  172. CoUninitialize();
  173. return 0;
  174. }
  175. COneStopHandler* CreateHandlerObject()
  176. {
  177. g_InCall = FALSE;
  178. return new CMailHandler();
  179. }
  180. STDMETHODIMP CMailHandler::DestroyHandler()
  181. {
  182. if (g_InCall)
  183. {
  184. // MessageBox(NULL,"UNINIT","",1);
  185. }
  186. if (m_fMapiInitialized)
  187. {
  188. m_fMapiInitialized = FALSE;
  189. MAPIUninitialize();
  190. }
  191. delete this;
  192. return NOERROR;
  193. }
  194. CMailHandler::CMailHandler()
  195. {
  196. m_fMapiInitialized = FALSE;
  197. m_dwSyncFlags = 0;
  198. m_hwnd = FALSE;
  199. m_fInPrepareForSync = FALSE;
  200. m_fInSynchronize = FALSE;
  201. m_pCurHandlerItem = NULL;
  202. }
  203. CMailHandler::~CMailHandler()
  204. {
  205. }
  206. STDMETHODIMP CMailHandler::Initialize(DWORD dwReserved,DWORD dwSyncFlags,
  207. DWORD cbCookie,const BYTE *lpCooke)
  208. {
  209. HRESULT hr = NOERROR; // if already initialized just return NOERROR;
  210. DWORD pThreadID;
  211. HANDLE phThread;
  212. m_dwSyncFlags = dwSyncFlags;
  213. InitializeCriticalSection(&m_CriticalSection);
  214. // create workerthread
  215. if (hr = (NOERROR == CreateWorkerThread(&pThreadID,&phThread,
  216. this)))
  217. {
  218. if (FALSE == m_fMapiInitialized)
  219. {
  220. MAPIINIT_0 mapiInit;
  221. mapiInit.ulVersion = MAPI_INIT_VERSION;
  222. mapiInit.ulFlags = MAPI_MULTITHREAD_NOTIFICATIONS;
  223. if (NOERROR == (hr = MAPIInitialize( 0 /* &mapiInit) */) ) )
  224. {
  225. m_fMapiInitialized = TRUE;
  226. // if mapi was initialized, go ahead and get profile stuff.
  227. hr = GetProfileInformation();
  228. }
  229. }
  230. }
  231. return hr;
  232. }
  233. STDMETHODIMP CMailHandler::GetHandlerInfo(LPSYNCMGRHANDLERINFO *ppSyncMgrHandlerInfo)
  234. {
  235. return E_NOTIMPL;
  236. }
  237. HRESULT CMailHandler::GetProfileInformation()
  238. {
  239. DWORD dwProfileCount = 0;
  240. LPSYNCMGRHANDLERITEMS pOfflineItemsHolder;
  241. LPHANDLERITEM pOfflineItem;
  242. HRESULT hr;
  243. LPPROFADMIN pProfAdmin;
  244. hr = MAPIAdminProfiles(0,&pProfAdmin);
  245. if (NOERROR == hr)
  246. {
  247. LPMAPITABLE pMapiTable;
  248. if (NOERROR == pProfAdmin->GetProfileTable(0,&pMapiTable))
  249. {
  250. LPSRowSet pRows;
  251. SizedSPropTagArray(1, taga) = { 1, { PR_DISPLAY_NAME } };
  252. if (NOERROR == pMapiTable->SetColumns((LPSPropTagArray)&taga, TBL_BATCH) )
  253. {
  254. while (NOERROR == pMapiTable->QueryRows(1,0,&pRows)
  255. && (pRows->cRows > 0) )
  256. {
  257. if (NULL == (pOfflineItemsHolder = GetOfflineItemsHolder()) )
  258. { // if first item, set up the enumerator.
  259. pOfflineItemsHolder = CreateOfflineHandlerItemsList();
  260. // if still NULL, break out of loop
  261. if (NULL == pOfflineItemsHolder)
  262. break;
  263. SetOfflineItemsHolder(pOfflineItemsHolder);
  264. }
  265. // add the item to the list.
  266. if (pOfflineItem = (LPHANDLERITEM) AddOfflineItemToList(pOfflineItemsHolder,sizeof(HANDLERITEM) ))
  267. {
  268. char *pszProfileName = pRows->aRow[0].lpProps[0].Value.lpszA;
  269. SYNCMGRITEMID offType;
  270. pOfflineItem->pmdbx = NULL;
  271. pOfflineItem->lpSession = NULL;
  272. pOfflineItem->fItemCompleted = FALSE;
  273. memcpy(pOfflineItem->szProfileName,pszProfileName,
  274. strlen(pszProfileName) + 1);
  275. // add outlook specific data
  276. pOfflineItem->baseItem.offlineItem.cbSize = sizeof(SYNCMGRITEM);
  277. // review, for now just generate a new guid
  278. CoCreateGuid(&offType);
  279. pOfflineItem->baseItem.offlineItem.ItemID = offType;
  280. // pOfflineItem->baseItem.offlineItem.dwItemID = dwProfileCount + 1;
  281. pOfflineItem->baseItem.offlineItem.dwItemState = SYNCMGRITEMSTATE_CHECKED;
  282. pOfflineItem->baseItem.offlineItem.hIcon =
  283. LoadIcon(g_hmodThisDll,MAKEINTRESOURCE(IDI_IOUTLOOK));
  284. pOfflineItem->baseItem.offlineItem.dwFlags = 0L;
  285. // for now, just use the path as the description
  286. // need to change this.
  287. // if we are not already unicode, have to convert now
  288. #ifndef UNICODE
  289. MultiByteToWideChar(CP_ACP,0,pszProfileName,-1,
  290. pOfflineItem->baseItem.offlineItem.wszItemName,MAX_SYNCMGRITEMNAME);
  291. #else
  292. // bug, can overrun buffer big time.
  293. // already unicode, just copy it in.
  294. memcpy(pOfflineItem->baseItem.offlineItem.wszItemName,
  295. pszProfileName,strlen(pszProfileName) + 1);
  296. #endif // UNICODE
  297. GetItemIdForHandlerItem(CLSID_OneStopHandler,pOfflineItem->baseItem.offlineItem.wszItemName,
  298. &pOfflineItem->baseItem.offlineItem.ItemID,TRUE);
  299. // don't do anything on the status for now.
  300. // pOfflineItem->offlineItem.wszStatus = NULL;
  301. ++dwProfileCount; // increment the briefcase count.
  302. }
  303. MAPIFreeBuffer(pRows);
  304. }
  305. }
  306. pMapiTable->Release();
  307. }
  308. pProfAdmin->Release();
  309. }
  310. return dwProfileCount ? S_OK : S_FALSE;
  311. }
  312. STDMETHODIMP CMailHandler::PrepareForSync(ULONG cbNumItems,SYNCMGRITEMID *pItemIDs,
  313. HWND hWndParent,DWORD dwReserved)
  314. {
  315. LPMAPITABLE ptbl = NULL;
  316. LPSRowSet prs = NULL;
  317. LPMDB pmdb = NULL;
  318. LPSPropValue pval = NULL;
  319. LPHANDLERITEM pHandlerItem;
  320. HRESULT hr = E_FAIL;
  321. LPSYNCMGRSYNCHRONIZECALLBACK pOfflineSynchronizeCallback = GetOfflineSynchronizeCallback();
  322. SYNCMGRPROGRESSITEM progItem;
  323. pHandlerItem = (LPHANDLERITEM) GetOfflineItemsHolder()->pFirstOfflineItem;
  324. if (m_fMapiInitialized)
  325. {
  326. // loop through items setting state based on PrepareForSyncSettings
  327. for ( ; pHandlerItem; pHandlerItem = (LPHANDLERITEM) pHandlerItem->baseItem.pNextOfflineItem)
  328. {
  329. ULONG NumItemsCount = cbNumItems;
  330. SYNCMGRITEMID *pCurItemID = pItemIDs;
  331. // see if item has been specified to sync, if not, update the state
  332. // to reflect this else go ahead and prepare.
  333. pHandlerItem->baseItem.offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED;
  334. while (NumItemsCount--)
  335. {
  336. if (IsEqualGUID(*pCurItemID,pHandlerItem->baseItem.offlineItem.ItemID))
  337. {
  338. pHandlerItem->baseItem.offlineItem.dwItemState = SYNCMGRITEMSTATE_CHECKED;
  339. break;
  340. }
  341. ++pCurItemID;
  342. }
  343. }
  344. // we have now set the information. Call worker function
  345. // on second thread to do the actual work.
  346. m_hwndParent = hWndParent;
  347. m_fInPrepareForSync = TRUE;
  348. // should really be posting message so can return from call immediately
  349. PrepareForSyncCall();
  350. }
  351. else
  352. {
  353. progItem.mask = SYNCMGRPROGRESSITEM_STATUSTEXT | SYNCMGRPROGRESSITEM_STATUSTYPE ; progItem.lpcStatusText = L"Connecting to Server";
  354. progItem.dwStatusType = SYNCMGRSTATUS_FAILED;
  355. progItem.lpcStatusText = L"Could Not Initialize MAPI";
  356. // applies to all items
  357. while (pHandlerItem)
  358. {
  359. if (pOfflineSynchronizeCallback)
  360. {
  361. pOfflineSynchronizeCallback->Progress(
  362. (pHandlerItem->baseItem.offlineItem.ItemID)
  363. ,&progItem);
  364. }
  365. pHandlerItem = (LPHANDLERITEM) pHandlerItem->baseItem.pNextOfflineItem;
  366. }
  367. return E_FAIL;
  368. }
  369. return NOERROR;
  370. }
  371. // called on worker thread
  372. void CMailHandler::PrepareForSyncCall()
  373. {
  374. LPMAPITABLE ptbl = NULL;
  375. LPSRowSet prs = NULL;
  376. LPMDB pmdb = NULL;
  377. SRestriction res;
  378. SPropValue val;
  379. LPSPropValue pval = NULL;
  380. LPHANDLERITEM pHandlerItem;
  381. SYNCMGRITEMID ItemID;
  382. HRESULT hr = E_FAIL;
  383. LPSYNCMGRSYNCHRONIZECALLBACK pOfflineSynchronizeCallback = GetOfflineSynchronizeCallback();
  384. SYNCMGRPROGRESSITEM progItem;
  385. // take critical section except for when calling MapiLogon
  386. // if a setitemStatus comes in can wait for this to complete
  387. // or kill handler after so long.
  388. EnterCriticalSection(&m_CriticalSection);
  389. pHandlerItem = (LPHANDLERITEM) GetOfflineItemsHolder()->pFirstOfflineItem;
  390. for ( ; pHandlerItem; pHandlerItem = (LPHANDLERITEM) pHandlerItem->baseItem.pNextOfflineItem)
  391. {
  392. char *pname = pHandlerItem->szProfileName;
  393. if (pHandlerItem->baseItem.offlineItem.dwItemState == SYNCMGRITEMSTATE_CHECKED)
  394. {
  395. ProfileItem *pCurItem;
  396. BOOL fFoundMatch = FALSE;
  397. DWORD dwLogonFlags;
  398. // if item is already in the list then continue, else
  399. // add the profile name to the list
  400. pCurItem = g_pProfileItem;
  401. while (pCurItem)
  402. {
  403. if (0 == strcmp(pCurItem->profileName,pHandlerItem->szProfileName))
  404. {
  405. fFoundMatch = TRUE;
  406. break;
  407. }
  408. pCurItem = pCurItem->pNextProfileItem;
  409. }
  410. if (fFoundMatch)
  411. continue;
  412. // didn't find a match so go ahead and add this one.
  413. pCurItem = (ProfileItem*) CoTaskMemAlloc(sizeof(ProfileItem));
  414. if (pCurItem)
  415. {
  416. pCurItem->profileName = pHandlerItem->szProfileName;
  417. pCurItem->dwThreadId = GetCurrentThreadId();
  418. pCurItem->pNextProfileItem = g_pProfileItem;
  419. g_pProfileItem = pCurItem;
  420. }
  421. pHandlerItem->baseItem.offlineItem.dwItemState = SYNCMGRITEMSTATE_CHECKED;
  422. ItemID = pHandlerItem->baseItem.offlineItem.ItemID;
  423. progItem.mask = SYNCMGRPROGRESSITEM_STATUSTEXT | SYNCMGRPROGRESSITEM_STATUSTYPE ;
  424. progItem.lpcStatusText = L"Connecting to Server";
  425. progItem.dwStatusType = SYNCMGRSTATUS_PENDING;
  426. if (pOfflineSynchronizeCallback)
  427. pOfflineSynchronizeCallback->Progress(ItemID,&progItem);
  428. dwLogonFlags = MAPI_EXTENDED | MAPI_TIMEOUT_SHORT
  429. | MAPI_NEW_SESSION | MAPI_NO_MAIL;
  430. if (SYNCMGRFLAG_MAYBOTHERUSER & m_dwSyncFlags)
  431. {
  432. dwLogonFlags |= MAPI_LOGON_UI;
  433. }
  434. m_pCurHandlerItem = pHandlerItem; // set handler item
  435. LPMAPISESSION lpSession; // mapi logon session for this item.
  436. LeaveCriticalSection(&m_CriticalSection);
  437. if (NOERROR != (hr = MAPILogonEx( (DWORD) m_hwndParent,
  438. pname,NULL,
  439. dwLogonFlags ,&lpSession)) )
  440. {
  441. EnterCriticalSection(&m_CriticalSection);
  442. // if a SetItemStatus came in while logon then don't
  443. // change the progress.
  444. if (!pHandlerItem->fItemCompleted)
  445. {
  446. progItem.mask |= SYNCMGRPROGRESSITEM_PROGVALUE
  447. | SYNCMGRPROGRESSITEM_MAXVALUE;
  448. progItem.iProgValue = 1;
  449. progItem.iMaxValue = 1;
  450. progItem.lpcStatusText = L"Unable to Logon to Server";
  451. progItem.dwStatusType = SYNCMGRSTATUS_FAILED;
  452. if (pOfflineSynchronizeCallback)
  453. pOfflineSynchronizeCallback->Progress(ItemID,&progItem);
  454. }
  455. continue;
  456. }
  457. EnterCriticalSection(&m_CriticalSection);
  458. // if setitemstate came in during logon just release session
  459. if (pHandlerItem->fItemCompleted)
  460. {
  461. if (lpSession)
  462. lpSession->Release();
  463. continue;
  464. }
  465. pHandlerItem->lpSession = lpSession;
  466. // now make sure that there is a valid message store and we
  467. // are connected to a remote server.
  468. // Find the MessageStore
  469. SizedSPropTagArray(2, taga) = { 2, { PR_ENTRYID, PR_MDB_PROVIDER } };
  470. hr = pHandlerItem->lpSession->GetMsgStoresTable(0, &ptbl);
  471. if (HR_FAILED(hr))
  472. {
  473. ptbl->Release();
  474. pHandlerItem->lpSession->Release();
  475. pHandlerItem->lpSession = NULL;
  476. continue;
  477. }
  478. hr = ptbl->SetColumns((LPSPropTagArray)&taga, TBL_BATCH);
  479. if (HR_FAILED(hr))
  480. {
  481. ptbl->Release();
  482. pHandlerItem->lpSession->Release();
  483. pHandlerItem->lpSession = NULL;
  484. continue;
  485. }
  486. res.rt = RES_PROPERTY;
  487. res.res.resProperty.relop = RELOP_EQ;
  488. res.res.resProperty.lpProp = &val;
  489. res.res.resProperty.ulPropTag = PR_MDB_PROVIDER;
  490. val.ulPropTag = PR_MDB_PROVIDER;
  491. val.Value.bin.cb = sizeof(MAPIUID);
  492. val.Value.bin.lpb = (LPBYTE)pbExchangeProviderPrimaryUserGuid;
  493. hr = ptbl->FindRow(&res, BOOKMARK_BEGINNING, 0);
  494. if (HR_FAILED(hr))
  495. {
  496. ptbl->Release();
  497. pHandlerItem->lpSession->Release();
  498. pHandlerItem->lpSession = NULL;
  499. continue;
  500. }
  501. hr = ptbl->QueryRows(1, 0, &prs);
  502. ptbl->Release(); // All done with MapiTable Object.
  503. ptbl = NULL;
  504. if (HR_FAILED(hr))
  505. {
  506. pHandlerItem->lpSession->Release();
  507. pHandlerItem->lpSession = NULL;
  508. continue;
  509. }
  510. if (prs->cRows == 0)
  511. {
  512. hr = ResultFromScode(MAPI_E_NOT_FOUND);
  513. pHandlerItem->lpSession->Release();
  514. pHandlerItem->lpSession = NULL;
  515. continue;
  516. }
  517. hr = pHandlerItem->lpSession->OpenMsgStore(0, prs->aRow[0].lpProps[0].Value.bin.cb,
  518. (LPENTRYID)prs->aRow[0].lpProps[0].Value.bin.lpb, NULL,
  519. MAPI_DEFERRED_ERRORS | MDB_TEMPORARY | MDB_NO_MAIL | MDB_WRITE,
  520. &pmdb);
  521. if (HR_FAILED(hr))
  522. {
  523. pHandlerItem->lpSession->Release();
  524. pHandlerItem->lpSession = NULL;
  525. continue;;
  526. }
  527. // If offline then don't try to sync anything.
  528. hr = HrGetOneProp((LPMAPIPROP) pmdb, PR_STATUS_CODE, &pval);
  529. // Review - this returns a success code but indicates couldn't
  530. // get all the information.
  531. if (HR_FAILED(hr))
  532. {
  533. pmdb->Release();
  534. pHandlerItem->lpSession->Release();
  535. pHandlerItem->lpSession = NULL;
  536. continue;
  537. }
  538. ULONG ulStatus = pval->Value.ul;
  539. MAPIFreeBuffer(pval);
  540. if (ulStatus & STATUS_OFFLINE)
  541. {
  542. progItem.mask |= SYNCMGRPROGRESSITEM_PROGVALUE
  543. | SYNCMGRPROGRESSITEM_MAXVALUE;
  544. progItem.iProgValue = 1;
  545. progItem.iMaxValue = 1;
  546. progItem.lpcStatusText = L"Mail is Offline";
  547. progItem.dwStatusType = SYNCMGRSTATUS_FAILED;
  548. if (pOfflineSynchronizeCallback)
  549. pOfflineSynchronizeCallback->Progress(ItemID,&progItem);
  550. pmdb->Release();
  551. pHandlerItem->lpSession->Release();
  552. pHandlerItem->lpSession = NULL;
  553. continue;
  554. }
  555. hr = pmdb->QueryInterface(IID_IMDBX, (LPVOID *)&pHandlerItem->pmdbx);
  556. pmdb->Release();
  557. pmdb = NULL;
  558. if (FAILED(hr))
  559. {
  560. pHandlerItem->lpSession->Release();
  561. pHandlerItem->lpSession = NULL;
  562. pHandlerItem->pmdbx = NULL;
  563. continue;
  564. }
  565. progItem.lpcStatusText = L"Connected to Server";
  566. if (pOfflineSynchronizeCallback)
  567. pOfflineSynchronizeCallback->Progress(ItemID,&progItem);
  568. }
  569. }
  570. m_fInPrepareForSync = FALSE;
  571. m_pCurHandlerItem = NULL;
  572. LeaveCriticalSection(&m_CriticalSection);
  573. // let caller know we are done preparing.
  574. if (pOfflineSynchronizeCallback)
  575. pOfflineSynchronizeCallback->PrepareForSyncCompleted(NOERROR);
  576. }
  577. STDMETHODIMP CMailHandler::Synchronize(HWND hwnd)
  578. {
  579. // set up hwnd then call syncrhonize on the worker thread
  580. m_hwndParent = hwnd;
  581. m_fInSynchronize = TRUE;
  582. m_pCurHandlerItem = NULL;
  583. // should be posting this so can return immediately but seem to be some mapi problems switching thread?
  584. // PostMessage(m_hwnd,WM_WORKERMSG_SYNCHRONIZE,0,0);
  585. SynchronizeCall();
  586. return NOERROR;
  587. }
  588. // called on worker thread
  589. void CMailHandler::SynchronizeCall()
  590. {
  591. STDPROG stdprog;
  592. ULONG ulFlags;
  593. HRESULT hr;
  594. LPHANDLERITEM pHandlerItem;
  595. SYNCMGRITEMID ItemID;
  596. LPSYNCMGRSYNCHRONIZECALLBACK pOfflineSynchronizeCallback = GetOfflineSynchronizeCallback();
  597. // stay in critical section except in synchronize out call.
  598. EnterCriticalSection(&m_CriticalSection);
  599. pHandlerItem = (LPHANDLERITEM) GetOfflineItemsHolder()->pFirstOfflineItem;
  600. for ( ; pHandlerItem; pHandlerItem = (LPHANDLERITEM) pHandlerItem->baseItem.pNextOfflineItem)
  601. {
  602. ItemID = pHandlerItem->baseItem.offlineItem.ItemID;
  603. if (NULL == pHandlerItem->pmdbx
  604. || SYNCMGRITEMSTATE_UNCHECKED == pHandlerItem->baseItem.offlineItem.dwItemState)
  605. {
  606. if (pHandlerItem->lpSession)
  607. {
  608. pHandlerItem->lpSession->Release();
  609. pHandlerItem->lpSession = NULL;
  610. }
  611. continue;
  612. }
  613. // point to folder if only want to synchronize a single folder.
  614. ULONG cbEntryID = 0;
  615. LPENTRYID lpEntryID = 0;
  616. ulFlags = SYNC_OUTGOING_MAIL |
  617. SYNC_UPLOAD_HIERARCHY | SYNC_DOWNLOAD_HIERARCHY |
  618. SYNC_UPLOAD_FAVORITES | SYNC_DOWNLOAD_FAVORITES |
  619. SYNC_UPLOAD_VIEWS | SYNC_DOWNLOAD_VIEWS |
  620. SYNC_UPLOAD_CONTENTS | SYNC_DOWNLOAD_CONTENTS
  621. | SYNC_FORMS ;
  622. stdprog.hwndParent = NULL;
  623. stdprog.nProgCur = 0;
  624. stdprog.nProgMax = 100;
  625. stdprog.idAVI = 0;
  626. stdprog.szCaption = NULL;
  627. stdprog.ulFlags = 0;
  628. stdprog.hwndDlg = NULL;
  629. stdprog.fCancelled = FALSE;
  630. stdprog.dwStartTime = GetTickCount();
  631. stdprog.dwShowTime = 0;
  632. stdprog.hcursor = (HCURSOR) &ItemID; // overload teh hCursor with the itemID
  633. stdprog.wndprocCancel = (WNDPROC) GetOfflineSynchronizeCallback(); // overload wndprocCancel for Progress
  634. LPMAPISESSION lpSession = pHandlerItem->lpSession;
  635. m_fInSynchronize = FALSE;
  636. m_pCurHandlerItem = pHandlerItem;
  637. LeaveCriticalSection(&m_CriticalSection);
  638. hr = pHandlerItem->pmdbx->Synchronize(ulFlags,lpSession,
  639. cbEntryID, lpEntryID, &stdprog,
  640. ProgressUpdate,
  641. (GETFORMMSGPROC)FormGetFormMessage);
  642. EnterCriticalSection(&m_CriticalSection);
  643. // Update the Progress Bar to complete status.
  644. if (NOERROR == hr)
  645. {
  646. SYNCMGRPROGRESSITEM progItem;
  647. progItem.mask = SYNCMGRPROGRESSITEM_STATUSTEXT
  648. | SYNCMGRPROGRESSITEM_STATUSTYPE
  649. | SYNCMGRPROGRESSITEM_PROGVALUE
  650. | SYNCMGRPROGRESSITEM_MAXVALUE;
  651. progItem.iProgValue = stdprog.nProgCur;
  652. progItem.iMaxValue = stdprog.nProgMax;
  653. if (stdprog.fCancelled == FALSE)
  654. {
  655. progItem.dwStatusType = SYNCMGRSTATUS_SUCCEEDED;
  656. progItem.lpcStatusText = L"Update Completed Successfully";
  657. GetOfflineSynchronizeCallback()->Progress(ItemID,&progItem);
  658. }
  659. else
  660. { progItem.dwStatusType = SYNCMGRSTATUS_SKIPPED;
  661. progItem.lpcStatusText = L"";
  662. GetOfflineSynchronizeCallback()->Progress(ItemID,&progItem);
  663. }
  664. }
  665. else
  666. {
  667. SYNCMGRPROGRESSITEM progItem;
  668. progItem.mask = SYNCMGRPROGRESSITEM_STATUSTEXT
  669. | SYNCMGRPROGRESSITEM_STATUSTYPE
  670. | SYNCMGRPROGRESSITEM_PROGVALUE
  671. | SYNCMGRPROGRESSITEM_MAXVALUE;
  672. progItem.iProgValue = stdprog.nProgCur;
  673. progItem.iMaxValue = stdprog.nProgMax;
  674. if (stdprog.fCancelled == FALSE)
  675. {
  676. progItem.dwStatusType = SYNCMGRSTATUS_FAILED;
  677. progItem.lpcStatusText = L"Errors Occured";
  678. GetOfflineSynchronizeCallback()->Progress(ItemID,&progItem);
  679. //Log Errors
  680. SYNCMGRLOGERRORINFO errorItem;
  681. errorItem.mask = NULL;
  682. //Log warning: One without more info
  683. GetOfflineSynchronizeCallback()->LogError(SYNCMGRLOGLEVEL_WARNING,
  684. L"Outlook had a warning occur.",
  685. &errorItem);
  686. errorItem.mask |= SYNCMGRLOGERROR_ERRORFLAGS;
  687. errorItem.dwSyncMgrErrorFlags = SYNCMGRERRORFLAG_ENABLEJUMPTEXT ;
  688. errorItem.mask |= SYNCMGRLOGERROR_ERRORID;
  689. errorItem.ErrorID = CLSID_OneStopHandler ;
  690. //Log error: One with more info
  691. GetOfflineSynchronizeCallback()->LogError(SYNCMGRLOGLEVEL_ERROR,
  692. L"Outlook had an error occur.",
  693. &errorItem);
  694. errorItem.ErrorID = IID_IMDBX;
  695. //Log information: One with more info
  696. GetOfflineSynchronizeCallback()->LogError(SYNCMGRLOGLEVEL_INFORMATION,
  697. L"Outlook had lengthy informational log entry that was quite verbose and wordy, chalk full of needless verbage and diatribe, that the user will never spend any time reading because it was simply too long.",
  698. &errorItem);
  699. // now delete thsm
  700. //GetOfflineSynchronizeCallback()->DeleteLogError(CLSID_OneStopHandler,0);
  701. }
  702. }
  703. pHandlerItem->fItemCompleted = TRUE;
  704. if (pHandlerItem->pmdbx)
  705. {
  706. pHandlerItem->pmdbx->Release();
  707. pHandlerItem->pmdbx = NULL;
  708. }
  709. if (pHandlerItem->lpSession)
  710. {
  711. pHandlerItem->lpSession->Release();
  712. pHandlerItem->lpSession = NULL;
  713. }
  714. // when the synchronize is done it is okay to open a new session
  715. // to the same profile
  716. ProfileItem *pCurItem;
  717. ProfileItem profItemDummy;
  718. BOOL fFoundMatch = FALSE;
  719. // if item is already in the list then continue, else
  720. // add the profile name to the list
  721. profItemDummy.pNextProfileItem = g_pProfileItem;
  722. pCurItem = &profItemDummy;
  723. while (pCurItem->pNextProfileItem)
  724. {
  725. if (0 == strcmp(pCurItem->pNextProfileItem->profileName,pHandlerItem->szProfileName))
  726. {
  727. ProfileItem *pMatch;
  728. pMatch = pCurItem->pNextProfileItem;
  729. pCurItem->pNextProfileItem = pMatch->pNextProfileItem;
  730. g_pProfileItem = profItemDummy.pNextProfileItem;
  731. CoTaskMemFree(pMatch);
  732. break;
  733. }
  734. pCurItem = pCurItem->pNextProfileItem;
  735. }
  736. }
  737. m_fInSynchronize = FALSE;
  738. m_pCurHandlerItem = NULL;
  739. LeaveCriticalSection(&m_CriticalSection);
  740. if (pOfflineSynchronizeCallback)
  741. {
  742. pOfflineSynchronizeCallback->SynchronizeCompleted(NOERROR);
  743. }
  744. }
  745. STDMETHODIMP CMailHandler::SetItemStatus(REFSYNCMGRITEMID ItemID,DWORD dwSyncMgrStatus)
  746. {
  747. LPHANDLERITEM pHandlerItem;
  748. LPSYNCMGRSYNCHRONIZECALLBACK pOfflineSynchronizeCallback = GetOfflineSynchronizeCallback();
  749. SYNCMGRPROGRESSITEM progItem;
  750. // m_pCurHandlerItem
  751. // either called before start PrepareForSync
  752. // in PrepareForsync
  753. // BetweenPrepareForSync and Synchronize
  754. // in synchronize
  755. // after synchronize.
  756. // pHandlerItem->baseItem.offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED;
  757. progItem.mask = SYNCMGRPROGRESSITEM_STATUSTYPE
  758. | SYNCMGRPROGRESSITEM_PROGVALUE
  759. | SYNCMGRPROGRESSITEM_MAXVALUE;
  760. progItem.iProgValue = 10;
  761. progItem.iMaxValue = 10;
  762. progItem.dwStatusType = SYNCMGRSTATUS_SKIPPED;
  763. EnterCriticalSection(&m_CriticalSection);
  764. // find the HandlerItem associated with the ItemID.
  765. pHandlerItem = (LPHANDLERITEM) GetOfflineItemsHolder()->pFirstOfflineItem;
  766. for ( ; pHandlerItem; pHandlerItem = (LPHANDLERITEM) pHandlerItem->baseItem.pNextOfflineItem)
  767. {
  768. if (IsEqualGUID(ItemID,(pHandlerItem->baseItem.offlineItem.ItemID)))
  769. {
  770. break;
  771. }
  772. }
  773. // if item isn't complete try to stop it.
  774. if (pHandlerItem && !pHandlerItem->fItemCompleted)
  775. {
  776. // if the curItem isn't set then nothing is currently going on
  777. // with this item. If this is the case then just clean
  778. // it up and set the progress accordingly
  779. if (pHandlerItem != m_pCurHandlerItem)
  780. {
  781. pHandlerItem->baseItem.offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED;
  782. if (pHandlerItem->pmdbx)
  783. {
  784. pHandlerItem->pmdbx->Release();
  785. pHandlerItem->pmdbx = NULL;
  786. }
  787. if (pHandlerItem->lpSession)
  788. {
  789. pHandlerItem->lpSession->Release();
  790. pHandlerItem->lpSession = NULL;
  791. }
  792. pHandlerItem->fItemCompleted = TRUE;
  793. if (pOfflineSynchronizeCallback)
  794. pOfflineSynchronizeCallback->Progress(ItemID,&progItem);
  795. }
  796. else
  797. {
  798. // call is currently working on this item so just
  799. // wait for now until the item finishes
  800. // should rely wait and if doesn't abort after so long kill
  801. // if this is a prepareforsync set itemcomplted to true
  802. // and set the callback
  803. if (m_fInPrepareForSync)
  804. {
  805. pHandlerItem->fItemCompleted = TRUE;
  806. if (pOfflineSynchronizeCallback)
  807. pOfflineSynchronizeCallback->Progress(ItemID,&progItem);
  808. }
  809. }
  810. }
  811. LeaveCriticalSection(&m_CriticalSection);
  812. return NOERROR;
  813. }
  814. STDMETHODIMP CMailHandler::ShowError(HWND hWndParent,REFSYNCMGRERRORID ErrorID)
  815. {
  816. #if _IMPL
  817. // Can show any synchronization conflicts. Also gives a chance
  818. // to display any errors that occured during synchronization
  819. if (SYNCMGRFLAG_MAYBOTHERUSER & m_dwSyncFlags)
  820. {
  821. // can show any conflicts or errors that occured.
  822. }
  823. // FOR RETRYSYNC TEST FIND FIRST CHECKED ITEMid AND SET THIS
  824. LPHANDLERITEM pHandlerItem;
  825. pHandlerItem = (LPHANDLERITEM) GetOfflineItemsHolder()->pFirstOfflineItem;
  826. for ( ; pHandlerItem; pHandlerItem = (LPHANDLERITEM) pHandlerItem->baseItem.pNextOfflineItem)
  827. {
  828. // see if item has been specified to sync, if not, update the state
  829. // to reflect this else go ahead and prepare.
  830. if (pHandlerItem->baseItem.offlineItem.dwItemState = SYNCMGRITEMSTATE_CHECKED)
  831. {
  832. break;
  833. }
  834. }
  835. if (pHandlerItem)
  836. {
  837. *ppItemIDs = (SYNCMGRITEMID *) CoTaskMemAlloc(sizeof(SYNCMGRITEMID));
  838. *ppItemIDs[0] = pHandlerItem->baseItem.offlineItem.ItemID;
  839. *pcbNumItems = 1;
  840. }
  841. #endif // _IMPL
  842. return E_NOTIMPL;
  843. // return *pcbNumItems ? S_SYNCMGR_RETRYSYNC :
  844. }
  845. // Help functions and callbacks
  846. VOID CALLBACK ProgressUpdate(STDPROG *stg, LPSTR lpcStatusText, INT iCurValue, INT iMaxValue)
  847. {
  848. HRESULT hr;
  849. ISyncMgrSynchronizeCallback *lpCallBack = (ISyncMgrSynchronizeCallback *) stg->wndprocCancel;
  850. SYNCMGRITEMID* pItemID = (SYNCMGRITEMID *) stg->hcursor;
  851. WCHAR StatusBuf[256];
  852. DWORD dwoffset = 0;
  853. // status text we get back states "Synchronizing Folder 'inbox',
  854. // we always want to wack off the Synchronizing
  855. #ifdef _TRUNCATESTATUS
  856. if (lpcStatusText && (0 == strnicmp("Synchronizing ",lpcStatusText,sizeof("Synchronizing"))) )
  857. {
  858. dwoffset = sizeof("Synchronizing");
  859. }
  860. #endif // _TRUNCATESTATUS
  861. if (0 == MultiByteToWideChar(CP_ACP,0,lpcStatusText + dwoffset,-1,
  862. StatusBuf,256))
  863. {
  864. *StatusBuf = L'\0';
  865. }
  866. stg->nProgCur = iCurValue;
  867. stg->nProgMax = iMaxValue;
  868. // now estimate the time remaining
  869. DWORD dwElapsed = GetTickCount() - stg->dwStartTime;
  870. DWORD dwTicksRemaining = 0;
  871. if (0 != iCurValue)
  872. {
  873. dwTicksRemaining = ((iMaxValue - iCurValue) * dwElapsed)/iCurValue;
  874. }
  875. SYNCMGRPROGRESSITEM progItem;
  876. progItem.mask = SYNCMGRPROGRESSITEM_STATUSTEXT
  877. | SYNCMGRPROGRESSITEM_STATUSTYPE
  878. | SYNCMGRPROGRESSITEM_PROGVALUE
  879. | SYNCMGRPROGRESSITEM_MAXVALUE;
  880. progItem.iProgValue = iCurValue;
  881. progItem.iMaxValue = iMaxValue;
  882. progItem.lpcStatusText = StatusBuf;
  883. progItem.dwStatusType = SYNCMGRSTATUS_UPDATING;
  884. hr = lpCallBack->Progress(*pItemID,&progItem);
  885. if (S_SYNCMGR_CANCELITEM == hr || S_SYNCMGR_CANCELALL == hr)
  886. {
  887. stg->fCancelled = TRUE; // Stop the Transfer.
  888. }
  889. }
  890. extern "C" HRESULT FormGetFormMessage(LPMAPIFORMINFO pinfo, ULONG FAR *pulReg,
  891. LPSTR lpcClass, LPMESSAGE FAR *ppmsg); // in IFRMREGU.CPP
  892. // Review, implementation in ifrmregu.cpp
  893. HRESULT FormGetFormMessage(LPMAPIFORMINFO, ULONG FAR *,
  894. LPSTR, LPMESSAGE FAR *)
  895. {
  896. return ResultFromScode(MAPI_E_CALL_FAILED);
  897. }
  898. HRESULT HrGetOneProp(LPMAPIPROP lpIProp, ULONG ulTag, LPSPropValue* lppProp)
  899. {
  900. SizedSPropTagArray(1,ptag) = {1, {ulTag}};
  901. ULONG ulT;
  902. *lppProp = NULL;
  903. return lpIProp->GetProps((LPSPropTagArray)&ptag,0, &ulT, lppProp);
  904. }