home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / mapi / remote.xp / xplogon.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  123.3 KB  |  3,225 lines

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. //  File Name
  4. //      XPLOGON.CPP
  5. //
  6. //  Description
  7. //      This file implements the IXPLogon interface with the methods specified
  8. //      in the MAPI SPI 1.0.
  9. //
  10. //  Authors
  11. //      Irving De la Cruz
  12. //      Les Thaler
  13. //
  14. //  Revision: 1.7
  15. //
  16. // Written for Microsoft Windows Developer Support
  17. // Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved.
  18. //
  19. //
  20. #include "XPWDSR.H"
  21.  
  22. #define DO_INFO_TRACES
  23. #ifdef DO_INFO_TRACES
  24. #define InfoTrace(a)   TraceInfoMessage(a)
  25. #else
  26. #define InfoTrace(a)
  27. #endif // DO_INFO_TRACES
  28.  
  29. // Remark this line to turn verbose tracing OFF
  30. MAPIUID guidABEntries = AB_UID_PROVIDER;
  31. MAPIUID guidXPABEntries = XP_WINDS_AB_ENTRIES;
  32. MAPIUID * puidWINDSEntries;
  33. TCHAR gszProviderName[] = TEXT("WINDS Transport Service");
  34. LPTSTR gszWINDSAddressType = WINDS_ADDRESS_TYPE;
  35. LPTSTR * gpszXPAddressTypes;
  36.  
  37. void CALLBACK UploadTimerProc (HWND    hOwnerWnd,
  38.                                UINT    Message,
  39.                                UINT    idEvent,
  40.                                DWORD   dwTime);
  41.  
  42. ///////////////////////////////////////////////////////////////////////////////
  43. //    CXPLogon::CXPLogon()
  44. //
  45. //    Parameters
  46. //      hInstance               Instance of the provider DLL
  47. //      pSupObj                 Pointer to IMAPISupport object used in
  48. //                              CXPLogon methods
  49. //      pszHeadersFile          Full file path where the remote headers are
  50. //                              store in the local computer.
  51. //      fGetHeaders             Weather or not to get the headers of the
  52. //                              mailbox during a scheduled connection.
  53. //      pUserMailboxInfo        Pointer to a MAILBOX_INFO structure with the
  54. //                              information of the logged user as store in
  55. //                              the WINDS server.
  56. //      dwMailboxID             ID of the user in the WINDS system.
  57. //      pszRemoteServer         Name of the remote WINDS server to which
  58. //                              this transport communicates
  59. //      hUIMutex                Handle to a WINDS mutex to allow single access
  60. //                              to the UI configuration of this provider.
  61. //
  62. //    Purpose
  63. //      Constructor of the object. Parameters are passed to initialize the
  64. //      data members with the appropiate values.
  65. //
  66. //    Return Value
  67. //      None
  68. //
  69. CXPLogon::CXPLogon (HINSTANCE       hInstance,
  70.                     LPMAPISUP       pSupObj,
  71.                     LPTSTR          pszHeadersFile,
  72.                     BOOL            fGetHeaders,
  73.                     PMAILBOX_INFO   pUserMailboxInfo,
  74.                     DWORD           dwMailboxID,
  75.                     LPTSTR          pszRemoteServer,
  76.                     HANDLE          hUIMutex)
  77. {
  78.     InfoTrace ("CXPLogon: Constructor called");
  79.     m_cRef           = 1;
  80.     m_pIdentityProps = NULL;
  81.     m_hInstance      = hInstance;
  82.     m_pSupObj        = pSupObj;
  83.     m_pStatusObj     = NULL;
  84.     m_pIdentityProps = NULL;
  85.     ZeroMemory (&m_stDelivTime, sizeof(SYSTEMTIME));
  86.     m_uTimerID       = 0;
  87.     m_hTimerWnd      = NULL;
  88.     m_fABWDSInstalled = FALSE;
  89.     m_fGetHeaders    = fGetHeaders;
  90.     m_hUIMutex       = hUIMutex;
  91.     m_raAction       = REMOTE_ACTION_IDLE;
  92.     m_UserInfo       = *pUserMailboxInfo;
  93.     ZeroMemory (&m_UserEID, CB_PRIVATE_EID);
  94.     m_UserEID.uidGlobal = guidABEntries;
  95.     m_UserEID.bVersion = ENTRYID_VERSION;
  96.     m_UserEID.bObject = MAPI_MAILUSER;
  97.     m_UserEID.dwObjID = dwMailboxID;
  98.  
  99.     m_pSupObj->AddRef();
  100.  
  101.     lstrcpy (m_szHeaders, pszHeadersFile);
  102.     lstrcpy (m_szServer, pszRemoteServer);
  103.  
  104.     wsprintf (m_szAddress, TEXT("%s\\%s"), m_szServer, m_UserInfo.szMailboxName);
  105.  
  106.     // Initialize the messages download list object
  107.     m_List.SetLogon(this);
  108.     if (!m_List.Init())
  109.     {
  110.         throw CException (E_OUTOFMEMORY);
  111.     }
  112.     SetTransportState (WAITING);
  113. }
  114.  
  115. ///////////////////////////////////////////////////////////////////////////////
  116. //    CXPLogon::~CXPLogon()
  117. //
  118. //    Parameters
  119. //      None
  120. //
  121. //    Purpose
  122. //      Destructor of CXPLogon. Releases memory allocated for internal
  123. //      properties during the life of this transport logon object.
  124. //
  125. //    Return Value
  126. //      None
  127. //
  128. CXPLogon::~CXPLogon()
  129. {
  130.     InfoTrace ("CXPLogon: Destructor called");
  131.     gpfnFreeBuffer (m_pIdentityProps);
  132.     CloseHandle (m_hUIMutex);
  133.  
  134.     EmptyInboundQueue();
  135.  
  136.     // Delete CMAPIStatus Object
  137. /*    if (m_pStatusObj)
  138.     {
  139.         m_pStatusObj->Release();
  140.         m_pStatusObj = NULL;
  141.     }
  142. */
  143.     // Release the IMAPISupport object
  144.     m_pSupObj->Release();
  145.     m_pSupObj = NULL;
  146.  
  147.     // Just as a safety precaution, make sure the timer is off.
  148.     StopUploadTimer();
  149.  
  150.     // Just in case the DLL is still hanging around
  151.     PrivUninitialize3DCtl (m_hInstance);
  152. }
  153.  
  154. ///////////////////////////////////////////////////////////////////////////////
  155. //    CXPLogon::QueryInterface()
  156. //
  157. //    Parameters
  158. //      { Refer to OLE Documentation on this method }
  159. //
  160. //    Purpose
  161. //      Returns a pointer to a interface requested if the interface is
  162. //      supported and implemented by this object. If it is not supported, it
  163. //      returns NULL
  164. //
  165. //    Return Value
  166. //      An HRESULT
  167. //
  168. STDMETHODIMP CXPLogon::QueryInterface (REFIID riid, LPVOID * ppvObj)
  169. {
  170.     // OLE requires NULLing parameter
  171.     *ppvObj = NULL;
  172.     // If this is one of the two IID return an interface pointer to it
  173.     if (riid == IID_IXPLogon || riid == IID_IUnknown)
  174.     {
  175.         *ppvObj = (LPVOID)this;
  176.         // Increase usage count of this object
  177.         AddRef();
  178.         return S_OK;
  179.     }
  180.     // This object does not support the interface requested
  181.     return E_NOINTERFACE;
  182. }
  183.  
  184. ///////////////////////////////////////////////////////////////////////////////
  185. // IXPLogon virtual member functions implementation
  186. //
  187.  
  188. ///////////////////////////////////////////////////////////////////////////////
  189. //    CXPLogon::AddressType()
  190. //
  191. //    Parameters
  192. //      { Refer to MAPI Documentation on this method }
  193. //
  194. //    Purpose
  195. //      Called by the MAPI Spooler when initializing this XP logon object to
  196. //      allow the transport to register the address it will handle
  197. //
  198. //    Return Value
  199. //      S_OK always
  200. //
  201. STDMETHODIMP CXPLogon::AddressTypes (ULONG *        pulFlags,
  202.                                      ULONG *        pcAdrType,
  203.                                      LPTSTR **      pppAdrTypeArray,
  204.                                      ULONG *        pcMAPIUID,
  205.                                      LPMAPIUID **   pppMAPIUIDArray)
  206. {
  207.     InfoTrace ("CXPLogon::AddressTypes method called");
  208.     CheckParameters_IXPLogon_AddressTypes (this,
  209.                                            pulFlags,
  210.                                            pcAdrType,
  211.                                            pppAdrTypeArray,
  212.                                            pcMAPIUID,
  213.                                            pppMAPIUIDArray);
  214.  
  215.     // This is how many address type strings we are returning in pppAdrTypeArray
  216.     *pcAdrType = 1;
  217.  
  218.     // Copy address types back to the MAPI pointer
  219.     gpszXPAddressTypes = &gszWINDSAddressType;
  220.     *pppAdrTypeArray = gpszXPAddressTypes;
  221.     *pulFlags = fMapiUnicode;
  222.  
  223.     // The UID for routing message has to be set here. If it is not used, set it NULL.
  224.     // In the case of WINDS transport registers support for the UID of
  225.     // the WINDS address book's entry ID
  226.     puidWINDSEntries = &guidABEntries;
  227.     *pcMAPIUID = 1;
  228.     *pppMAPIUIDArray = &puidWINDSEntries;
  229.     return S_OK;
  230. }
  231.  
  232. ///////////////////////////////////////////////////////////////////////////////
  233. //    CXPLogon::RegisterOptions()
  234. //
  235. //    Parameters
  236. //      { Refer to MAPI Documentation on this method }
  237. //
  238. //    Purpose
  239. //      This transport does not registers any per-recipient or per-message
  240. //      option processing, so we return 0 options. And NULL in the OPTIONDATA
  241. //      structure pointer.
  242. //
  243. //    Return Value
  244. //      An HRESULT
  245. //
  246. STDMETHODIMP CXPLogon::RegisterOptions (ULONG *         pulFlags,
  247.                                         ULONG *         pcOptions,
  248.                                         LPOPTIONDATA *  ppOptions)
  249. {
  250.     InfoTrace ("CXPLogon::RegisterOptions method called");
  251.     CheckParameters_IXPLogon_RegisterOptions (this,
  252.                                               pulFlags,
  253.                                               pcOptions,
  254.                                               ppOptions);
  255.     *pulFlags = 0;
  256.     *pcOptions = 0;
  257.     *ppOptions = NULL;
  258.     return S_OK;
  259. }
  260.  
  261. ///////////////////////////////////////////////////////////////////////////////
  262. //    CXPLogon::TransportNotify()
  263. //
  264. //    Parameters
  265. //      { Refer to MAPI Documentation on this method }
  266. //
  267. //    Purpose
  268. //      Update the status row registered by this transport with MAPI
  269. //
  270. //    Return Value
  271. //      An HRESULT
  272. //
  273. STDMETHODIMP CXPLogon::TransportNotify (ULONG * pulFlags, LPVOID * ppvData)
  274. {
  275.     InfoTrace ("CXPLogon::TransportNotify method called");
  276.     CheckParameters_IXPLogon_TransportNotify (this,
  277.                                               pulFlags,
  278.                                               ppvData);
  279.  
  280.     ULONG ulOldStatus = GetTransportStatusCode();
  281.     // Set appropriate status flags and re-register status row
  282.     if (*pulFlags & NOTIFY_BEGIN_INBOUND)
  283.     {
  284.         InfoTrace ("CXPLogon::TransportNotify: Begin INBOUND logic");
  285.         AddStatusBits (STATUS_INBOUND_ENABLED);
  286.     }
  287.     if (*pulFlags & NOTIFY_END_INBOUND)
  288.     {
  289.         InfoTrace ("CXPLogon::TransportNotify: Terminate INBOUND logic");
  290.         RemoveStatusBits (STATUS_INBOUND_ENABLED);
  291.     }
  292.     if (*pulFlags & NOTIFY_BEGIN_OUTBOUND)
  293.     {
  294.         InfoTrace ("CXPLogon::TransportNotify: Begin OUTBOUND logic");
  295.         AddStatusBits (STATUS_OUTBOUND_ENABLED);
  296.         InitializeTimer();
  297.         StartUploadTimer();
  298.     }
  299.     if (*pulFlags & NOTIFY_END_OUTBOUND)
  300.     {
  301.         InfoTrace ("CXPLogon::TransportNotify: Terminate OUTBOUND logic");
  302.         RemoveStatusBits (STATUS_OUTBOUND_ENABLED);
  303.         StopUploadTimer();
  304.     }
  305.     if (*pulFlags & NOTIFY_BEGIN_OUTBOUND_FLUSH)
  306.     {
  307.         InfoTrace ("CXPLogon::TransportNotify: Begin OUTBOUND flush");
  308.         // If the spooler need to flush us for some reason, we put ourselves
  309.         // in FLUSH and tell the spooler to give us any deferred messages
  310.         // it has queued up for us.
  311.         AddStatusBits (UPLOADING_MESSAGES); // Add these bits to the status code
  312.         SetTransportState (READY);
  313.         m_pSupObj->SpoolerNotify (NOTIFY_SENTDEFERRED, NULL);
  314.     }
  315.     if (*pulFlags & NOTIFY_END_INBOUND_FLUSH)
  316.     {
  317.         InfoTrace ("CXPLogon::TransportNotify: Terminate INBOUND flush");
  318.         if (FALSE == m_List.AreTherePendingDownloads())
  319.         {
  320.             RemoveStatusBits (DOWNLOADING_MESSAGES); // Tell the spooler to take us off from inbound flush
  321.             AddStatusBits (STATUS_OFFLINE); // The transport is now OFF line
  322.             UpdateStatus (TRUE, TRUE);
  323.         }
  324.     }
  325.     if (*pulFlags & NOTIFY_END_OUTBOUND_FLUSH)
  326.     {
  327.         InfoTrace ("CXPLogon::TransportNotify: Terminate OUTBOUND flush");
  328.         if (GetTransportStatusCode() & UPLOADING_MESSAGES)
  329.         {
  330.             // When the spooler finished sending messages to our CXPLogon::SubmitMessage
  331.             // method, he will call us to let us know there is nothing else we need to
  332.             // flush, and we need to reset our status row.
  333.             RemoveStatusBits (UPLOADING_MESSAGES); // Tell the spooler to take us off from outbound flush
  334.             AddStatusBits (STATUS_OFFLINE); // The transport is now OFF line
  335.             if (PENDING_RETURN_CODE != GetTransportState())
  336.             {
  337.                 SetTransportState (WAITING);
  338.             }
  339.         }
  340.     }
  341.  
  342.     // We get called here if user deletes/modifies queued msg in Outbox.
  343.     // If the user modifies and resends it, we get called again at SubmitMessage.
  344.     // If the user closes the message viewer or just saves it, it's removed
  345.     // from the spooler queue and discarded.
  346.     if (*pulFlags & NOTIFY_ABORT_DEFERRED)
  347.     {
  348.         InfoTrace ("CXPLogon::TransportNotify: Abort deferred message");
  349.     }
  350.     if (*pulFlags & NOTIFY_CANCEL_MESSAGE)
  351.     {
  352.         InfoTrace ("CXPLogon::TransportNotify: Cancel message");
  353.     }
  354.     if (ulOldStatus != GetTransportStatusCode())
  355.     {
  356.         UpdateStatus();
  357.     }
  358.     if (*pulFlags & NOTIFY_BEGIN_INBOUND)
  359.     {
  360.         // If we have pending message from a previous session, notify the
  361.         // spooler that we are ready to give them to the store.
  362.         if (m_List.AreTherePendingDownloads())
  363.         {
  364.             // Put ourselves into flush mode and have the spooler call us until
  365.             // we are finished putting the downloaded messages into the default store.
  366.             AddStatusBits (DOWNLOADING_MESSAGES); // Add these bits to the status code
  367.             UpdateStatus();
  368.         }
  369.     }
  370.     return S_OK;
  371. }
  372.  
  373. ///////////////////////////////////////////////////////////////////////////////
  374. //    CXPLogon::Idle()
  375. //
  376. //    Parameters
  377. //      { Refer to MAPI Documentation on this method }
  378. //
  379. //    Purpose
  380. //      Stub method. We should not get called here, because we told
  381. //      the spooler not to call us here.
  382. //
  383. //    Return Value
  384. //      S_OK always.
  385. //
  386. STDMETHODIMP CXPLogon::Idle (ULONG ulFlags)
  387. {
  388.     InfoTrace ("CXPLogon::Idle method called");
  389.     CheckParameters_IXPLogon_Idle (this, ulFlags);
  390.     return S_OK;
  391. }
  392.  
  393. ///////////////////////////////////////////////////////////////////////////////
  394. //    CXPLogon::TransportLogoff()
  395. //
  396. //    Parameters
  397. //      ulFlags     Priority with which the transport should log off
  398. //
  399. //    Purpose
  400. //      This method is called by the spooler when the transport should do final
  401. //      arragements before it gets released.
  402. //
  403. //    Return Value
  404. //      An HRESULT
  405. //
  406. STDMETHODIMP CXPLogon::TransportLogoff (ULONG ulFlags)
  407. {
  408.     InfoTrace ("CXPLogon::TransportLogoff method called");
  409.     CheckParameters_IXPLogon_TransportLogoff (this, ulFlags);
  410.     StopUploadTimer();
  411.  
  412.     EmptyInboundQueue();
  413.  
  414.     // We should attempt to remove the transport's status row from
  415.     // the system, but if we fail we won't fail the call.
  416.     HRESULT hResult = m_pSupObj->ModifyStatusRow (0, NULL, 0);
  417.     TraceResult ("CXPLogon::TransportLogoff: Failed to remove status row", hResult);
  418.  
  419.     if (m_pStatusObj)
  420.     {
  421.         m_pStatusObj->Release();
  422.         m_pStatusObj = NULL;
  423.     }
  424.  
  425.     return S_OK;
  426. }
  427.  
  428. ///////////////////////////////////////////////////////////////////////////////
  429. //    CXPLogon::SubmitMessage()
  430. //
  431. //    Parameters
  432. //      { Refer to MAPI Documentation on this method }
  433. //
  434. //    Purpose
  435. //      This method is called by the spooler when a client submits a
  436. //      message to a recipient whose address type this transport handles.
  437. //      The spooler calls this method twice for each deferred message.
  438. //      The first time (before the delivery time) when the message is
  439. //      submitted by the client, we simply return. The message is then queued
  440. //      by the spooler for later delivery. We keep track of when it's time
  441. //      to send deferred messages.
  442. //
  443. //      The second time we're called, the state variable will be 'READY' and
  444. //      we go ahead and start the actual transmission. While we're in the
  445. //      body of this function, the implied state is 'SENDING'
  446. //
  447. //      If the client logs out of this session, any pending messages get
  448. //      queued again the next time it logs in.
  449. //
  450. //      In this transport we get a recipient table, we restrict the table for
  451. //      unmarked recipients. After the table is ready we invoke a helper
  452. //      method to do the actual transmission.
  453. //
  454. //    Return Value
  455. //      An HRESULT
  456. //
  457. STDMETHODIMP CXPLogon::SubmitMessage (ULONG     ulFlags,
  458.                                       LPMESSAGE pMsgObj,
  459.                                       ULONG *   pulMsgRef,
  460.                                       ULONG *   pulReturnParm)
  461. {
  462.     InfoTrace ("CXPLogon::SubmitMessage method called");
  463.     CheckParameters_IXPLogon_SubmitMessage (this,
  464.                                             ulFlags,
  465.                                             pMsgObj,
  466.                                             pulMsgRef,
  467.                                             pulReturnParm);
  468.  
  469.     #ifdef _DEBUG
  470.     // Before we get called in SubmitMessage or StartMessage, the spooler should have called
  471.     // us in TransportNotify() to let us know which logic (IN, OUT, BOTH) of the
  472.     // transporting mechanism should be activated for this IXPLogon session.
  473.     if (!(GetTransportStatusCode() & STATUS_OUTBOUND_ENABLED))
  474.     {
  475.         TraceMessage ("CXPLogon::SubmitMessage: We can't send a message if the outbound is not enabled");
  476.         ASSERTMSG (FALSE, "We can't submit a message if we have not started OUTBOUND logic");
  477.         return MAPI_E_NOT_ME;
  478.     }
  479.     #endif // _DEBUG
  480.  
  481.     // Lock the object against the timer thread re-entrancy. Timer events are handle
  482.     // in a separate thread. This section will get unlocked in the CPXLogon::EndMessage() call
  483.     if (WAITING == GetTransportState())
  484.     {
  485.         // Not scheduled upload time, return and let IXPLogon::EndMessage() defer the message
  486.         InfoTrace ("CXPLogon::SubmitMessage: Deferring message for scheduled submission");
  487.         return S_OK;
  488.     }
  489.     InfoTrace ("CXPLogon::SubmitMessage: Processing deferred message");
  490.  
  491.     CheckSpoolerYield (TRUE);
  492.  
  493.     // Notify the MAPI spooler the transport is going to flush any queued up messages and submit them.
  494.     // Transport to used shared resources, such as COM ports, should put them selves into flush mode
  495.     // when submitting a batch of messages. This gives the transport the advantage of locking shared
  496.     // resources across submitted messages and ensures that the spooler does not call other
  497.     // transports while another is flushing because it could have something locked.
  498.     // This transport does not uses any shared resources that we would need to lock, but for the
  499.     // purposes of demonstration, we put our selves into flush mode until the MAPI spooler
  500.     // tells us to step down.
  501.     if (!(GetTransportStatusCode() & UPLOADING_MESSAGES))
  502.     {
  503.         AddStatusBits (UPLOADING_MESSAGES);
  504.         UpdateStatus();
  505.     }
  506.  
  507.     // Get the recipient table from the message
  508.     LPMAPITABLE pTable = NULL;
  509.     HRESULT hResult = pMsgObj->GetRecipientTable (fMapiUnicode, &pTable);
  510.     if (hResult)
  511.     {
  512.         TraceResult ("CXPLogon::SubmitMessage: Failed to get the recipient table", hResult);
  513.         goto ErrorExit;
  514.     }
  515.  
  516.     // The spooler marks all the message recipients this transport has to
  517.     // handle with PR_RESPONSIBILITY set to FALSE
  518.     SPropValue spvRecipUnsent;
  519.     spvRecipUnsent.ulPropTag                       = PR_RESPONSIBILITY;
  520.     spvRecipUnsent.Value.b                         = FALSE;
  521.  
  522.     SRestriction srRecipientUnhandled;
  523.     srRecipientUnhandled.rt                        = RES_PROPERTY;
  524.     srRecipientUnhandled.res.resProperty.relop     = RELOP_EQ;
  525.     srRecipientUnhandled.res.resProperty.ulPropTag = PR_RESPONSIBILITY;
  526.     srRecipientUnhandled.res.resProperty.lpProp    = &spvRecipUnsent;
  527.  
  528.     hResult = pTable->Restrict (&srRecipientUnhandled, 0L);
  529.     if (hResult)
  530.     {
  531.         TraceResult ("CXPLogon::SubmitMessage: Failed to restrict the recipient table", hResult);
  532.         goto ErrorExit;
  533.     }
  534.  
  535.     // Let the MAPI spooler do other things
  536.     CheckSpoolerYield();
  537.  
  538.     hResult = HrAddColumns (pTable,
  539.                             (LPSPropTagArray)&sptRecipTable,
  540.                             gpfnAllocateBuffer,
  541.                             gpfnFreeBuffer);
  542.     if (hResult)
  543.     {
  544.         TraceResult ("CXPLogon::SubmitMessage: Failed to expand the columns in the recipient table", hResult);
  545.         goto ErrorExit;
  546.     }
  547.  
  548.     LPSRowSet pRecipRows;
  549.     hResult = HrQueryAllRows (pTable,
  550.                               NULL,
  551.                               NULL,
  552.                               NULL,
  553.                               0,
  554.                               &pRecipRows);
  555.     if (!hResult)
  556.     {
  557.         // Let the MAPI spooler do other things
  558.         CheckSpoolerYield();
  559.         // Send to the recipients which we can reach, and generate NDR's for the ones we can't
  560.         hResult = SendMailMessage (pMsgObj, pRecipRows);
  561.         FreeProws (pRecipRows);
  562.         if (!hResult)
  563.         {
  564.             // Now we need to save changes on the message and close it.
  565.             // After this, the message object can't be used.
  566.             hResult = pMsgObj->SaveChanges (0);
  567.             TraceResult ("CXPLogon::SubmitMessage: Failed to save the message object", hResult);
  568.         }
  569.     }
  570.  
  571. ErrorExit:
  572.      // Release the table, we're finished with it
  573.     if (pTable)
  574.     {
  575.         pTable->Release();
  576.     }
  577.  
  578.     // Release the spooler's message if needed to
  579.     if (pMsgObj)
  580.     {
  581.         pMsgObj->Release ();
  582.     }
  583.  
  584.     // In case there is a warning or error floating around, don't let it escape to the spooler.
  585.     if (FAILED(hResult))
  586.     {
  587.         // We default to MAPI_E_NOT_ME so that the spooler would attempt handle
  588.         // the message to other transport (currently running in this profile)
  589.         // that handle the same address type as ours.
  590.         TraceMessage ("CXPLogon::SubmitMessage: An error occurred in the submission, returning MAPI_E_NOT_ME");
  591.         hResult = MAPI_E_NOT_ME;
  592.     }
  593.     else
  594.     {
  595.         hResult = S_OK;
  596.     }
  597.     return hResult;
  598. }
  599.  
  600. ///////////////////////////////////////////////////////////////////////////////
  601. //    CXPLogon::EndMessage()
  602. //
  603. //    Parameters
  604. //      { Refer to MAPI Documentation on this method }
  605. //
  606. //    Purpose
  607. //      This method is called by the spooler for each message we're to
  608. //      deliver. It's the mate to SubmitMessage. We're called here twice
  609. //      for each deferred message and once for non-deferred (realtime)
  610. //      messages.
  611. //
  612. //      We first check the transport state, and if we're
  613. //      WAITING for the scheduled delivery time to arrive, we return
  614. //      END_DONT_RESEND in *pulFlags, which tells the spooler to queue this
  615. //      message for deferred delivery.
  616. //
  617. //      If the state is SENDING, we're getting called here after
  618. //      a message has been dequeued and delivered. Return 0 in *pulFlags
  619. //      to tell the spooler the message has been delivered.
  620. //
  621. //    Return Value
  622. //      An HRESULT
  623. //
  624. STDMETHODIMP CXPLogon::EndMessage (ULONG ulMsgRef, ULONG * pulFlags)
  625. {
  626.     InfoTrace ("CXPLogon::EndMessage method called");
  627.     CheckParameters_IXPLogon_EndMessage (this, ulMsgRef, pulFlags);
  628.  
  629.     if (WAITING == GetTransportState())
  630.     {
  631.         // Tell spooler to queue msg for deferred delivery
  632.         *pulFlags = END_DONT_RESEND;
  633.     }
  634.     else
  635.     {
  636.         *pulFlags = 0;
  637.     }
  638.     return S_OK;
  639. }
  640.  
  641. ///////////////////////////////////////////////////////////////////////////////
  642. //    CXPLogon::Poll()
  643. //
  644. //    Parameters
  645. //      { Refer to MAPI Documentation on this method }
  646. //
  647. //    Purpose
  648. //      Stub method. We should not get called here, because we told
  649. //      the spooler not to call us here.
  650. //
  651. //    Return Value
  652. //      An HRESULT
  653. //
  654. STDMETHODIMP CXPLogon::Poll (ULONG * pulIncoming)
  655. {
  656.     InfoTrace ("CXPLogon::Poll method called");
  657.     CheckParameters_IXPLogon_Poll (this, pulIncoming);
  658.     return S_OK;
  659. }
  660.  
  661. ///////////////////////////////////////////////////////////////////////////////
  662. //    CXPLogon::StartMessage()
  663. //
  664. //    Parameters
  665. //      { Refer to MAPI Documentation on this method }
  666. //
  667. //    Purpose
  668. //      This method gets called when an incoming message is pending to be
  669. //      processed.
  670. //
  671. //    Return Value
  672. //      An HRESULT
  673. //
  674. STDMETHODIMP CXPLogon::StartMessage (ULONG      ulFlags,
  675.                                      LPMESSAGE  pMsgObj,
  676.                                      ULONG *    pulMsgRef)
  677. {
  678.     InfoTrace ("CXPLogon::StartMessage method called");
  679.     CheckParameters_IXPLogon_StartMessage (this, ulFlags, pMsgObj, pulMsgRef);
  680.  
  681.     // Initialize the pseudo-timer for the SpoolerYield() call
  682.     CheckSpoolerYield (TRUE);
  683.  
  684.     HRESULT hResult = S_OK;
  685.     LPSTREAM pFileStream = NULL;
  686.     CCachedStream * pStream = NULL;
  687.     LPITNEF pTNEFObj = NULL;
  688.     SPropTagArray sptExcludedProps = { 0 };
  689.  
  690.     PLIST_NODE pNode = m_List.GetDownloadNode();
  691.     if (!pNode)
  692.     {
  693.         RemoveStatusBits (DOWNLOADING_MESSAGES); // Remove these bits from the status code
  694.         UpdateStatus();
  695.         if (READY == GetTransportState())
  696.         {
  697.             SetTransportState (WAITING);
  698.         }
  699.         goto ErrorExit;
  700.     }
  701.     // Close the file so that we may open it in the call below
  702.     if (pNode->hFile)
  703.     {
  704.         CloseHandle (pNode->hFile);
  705.         // NULL the handle to avoid closing the file again during cleanup.
  706.         pNode->hFile = NULL;
  707.     }
  708.     // Open the stream where the message properties are.
  709.     hResult = OpenStreamOnFile (gpfnAllocateBuffer,
  710.                                 gpfnFreeBuffer,
  711.                                 STGM_READ,
  712.                                 pNode->szFileName,
  713.                                 NULL,
  714.                                 &pFileStream);
  715.     if (hResult)
  716.     {
  717.         TraceResult ("CXPLogon::StartMessage: Failed to open stream on the message file", hResult);
  718.         goto ErrorExit;
  719.     }
  720.     try
  721.     {
  722.         // Create a buffer IStream interface for the TNEF decoding
  723.         pStream = new CCachedStream (pFileStream, XPSOF_READ);
  724.         if (!pStream)
  725.         {
  726.             TraceMessage ("CXPLogon::StartMessage: Failed to allocate cached stream object");
  727.             hResult = E_OUTOFMEMORY;
  728.         }
  729.     }
  730.     catch (CException & Exception)
  731.     {
  732.         hResult = Exception.GetError();
  733.     }
  734.     pFileStream->Release();
  735.     pFileStream = NULL;
  736.     if (hResult)
  737.     {
  738.         goto ErrorExit;
  739.     }
  740.     hResult = OpenTnefStream (m_pSupObj,
  741.                               pStream,
  742.                               TNEF_FILE_NAME,
  743.                               TNEF_DECODE,
  744.                               pMsgObj,
  745.                               0,    // Not needed when decoding TNEF
  746.                               &pTNEFObj);
  747.     if (hResult)
  748.     {
  749.         TraceResult ("CXPLogon::StartMessage: Failed to open TNEF stream object", hResult);
  750.         goto ErrorExit;
  751.     }
  752.  
  753.     // Let the MAPI spooler do other things
  754.     CheckSpoolerYield();
  755.  
  756.     // Get the properties for the message which are encoded in the TNEF
  757.     // message stream. The sptExcludedProps argument, is just a stub,
  758.     // we are not excluding any properties
  759.     LPSTnefProblemArray pProblems;
  760.     pProblems = NULL;
  761.     hResult = pTNEFObj->ExtractProps (TNEF_PROP_EXCLUDE, &sptExcludedProps, &pProblems);
  762.     if (pProblems)
  763.     {
  764.         gpfnFreeBuffer (pProblems);
  765.         pProblems = NULL;
  766.     }
  767.     if (hResult)
  768.     {
  769.         TraceResult ("CXPLogon::StartMessage: Failed to open TNEF stream", hResult);
  770.         goto ErrorExit;
  771.     }
  772.     if (MAPI_E_CORRUPT_DATA == SetIncomingProps (pMsgObj, pNode))
  773.     {
  774.         pNode = NULL; // To avoid deleting it below.
  775.     }
  776.  
  777.     // Save all chages and new properties, back on the message
  778.     // Don't release the message object. The spooler is still using it.
  779.     // After this, the message object can't be used by this transport.
  780.     hResult = pMsgObj->SaveChanges (0);
  781.     TraceResult ("CXPLogon::StartMessage: Failed to save the message object", hResult);
  782.  
  783.     // Let the MAPI spooler do other things
  784.     CheckSpoolerYield();
  785.  
  786. ErrorExit:
  787.     // Release used objects
  788.     if (pTNEFObj)
  789.     {
  790.         pTNEFObj->Release();
  791.     }
  792.     if (pStream)
  793.     {
  794.         pStream->Release();
  795.     }
  796.     if (pNode)
  797.     {
  798.         if (pNode->hFile)
  799.         {
  800.             CloseHandle (pNode->hFile);
  801.         }
  802.         // Delete the file ONLY if the we were successful putting the
  803.         // file data into the message. Otherwise leave the file in
  804.         // the directory, and it will be picked up later
  805.         if (S_OK == hResult)
  806.         {
  807.             DeleteFile (pNode->szFileName);
  808.         }
  809.         delete pNode;
  810.     }
  811.     m_List.UpdateProgress();
  812.     return hResult;
  813. }
  814.  
  815. ///////////////////////////////////////////////////////////////////////////////
  816. //    CXPLogon::OpenStatusEntry()
  817. //
  818. //    Parameters
  819. //      { Refer to MAPI Documentation on this method }
  820. //
  821. //    Purpose
  822. //      This method is called to get an IMAPIStatus object for this XPLOGON
  823. //      session.
  824. //
  825. //    Return Value
  826. //      An HRESULT
  827. //
  828. STDMETHODIMP CXPLogon::OpenStatusEntry (LPCIID          pInterface,
  829.                                         ULONG           ulFlags,
  830.                                         ULONG *         pulObjType,
  831.                                         LPMAPISTATUS *  ppEntry)
  832. {
  833.     InfoTrace ("CXPLogon::OpenStatusEntry method called");
  834.     CheckParameters_IXPLogon_OpenStatusEntry (this,
  835.                                               pInterface,
  836.                                               ulFlags,
  837.                                               pulObjType,
  838.                                               ppEntry);
  839.     if (MAPI_MODIFY & ulFlags)
  840.     {
  841.         TraceMessage ("CXPLogon::OpenStatusEntry: We don't support WRITE access to the status object");
  842.         return E_ACCESSDENIED;
  843.     }
  844.  
  845.     HRESULT hResult = S_OK;
  846.     // Now, if we already have created a status object on this logon context,
  847.     // we'll just use QueryInterface to get a new copy (AddRef() it) of the object
  848.     if (!m_pStatusObj)
  849.     {
  850.         // Get the profile section of the PROVIDER so that we may get some properties,
  851.         // assigned by MAPI to this provider, directly on the status object.
  852.         LPPROFSECT pProfileObj = NULL;
  853.         m_pSupObj->OpenProfileSection (NULL, 0, &pProfileObj);
  854.         // If we don't have an object, create it, and save a copy in the logon object
  855.         m_pStatusObj = new CMAPIStatus (this, pProfileObj);
  856.         if (!m_pStatusObj)
  857.         {
  858.             TraceMessage ("CXPLogon::OpenStatusEntry: Could not allocate new CMAPIStatus object");
  859.             hResult = E_OUTOFMEMORY;
  860.         }
  861.         // The constructor of CMAPIStatus AddRef()'ed this object
  862.         if (pProfileObj)
  863.         {
  864.             pProfileObj->Release();
  865.         }
  866.     }
  867.     if (!hResult)
  868.     {
  869.         // The transport will return *ppEntry == NULL or a pointer to the new memory allocated
  870.         m_pStatusObj->AddRef();
  871.         *ppEntry = m_pStatusObj;
  872.         *pulObjType = MAPI_STATUS;
  873.     }
  874.     return hResult;
  875. }
  876.  
  877. ///////////////////////////////////////////////////////////////////////////////
  878. //    CXPLogon::ValidateState()
  879. //
  880. //    Parameters
  881. //      { Refer to MAPI Documentation on this method }
  882. //
  883. //    Purpose
  884. //      This function gets caller by a client in order to validate the
  885. //      transport logon properties. This function open the profile with the
  886. //      most up-to-date properties and then compares them to what the transport
  887. //      has stored internally
  888. //
  889. //    Return Value
  890. //      An HRESULT
  891. //
  892. STDMETHODIMP CXPLogon::ValidateState (ULONG ulUIParam, ULONG ulFlags)
  893. {
  894.     InfoTrace ("CXPLogon::ValidateState method called");
  895.     CheckParameters_IXPLogon_ValidateState (this,
  896.                                             ulUIParam,
  897.                                             ulFlags);
  898.  
  899.     // Try to open the transport profile section
  900.     LPPROFSECT pProfileObj;
  901.     HRESULT hResult = OpenServiceProfileSection (m_pSupObj, &pProfileObj, gpfnFreeBuffer);
  902.     if (hResult)
  903.     {
  904.         return hResult;
  905.     }
  906.  
  907.     LPSPropValue pProps = NULL;
  908.     ULONG ulPropCount;
  909.     // Read the properties stored in the profile of this user
  910.     hResult = pProfileObj->GetProps ((LPSPropTagArray)&sptLogonProps,
  911.                                      fMapiUnicode,
  912.                                      &ulPropCount,
  913.                                      &pProps);
  914.     TraceResult ("CXPLogon::ValidateState: Failed to get profile properties", hResult);
  915.     if (!hResult)
  916.     {
  917.         // Now, compare what the transport thinks the information in the profile is
  918.         // to the real data. If they are different, tell the spooler the transport
  919.         // would like to be reloaded
  920.         if (lstrcmp (m_UserInfo.szMailboxName, pProps[MAILBOX_NAME].Value.LPSZ)||
  921.             lstrcmp (m_UserInfo.szPassword, pProps[PASSWORD].Value.LPSZ) ||
  922.             lstrcmp (m_UserInfo.szFullName, pProps[USER_NAME].Value.LPSZ) ||
  923.             lstrcmpi (m_szServer, pProps[SERVER_NAME].Value.LPSZ))
  924.         {
  925.             hResult = m_pSupObj->SpoolerNotify (NOTIFY_CONFIG_CHANGE, NULL);
  926.             TraceResult ("CXPLogon::ValidateState: Failed to notify the spooler", hResult);
  927.         }
  928.     }
  929.     pProfileObj->Release();
  930.     gpfnFreeBuffer (pProps);
  931.     return hResult;
  932. }
  933.  
  934. ///////////////////////////////////////////////////////////////////////////////
  935. //    CXPLogon::FlushQueues()
  936. //
  937. //    Parameters
  938. //      { Refer to MAPI Documentation on this method }
  939. //
  940. //    Purpose
  941. //      Called by the MAPI spooler when, upon request of the client or
  942. //      ourselves, we need to flush the inbound or outbound queue.
  943. //      Here we make connections to the server to download messages, refresh
  944. //      the remote message headers, and request the spooler to send us any
  945. //      deferred messages.
  946. //      Transport connecting only in FlushQueues() allow the MAPI spooler to
  947. //      better manage contention of multiple transport accessing common
  948. //      communication resources (such as COM ports) and let the spooler give us
  949. //      messages to process when is best for the overall subsystem.
  950. //
  951. //    Return Value
  952. //      An HRESULT
  953. //
  954. STDMETHODIMP CXPLogon::FlushQueues (ULONG       ulUIParam,
  955.                                     ULONG       cbTargetTransport,
  956.                                     LPENTRYID   pTargetTransport,
  957.                                     ULONG       ulFlags)
  958. {
  959.     InfoTrace ("CXPLogon::FlushQueues method called");
  960.     CheckParameters_IXPLogon_FlushQueues (this,
  961.                                           ulUIParam,
  962.                                           cbTargetTransport,
  963.                                           pTargetTransport,
  964.                                           ulFlags);
  965.  
  966.     HRESULT hResult = S_OK;
  967.     // Update status flags and then update update the transport status rows
  968.     if (ulFlags & FLUSH_UPLOAD)
  969.     {
  970.         // If the remote server is offline, the transport is not going to send the deferred messages
  971.         if (TRUE == IsWINDSServerAvailable (m_szServer))
  972.         {
  973.             // If we are already in delivery mode submitted messages will not get deferred,
  974.             // so don't bother the spooler asking for deferred messages.
  975.             if (READY != GetTransportState())
  976.             {
  977.                 // Guard against re-entrancy from the timer call back which happens on a separate thread
  978.                 if (PROCESSING_TIMER_EVENT != GetTransportState())
  979.                 {
  980.                     SetTransportState (READY);
  981.                 }
  982.                 // We pass NULL for the entry ID so that the spooler would resend ALL the
  983.                 // deferred messages. Transport may pass individual entry IDs for specific messages.
  984.                 hResult = m_pSupObj->SpoolerNotify (NOTIFY_SENTDEFERRED, NULL);
  985.             }
  986.         }
  987.     }
  988.     if (ulFlags & FLUSH_DOWNLOAD)
  989.     {
  990.         if (HEADERS_AND_DOWNLOAD == GetTransportState())
  991.         {
  992.             m_hRemoteActionErr = ProcessHeaders();
  993.             if (!m_hRemoteActionErr && !m_fCancelPending)
  994.             {
  995.                 m_pSupObj->SpoolerYield (0);
  996.                 if (m_List.AreTherePendingDownloads())
  997.                 {
  998.                     // Put ourselves into flush mode and have the spooler call us until
  999.                     // we are finished putting the downloaded messages into the default store.
  1000.                     AddStatusBits (DOWNLOADING_MESSAGES); // Add these bits to the status code
  1001.                 }
  1002.                 if (!m_fCancelPending)
  1003.                 {
  1004.                     // Upload any deferred messages the spooler has for our transport
  1005.                     if (IsWINDSServerAvailable (m_szServer))
  1006.                     {
  1007.                         m_pSupObj->SpoolerNotify (NOTIFY_SENTDEFERRED, NULL);
  1008.                     }
  1009.                     if (!m_fCancelPending)
  1010.                     {
  1011.                         m_hRemoteActionErr = DownloadMessageHeaders();
  1012.                         if (!m_hRemoteActionErr && !(GetTransportStatusCode() & DOWNLOADING_MESSAGES))
  1013.                         {
  1014.                             RemoveStatusBits (STATUS_INBOUND_FLUSH);
  1015.                             UpdateStatus (TRUE, TRUE);
  1016.                             m_pSupObj->SpoolerYield (0);
  1017.                         }
  1018.                     }
  1019.                 }
  1020.             }
  1021.             SetTransportState (PENDING_RETURN_CODE);
  1022.             AddStatusBits (STATUS_OFFLINE);
  1023.             if (m_hRemoteActionErr)
  1024.             {
  1025.                 RemoveStatusBits (STATUS_INBOUND_FLUSH);
  1026.                 UpdateStatus (TRUE, TRUE);
  1027.             }
  1028.             else
  1029.             {
  1030.                 if (m_fCancelPending)
  1031.                 {
  1032.                     UpdateStatus (TRUE, TRUE);
  1033.                 }
  1034.                 else
  1035.                 {
  1036.                     UpdateStatus();
  1037.                 }
  1038.             }
  1039.         }
  1040.         else
  1041.         {
  1042.             if (PROCESSING_TIMER_EVENT == GetTransportState())
  1043.             {
  1044.                 if (m_fGetHeaders)
  1045.                 {
  1046.                     RemoveStatusBits (STATUS_OFFLINE);
  1047.                     UpdateStatus();
  1048.                     DownloadMessageHeaders();
  1049.                     AddStatusBits (STATUS_OFFLINE);
  1050.                     UpdateStatus();
  1051.                 }
  1052.             }
  1053.             else
  1054.             {
  1055.                 if (m_List.AreTherePendingDownloads())
  1056.                 {
  1057.                     // Put ourselves into flush mode and have the spooler call us until
  1058.                     // we are finished putting the downloaded messages into the default store.
  1059.                     AddStatusBits (DOWNLOADING_MESSAGES); // Add these bits to the status code
  1060.                     UpdateStatus();
  1061.                 }
  1062.             }
  1063.         }
  1064.     }
  1065.     return hResult;
  1066. }
  1067.  
  1068. ///////////////////////////////////////////////////////////////////////////////
  1069. //    CXPLogon::ProcessHeaders()
  1070. //
  1071. //    Parameters
  1072. //      None.
  1073. //
  1074. //    Purpose
  1075. //      This method is called from the status object's ValidateState when
  1076. //      it's passed PROCESS_XP_HEADER_CACHE. It starts the download processing.
  1077. //      First we get the header folder contents table and restrict it to rows
  1078. //      where PR_MSG_STATUS is >= MSGSTATUS_REMOTE_DOWNLOAD. These are rows
  1079. //      for messages that have the MSGSTATUS_REMOTE_DOWNLOAD, or
  1080. //      MSGSTATUS_REMOTE_DELETE, or both, bits set.
  1081. //
  1082. //      We then connect to the server to obtain the download named pipe and
  1083. //      start the download.
  1084. //
  1085. //    Return Value
  1086. //      An HRESULT
  1087. //
  1088. STDMETHODIMP CXPLogon::ProcessHeaders()
  1089. {
  1090.     // If we don't have a folder in the status object or the folder doesn't
  1091.     // have an initialized contents table, there is nothing to do.
  1092.     if (!m_pStatusObj->m_pHeaderFolder || !m_pStatusObj->m_pHeaderFolder->m_pTableData)
  1093.     {
  1094.         return S_OK;
  1095.     }
  1096.  
  1097.     // Get headers folder contents table, some are marked for download
  1098.     LPMAPITABLE pTable;
  1099.     HRESULT hResult = m_pStatusObj->m_pHeaderFolder->m_pTableData->HrGetView (NULL, NULL, 0, &pTable);
  1100.     if (hResult)
  1101.     {
  1102.         TraceResult ("CXPLogon::ProcessHeaders: Failed to get a view on the contents table", hResult);
  1103.         return hResult;
  1104.     }
  1105.  
  1106.     // we only want rows marked for download, form restriction
  1107.     SPropValue spvFilter = { 0 };
  1108.     spvFilter.ulPropTag = PR_MSG_STATUS;
  1109.     spvFilter.Value.l = MSGSTATUS_REMOTE_DOWNLOAD;
  1110.  
  1111.     // This restriction relies in the current values of the flags in PR_MSG_STATUS
  1112.     SRestriction srStatus = { 0 };
  1113.     srStatus.rt = RES_PROPERTY;
  1114.     srStatus.res.resProperty.relop = RELOP_GE;
  1115.     srStatus.res.resProperty.ulPropTag = PR_MSG_STATUS;
  1116.     srStatus.res.resProperty.lpProp = &spvFilter;
  1117.  
  1118.     hResult = pTable->Restrict (&srStatus, 0);
  1119.     if (hResult)
  1120.     {
  1121.         // This is a non-fatal error (i.e. the implementation does not support restrictions)
  1122.         TraceResult ("CXPLogon::ProcessHeaders: Failed to restrict the table", hResult);
  1123.     }
  1124.     ULONG ulRowCount;
  1125.     // Providers and client should not, when ever possible, use IMAPITable::GetRowCount()
  1126.     // because depending on the implementation of the table, the returned count by not
  1127.     // be exact.
  1128.     // We use it here for convenience knowing the fact the MAPI's ITableData implementation
  1129.     // returns and EXACT count of the rows in the memory table.
  1130.     hResult = pTable->GetRowCount (0, &ulRowCount);
  1131.     if (hResult)
  1132.     {
  1133.         goto ErrorExit;
  1134.     }
  1135.     // Nothing to do.
  1136.     if (0 == ulRowCount)
  1137.     {
  1138.         goto ErrorExit;
  1139.     }
  1140.     else
  1141.     {
  1142.         // Make sure our download directory is around.
  1143.         TCHAR szTmpDir[_MAX_PATH], szDownloadDir[_MAX_PATH];
  1144.         GetTempPath (_MAX_PATH, szTmpDir);
  1145.         lstrcat (szTmpDir, WINDS_DATA_DIRECTORY);
  1146.         wsprintf (szDownloadDir, WINDS_DOWNLOAD_DIR_NAME_FORMAT, szTmpDir, m_UserInfo.szMailboxName);
  1147.         CreateDirectory (szDownloadDir, NULL);
  1148.     }
  1149.  
  1150.     // Initialize the progress property in the status row of this transport.
  1151.     UpdateProgress (0, REMOTE_ACTION_DOWNLOADING_MSGS);
  1152.  
  1153.     // Make a call to the remote server to open the connection
  1154.     HANDLE hPipe;
  1155.     hResult = OpenRemoteServerDownLoadPipe (m_szServer, m_UserInfo.szMailboxName, &hPipe);
  1156.     if (hResult)
  1157.     {
  1158.         goto ErrorExit;
  1159.     }
  1160.     // Do the actual download transmission now.
  1161.     hResult = m_List.DownLoadMsgs (pTable, ulRowCount, hPipe);
  1162.     CloseHandle (hPipe);
  1163.  
  1164.     TerminateRemoteConnections();
  1165.  
  1166. ErrorExit:
  1167.     TraceResult ("CXPLogon::ProcessHeaders", hResult);
  1168.     pTable->Release();
  1169.     return hResult;
  1170. }
  1171.  
  1172. ///////////////////////////////////////////////////////////////////////////////
  1173. //    CXPLogon::UpdateProgress()
  1174. //
  1175. //    Parameters
  1176. //      lPercentComplete    Percent completion of download
  1177. //      raFlag              Flag that indicate to what remote action the
  1178. //                          update is tied to, so that we may properly
  1179. //                          update PR_REMOTE_PROGRESS_TEXT.
  1180. //
  1181. //    Purpose
  1182. //      Sets the status row property PR_REMOTE_PROGRESS with a number from
  1183. //      0-100. In example, if 10 messages are queued and 5 have been
  1184. //      downloaded, lPercentComplete will be 50, regardless of individual
  1185. //      messages' sizes.
  1186. //
  1187. //    Return Value
  1188. //      None.
  1189. //
  1190. void WINAPI CXPLogon::UpdateProgress (long          lPercentComplete,
  1191.                                       REMOTE_ACTION raFlag)
  1192. {
  1193.     SPropValue spProgress[2] = { 0 };
  1194.     ULONG cProps = 1;
  1195.     TCHAR szRemoteProgress[64] = { 0 };
  1196.     spProgress[0].ulPropTag = PR_REMOTE_PROGRESS;
  1197.     spProgress[0].Value.l = lPercentComplete;
  1198.     if (raFlag != m_raAction)
  1199.     {
  1200.         spProgress[1].ulPropTag = PR_REMOTE_PROGRESS_TEXT;
  1201.         spProgress[1].Value.LPSZ = szRemoteProgress;
  1202.         DWORD idString;
  1203.         m_raAction = raFlag;
  1204.         switch (raFlag)
  1205.         {
  1206.             case REMOTE_ACTION_DOWNLOADING_MSGS :
  1207.                 idString = IDS_REMOTE_DOWNLOADING;
  1208.                 break;
  1209.             case REMOTE_ACTION_PROCESSING_MSGS :
  1210.                 idString = IDS_REMOTE_PROCESSING_MSGS;
  1211.                 break;
  1212.             case REMOTE_ACTION_HEADER_REFRESH :
  1213.                 idString = IDS_REMOTE_REFRESHING_HEADERS;
  1214.                 break;
  1215.             case REMOTE_ACTION_IDLE :
  1216.                 idString = 0;
  1217.                 break;
  1218.         }
  1219.         if (idString)
  1220.         {
  1221.             LoadString (m_hInstance, idString, szRemoteProgress, 64);
  1222.         }
  1223.         cProps++;
  1224.     }
  1225.  
  1226.     HRESULT hResult = m_pSupObj->ModifyStatusRow (cProps, spProgress, STATUSROW_UPDATE);
  1227.     TraceResult ("CXPLogon::UpdateProgress: Failed to modify the status row", hResult);
  1228. }
  1229.  
  1230. ///////////////////////////////////////////////////////////////////////////////
  1231. //    CXPLogon::LoadStatusString()
  1232. //
  1233. //    Parameters
  1234. //      pString         Pointer to a string which will hold the status string
  1235. //      uStringSize     Maximum number of characters allowed in the string
  1236. //
  1237. //    Purpose
  1238. //      Loads a string from the transport's stringtable. This method is called
  1239. //      by the CXPLogon::UpdateStatus method when updating a status row. This
  1240. //      method loads the string based on the status bits of the transport
  1241. //      status code
  1242. //
  1243. //    Return Value
  1244. //      TRUE            If the string was found in the string table.
  1245. //      FALSE           The string was not found. The String indicated by
  1246. //                      pString is set to hold 0 characters
  1247. //
  1248. BOOL WINAPI CXPLogon::LoadStatusString (LPTSTR pString, UINT uStringSize)
  1249. {
  1250.     UINT uStringID;
  1251.     DWORD dwStatusCode = GetTransportStatusCode();
  1252.     if (dwStatusCode & (DOWNLOADING_MESSAGES | UPLOADING_MESSAGES))
  1253.     {
  1254.         uStringID = IDS_XP_STATUS_TIMER_EVENT;
  1255.     }
  1256.     else
  1257.     {
  1258.         if (dwStatusCode & DOWNLOADING_MESSAGES)
  1259.         {
  1260.             if (dwStatusCode & STATUS_OFFLINE)
  1261.             {
  1262.                 uStringID = IDS_XP_STATUS_FLUSHING_INBOUND;
  1263.             }
  1264.             else
  1265.             {
  1266.                 uStringID = IDS_XP_STATUS_INBOUND_AND_HEADERS;
  1267.             }
  1268.         }
  1269.         else
  1270.         {
  1271.             if (dwStatusCode & UPLOADING_MESSAGES)
  1272.             {
  1273.                 uStringID = IDS_XP_STATUS_FLUSHING_OUTBOUND;
  1274.             }
  1275.             else
  1276.             {
  1277.                 if (dwStatusCode & STATUS_INBOUND_ENABLED &&
  1278.                     dwStatusCode & STATUS_OUTBOUND_ENABLED)
  1279.                 {
  1280.                     uStringID = IDS_XP_STATUS_READY;
  1281.                 }
  1282.                 else
  1283.                 {
  1284.                     uStringID = IDS_XP_STATUS_AVAILABLE;
  1285.                 }
  1286.             }
  1287.         }
  1288.     }
  1289.     if (LoadString (m_hInstance, uStringID, pString, uStringSize))
  1290.     {
  1291.         return TRUE;
  1292.     }
  1293.     pString[0] = '\0';
  1294.     return FALSE;
  1295. }
  1296.  
  1297. ///////////////////////////////////////////////////////////////////////////////
  1298. //    CXPLogon::MakeSearchKey()
  1299. //
  1300. //    Parameters
  1301. //      pParentMemBlock     Pointer the a block of memory allocated with the
  1302. //                          MAPI Mem. alloc. functions.
  1303. //      pszAddress          String with the actual mail address
  1304. //      pcbSearchKey        Pointer to a ULONG which returns the size
  1305. //                          (in bytes) of the newly created search key
  1306. //      ppSearchKey         Pointer to an array ob BYTES
  1307. //
  1308. //    Purpose
  1309. //      Returns a MAPI search key of the form TYPE:ADDRESS in upper case.
  1310. //      The string is allocated with the MAPI Mem Alloc functions and linked
  1311. //      to the block of memory indicated by pParentMemBlock
  1312. //
  1313. //    Return Value
  1314. //      An HRESULT
  1315. //
  1316. STDMETHODIMP CXPLogon::MakeSearchKey (LPVOID        pParentMemBlock,
  1317.                                       LPTSTR        pszAddress,
  1318.                                       ULONG *       pcbSearchKey,
  1319.                                       LPBYTE *      ppSearchKey)
  1320. {
  1321.     LPTSTR pszSearchKey;
  1322.     // The 2 is for the COLON and the NULL terminator
  1323.     ULONG cbStrSize = sizeof (TCHAR) * (2 + lstrlen (WINDS_ADDRESS_TYPE) + lstrlen (pszAddress));
  1324.     HRESULT hResult = gpfnAllocateMore (cbStrSize, pParentMemBlock, (LPVOID *)&pszSearchKey);
  1325.     TraceResult ("CXPLogon::MakeSearchKey: Failed to allocate search key string", hResult);
  1326.     if (!hResult)
  1327.     {
  1328.         // We need to convert the address to upper case
  1329.         wsprintf (pszSearchKey, TEXT("%s:%s"), WINDS_ADDRESS_TYPE, pszAddress);
  1330.         CharUpper (pszSearchKey);
  1331.         *pcbSearchKey = cbStrSize;
  1332.         *ppSearchKey = (LPBYTE)pszSearchKey;
  1333.     }
  1334.     return hResult;
  1335. }
  1336.  
  1337. ///////////////////////////////////////////////////////////////////////////////
  1338. //    CXPLogon::UpdateStatus()
  1339. //
  1340. //    Parameters
  1341. //      fAddValidate
  1342. //      fValidateOkState
  1343. //
  1344. //    Purpose
  1345. //      Updates the transport status row of this transport in the MAPI Mail
  1346. //      subsystem. Updates the flags according the internal state flags
  1347. //      maintained in status code of the transport and loads a readable status
  1348. //      string to reset the status row. The caller of this method should update
  1349. //      the status code member variable prior to calling UpdateStatus()
  1350. //
  1351. //    Return Value
  1352. //      None
  1353. //
  1354. void WINAPI CXPLogon::UpdateStatus (BOOL fAddValidate, BOOL fValidateOkState)
  1355. {
  1356.     ULONG cProps = 1;
  1357.     SPropValue rgProps[3] = { 0 };
  1358.     //  Store the new Transport Provider Status Code
  1359.     rgProps[0].ulPropTag = PR_STATUS_CODE;
  1360.     rgProps[0].Value.l = GetTransportStatusCode();
  1361.  
  1362.     TCHAR szStatus[64];
  1363.     if  (LoadStatusString (szStatus, sizeof(szStatus) - 1 ))
  1364.     {
  1365.         rgProps[1].ulPropTag = PR_STATUS_STRING;
  1366.         rgProps[1].Value.LPSZ = szStatus;
  1367.         cProps++;
  1368.     }
  1369.     if (fAddValidate)
  1370.     {
  1371.         ULONG index;
  1372.         if (2 == cProps)
  1373.         {
  1374.             index = 2;
  1375.         }
  1376.         else
  1377.         {
  1378.             index = 1;
  1379.         }
  1380.         rgProps[index].ulPropTag = PR_REMOTE_VALIDATE_OK;
  1381.         rgProps[index].Value.b = fValidateOkState;
  1382.         cProps++;
  1383.     }
  1384.     //  OK. Notify the messaging subsystem of state changes in this transport.
  1385.     HRESULT hResult = m_pSupObj->ModifyStatusRow (cProps, rgProps, STATUSROW_UPDATE);
  1386.     TraceResult ("CXPLogon::UpdateStatus: ModifyStatusRow failed", hResult);
  1387. }
  1388.  
  1389. ///////////////////////////////////////////////////////////////////////////////
  1390. //    CXPLogon::InitializeTransportStatusFlags()
  1391. //
  1392. //    Parameters
  1393. //      ulFlags     Flags passed in to initialize the transport status flags.
  1394. //
  1395. //    Purpose
  1396. //      Initialize the transport status flags with the flags passed in by the
  1397. //      MAPI spooler. This method gets called when initializing a CXPLogon
  1398. //      object in the CXPProvider::TransportLogon method.
  1399. //
  1400. //    Return Value
  1401. //      None
  1402. //
  1403. void WINAPI CXPLogon::InitializeTransportStatusFlags (ULONG ulFlags)
  1404. {
  1405.     AddStatusBits (STATUS_AVAILABLE | STATUS_OFFLINE | STATUS_REMOTE_ACCESS);
  1406.     if (!(ulFlags & LOGON_NO_INBOUND))
  1407.     {
  1408.         AddStatusBits (STATUS_INBOUND_ENABLED);
  1409.     }
  1410.     if (!(ulFlags & LOGON_NO_OUTBOUND))
  1411.     {
  1412.         AddStatusBits (STATUS_OUTBOUND_ENABLED);
  1413.     }
  1414. }
  1415.  
  1416. ///////////////////////////////////////////////////////////////////////////////
  1417. //    CXPLogon::SetIdentityProps()
  1418. //
  1419. //    Parameters
  1420. //      None
  1421. //
  1422. //    Purpose
  1423. //      Initializes the transport ID array with the properties found in the
  1424. //      profile for the user.
  1425. //      If the ABWDS address book is installed, we open an entry in it to
  1426. //      supply the identity properties.
  1427. //
  1428. //    Return Value
  1429. //      An HRESULT
  1430. //
  1431. STDMETHODIMP CXPLogon::SetIdentityProps()
  1432. {
  1433.     // Allocate the property array for transport. This memory block is freed
  1434.     // in the destructor of the CXPLogon object
  1435.     HRESULT hResult = gpfnAllocateBuffer (sizeof(SPropValue)*NUM_IDENTITY_PROPS, (LPVOID *)&m_pIdentityProps);
  1436.     if (hResult)
  1437.     {
  1438.         TraceResult ("CXPLogon::SetIdentityProps: Failed to allocate property array", hResult);
  1439.         return hResult;
  1440.     }
  1441.     LPMAILUSER pUser;
  1442.     ULONG ulObjType;
  1443.     TCHAR szSearchKey[128];
  1444.     LPTSTR pStr;
  1445.     // Try to open out entry ID in the ABWDS address book (see if it is around)
  1446.     // If the call fails, we must create a one-off for our identity.
  1447.     hResult = m_pSupObj->OpenEntry (CB_PRIVATE_EID,
  1448.                                     (LPENTRYID)&m_UserEID,
  1449.                                     NULL,
  1450.                                     0,
  1451.                                     &ulObjType,
  1452.                                     (LPUNKNOWN *)&pUser);
  1453.     if (S_OK == hResult)
  1454.     {
  1455.         ASSERTMSG (MAPI_MAILUSER == ulObjType, "What kind object is this?");
  1456.  
  1457.         // Looks like ABWDS is around, mark the internal flag.
  1458.         // ABWDS is the native WINDS address book service provider. If present,
  1459.         // instead of creating a one-off entry for the transport identity, we
  1460.         // will simply open an entry in the WINDS address book and get the
  1461.         // necessary properties out from it.
  1462.         m_fABWDSInstalled = TRUE;
  1463.  
  1464.         m_pIdentityProps[XPID_NAME].ulPropTag = PR_SENDER_NAME;
  1465.         m_pIdentityProps[XPID_NAME].Value.LPSZ = m_UserInfo.szFullName;
  1466.         m_pIdentityProps[XPID_EID].ulPropTag = PR_SENDER_ENTRYID;
  1467.         m_pIdentityProps[XPID_EID].Value.bin.cb = CB_PRIVATE_EID;
  1468.         m_pIdentityProps[XPID_EID].Value.bin.lpb = (LPBYTE)&m_UserEID;
  1469.         wsprintf (szSearchKey,
  1470.                   TEXT("%s:%s\\%s"),
  1471.                   WINDS_ADDRESS_TYPE,
  1472.                   m_szServer,       // this is already in the format "\\<servername>"
  1473.                   m_UserInfo.szMailboxName);
  1474.         CharUpper (szSearchKey);
  1475.         hResult = gpfnAllocateMore (Cbtszsize(szSearchKey), m_pIdentityProps, (LPVOID *)&pStr);
  1476.         if (hResult)
  1477.         {
  1478.             TraceResult ("CXPLogon::SetIdentityProps: Failed to allocate string for native search key", hResult);
  1479.             m_pIdentityProps[XPID_SEARCH_KEY].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_SENDER_SEARCH_KEY));
  1480.         }
  1481.         else
  1482.         {
  1483.             lstrcpy (pStr, szSearchKey);
  1484.             m_pIdentityProps[XPID_SEARCH_KEY].ulPropTag = PR_SENDER_SEARCH_KEY;
  1485.             m_pIdentityProps[XPID_SEARCH_KEY].Value.bin.cb = Cbtszsize(pStr);
  1486.             m_pIdentityProps[XPID_SEARCH_KEY].Value.bin.lpb = (LPBYTE)pStr;
  1487.         }
  1488.         pUser->Release();
  1489.         return hResult;
  1490.     }
  1491.  
  1492.     ///////////////////////////////////////////////////////////////////////////
  1493.     // Set the PR_SENDER_ENTRYID property. Create an entry for the user and
  1494.     // set it in the property array
  1495.     m_pIdentityProps[XPID_EID].ulPropTag = PR_SENDER_ENTRYID;
  1496.     LPBYTE lpeid = NULL;
  1497.     ULONG  cbeid = 0;
  1498.     hResult = m_pSupObj->CreateOneOff (m_UserInfo.szFullName,
  1499.                                        WINDS_ADDRESS_TYPE,
  1500.                                        m_szAddress,
  1501.                                        fMapiUnicode,
  1502.                                        &cbeid,
  1503.                                        (LPENTRYID *)&lpeid);
  1504.  if (hResult)
  1505.     {
  1506.         TraceResult ("CXPLogon::SetIdentityProps: Failed to create a one off identity", hResult);
  1507.         m_pIdentityProps[XPID_EID].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_SENDER_ENTRYID));
  1508.         return hResult;
  1509.     }
  1510.  
  1511.     hResult = gpfnAllocateMore(cbeid, m_pIdentityProps,
  1512.                             (LPVOID *)&m_pIdentityProps[XPID_EID].Value.bin.lpb);
  1513.     if(hResult)
  1514.     {
  1515.         TraceResult ("CXPLogon::SetIdentityProps: Failed to allocate memory", hResult);
  1516.         m_pIdentityProps[XPID_EID].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_SENDER_ENTRYID));
  1517.         gpfnFreeBuffer(lpeid);
  1518.         lpeid = NULL;
  1519.         return hResult;
  1520.     }
  1521.  
  1522.     m_pIdentityProps[XPID_EID].Value.bin.cb = cbeid;
  1523.     CopyMemory(m_pIdentityProps[XPID_EID].Value.bin.lpb, lpeid, cbeid);
  1524.  
  1525.     gpfnFreeBuffer(lpeid);
  1526.     lpeid = NULL;
  1527.  
  1528.     //////////////////////////////////////////////////////////////////////////
  1529.     // Set the PR_SENDER_NAME property
  1530.     m_pIdentityProps[XPID_NAME].ulPropTag = PR_SENDER_NAME;
  1531.     LPTSTR pStr1, pStr2 = m_UserInfo.szFullName;
  1532.     hResult = gpfnAllocateMore (Cbtszsize(pStr2), m_pIdentityProps, (LPVOID *)&pStr1);
  1533.     if (hResult)
  1534.     {
  1535.         TraceResult ("CXPLogon::SetIdentityProps: Memory allocation failed for display name string", hResult);
  1536.         m_pIdentityProps[XPID_NAME].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_SENDER_NAME));
  1537.         return hResult;
  1538.     }
  1539.     lstrcpy (pStr1, pStr2);
  1540.     m_pIdentityProps[XPID_NAME].Value.LPSZ = pStr1;
  1541.  
  1542.     //////////////////////////////////////////////////////////////////////////
  1543.     // Set the PR_SENDER_SEARCH_KEY property
  1544.     m_pIdentityProps[XPID_SEARCH_KEY].ulPropTag = PR_SENDER_SEARCH_KEY;
  1545.     // First, create the search key
  1546.     hResult = MakeSearchKey (m_pIdentityProps,
  1547.                              m_szAddress,
  1548.                              &m_pIdentityProps[XPID_SEARCH_KEY].Value.bin.cb,
  1549.                              &m_pIdentityProps[XPID_SEARCH_KEY].Value.bin.lpb);
  1550.     if (hResult)
  1551.     {
  1552.         m_pIdentityProps[XPID_SEARCH_KEY].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_SENDER_SEARCH_KEY));
  1553.     }
  1554.     return hResult;
  1555. }
  1556.  
  1557. ///////////////////////////////////////////////////////////////////////////////
  1558. //    CXPLogon::SetSessionFlags()
  1559. //
  1560. //    Parameters
  1561. //      [IN, OUT]   pulFlags    Flags returned to caller
  1562. //
  1563. //    Purpose
  1564. //      To set the flags we will return to the spooler during the CXPLogon
  1565. //      initialization in the CXPProvider::TransportLogon method. The ulFlags
  1566. //      parameter is filled as a return value to the caller
  1567. //
  1568. //    Return Value
  1569. //      None
  1570. //
  1571. void WINAPI CXPLogon::SetSessionFlags (ULONG * pulFlags)
  1572. {
  1573.     if (*pulFlags & LOGON_NO_CONNECT)
  1574.     {
  1575.         // Nothing is set
  1576.         *pulFlags = 0;
  1577.     }
  1578.     else
  1579.     {
  1580.         // We DON'T want the MAPI spooler to call our IXPLogon::Idle method (LOGON_SP_IDLE not added)
  1581.         // We DON'T want the MAPI spooler to call our IXPLogon::Poll method (LOGON_SP_POLL not added)
  1582.         // Also the names have to be fully resolved (LOGON_SP_RESOLVE)
  1583.         *pulFlags = LOGON_SP_RESOLVE;
  1584.     }
  1585. }
  1586.  
  1587. ///////////////////////////////////////////////////////////////////////////////
  1588. //    CXPLogon::SendMailMessage()
  1589. //
  1590. //    Parameters
  1591. //      pMsgObj     Pointer to an IMessage object passed to us by the
  1592. //                  MAPI spooler.
  1593. //      pRecipRows  Row set with ALL the rows from the recipient table which
  1594. //                  have the properties of the recipients we are sending the
  1595. //                  message to.
  1596. //
  1597. //    Purpose
  1598. //      This method gets called by SubmitMessage() to do the delivery of the
  1599. //      message to the list of recipients. Each recipient will have the
  1600. //      PR_RESPONSIBILITY property set indicating weather or not message
  1601. //      reached the recipient.
  1602. //
  1603. //    Return Value
  1604. //      An HRESULT
  1605. //
  1606. STDMETHODIMP CXPLogon::SendMailMessage (LPMESSAGE pMsgObj, LPSRowSet pRecipRows)
  1607. {
  1608.     LPSPropValue pMsgProps = NULL;
  1609.     LPADRLIST pAdrList = NULL, pAdrListFailed = NULL;
  1610.     LPSTnefProblemArray pProblems = NULL;
  1611.     CCachedStream * pStream = NULL;
  1612.     LPSTREAM pFileStream;
  1613.     HANDLE hMsgFile = NULL;
  1614.     HRESULT hOpenConnect, hUploadError;
  1615.     BOOL fErrorInServer, fSentSuccessfully;
  1616.     ULONG ulRow, ulCount1 = 0 , ulCount2 = 0;
  1617.     LPTSTR pszServer, pszMailbox;
  1618.     LPSPropValue pProps;
  1619.     TCHAR szHeaderText[1024], szTmpFile[_MAX_PATH], szConnectInfo[MAX_STRING_SIZE+1] = { 0 };
  1620.     if (!GetMsgTempFileName (szTmpFile))   // Not the Win32 API, but an internal implementation
  1621.     {
  1622.         return E_FAIL;
  1623.     }
  1624.  
  1625.     // Create a stream where all message information will be saved.
  1626.     HRESULT hResult = OpenStreamOnFile (gpfnAllocateBuffer,
  1627.                                         gpfnFreeBuffer,
  1628.                                         STGM_CREATE | STGM_READWRITE,
  1629.                                         szTmpFile,
  1630.                                         NULL,
  1631.                                         &pFileStream);
  1632.     if (hResult)
  1633.     {
  1634.         TraceResult ("CXPLogon::SendMailMessage: Failed to create stream object", hResult);
  1635.         return hResult;
  1636.     }
  1637.     try
  1638.     {
  1639.         pStream = new CCachedStream (pFileStream, XPSOF_READWRITE);
  1640.         if (!pStream)
  1641.         {
  1642.             TraceMessage ("CXPLogon::SendMailMessage: Failed to allocate cached stream object");
  1643.             hResult = E_OUTOFMEMORY;
  1644.         }
  1645.     }
  1646.     catch (CException & Exception)
  1647.     {
  1648.         hResult = Exception.GetError();
  1649.     }
  1650.     pFileStream->Release();
  1651.     pFileStream = NULL;
  1652.     if (hResult)
  1653.     {
  1654.         return hResult;
  1655.     }
  1656.  
  1657.     // The wKey is a key used to identify the TNEF property stream. Transports
  1658.     // should generate a pseudo-random number for this field. Here we get
  1659.     // a number based upon the system's tic count
  1660.     // Note that this number cannot be zero of the  OpenTnefStream call will be fail.
  1661.     WORD wKey = LOWORD (GetTickCount());
  1662.     if (!wKey)
  1663.     {
  1664.         ASSERTMSG (FALSE, "I'll be darn! It's zero!!!");
  1665.         wKey = LOWORD (GetTickCount()) + 1;
  1666.         ASSERTMSG (0 != wKey, "No way! What is going on!?!?");
  1667.     }
  1668.     LPITNEF pTNEFObj = NULL;
  1669.     hResult = OpenTnefStream (m_pSupObj,
  1670.                               pStream,
  1671.                               TNEF_FILE_NAME,
  1672.                               TNEF_ENCODE | TNEF_PURE,
  1673.                               pMsgObj,
  1674.                               wKey,
  1675.                               &pTNEFObj);
  1676.     if (hResult)
  1677.     {
  1678.         TraceResult ("CXPLogon::SendMailMessage: Failed to create TNEF object", hResult);
  1679.         goto ErrorExit;
  1680.     }
  1681.  
  1682.     // Let the MAPI spooler do other things
  1683.     CheckSpoolerYield();
  1684.  
  1685.     // Get the current time. We need to set some properties that require the time
  1686.     SYSTEMTIME st;
  1687.     FILETIME ft;
  1688.     GetSystemTime (&st);
  1689.     SystemTimeToFileTime (&st, &ft);
  1690.  
  1691.     SetOutgoingProps (pMsgObj, ft);
  1692.  
  1693.     // Check what properties there are and exclude the Non-Transmittables ones
  1694.     LPSPropTagArray pTags;
  1695.     hResult = pMsgObj->GetPropList (fMapiUnicode, &pTags);
  1696.     if (hResult)
  1697.     {
  1698.         TraceResult ("CXPLogon::SendMailMessage: Failed to get the message property tags", hResult);
  1699.         goto ErrorExit;
  1700.     }
  1701.  
  1702.     // Let the MAPI spooler do other things
  1703.     CheckSpoolerYield();
  1704.  
  1705.     // In this sample transport we opted to let TNEF encapsulate
  1706.     // the table of recipients of this message. This has a side effect: The
  1707.     // addresses of the recipients get munged into the TNEF stream. So, for
  1708.     // example, if the TNEF file goes through a foreign mail system and it
  1709.     // needs to translate the address of the recipients, it will not be
  1710.     // able to. A tranport using TNEF which trasmits messages to a foreign
  1711.     // mail system, must do custom processing of the recipients and their
  1712.     // address so the receiving side will understands them.
  1713.     // This sample code must be modified if you want it as the base code
  1714.     // for a gateway transport.
  1715.     // Here we also let TNEF encode all the attachments we want to send.
  1716.     ULONG cValues, i;
  1717.     cValues = 0;
  1718.     for (i=0; i<pTags->cValues; i++)
  1719.     {
  1720.         // Use the FIsTransmittable macro in MAPI to determine if a
  1721.         // property is transmittable or not.
  1722.         if (!FIsTransmittable(pTags->aulPropTag[i]) &&
  1723.             PR_MESSAGE_RECIPIENTS != pTags->aulPropTag[i] &&
  1724.             PR_MESSAGE_ATTACHMENTS != pTags->aulPropTag[i])
  1725.         {
  1726.             pTags->aulPropTag[cValues++] = pTags->aulPropTag[i];
  1727.         }
  1728.     }
  1729.     pTags->cValues = cValues;
  1730.  
  1731.     // Encode the properties now
  1732.     hResult = pTNEFObj->AddProps (TNEF_PROP_EXCLUDE, 0, NULL, pTags);
  1733.     gpfnFreeBuffer (pTags);
  1734.     if (FAILED(hResult)) // There could be warnings
  1735.     {
  1736.         TraceResult ("CXPLogon::SendMailMessage: Failed to Add the TNEF properties to the message stream", hResult);
  1737.         goto ErrorExit;
  1738.     }
  1739.  
  1740.     hResult = pTNEFObj->Finish (0, &wKey, &pProblems);
  1741.     if (FAILED(hResult)) // There could be warnings
  1742.     {
  1743.         TraceResult ("CXPLogon::SendMailMessage: Failed to save the TNEF props", hResult);
  1744.         goto ErrorExit;
  1745.     }
  1746.     if (pProblems)
  1747.     {
  1748.         TraceMessage ("CXPLogon::SendMailMessage: Some problem(s) occurred while finishing the TNEF encoding");
  1749.         gpfnFreeBuffer (pProblems);
  1750.         pProblems = NULL;
  1751.         // Don't have to fail submission
  1752.     }
  1753.  
  1754.     // Release used objects and NULL the object pointers to avoid re-releasing them during cleanup
  1755.     if (NULL != pTNEFObj)
  1756.     {
  1757.         pTNEFObj->Release();
  1758.         pTNEFObj = NULL;
  1759.     }
  1760.     if (NULL != pStream)
  1761.     {
  1762.         pStream->Release();
  1763.         pStream = NULL;
  1764.     }
  1765.  
  1766.     hMsgFile = CreateFile (szTmpFile,
  1767.                            GENERIC_READ,
  1768.                            0,
  1769.                            NULL,
  1770.                            OPEN_EXISTING,
  1771.                            FILE_ATTRIBUTE_TEMPORARY |
  1772.                            FILE_FLAG_DELETE_ON_CLOSE |
  1773.                            FILE_FLAG_SEQUENTIAL_SCAN,
  1774.                            NULL);
  1775.     if (INVALID_HANDLE_VALUE == hMsgFile)
  1776.     {
  1777.         TraceResult ("CXPLogon::SendMailMessage: Failed to open local msg file", GetLastError());
  1778.         hResult = E_FAIL;
  1779.         goto ErrorExit;
  1780.     }
  1781.  
  1782.     // Get some properties in the message need to send the message
  1783.     // and delivery report information
  1784.     hResult = pMsgObj->GetProps ((LPSPropTagArray)&sptPropsForHeader,
  1785.                                  fMapiUnicode,
  1786.                                  &cValues,
  1787.                                  &pMsgProps);
  1788.     if (FAILED(hResult))
  1789.     {
  1790.         TraceResult ("CXPLogon::SendMailMessage: Failed to get message props", hResult);
  1791.         goto ErrorExit;
  1792.     }
  1793.  
  1794.     // We need to check if the sender requeste a delivery report or not.
  1795.     BOOL fNeedDeliveryReport;
  1796.     if (PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED == pMsgProps[MSG_DR_REPORT].ulPropTag &&
  1797.         pMsgProps[MSG_DR_REPORT].Value.b)
  1798.     {
  1799.         fNeedDeliveryReport = TRUE;
  1800.     }
  1801.     else
  1802.     {
  1803.         fNeedDeliveryReport = FALSE;
  1804.     }
  1805.  
  1806.     // Create the header that we transmit to the server. The remote host will
  1807.     // store this information and make it available to us when then recipients
  1808.     // of the message ask for a headers update on their remote mailboxes.
  1809.     CreateMsgHeaderTextLine (pMsgProps, szHeaderText, ft);
  1810.  
  1811.     // Let the MAPI spooler do other things
  1812.     CheckSpoolerYield();
  1813.  
  1814.     hOpenConnect = OpenServerUploadPipe (m_szServer,
  1815.                                          m_UserInfo.szMailboxName,
  1816.                                          hMsgFile,
  1817.                                          szConnectInfo,
  1818.                                          &fErrorInServer);
  1819.     for (ulRow=0; ulRow<pRecipRows->cRows; ulRow++)
  1820.     {
  1821.         pProps = pRecipRows->aRow[ulRow].lpProps;
  1822.  
  1823.         // Assume the worst. If hOpenConnect is not S_OK, the fSentSuccessfully
  1824.         // must be FALSE to generate NDR for each recipient.
  1825.         fSentSuccessfully = FALSE;
  1826.         if (S_OK == hOpenConnect)
  1827.         {
  1828.             if (IsValidAddress (pProps[RECIP_EMAIL_ADR].Value.LPSZ, &pszServer, &pszMailbox))
  1829.             {
  1830.                 // pszServer should be the same as m_szServer
  1831.                 hUploadError = SendMsgToAccount (pszServer,
  1832.                                                  pszMailbox,
  1833.                                                  szHeaderText,
  1834.                                                  szConnectInfo,
  1835.                                                  &fErrorInServer);
  1836.                 if (!hUploadError)
  1837.                 {
  1838.                     // If we got here, we assume the message has been received in the server
  1839.                     fSentSuccessfully = TRUE;
  1840.                 }
  1841.             }
  1842.             else
  1843.             {
  1844.                 hUploadError = MAKE_HRESULT(1, FACILITY_WIN32, ERROR_INVALID_ADDRESS);
  1845.             }
  1846.         }
  1847.         else
  1848.         {
  1849.             hUploadError = hOpenConnect;
  1850.         }
  1851.  
  1852.         // Let the MAPI spooler do other things
  1853.         CheckSpoolerYield();
  1854.  
  1855.         // Set the PR_RESPONSIBILITY flag to indicated we have handled this
  1856.         // recipient (row). If the flag is not modified, MAPI will pass this
  1857.         // message to the next transport in the profile that knows how to
  1858.         // handle the address types this transport knows.
  1859.         // In this case WE want to be the last to handle the message. If
  1860.         // we fail the submission, we should tell MAPI to generate an NDR.
  1861.         pProps[RECIP_RESPONSIBILITY].ulPropTag = PR_RESPONSIBILITY;
  1862.         pProps[RECIP_RESPONSIBILITY].Value.b = TRUE;
  1863.  
  1864.         // Set the report time for DR's and NDR's
  1865.         pProps[RECIP_REPORT_TIME].ulPropTag = PR_REPORT_TIME;
  1866.         pProps[RECIP_REPORT_TIME].Value.ft = ft;
  1867.  
  1868.         if (!fSentSuccessfully)
  1869.         {
  1870.             // Make the spooler generate an NDR instead of DR
  1871.             pProps[RECIP_DELIVER_TIME].ulPropTag = PR_NULL;
  1872.  
  1873.             // The Spooler will generate an NDR report and will fill in
  1874.             // all required properties in the StatusRecips call. The only
  1875.             // thing we need to do is to fill in a specific per-recipient
  1876.             // text description of the problem. It's good to have real
  1877.             // info from the transport indicating the real cause for the
  1878.             // failure
  1879.             wsprintf (szHeaderText,
  1880.                       TEXT("\tThe WINDS transport service failed to deliver the message to this recipient.\r\n"
  1881.                            "\tRecipient Address: Server: %s  Mailbox: %s. Delivery error: %#08X.  %s"),
  1882.                            (pszServer ? pszServer : ""),
  1883.                            (pszMailbox ? pszMailbox : ""),
  1884.                            hUploadError,
  1885.                       (fErrorInServer ? TEXT("The error occurred in the server host.") :
  1886.                                         TEXT("The error occurred in local processing.")));
  1887.             if (fErrorInServer && hUploadError)
  1888.             {
  1889.                 DWORD dwServerErrorIDS = 0;
  1890.                 switch (hUploadError)
  1891.                 {
  1892.                     case HRESULT_FROM_WIN32(ERROR_INVALID_ACCOUNT_NAME) :
  1893.                         dwServerErrorIDS = IDS_DELIVERY_ERROR_INVALID_ACCT;
  1894.                         break;
  1895.                     case HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER) :
  1896.                         dwServerErrorIDS = IDS_DELIVERY_ERROR_NO_SUCH_USER;
  1897.                         break;
  1898.                     case HRESULT_FROM_WIN32 (ERROR_HOST_UNREACHABLE) :
  1899.                         dwServerErrorIDS = IDS_DELIVERY_ERROR_OFFLINE_SERVER;
  1900.                         break;
  1901.                     case HRESULT_FROM_WIN32 (ERROR_INVALID_ADDRESS) :
  1902.                         dwServerErrorIDS = IDS_DELIVERY_ERROR_INVALID_ADDRESS;
  1903.                         break;
  1904.                     case E_OUTOFMEMORY :
  1905.                         dwServerErrorIDS = IDS_DELIVERY_ERROR_OUTOFMEMORY;
  1906.                         break;
  1907.                     case E_INVALIDARG :
  1908.                         dwServerErrorIDS = IDS_DELIVERY_ERROR_INVALID_PARAM;
  1909.                         break;
  1910.                 }
  1911.                 if (!dwServerErrorIDS && FACILITY_STORAGE == HRESULT_FACILITY(hUploadError))
  1912.                 {
  1913.                     dwServerErrorIDS = IDS_DELIVERY_ERROR_ISTORAGE;
  1914.                 }
  1915.                 if (!dwServerErrorIDS && FACILITY_WIN32 == HRESULT_FACILITY(hUploadError))
  1916.                 {
  1917.                     dwServerErrorIDS = IDS_DELIVERY_ERROR_WIN32;
  1918.                 }
  1919.                 if (dwServerErrorIDS)
  1920.                 {
  1921.                     TCHAR szBuffer[256];
  1922.                     if (LoadString (m_hInstance, dwServerErrorIDS, szBuffer, 255))
  1923.                     {
  1924.                         lstrcat (szHeaderText, TEXT("\r\n\t"));
  1925.                         lstrcat (szHeaderText, szBuffer);
  1926.                     }
  1927.                 }
  1928.             }
  1929.             LPTSTR pStr;
  1930.             hResult = gpfnAllocateMore (Cbtszsize(szHeaderText), pProps, (LPVOID *)&pStr);
  1931.             if (SUCCEEDED(hResult))
  1932.             {
  1933.                 // Copy the formatted string and hook it into the
  1934.                 // pre-allocated (by MAPI) column
  1935.                 lstrcpy (pStr, szHeaderText);
  1936.                 pProps[RECIP_REPORT_TEXT].ulPropTag = PR_REPORT_TEXT;
  1937.                 pProps[RECIP_REPORT_TEXT].Value.LPSZ = pStr;
  1938.             }
  1939.             else
  1940.             {
  1941.                 pProps[RECIP_REPORT_TEXT].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_REPORT_TEXT));
  1942.                 pProps[RECIP_REPORT_TEXT].Value.err = hResult;
  1943.                 TraceResult ("CXPLogon::SendMailMessage: memory allocation failed for the NDR report string", hResult);
  1944.             }
  1945.         }
  1946.         else
  1947.         {
  1948.             // For delivery report, each recipient must have this property set.
  1949.             // Otherwise the spooler will default to generate an NDR instead.
  1950.             pProps[RECIP_DELIVER_TIME].ulPropTag = PR_DELIVER_TIME;
  1951.             pProps[RECIP_DELIVER_TIME].Value.ft = ft;
  1952.  
  1953.             pProps[RECIP_REPORT_TEXT].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_REPORT_TEXT));
  1954.             pProps[RECIP_REPORT_TEXT].Value.err = S_OK;
  1955.         }
  1956.  
  1957.         // Based on the result of the submission to the remote host we determine into
  1958.         // which address list to add this recipient
  1959.         LPADRLIST * ppTmpList = (fSentSuccessfully ? &pAdrList : &pAdrListFailed);
  1960.         ULONG ulTmpCount = (fSentSuccessfully ? ulCount1 : ulCount2);
  1961.  
  1962.         // Does the list where this recipient goes have enough room for one more entry?
  1963.         // If not, resize the address list to hold QUERY_SIZE more entries.
  1964.         if (!(*ppTmpList) || ((*ppTmpList)->cEntries + 1 > ulTmpCount))
  1965.         {
  1966.             hResult= GrowAddressList (ppTmpList, 10, &ulTmpCount);
  1967.             if (hResult)
  1968.             {
  1969.                 goto ErrorExit;
  1970.             }
  1971.             ulCount1 = (fSentSuccessfully ? ulTmpCount : ulCount1);
  1972.             ulCount2 = (!fSentSuccessfully ? ulTmpCount : ulCount2);
  1973.         }
  1974.  
  1975.         // We have room now so store the new ADRENTRY. As part of the
  1976.         // storage, we're going to copy the SRow pointer from the SRowSet
  1977.         // into the ADRENTRY. Once we've done this, we won't need the
  1978.         // SRowSet any more ... and the SRow will be released when
  1979.         // we unwind the ADRLIST
  1980.         (*ppTmpList)->aEntries[(*ppTmpList)->cEntries].cValues = pRecipRows->aRow[ulRow].cValues;
  1981.         (*ppTmpList)->aEntries[(*ppTmpList)->cEntries].rgPropVals = pRecipRows->aRow[ulRow].lpProps;
  1982.  
  1983.         // Increase the number of entries in the address list
  1984.         (*ppTmpList)->cEntries++;
  1985.  
  1986.         // Now that we are finished with this row (it is in the right
  1987.         // adrlist) we want to disassociate it from the rowset
  1988.         // so we don't delete this before we modify the recipients list
  1989.         pRecipRows->aRow[ulRow].lpProps = NULL;
  1990.         pRecipRows->aRow[ulRow].cValues = 0;
  1991.  
  1992.     }
  1993.  
  1994.     // Let the MAPI spooler do other things
  1995.     CheckSpoolerYield();
  1996.  
  1997.     // Do we have some recipients that the message arrived to?
  1998.     if (pAdrList)
  1999.     {
  2000.         hResult = pMsgObj->ModifyRecipients (MODRECIP_MODIFY, pAdrList);
  2001.         TraceResult ("CXPLogon::SendMailMessage: ModifyRecipients failed (DELIVERED)", hResult);
  2002.         hResult = S_OK; // We'll drop the error code from the modify recipients call
  2003.         if (fNeedDeliveryReport)
  2004.         {
  2005.             hResult = m_pSupObj->StatusRecips (pMsgObj, pAdrList);
  2006.             TraceResult ("CXPLogon::SendMailMessage: StatusRecips (DR) failed", hResult);
  2007.             if (!HR_FAILED(hResult))
  2008.             {
  2009.                 // If we were successful, we should null out the pointer becase MAPI released
  2010.                 // the memory for this structure. And we should not try to release it
  2011.                 // again in the cleanup code.
  2012.                 pAdrList = NULL;
  2013.             }
  2014.         }
  2015.     }
  2016.     // Do we have some recipients that the message DID NOT arrived to?
  2017.     if (pAdrListFailed)
  2018.     {
  2019.         hResult = pMsgObj->ModifyRecipients (MODRECIP_MODIFY, pAdrListFailed);
  2020.         // We'll drop the error code from the modify recipients call
  2021.         TraceResult ("CXPLogon::SendMailMessage: ModifyRecipients failed (NON-DELIVERED)", hResult);
  2022.  
  2023.         // The address list has the entries with the PR_RESPONSIBILITY set, so the
  2024.         // spooler will know if it has to generate NDR reports.
  2025.         hResult = m_pSupObj->StatusRecips (pMsgObj, pAdrListFailed);
  2026.         TraceResult ("CXPLogon::SendMailMessage: StatusRecips (NDR) failed", hResult);
  2027.         if (!HR_FAILED(hResult))
  2028.         {
  2029.             // If we were successful, we should null out the pointer becase MAPI released
  2030.             // the memory for this structure. And we should not try to release it
  2031.             // again in the cleanup code.
  2032.             pAdrListFailed = NULL;
  2033.         }
  2034.     }
  2035.  
  2036. ErrorExit:
  2037.     gpfnFreeBuffer (pProblems);
  2038.     gpfnFreeBuffer (pMsgProps);
  2039.     FreePadrlist (pAdrList);
  2040.     FreePadrlist (pAdrListFailed);
  2041.  
  2042.     // Release used objects
  2043.     if (pTNEFObj)
  2044.     {
  2045.         pTNEFObj->Release();
  2046.     }
  2047.     if (pStream)
  2048.     {
  2049.         pStream->Release();
  2050.     }
  2051.     // Close the file if it has been opened
  2052.     CloseHandle (hMsgFile);
  2053.  
  2054.     // Delete the message file in case the function fails before the call to CreateFile() above
  2055.     DeleteFile (szTmpFile);
  2056.  
  2057.     // Call the sender function will NULL parameters to reset internal data buffers
  2058.     if (lstrlen(szConnectInfo))
  2059.     {
  2060.         FinishUploadConnection (m_szServer, szConnectInfo);
  2061.     }
  2062.  
  2063.     // Once we have sent all messages to the remote server(s), terminate all connections
  2064.     TerminateRemoteConnections();
  2065.  
  2066.     // Let the MAPI spooler do other things
  2067.     CheckSpoolerYield();
  2068.  
  2069.     return hResult;
  2070. }
  2071.  
  2072. ///////////////////////////////////////////////////////////////////////////////
  2073. //    CXPLogon::SetOutgoingProps()
  2074. //
  2075. //    Parameters
  2076. //      pMsgObj     Pointer to the IMessage object we are processing.
  2077. //      ft          FILETIME structure with the time the message is
  2078. //                  being delivered.
  2079. //
  2080. //    Purpose
  2081. //      This function checks that the sender information is on the message
  2082. //      so that we may get replies from the intended recipients.
  2083. //
  2084. //    Return Value
  2085. //      None.
  2086. //
  2087. void WINAPI CXPLogon::SetOutgoingProps (LPMESSAGE pMsgObj, FILETIME ft)
  2088. {
  2089.     LPSPropValue pSender;
  2090.     ULONG ulValues;
  2091.     HRESULT hResult = pMsgObj->GetProps ((LPSPropTagArray)&sptOutMsgProps, fMapiUnicode, &ulValues, &pSender);
  2092.     if (FAILED(hResult))
  2093.     {
  2094.         TraceResult ("CXPLogon::SetOutgoingProps: Failed to get properties from the message", hResult);
  2095.         pSender = NULL; // So that we may recover and continue using default values
  2096.     }
  2097.     ASSERT (2 == ulValues);
  2098.     #define NUM_OUTGOING_PROPS  11
  2099.     SPropValue spvProps[NUM_OUTGOING_PROPS] = { 0 };
  2100.     ULONG i = 0;
  2101.     // If no sender has been stamped on the message use the identity of the transport
  2102.     if (!pSender || PR_SENDER_ENTRYID != pSender[0].ulPropTag)
  2103.     {
  2104.         spvProps[i].ulPropTag = PR_SENDER_NAME;
  2105.         spvProps[i++].Value.LPSZ = m_UserInfo.szFullName;
  2106.  
  2107.         spvProps[i].ulPropTag = PR_SENDER_EMAIL_ADDRESS;
  2108.         spvProps[i++].Value.LPSZ = m_szAddress;
  2109.  
  2110.         spvProps[i].ulPropTag = PR_SENDER_ADDRTYPE;
  2111.         spvProps[i++].Value.LPSZ = WINDS_ADDRESS_TYPE;
  2112.  
  2113.         spvProps[i].ulPropTag = PR_SENDER_ENTRYID;
  2114.         spvProps[i++].Value.bin = m_pIdentityProps[XPID_EID].Value.bin;
  2115.  
  2116.         spvProps[i].ulPropTag = PR_SENDER_SEARCH_KEY;
  2117.         spvProps[i++].Value.bin = m_pIdentityProps[XPID_SEARCH_KEY].Value.bin;
  2118.     }
  2119.     // The MS Exchange mail viewer requires these properties
  2120.     if (!pSender || PR_SENT_REPRESENTING_NAME != pSender[1].ulPropTag)
  2121.     {
  2122.         spvProps[i].ulPropTag = PR_SENT_REPRESENTING_NAME;
  2123.         spvProps[i++].Value.LPSZ = m_UserInfo.szFullName;
  2124.         spvProps[i].ulPropTag = PR_SENT_REPRESENTING_SEARCH_KEY;
  2125.         spvProps[i++].Value.bin = m_pIdentityProps[XPID_SEARCH_KEY].Value.bin;
  2126.         spvProps[i].ulPropTag = PR_SENT_REPRESENTING_ENTRYID;
  2127.         spvProps[i++].Value.bin = m_pIdentityProps[XPID_EID].Value.bin;
  2128.         spvProps[i].ulPropTag = PR_SENT_REPRESENTING_ADDRTYPE;
  2129.         spvProps[i++].Value.LPSZ = WINDS_ADDRESS_TYPE;
  2130.         spvProps[i].ulPropTag = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
  2131.         spvProps[i++].Value.LPSZ = m_szAddress;
  2132.     }
  2133.     gpfnFreeBuffer (pSender);
  2134.  
  2135.     // Set the time when this transport actually transmitted the message
  2136.     spvProps[i].ulPropTag = PR_MESSAGE_DELIVERY_TIME;
  2137.     spvProps[i++].Value.ft = ft;
  2138.     spvProps[i].ulPropTag = PR_PROVIDER_SUBMIT_TIME;
  2139.     spvProps[i++].Value.ft = ft;
  2140.  
  2141.     ASSERT (i <= NUM_OUTGOING_PROPS);
  2142.     hResult = pMsgObj->SetProps (i, spvProps, NULL);
  2143.     TraceResult ("CXPLogon::SetOutgoingProps: Failed to set properties in the message", hResult);
  2144. }
  2145.  
  2146. ///////////////////////////////////////////////////////////////////////////////
  2147. //    CXPLogon::SetIncomingProps()
  2148. //
  2149. //    Parameters
  2150. //      pMsgObj     Pointer to the IMessage object we are processing.
  2151. //
  2152. //    Purpose
  2153. //      This function sets the PR_SENT_REPRESENTING_xxx properties in an
  2154. //      incoming message if those properties aren't already in the message.
  2155. //      For these properties we use the values for the sender of the message.
  2156. //      We also walk the recipient list restricted to the address type of our
  2157. //      transport looking for us, the recipient of the message, s that we may
  2158. //      set the PR_MESSAGE_TO_xxx and PR_MESSAGE_RECIP_ME properties in
  2159. //      the message.
  2160. //
  2161. //    Return Value
  2162. //      An HRESULT
  2163. //
  2164. HRESULT WINAPI CXPLogon::SetIncomingProps (LPMESSAGE pMsgObj, PLIST_NODE pNode)
  2165. {
  2166.     // This is the maximum number of properties that we could set in the message
  2167.     #define NUM_INCOMING_PROPS      28
  2168.     SPropValue spvProps[NUM_INCOMING_PROPS] =  { 0 };
  2169.     ULONG ulValues, i = 0;
  2170.     BOOL fNameIsMissing = FALSE, fAddressIsMissing = FALSE;
  2171.     TCHAR szUnknown[] = TEXT("Unknown"), szAddress[128];
  2172.     TCHAR szUnknownAddress[] = TEXT("\\\\Unknown\\Unknown"), szSender[MAX_STRING_SIZE+1];
  2173.     LPTSTR pszAlias, pszServer;
  2174.     LPSPropValue pProps = NULL, pSenderSKEY = NULL, pSenderEID = NULL, pObjProps = NULL;
  2175.     LPVOID lpToBeFreed = NULL;
  2176.  
  2177.     HRESULT hResult = pMsgObj->GetProps ((LPSPropTagArray)&sptNewMsgProps, fMapiUnicode, &ulValues, &pProps);
  2178.     if (FAILED(hResult))
  2179.     {
  2180.         TraceResult ("CXPLogon::SetIncomingProps: Failed to get the message changes", hResult);
  2181.         goto RecoverAndContinue;
  2182.     }
  2183.     hResult = S_OK; // Get rid of any warnings
  2184.     // These properties should ALWAYS be in an incoming message, but just in case, we have a backup plan.
  2185.     if ((PR_SENDER_NAME != pProps[NEW_SENDER_NAME].ulPropTag ||
  2186.         PR_SENDER_EMAIL_ADDRESS != pProps[NEW_SENDER_EMAIL].ulPropTag) && !pNode->fRetry)
  2187.     {
  2188.         pNode->fRetry = TRUE;
  2189.         m_List.ReQueueNode (pNode);
  2190.         gpfnFreeBuffer (pProps);
  2191.         return MAPI_E_CORRUPT_DATA;
  2192.     }
  2193.  
  2194.     if (PR_SENDER_NAME != pProps[NEW_SENDER_NAME].ulPropTag)
  2195.     {
  2196.         fNameIsMissing = TRUE;
  2197.         pProps[NEW_SENDER_NAME].ulPropTag = PR_SENDER_NAME;
  2198.         if (PR_SENDER_EMAIL_ADDRESS == pProps[NEW_SENDER_EMAIL].ulPropTag)
  2199.         {
  2200.             // Decompose the address and get the alias. At least this is better that UNKNOWN!
  2201.             lstrcpy (szAddress, pProps[NEW_SENDER_EMAIL].Value.LPSZ);
  2202.             DecomposeAddress (szAddress, &pszServer, &pszAlias);
  2203.             lstrcpy (szSender, pszAlias);
  2204.             pProps[NEW_SENDER_NAME].Value.LPSZ = szSender;
  2205.         }
  2206.         else
  2207.         {
  2208.             // This is the best we can do.
  2209.             pProps[NEW_SENDER_NAME].Value.LPSZ = szUnknown;
  2210.         }
  2211.     }
  2212.     if (PR_SENDER_EMAIL_ADDRESS != pProps[NEW_SENDER_EMAIL].ulPropTag)
  2213.     {
  2214.         pProps[NEW_SENDER_EMAIL].ulPropTag = PR_SENDER_EMAIL_ADDRESS;
  2215.         pProps[NEW_SENDER_EMAIL].Value.LPSZ = szUnknownAddress;
  2216.         fAddressIsMissing = TRUE;
  2217.     }
  2218.     spvProps[i].ulPropTag = PR_SENDER_ADDRTYPE;
  2219.     spvProps[i++].Value.LPSZ = WINDS_ADDRESS_TYPE;
  2220.     spvProps[i].ulPropTag = PR_SENT_REPRESENTING_ADDRTYPE;
  2221.     spvProps[i++].Value.LPSZ = WINDS_ADDRESS_TYPE;
  2222.  
  2223.     // Looks like ABWDS is around, mark the internal flag.
  2224.     // ABWDS is the native WINDS address book service provider. If present,
  2225.     // instead of creating a one-off entry for the sender, we
  2226.     // will simply open the entry in the WINDS address book and get the
  2227.     // necessary properties out from it.
  2228.     // Note that in other to open an entry in the ABWDS address book provider
  2229.     // we need to have at least the email address of the sender, otherwise we
  2230.     // will default to create one-off entries in the sender (and delagate sender)
  2231.     if (m_fABWDSInstalled && !fAddressIsMissing)
  2232.     {
  2233.         PRIVATE_XP_ENTRYID eidEntry = { 0 };
  2234.         eidEntry.uidGlobal = guidXPABEntries;
  2235.         eidEntry.uidWINDSEntries = guidABEntries;
  2236.         // Decompose the address and get the alias.
  2237.         DecomposeAddress (pProps[NEW_SENDER_EMAIL].Value.LPSZ, &pszServer, &pszAlias);
  2238.         lstrcpy (eidEntry.szObjectAlias, pszAlias);
  2239.         // Put the Email address back to what it was
  2240.         RecomposeAddress (pszServer, pszAlias, pProps[NEW_SENDER_EMAIL].Value.LPSZ);
  2241.         LPMAILUSER pUser = NULL;
  2242.         ULONG ulObjType;
  2243.         const static SizedSPropTagArray (3, sptWINDSObjProps) =
  2244.         {
  2245.             3,
  2246.             {
  2247.                 PR_DISPLAY_NAME,
  2248.                 PR_SEARCH_KEY,
  2249.                 PR_ENTRYID
  2250.             }
  2251.         };
  2252.  
  2253.         HRESULT hTmpResult = m_pSupObj->OpenEntry (CB_PRIVATE_XP_EID,
  2254.                                                    (LPENTRYID)&eidEntry,
  2255.                                                    NULL,
  2256.                                                    0,
  2257.                                                    &ulObjType,
  2258.                                                    (LPUNKNOWN *)&pUser);
  2259.         if (!hTmpResult)
  2260.         {
  2261.             hTmpResult = pUser->GetProps ((LPSPropTagArray)&sptWINDSObjProps,
  2262.                                           fMapiUnicode,
  2263.                                           &ulValues,
  2264.                                           &pObjProps);
  2265.             if (SUCCEEDED(hTmpResult)) // We might get warnings
  2266.             {
  2267.                 if (PR_DISPLAY_NAME == pObjProps[0].ulPropTag &&
  2268.                     PR_SEARCH_KEY == pObjProps[1].ulPropTag &&
  2269.                     PR_ENTRYID == pObjProps[2].ulPropTag)
  2270.                 {
  2271.                     if (fNameIsMissing)
  2272.                     {
  2273.                         spvProps[i].ulPropTag = PR_SENDER_NAME;
  2274.                         spvProps[i++].Value.LPSZ = pObjProps[0].Value.LPSZ;
  2275.                     }
  2276.                     spvProps[i].ulPropTag = PR_SENDER_SEARCH_KEY;
  2277.                     spvProps[i++].Value.bin = pObjProps[1].Value.bin;
  2278.                     spvProps[i].ulPropTag = PR_ENTRYID;
  2279.                     spvProps[i++].Value.bin = pObjProps[2].Value.bin;
  2280.                     if (PR_SENT_REPRESENTING_NAME != pProps[NEW_SENT_NAME].ulPropTag &&
  2281.                         PR_SENT_REPRESENTING_EMAIL_ADDRESS != pProps[NEW_SENT_EMAIL].ulPropTag)
  2282.                     {
  2283.                         // If the message doesn't have PR_SENT_REPRESENTING_xxx use the sender
  2284.                         // properties for the delagate sender properties
  2285.                         spvProps[i].ulPropTag    = PR_SENT_REPRESENTING_NAME;
  2286.                         spvProps[i++].Value.LPSZ = pObjProps[0].Value.LPSZ;
  2287.                         spvProps[i].ulPropTag    = PR_SENT_REPRESENTING_SEARCH_KEY;
  2288.                         spvProps[i++].Value.bin  = pObjProps[1].Value.bin;
  2289.                         spvProps[i].ulPropTag    = PR_SENT_REPRESENTING_ENTRYID;
  2290.                         spvProps[i++].Value.bin  = pObjProps[2].Value.bin;
  2291.                         spvProps[i].ulPropTag    = PR_SENT_REPRESENTING_ADDRTYPE;
  2292.                         spvProps[i++].Value.LPSZ = WINDS_ADDRESS_TYPE;
  2293.                         spvProps[i].ulPropTag    = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
  2294.                         spvProps[i++].Value.LPSZ = pProps[NEW_SENDER_EMAIL].Value.LPSZ;
  2295.                     }
  2296.                 }
  2297.                 else
  2298.                 {
  2299.                     // We couldn't find the properties needed from the WINDS
  2300.                     // address book entry. Continue with one-off entries
  2301.                     hTmpResult = MAPI_W_ERRORS_RETURNED;
  2302.                 }
  2303.             }
  2304.             pUser->Release();
  2305.         }
  2306.         if (S_OK == hTmpResult)
  2307.         {
  2308.             goto ContinueAddingProps;
  2309.         }
  2310.     }
  2311.  
  2312.     // We need to make a search key for this recipient.
  2313.     spvProps[i].ulPropTag = PR_SENDER_SEARCH_KEY;
  2314.     hResult = MakeSearchKey (pProps,
  2315.                              pProps[NEW_SENDER_EMAIL].Value.LPSZ,
  2316.                              &spvProps[i].Value.bin.cb,
  2317.                              &spvProps[i].Value.bin.lpb);
  2318.     if (!hResult)
  2319.     {
  2320.         pSenderSKEY = &spvProps[i];
  2321.         i++;
  2322.         // We need to create a one-off entry in the address book so that we may
  2323.         // reply to this recipient. If the recipient already exist, its
  2324.         // entry Id will be returned instead.
  2325.         spvProps[i].ulPropTag = PR_SENDER_ENTRYID;
  2326.  
  2327.         //else
  2328.         {
  2329.             hResult = m_pSupObj->CreateOneOff (pProps[NEW_SENDER_NAME].Value.LPSZ,
  2330.                                                WINDS_ADDRESS_TYPE,
  2331.                                                pProps[NEW_SENDER_EMAIL].Value.LPSZ,
  2332.                                                fMapiUnicode,
  2333.                                                &spvProps[i].Value.bin.cb,
  2334.                                                (LPENTRYID *)&spvProps[i].Value.bin.lpb);
  2335.         }
  2336.         if (hResult)
  2337.         {
  2338.             i--; // Ignore this property and the previous
  2339.         }
  2340.         else
  2341.         {
  2342.             pSenderEID = &spvProps[i];
  2343.             i++;
  2344.         }
  2345.     }
  2346.     TraceResult ("CXPLogon::SetIncomingProps: Failed to create a one-off for the sender", hResult);
  2347.  
  2348.     if (PR_SENT_REPRESENTING_NAME == pProps[NEW_SENT_NAME].ulPropTag &&
  2349.         PR_SENT_REPRESENTING_EMAIL_ADDRESS == pProps[NEW_SENT_EMAIL].ulPropTag)
  2350.     {
  2351.         spvProps[i].ulPropTag = PR_SENT_REPRESENTING_SEARCH_KEY;
  2352.         hResult = MakeSearchKey (pProps,
  2353.                                  pProps[NEW_SENT_EMAIL].Value.LPSZ,
  2354.                                  &spvProps[i].Value.bin.cb,
  2355.                                  &spvProps[i].Value.bin.lpb);
  2356.         if (!hResult)
  2357.         {
  2358.             i++;
  2359.             spvProps[i].ulPropTag = PR_SENT_REPRESENTING_ENTRYID;
  2360.             hResult = m_pSupObj->CreateOneOff (pProps[NEW_SENT_NAME].Value.LPSZ,
  2361.                                                WINDS_ADDRESS_TYPE,
  2362.                                                pProps[NEW_SENT_EMAIL].Value.LPSZ,
  2363.                                                fMapiUnicode,
  2364.                                                &spvProps[i].Value.bin.cb,
  2365.                                                (LPENTRYID *)&spvProps[i].Value.bin.lpb);
  2366.             if (hResult)
  2367.             {
  2368.                 i--; // Ignore this property and the previous
  2369.             }
  2370.             else
  2371.             {
  2372.                 lpToBeFreed = spvProps[i].Value.bin.lpb;
  2373.                 i++;
  2374.             }
  2375.         }
  2376.         // We trace the error but is not fatal
  2377.         TraceResult ("CXPLogon::SetIncomingProps: Failed to create a oneoff for the delegated sender", hResult);
  2378.     }
  2379.     else
  2380.     {
  2381.         // If the message doesn't have PR_SENT_REPRESENTING_xxx properties AND we were
  2382.         // successful at creating the one-off for the sender, we set PR_SENT_REPRESENTING_xxx
  2383.         // to the same values as PR_SENDER_xxx
  2384.         if (!hResult)
  2385.         {
  2386.             spvProps[i].ulPropTag       = PR_SENT_REPRESENTING_NAME;
  2387.             spvProps[i++].Value.LPSZ    = pProps[NEW_SENDER_NAME].Value.LPSZ;
  2388.             spvProps[i].ulPropTag       = PR_SENT_REPRESENTING_ADDRTYPE;
  2389.             spvProps[i++].Value.LPSZ    = WINDS_ADDRESS_TYPE;
  2390.             spvProps[i].ulPropTag       = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
  2391.             spvProps[i++].Value.LPSZ    = pProps[NEW_SENDER_EMAIL].Value.LPSZ;
  2392.  
  2393.             ASSERT (PR_SENDER_ENTRYID == pSenderEID->ulPropTag);
  2394.             spvProps[i].ulPropTag       = PR_SENT_REPRESENTING_ENTRYID;
  2395.             spvProps[i++].Value         = pSenderEID->Value;
  2396.  
  2397.             ASSERT (PR_SENDER_SEARCH_KEY == pSenderSKEY->ulPropTag);
  2398.             spvProps[i].ulPropTag       = PR_SENT_REPRESENTING_SEARCH_KEY;
  2399.             spvProps[i++].Value         = pSenderSKEY->Value;
  2400.         }
  2401.     }
  2402.  
  2403. ContinueAddingProps:
  2404.     // If the sender's address is the same as our address (the recipient), then the message
  2405.     // was sent from us, to ourselves, therefore we must set the flag bit in the message.
  2406.     if (0 == lstrcmpi (pProps[NEW_SENDER_EMAIL].Value.LPSZ, m_szAddress))
  2407.     {
  2408.         ASSERT (PR_MESSAGE_FLAGS == pProps[NEW_MSG_FLAGS].ulPropTag);
  2409.         spvProps[i].ulPropTag = PR_MESSAGE_FLAGS;
  2410.         spvProps[i++].Value.l = pProps[NEW_MSG_FLAGS].Value.l | MSGFLAG_FROMME;
  2411.     }
  2412.  
  2413. RecoverAndContinue:
  2414.     // We must stamp the message with this properties since we (this transport)
  2415.     // were the ones who processed the incoming message.
  2416.     spvProps[i].ulPropTag       = PR_RECEIVED_BY_ENTRYID;
  2417.     spvProps[i++].Value.bin     = m_pIdentityProps[XPID_EID].Value.bin;
  2418.     spvProps[i].ulPropTag       = PR_RECEIVED_BY_NAME;
  2419.     spvProps[i++].Value.LPSZ    = m_pIdentityProps[XPID_NAME].Value.LPSZ;
  2420.     spvProps[i].ulPropTag       = PR_RECEIVED_BY_SEARCH_KEY;
  2421.     spvProps[i++].Value.bin     = m_pIdentityProps[XPID_SEARCH_KEY].Value.bin;
  2422.     spvProps[i].ulPropTag       = PR_RECEIVED_BY_ADDRTYPE;
  2423.     spvProps[i++].Value.LPSZ    = WINDS_ADDRESS_TYPE;
  2424.     spvProps[i].ulPropTag       = PR_RECEIVED_BY_EMAIL_ADDRESS;
  2425.     spvProps[i++].Value.LPSZ    = m_szAddress;
  2426.  
  2427.     // In this transport we set PR_RCVD_xxx with the values of PR_RECEIVED_xxx
  2428.     spvProps[i].ulPropTag       = PR_RCVD_REPRESENTING_ENTRYID;
  2429.     spvProps[i++].Value.bin     = m_pIdentityProps[XPID_EID].Value.bin;
  2430.     spvProps[i].ulPropTag       = PR_RCVD_REPRESENTING_NAME;
  2431.     spvProps[i++].Value.LPSZ    = m_pIdentityProps[XPID_NAME].Value.LPSZ;
  2432.     spvProps[i].ulPropTag       = PR_RCVD_REPRESENTING_SEARCH_KEY;
  2433.     spvProps[i++].Value.bin     = m_pIdentityProps[XPID_SEARCH_KEY].Value.bin;
  2434.     spvProps[i].ulPropTag       = PR_RCVD_REPRESENTING_ADDRTYPE;
  2435.     spvProps[i++].Value.LPSZ    = WINDS_ADDRESS_TYPE;
  2436.     spvProps[i].ulPropTag       = PR_RCVD_REPRESENTING_EMAIL_ADDRESS;
  2437.     spvProps[i++].Value.LPSZ    = m_szAddress;
  2438.  
  2439.     SYSTEMTIME st;
  2440.     FILETIME ftTime;
  2441.     GetSystemTime (&st);
  2442.     SystemTimeToFileTime (&st, &ftTime);
  2443.     spvProps[i].ulPropTag = PR_MESSAGE_DOWNLOAD_TIME;
  2444.     spvProps[i++].Value.ft = ftTime;
  2445.  
  2446.     LPMAPITABLE pTable;
  2447.     LPSRowSet pRows;
  2448.     BOOL fFoundCC, fFoundTO;
  2449.     fFoundCC = fFoundTO = FALSE;
  2450.     LPSPropValue pRecip;
  2451.     SPropValue spvFilter = { 0 };
  2452.     spvFilter.ulPropTag = PR_ADDRTYPE;
  2453.     spvFilter.Value.LPSZ = WINDS_ADDRESS_TYPE;
  2454.  
  2455.     SRestriction srRecips = { 0 };
  2456.     srRecips.rt = RES_PROPERTY;
  2457.     srRecips.res.resProperty.relop = RELOP_EQ;
  2458.     srRecips.res.resProperty.ulPropTag = PR_ADDRTYPE;
  2459.     srRecips.res.resProperty.lpProp = &spvFilter;
  2460.  
  2461.     hResult = pMsgObj->GetRecipientTable (fMapiUnicode, &pTable);
  2462.     if (!hResult)
  2463.     {
  2464.         hResult = HrQueryAllRows (pTable,
  2465.                                   (LPSPropTagArray)&sptMsgRecipProps,
  2466.                                   &srRecips,
  2467.                                   NULL,
  2468.                                   0,
  2469.                                   &pRows);
  2470.         if (!hResult)
  2471.         {
  2472.             ASSERTMSG (pRows->cRows > 0, "Huh?, Zero rows?");
  2473.             for (ULONG ulIndex=0; ulIndex<pRows->cRows; ulIndex++)
  2474.             {
  2475.                 pRecip = pRows->aRow[ulIndex].lpProps;
  2476.                 ASSERTMSG (PR_EMAIL_ADDRESS == pRecip[0].ulPropTag, "Where is the PR_EMAIL_ADDRESS?");
  2477.                 ASSERTMSG (PR_RECIPIENT_TYPE == pRecip[1].ulPropTag, , "Where is the PR_RECIPIENT_TYPE?");
  2478.                 if (PR_EMAIL_ADDRESS == pRecip[0].ulPropTag)
  2479.                 {
  2480.                     // We use the address for the comparison because the sender might
  2481.                     // have create a one-off to send us this message and the only thing that this
  2482.                     // transport can count on being the same across all computers
  2483.                     // is the email address. The display name is modifyable by the user.
  2484.                     if (0 == lstrcmpi (pRecip[0].Value.LPSZ, m_szAddress))
  2485.                     {
  2486.                         if (PR_RECIPIENT_TYPE == pRecip[1].ulPropTag)
  2487.                         {
  2488.                             if (MAPI_TO == pRecip[1].Value.l)
  2489.                             {
  2490.                                 fFoundTO = TRUE;
  2491.                             }
  2492.                             else
  2493.                             {
  2494.                                 if (MAPI_CC == pRecip[1].Value.l)
  2495.                                 {
  2496.                                     fFoundCC = TRUE;
  2497.                                 }
  2498.                             }
  2499.                         }
  2500.                     }
  2501.                 }
  2502.             }
  2503.             FreeProws (pRows);
  2504.         }
  2505.         pTable->Release();
  2506.     }
  2507.     TraceResult ("CXPLogon::SetIncomingProps: Something went wrong setting PR_MESSAGE_xxx", hResult);
  2508.  
  2509.     if (!hResult)
  2510.     {
  2511.         spvProps[i].ulPropTag = PR_MESSAGE_TO_ME;
  2512.         spvProps[i++].Value.b = fFoundTO;
  2513.  
  2514.         spvProps[i].ulPropTag = PR_MESSAGE_CC_ME;
  2515.         spvProps[i++].Value.b = fFoundCC;
  2516.  
  2517.         spvProps[i].ulPropTag = PR_MESSAGE_RECIP_ME;
  2518.         spvProps[i++].Value.b = fFoundTO || fFoundCC;
  2519.     }
  2520.     ASSERT (i <= NUM_INCOMING_PROPS);
  2521.     hResult = pMsgObj->SetProps (i, spvProps, NULL);
  2522.     TraceResult ("CXPLogon::SetIncomingProps: Failed to set properties in the message", hResult);
  2523.     gpfnFreeBuffer(lpToBeFreed);
  2524.     if(pSenderEID)
  2525.         gpfnFreeBuffer(pSenderEID->Value.bin.lpb);
  2526.     gpfnFreeBuffer (pProps);
  2527.     gpfnFreeBuffer (pObjProps);
  2528.     return S_OK;
  2529. }
  2530.  
  2531. ///////////////////////////////////////////////////////////////////////////////
  2532. //    CXPLogon::GetMsgTempFileName()
  2533. //
  2534. //    Parameters
  2535. //      pszFileName     Pointer to a buffer allocated by the caller where the
  2536. //                      function returns a fully qualified path and file for
  2537. //                      a uniquely named temporary file.
  2538. //
  2539. //    Purpose
  2540. //      This function creates a temporary file name. The file name will be
  2541. //      returned in the pszFileName buffer which must have been allocated by
  2542. //      the caller. The file will have a fully qualified path to its location.
  2543. //      The location of the file is on the TEMP directory, as set in the system,
  2544. //      and within it, in the directory for the downloads of the WINDS
  2545. //      message transport
  2546. //
  2547. //    Return Value
  2548. //      TRUE if the function is successful at creating a temporary
  2549. //      unique file name. FALSE otherwise.
  2550. //
  2551. BOOL WINAPI CXPLogon::GetMsgTempFileName (LPTSTR pszFileName)
  2552. {
  2553.     TCHAR szTmpDir[_MAX_PATH], szDownloadDir[_MAX_PATH];
  2554.     // Ask the system for the TEMP directory
  2555.     DWORD dwChars = GetTempPath (_MAX_PATH, szTmpDir);
  2556.     if (dwChars)
  2557.     {
  2558.         lstrcat (szTmpDir, WINDS_DATA_DIRECTORY);
  2559.         wsprintf (szDownloadDir, WINDS_DOWNLOAD_DIR_NAME_FORMAT, szTmpDir, m_UserInfo.szMailboxName);
  2560.         dwChars = ::GetTempFileName (szDownloadDir,         // Call the Win32 API
  2561.                                      XP_MSG_FILE_PREFIX,    // Our transport's fixed prefix for temp files
  2562.                                      0,                     // Use the time to create a pseudo-random number
  2563.                                      pszFileName);          // Destination buffer
  2564.     }
  2565.     if (!dwChars)
  2566.     {
  2567.         TraceResult ("CXPLogon::GetMsgTempFileName: Failed to get temp path or file name", GetLastError());
  2568.     }
  2569.     return (0 != dwChars ? TRUE : FALSE);
  2570. }
  2571.  
  2572. ///////////////////////////////////////////////////////////////////////////////
  2573. //    CXPLogon::CreateMsgHeaderTextLine()
  2574. //
  2575. //    Parameters
  2576. //      pProps              Array of properties of the message being
  2577. //                          submitted where we get the information to create a
  2578. //                          message header string to send to the server
  2579. //      pszBuffer           Buffer where the function copies the formated data
  2580. //                          for the server message header string
  2581. //      time                Reference to a FILETIME structure with the time
  2582. //                          the transport is delivering the message.
  2583. //
  2584. //    Purpose
  2585. //      This function creates a string with the data needed in the server
  2586. //      side to update the remote server mailboxes message header file of
  2587. //      the recipients.
  2588. //
  2589. //    Return Value
  2590. //      None
  2591. //
  2592. void WINAPI CXPLogon::CreateMsgHeaderTextLine (LPSPropValue     pProps,
  2593.                                                LPTSTR           pszBuffer,
  2594.                                                FILETIME &       time)
  2595. {
  2596.     // In case we don't find the properties in the array, we need
  2597.     // to supply default values for the strings. This method
  2598.     // should never fail.
  2599.     LPTSTR pszSubject = TEXT("<Subject Not Found>");
  2600.     LPTSTR pszMsgClass = TEXT("IPM.Note");
  2601.     TCHAR szDisplayTo[256] = { 0 };
  2602.  
  2603.     // In case we don't have the expected properties, assign default values.
  2604.     long lMsgFlags = 0;
  2605.     if (PR_MESSAGE_FLAGS == pProps[MSG_FLAGS].ulPropTag)
  2606.     {
  2607.         // Filter out the flags to only allow the ones listed
  2608.         lMsgFlags = pProps[MSG_FLAGS].Value.l & (MSGFLAG_UNMODIFIED |
  2609.                                                  MSGFLAG_HASATTACH |
  2610.                                                  MSGFLAG_FROMME |
  2611.                                                  MSGFLAG_RESEND);
  2612.     }
  2613.     if (pProps[MSG_SIZE].ulPropTag != PR_MESSAGE_SIZE)
  2614.     {
  2615.         pProps[MSG_SIZE].Value.l = 1024; // Default to 1Kb
  2616.     }
  2617.     if (pProps[MSG_PRIORITY].ulPropTag != PR_PRIORITY)
  2618.     {
  2619.         pProps[MSG_PRIORITY].Value.l = PRIO_NORMAL;
  2620.     }
  2621.     if (pProps[MSG_IMPORTANCE].ulPropTag != PR_IMPORTANCE)
  2622.     {
  2623.         pProps[MSG_IMPORTANCE].Value.l = IMPORTANCE_NORMAL;
  2624.     }
  2625.     if (pProps[MSG_SENSITIVITY].ulPropTag != PR_SENSITIVITY)
  2626.     {
  2627.         pProps[MSG_SENSITIVITY].Value.l = SENSITIVITY_NONE;
  2628.     }
  2629.  
  2630.     // Get the string of TO recipients. If the string is too long, set the elipsis at the end of the buffer
  2631.     if (pProps[MSG_DISP_TO].ulPropTag == PR_DISPLAY_TO)
  2632.     {
  2633.         LPTSTR pszDisplayTo = pProps[MSG_DISP_TO].Value.LPSZ;
  2634.         if (lstrlen (pszDisplayTo) > 256)
  2635.         {
  2636.             pszDisplayTo[252] = '.'; // Copy the elipsis for a long string
  2637.             pszDisplayTo[253] = '.';
  2638.             pszDisplayTo[254] = '.';
  2639.             pszDisplayTo[255] = 0;
  2640.         }
  2641.         LPTSTR pszOneName = strtok (pszDisplayTo, ";");
  2642.         if (pszOneName)
  2643.         {
  2644.             pszOneName[lstrlen(pszOneName) - 1] = 0;
  2645.             lstrcpy (szDisplayTo, &pszOneName[1]);
  2646.         }
  2647.         while (pszOneName)
  2648.         {
  2649.             pszOneName = strtok (NULL, ";");
  2650.             if (pszOneName)
  2651.             {
  2652.                 pszOneName[0] = TEXT(';');
  2653.                 pszOneName[1] = TEXT(' ');
  2654.                 pszOneName[lstrlen(pszOneName) - 1] = 0;
  2655.                 lstrcat (szDisplayTo, pszOneName);
  2656.             }
  2657.         }
  2658.     }
  2659.     if (0 == szDisplayTo[0])
  2660.     {
  2661.         lstrcpy (szDisplayTo, TEXT("<Display To Names Not Found>"));
  2662.     }
  2663.     // Get the message class string.
  2664.     if (pProps[MSG_CLASS].ulPropTag == PR_MESSAGE_CLASS)
  2665.     {
  2666.         pszMsgClass = pProps[MSG_CLASS].Value.LPSZ;
  2667.     }
  2668.     // Get the subject. If the string is too long, set the elipsis at the end of the buffer
  2669.     if (pProps[MSG_SUBJECT].ulPropTag == PR_SUBJECT)
  2670.     {
  2671.         pszSubject = pProps[MSG_SUBJECT].Value.LPSZ;
  2672.         if (lstrlen (pszSubject) > 256)
  2673.         {
  2674.             pszSubject[252] = '.'; // Copy the elipsis for a long string
  2675.             pszSubject[253] = '.';
  2676.             pszSubject[254] = '.';
  2677.             pszSubject[255] = 0;
  2678.         }
  2679.     }
  2680.     // Write the properties into the buffer. This buffer is the header
  2681.     // information we send to the server The buffer passed in must hold
  2682.     // at least 1024 characters
  2683.     wsprintf (pszBuffer,
  2684.               // The string must end with a ',' (for the parsing logic
  2685.               // in the downloading of the headers)
  2686.               TEXT("%d,%s,%d,%s,%d,%s,%s,%d,%d,%d,%d,%d,%d,%d,"),
  2687.               lstrlen(m_UserInfo.szFullName),
  2688.               m_UserInfo.szFullName,
  2689.               lstrlen(szDisplayTo),
  2690.               szDisplayTo,
  2691.               lstrlen(pszSubject),
  2692.               pszSubject,
  2693.               pszMsgClass,
  2694.               lMsgFlags,
  2695.               pProps[MSG_SIZE].Value.l,
  2696.               pProps[MSG_PRIORITY].Value.l,
  2697.               pProps[MSG_IMPORTANCE].Value.l,
  2698.               pProps[MSG_SENSITIVITY].Value.l,
  2699.               time.dwLowDateTime,
  2700.               time.dwHighDateTime);
  2701. }
  2702.  
  2703. ///////////////////////////////////////////////////////////////////////////////
  2704. //    CXPLogon::IsValidAddress()
  2705. //
  2706. //    Parameters
  2707. //      pszAddress      The address of the recipient. This string gets
  2708. //                      decomposed locally, so it can't be re-used by
  2709. //                      the caller.
  2710. //      ppszServer      Pointer to a location where the function returns
  2711. //                      the remote server name
  2712. //      ppszMailbox     Pointer to a location where the function returns
  2713. //                      the remote user mailbox name
  2714. //
  2715. //    Purpose
  2716. //      This functions takes the address of a recipient and makes sure it is
  2717. //      valid (as far as syntax) and decomposes the address into two
  2718. //      components: Remote server name and Remote user mailbox name. The
  2719. //      two component strings are returned to the caller.
  2720. //
  2721. //    Return Value
  2722. //      TRUE if the address of the recipient is valid. FALSE otherwise.
  2723. //
  2724. BOOL WINAPI CXPLogon::IsValidAddress (LPTSTR        pszAddress,
  2725.                                       LPTSTR *      ppszServer,
  2726.                                       LPTSTR *      ppszMailbox)
  2727. {
  2728.     // Make sure that we can read the string
  2729.     if (!pszAddress || IsBadStringPtr (pszAddress, 32))
  2730.     {
  2731.         TraceMessage ("CXPLogon::IsValidAddress: Invalid string pointer");
  2732.         return FALSE;
  2733.     }
  2734.     DecomposeAddress (pszAddress, ppszServer, ppszMailbox);
  2735.     // Validate the names of the server and the mailbox
  2736.     if (!IsValidServerName (*ppszServer) || !*ppszMailbox)
  2737.     {
  2738.         TraceMessage ("CXPLogon::IsValidAddress: Invalid address for remote server host");
  2739.         return FALSE;
  2740.     }
  2741.     return TRUE;
  2742. }
  2743.  
  2744. ///////////////////////////////////////////////////////////////////////////////
  2745. //    CXPLogon::GrowAddressList()
  2746. //
  2747. //    Parameters
  2748. //      ppAdrList           Pointer to an address where the old address list
  2749. //                          is and where the new resized address list will
  2750. //                          be returned
  2751. //      ulResizeBy          Number of new address entries to add to the list
  2752. //      pulOldAndNewCount   Number of entries in the old address list. In
  2753. //                          this parameter, upon sucessful return, will have
  2754. //                          the number of in the new address list
  2755. //
  2756. //    Purpose
  2757. //      In this function, given an address list with pulOldAndNewCount of
  2758. //      entries, we resize the address list to hold the old number of
  2759. //      entries plus the ulResizeBy entries. The old address list contents
  2760. //      are copied to the new list and the count reset. The memory for the
  2761. //      old address list is released here.
  2762. //
  2763. //    Return Value
  2764. //      An HRESULT
  2765. //
  2766. STDMETHODIMP CXPLogon::GrowAddressList (LPADRLIST *   ppAdrList,
  2767.                                         ULONG         ulResizeBy,
  2768.                                         ULONG *       pulOldAndNewCount)
  2769. {
  2770.     LPADRLIST pNewAdrList;
  2771.     // Calculate how big the new buffer for the expanded address list should be
  2772.     ULONG cbSize = CbNewADRLIST ((*pulOldAndNewCount) + ulResizeBy);
  2773.     // Allocate the memory for it
  2774.     HRESULT hResult = gpfnAllocateBuffer (cbSize, (LPVOID *)&pNewAdrList);
  2775.     if (hResult)
  2776.     {
  2777.         // We can't continue
  2778.         TraceResult ("CXPLogon::GrowAddressList: Failed to allocate memory for resized address list", hResult);
  2779.         return hResult;
  2780.     }
  2781.  
  2782.     // Zero-out all memory for neatness
  2783.     ZeroMemory (pNewAdrList, cbSize);
  2784.  
  2785.     // If we had entries in the old address list, copy the memory from
  2786.     // the old addres list into the new expanded list
  2787.     if ((*pulOldAndNewCount))
  2788.     {
  2789.         CopyMemory (pNewAdrList, *ppAdrList, CbNewADRLIST ((*pulOldAndNewCount)));
  2790.     }
  2791.  
  2792.     // Set the number of entries in the new address list to the OLD size
  2793.     pNewAdrList->cEntries = (*pulOldAndNewCount);
  2794.  
  2795.     // We must return the number of available entries in the new expanded address list
  2796.     (*pulOldAndNewCount) += ulResizeBy;
  2797.  
  2798.     // Free the old memory and put the new pointer in place
  2799.     gpfnFreeBuffer (*ppAdrList);
  2800.     *ppAdrList = pNewAdrList;
  2801.     return hResult;
  2802. }
  2803.  
  2804. ///////////////////////////////////////////////////////////////////////////////
  2805. //    CXPLogon::CheckSpoolerYield()
  2806. //
  2807. //    Parameters
  2808. //      fReset      Resets the pseudo-timer
  2809. //
  2810. //    Purpose
  2811. //      Enforce the 0.2 second rule for transport that need to yield to the
  2812. //      MAPI spooler.  Called periodically while processing a message to
  2813. //      determine if we have used more than 0.2 seconds.  If so, then call
  2814. //      SpoolerYield(), else just continue.
  2815. //      This is called with fReset set to TRUE when we first enter one
  2816. //      of the Transport Logon methods (usually one that is known to
  2817. //      take a long time like StartMessage() or SubmitMessage(). )
  2818. //
  2819. //    Return Value
  2820. //      None.
  2821. //
  2822. void WINAPI CXPLogon::CheckSpoolerYield (BOOL fReset)
  2823. {
  2824.     DWORD dwStop;
  2825.     static DWORD dwStart;
  2826.     if (fReset)
  2827.     {
  2828.         dwStart = GetTickCount();
  2829.     }
  2830.     else
  2831.     {
  2832.         dwStop = GetTickCount();
  2833.         if ((dwStop - dwStart) > 200) // 200 milliseconds
  2834.         {
  2835.             m_pSupObj->SpoolerYield (0);
  2836.             dwStart = GetTickCount();
  2837.         }
  2838.     }
  2839. }
  2840.  
  2841. ///////////////////////////////////////////////////////////////////////////////
  2842. //    CXPLogon::InitializeStatusRow()
  2843. //
  2844. //    Parameters
  2845. //      ulFlags     0 if the properties are being created the first time.
  2846. //                  MODIFY_FLAGS if a change is being made to the properties
  2847. //
  2848. //    Purpose
  2849. //      To initialize or modify the status properties of a CXPLogon
  2850. //      object. This function allocates an array with NUM_STATUS_ROW_PROPS
  2851. //      properties and initializes them.
  2852. //
  2853. //    Return Value
  2854. //      An HRESULT
  2855. //
  2856. STDMETHODIMP CXPLogon::InitializeStatusRow (ULONG ulFlags)
  2857. {
  2858.     #define NUM_STATUS_ROW_PROPS    10
  2859.     SPropValue spvStatusRow[NUM_STATUS_ROW_PROPS] = { 0 };
  2860.     ULONG i = 0;
  2861.  
  2862.     ///////////////////////////////////////////////////////////////////////////
  2863.     // Set the PR_PROVIDER_DISPLAY property: The transport readable name
  2864.     spvStatusRow[i].ulPropTag = PR_PROVIDER_DISPLAY;
  2865.     spvStatusRow[i++].Value.LPSZ = TRANSPORT_DISPLAY_NAME_STRING;
  2866.  
  2867.     ///////////////////////////////////////////////////////////////////////////
  2868.     // Set the PR_RESOURCE_METHODS property. These are the methods implemented
  2869.     // in the our IMAPIStatus implementation (CMAPIStatus class.)
  2870.     spvStatusRow[i].ulPropTag = PR_RESOURCE_METHODS;
  2871.     // we support ALL the methods in our implementation of IMAPIStatus interface (except the WRITABLE ones)
  2872.     spvStatusRow[i++].Value.l = STATUS_SETTINGS_DIALOG |
  2873.                                 STATUS_FLUSH_QUEUES |
  2874.                                 STATUS_VALIDATE_STATE |
  2875.                                 STATUS_CHANGE_PASSWORD;
  2876.  
  2877.     ///////////////////////////////////////////////////////////////////////////
  2878.     // Set the PR_STATUS_CODE property.
  2879.     spvStatusRow[i].ulPropTag = PR_STATUS_CODE;
  2880.     spvStatusRow[i++].Value.l = GetTransportStatusCode();
  2881.  
  2882.     ///////////////////////////////////////////////////////////////////////////
  2883.     // Set the PR_STATUS_STRING property
  2884.     TCHAR szStatus[64];
  2885.     LoadStatusString (szStatus, sizeof(szStatus));
  2886.     spvStatusRow[i].ulPropTag = PR_STATUS_STRING;
  2887.     spvStatusRow[i++].Value.LPSZ = szStatus;
  2888.  
  2889.     ///////////////////////////////////////////////////////////////////////////
  2890.     // Set the PR_DISPLAY_NAME property
  2891.     TCHAR szDisplayName[64];
  2892.     wsprintf (szDisplayName, TEXT("%s (%s)"), TRANSPORT_DISPLAY_NAME_STRING, m_szServer);
  2893.     spvStatusRow[i].ulPropTag = PR_DISPLAY_NAME;
  2894.     spvStatusRow[i++].Value.LPSZ = szDisplayName;
  2895.  
  2896.     ///////////////////////////////////////////////////////////////////////////
  2897.     // Set the PR_IDENTITY_ENTRYID property
  2898.     spvStatusRow[i].ulPropTag = PR_IDENTITY_ENTRYID;
  2899.     spvStatusRow[i++].Value = m_pIdentityProps[XPID_EID].Value;
  2900.  
  2901.     ///////////////////////////////////////////////////////////////////////////
  2902.     // Set the PR_IDENTITY_DISPLAY property
  2903.     spvStatusRow[i].ulPropTag = PR_IDENTITY_DISPLAY;
  2904.     spvStatusRow[i++].Value.LPSZ = m_pIdentityProps[XPID_NAME].Value.LPSZ;
  2905.  
  2906.     ///////////////////////////////////////////////////////////////////////////
  2907.     // Set the PR_IDENTITY_SEARCH_KEY property
  2908.     spvStatusRow[i].ulPropTag = PR_IDENTITY_SEARCH_KEY;
  2909.     spvStatusRow[i++].Value = m_pIdentityProps[XPID_SEARCH_KEY].Value;
  2910.  
  2911.     ///////////////////////////////////////////////////////////////////////////
  2912.     // Set the PR_REMOTE_PROGRESS property
  2913.     spvStatusRow[i].ulPropTag = PR_REMOTE_PROGRESS;
  2914.     spvStatusRow[i++].Value.l = -1; // Not initialized
  2915.  
  2916.     ///////////////////////////////////////////////////////////////////////////
  2917.     // Set the PR_REMOTE_VALIDATE_OK property
  2918.     spvStatusRow[i].ulPropTag = PR_REMOTE_VALIDATE_OK;
  2919.     spvStatusRow[i++].Value.b = TRUE;
  2920.     ASSERT (NUM_STATUS_ROW_PROPS == i);
  2921.  
  2922.     // Write the entries on the provider's session status row
  2923.     HRESULT hResult = m_pSupObj->ModifyStatusRow (i, spvStatusRow, ulFlags);
  2924.     TraceResult ("CXPLogon::InitializeStatusRow: Failed to modify the status row", hResult);
  2925.     return hResult;
  2926. }
  2927.  
  2928. ///////////////////////////////////////////////////////////////////////////////
  2929. //    CXPLogon::CheckForUnfinishedDownloads()
  2930. //
  2931. //    Parameters
  2932. //      None.
  2933. //
  2934. //    Purpose
  2935. //      This function checks to see if we have files where downloaded messages
  2936. //      were lefted from a previous session or from a previous failed download
  2937. //      sequence. The message files, if any, should be in the data directory of
  2938. //      this service. If any files are found, the message queue is loaded and
  2939. //      the during out inbound logic initialization in
  2940. //      IXPLogon::TransportNotify(), we check the queue. If the queue is not
  2941. //      empty, we tell the transport to flush our inbound flush.
  2942. //
  2943. //    Return Value
  2944. //      None.
  2945. //
  2946. void WINAPI CXPLogon::CheckForUnfinishedDownloads()
  2947. {
  2948.     WIN32_FIND_DATA wfdFile = { 0 };
  2949.     TCHAR szTmpDir[_MAX_PATH], szDownloadDir[_MAX_PATH], szSearchMask[512];
  2950.     GetTempPath (_MAX_PATH, szTmpDir);
  2951.     lstrcat (szTmpDir, WINDS_DATA_DIRECTORY);
  2952.     wsprintf (szDownloadDir, WINDS_DOWNLOAD_DIR_NAME_FORMAT, szTmpDir, m_UserInfo.szMailboxName);
  2953.     wsprintf (szSearchMask, TEXT("%s\\%s*.%s"), szDownloadDir, XP_MSG_FILE_PREFIX, XP_MSG_FILE_SUFFIX);
  2954.     HANDLE hSearch = FindFirstFile (szSearchMask, &wfdFile);
  2955.     if (hSearch == INVALID_HANDLE_VALUE)
  2956.     {
  2957.         return;
  2958.     }
  2959.     BOOL fFound = TRUE;
  2960.     while (fFound)
  2961.     {
  2962.         wsprintf (szTmpDir, TEXT("%s\\%s"), szDownloadDir, wfdFile.cFileName);
  2963.         // If the file size of 0, then is probably some garbage file. Get rid of it.
  2964.         // A zero-sized file does not contains a message.
  2965.         if (0 == wfdFile.nFileSizeHigh && 0 == wfdFile.nFileSizeLow)
  2966.         {
  2967.             DeleteFile (szTmpDir);
  2968.         }
  2969.         else
  2970.         {
  2971.             m_List.QueuePendingMsgFile (szTmpDir);
  2972.         }
  2973.         fFound = FindNextFile (hSearch, &wfdFile);
  2974.     }
  2975. }
  2976.  
  2977. ///////////////////////////////////////////////////////////////////////////////
  2978. //    CXPLogon::DownloadMessageHeaders()
  2979. //
  2980. //    Parameters
  2981. //      None
  2982. //
  2983. //    Purpose
  2984. //      This function downloads the message headers for the user's mailbox and
  2985. //      is a remote folder is available, re-load the contents table of the
  2986. //      folder with the new message headers information.
  2987. //
  2988. //    Return Value
  2989. //      An HRESULT
  2990. //
  2991. STDMETHODIMP CXPLogon::DownloadMessageHeaders()
  2992. {
  2993.     // Guard against re-entrancy from the timer call back which happens on a separate thread
  2994.     UpdateProgress (0, REMOTE_ACTION_HEADER_REFRESH);     // Start the progress bar
  2995.     HRESULT hResult = GetHeadersFromServer (m_szServer,
  2996.                                             m_UserInfo.szMailboxName,
  2997.                                             m_szHeaders);
  2998.     UpdateProgress (50, REMOTE_ACTION_HEADER_REFRESH);    // Half way through
  2999.     if (!hResult && m_pStatusObj && m_pStatusObj->m_pHeaderFolder)
  3000.     {
  3001.         hResult = m_pStatusObj->m_pHeaderFolder->FillContentsTable (m_szHeaders);
  3002.     }
  3003.     UpdateProgress (100, REMOTE_ACTION_HEADER_REFRESH);   // Finish
  3004.     TraceResult ("CXPLogon::DownloadMessageHeaders", hResult);
  3005.     return hResult;
  3006. }
  3007.  
  3008. ///////////////////////////////////////////////////////////////////////////////
  3009. //    CXPLogon::EmptyInboundQueue()
  3010. //
  3011. //    Parameters
  3012. //      None
  3013. //
  3014. //    Purpose
  3015. //      Deletes all the node in the queue of downloaded messages, deleting
  3016. //      the used memory and closing the handle to the local temporary
  3017. //      message file.
  3018. //
  3019. //    Return Value
  3020. //      None
  3021. //
  3022. void WINAPI CXPLogon::EmptyInboundQueue()
  3023. {
  3024.     // If we have nodes at this point, is because the transport could not
  3025.     // process them at this time. They will be retrieved in the next time this
  3026.     // transport comes on line on the same profile or on a profile that has
  3027.     // the same user identity.
  3028.     PLIST_NODE pNode = m_List.GetDownloadNode();
  3029.     while (pNode)
  3030.     {
  3031.         CloseHandle (pNode->hFile);
  3032.         delete pNode;
  3033.         // If the list is empty, pNode will be NULL.
  3034.         // CList::GetDownloadNode() returns NULL for an empty list.
  3035.         pNode = m_List.GetDownloadNode();
  3036.     }
  3037. }
  3038.  
  3039. ///////////////////////////////////////////////////////////////////////////////
  3040. //    TimerWndProc()
  3041. //
  3042. //    Parameters
  3043. //      { Refer to Win32 documentation }
  3044. //
  3045. //    Purpose
  3046. //      Stub window procedure for the hidden timer window class.
  3047. //
  3048. //    Return Value
  3049. //      Default value return by the default window procedure
  3050. //
  3051. LRESULT CALLBACK TimerWndProc (HWND      hWnd,
  3052.                                UINT      message,
  3053.                                WPARAM    wParam,
  3054.                                LPARAM    lParam)
  3055. {
  3056.     return DefWindowProc(hWnd, message, wParam, lParam);
  3057. }
  3058.  
  3059.  
  3060. ///////////////////////////////////////////////////////////////////////////////
  3061. //    CXPLogon::InitializeTimer()
  3062. //
  3063. //    Parameters
  3064. //      None.
  3065. //
  3066. //    Purpose
  3067. //      Initialize the hidden window for the upload timer.
  3068. //
  3069. //    Return Value
  3070. //      None.
  3071. //
  3072. void WINAPI CXPLogon::InitializeTimer()
  3073. {
  3074.     WNDCLASS wc = { 0 };
  3075.     // Register the class for this application
  3076.     wc.lpfnWndProc      = TimerWndProc;
  3077.     wc.hInstance        = m_hInstance;
  3078.     wc.lpszClassName    = TIMER_WND_CLASS;
  3079.     if (!RegisterClass (&wc))
  3080.     {
  3081.         TraceResult ("InitializeTimer: Failed to register the timer window class", GetLastError());
  3082.         return;
  3083.     }
  3084.     m_hTimerWnd = CreateWindow (TIMER_WND_CLASS,
  3085.                                 NULL,
  3086.                                 WS_OVERLAPPEDWINDOW,
  3087.                                 0, 0, 0, 0,
  3088.                                 NULL,
  3089.                                 NULL,
  3090.                                 m_hInstance,
  3091.                                 NULL);
  3092.     if (NULL == m_hTimerWnd)
  3093.     {
  3094.         TraceResult ("InitializeTimer: Failed to create timer window", GetLastError());
  3095.         return;
  3096.     }
  3097.     SetWindowLong (m_hTimerWnd, GWL_USERDATA, (LONG)this);
  3098. }
  3099.  
  3100. ///////////////////////////////////////////////////////////////////////////////
  3101. //    CXPLogon::StartUploadTimer()
  3102. //
  3103. //    Parameters
  3104. //      None.
  3105. //
  3106. //    Purpose
  3107. //      This function sets a timer with the operating system. When
  3108. //      the specified period elapses, we will get called in the procedure
  3109. //      specified and we will then flush the incoming and outgoing queues.
  3110. //      Note that we round the delivery time to the HOUR
  3111. //
  3112. //    Return Value
  3113. //      None.
  3114. //
  3115. void WINAPI CXPLogon::StartUploadTimer()
  3116. {
  3117.     SYSTEMTIME stNow = { 0 };
  3118.     GetLocalTime (&stNow);
  3119.     WORD wUploadHour = m_stDelivTime.wHour;
  3120.     // If the time is 0:00, this is 12:00 AM midnight
  3121.     if (!wUploadHour)
  3122.     {
  3123.         wUploadHour = 24;
  3124.     }
  3125.     UINT uDelay = 0;
  3126.     // How many hours before the next upload?
  3127.     int nHours = wUploadHour - stNow.wHour;
  3128.     if (nHours <= 0)
  3129.     {
  3130.         // If the hours is 0 and the time hasn't arrived with in this hour,
  3131.         // set the upload timer for THIS hour.
  3132.         if (0 == nHours && stNow.wMinute < m_stDelivTime.wMinute)
  3133.         {
  3134.             nHours = -24;
  3135.             uDelay += (UINT)(m_stDelivTime.wMinute - stNow.wMinute) * 60 * 1000;
  3136.         }
  3137.         nHours += 24;
  3138.     }
  3139.     uDelay += (UINT)nHours * 60 * 60 * 1000; // Number of milliseconds in the hours
  3140.     m_uTimerID = SetTimer (m_hTimerWnd, 0, uDelay, UploadTimerProc);
  3141.     if (0 == m_uTimerID)
  3142.     {
  3143.         TraceResult ("CXPLogon::StartUploadTimer: Failed to set the timer", GetLastError());
  3144.     }
  3145. }
  3146.  
  3147. ///////////////////////////////////////////////////////////////////////////////
  3148. //    CXPLogon::StopUploadTimer()
  3149. //
  3150. //    Parameters
  3151. //      None.
  3152. //
  3153. //    Purpose
  3154. //      Terminates the timer used for the upload time.
  3155. //
  3156. //    Return Value
  3157. //      None
  3158. //
  3159. void WINAPI CXPLogon::StopUploadTimer()
  3160. {
  3161.     // If we have a timer around, terminate it and all its associated resources
  3162.     if (m_uTimerID)
  3163.     {
  3164.         KillTimer (m_hTimerWnd, m_uTimerID);
  3165.         DestroyWindow (m_hTimerWnd);
  3166.         UnregisterClass (TIMER_WND_CLASS, m_hInstance);
  3167.         m_hTimerWnd = NULL;
  3168.         m_uTimerID = 0;
  3169.     }
  3170. }
  3171.  
  3172. ///////////////////////////////////////////////////////////////////////////////
  3173. //    UploadTimerProc()
  3174. //
  3175. //    Parameters
  3176. //      { Refer to the Win32 TIMERPROC callback parameters }
  3177. //
  3178. //    Purpose
  3179. //      This function executes when the timer delay has expired.
  3180. //      Now we ask the spooler to send us the deferred messages, and we set
  3181. //      the transport state ready to receive updated headers.
  3182. //
  3183. //    Return Value
  3184. //      None
  3185. //
  3186. void CALLBACK UploadTimerProc (HWND    hTimerWnd,
  3187.                                UINT    Message,
  3188.                                UINT    idEvent,
  3189.                                DWORD   dwTime)
  3190. {
  3191.     KillTimer (hTimerWnd, idEvent);
  3192.     // We asked to get the CXPLogon object who set the timer. We now
  3193.     // check and see if the object is valid before we attempt to use it.
  3194.     CXPLogon * pLogon = (CXPLogon *)GetWindowLong (hTimerWnd, GWL_USERDATA);
  3195.     if (!pLogon ||
  3196.         IsBadWritePtr (pLogon, sizeof(CXPLogon)) ||
  3197.         IsBadReadPtr (pLogon, sizeof(CXPLogon)))
  3198.     {
  3199.         TraceMessage ("UploadTimerProc: We got a bogus logon object");
  3200.         return;
  3201.     }
  3202.     pLogon->m_uTimerID = 0;
  3203.  
  3204.     // If the transport's outgoing logic has been activated in this session,
  3205.     // send all the deferred messages, by flushing the transport.
  3206.     if (pLogon->GetTransportStatusCode() & STATUS_OUTBOUND_ENABLED)
  3207.     {
  3208.         pLogon->AddStatusBits (UPLOADING_MESSAGES);
  3209.     }
  3210.     // If the transport's incoming logic has been activated in this session,
  3211.     // get any pending messages that need to be placed in the default inbox,
  3212.     // and set the transport state, ready to get the headers from the server..
  3213.     if (pLogon->GetTransportStatusCode() & STATUS_INBOUND_ENABLED)
  3214.     {
  3215.         pLogon->AddStatusBits (DOWNLOADING_MESSAGES);
  3216.         pLogon->SetTransportState (PROCESSING_TIMER_EVENT);
  3217.     }
  3218.     // Modify the status row now
  3219.     pLogon->UpdateStatus();
  3220.     // Reset the time for the next upload. Uploading time is a recurring event.
  3221.     pLogon->StartUploadTimer();
  3222. }
  3223.  
  3224. // End of file for XPLOGON.CPP
  3225.