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 / xplist.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  19.3 KB  |  610 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. //  File Name 
  4. //      XPLIST.CPP
  5. //
  6. //  Description
  7. //      This file implements the classe CMsgQueue and CList used in this
  8. //      transport to process remote messages. The two classes help us process
  9. //      the list of messages selected in the Remote Viewer of a mail client.
  10. //
  11. //  Authors
  12. //      Les Thaler
  13. //      Irving De la Cruz
  14. //
  15. //  Revision: 1.7
  16. //  
  17. // Written for Microsoft Windows Developer Support
  18. // Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved.
  19. //
  20. #include "XPWDSR.H"
  21.  
  22. enum
  23. {
  24.     STAT,
  25.     EID,
  26.     NUM_PROPS
  27. };
  28. static const SizedSPropTagArray (NUM_PROPS, sptProps) =
  29. {
  30.     NUM_PROPS,
  31.     {
  32.         PR_MSG_STATUS,
  33.         PR_ENTRYID
  34.     }
  35. };
  36.  
  37. ///////////////////////////////////////////////////////////////////////////////
  38. //    CMsgQueue::CMsgQueue()
  39. //
  40. //    Parameters                        
  41. //      None.
  42. //
  43. //    Purpose
  44. //      CMsgQueue class constructor. 
  45. //
  46. //    Return Value
  47. //      None.
  48. //
  49. CMsgQueue::CMsgQueue()
  50. {
  51.     m_ulItems  = 0;
  52.     m_pHead = NULL;
  53.     m_pTail = NULL;
  54. }
  55.  
  56. ///////////////////////////////////////////////////////////////////////////////
  57. //    CMsgQueue::~MsgQueue()
  58. //
  59. //    Parameters                        
  60. //      None. 
  61. //
  62. //    Purpose
  63. //      CMsgQueue class destructor, walks the queue freeing each node
  64. //      and the node's members.
  65. //
  66. //    Return Value
  67. //      None
  68. //
  69. CMsgQueue::~CMsgQueue()
  70. {
  71.     if (!Empty())
  72.     {
  73.         PLIST_NODE pNode;
  74.         do     
  75.         {
  76.             pNode = m_pHead->pNext;
  77.             delete m_pHead;
  78.             m_pHead = pNode;
  79.         } while (pNode);
  80.     }
  81. }
  82.  
  83. ///////////////////////////////////////////////////////////////////////////////
  84. //    CMsgQueue::Insert()
  85. //
  86. //    Parameters                        
  87. //      pNode       Pointer to the node to queue
  88. //
  89. //    Purpose
  90. //      Queues a node into the message queue
  91. //
  92. //    Return Value
  93. //      None.
  94. //
  95. void WINAPI CMsgQueue::Insert (PLIST_NODE pNode)
  96. {
  97.     pNode->pNext = NULL;
  98.     if (Empty())
  99.     {
  100.         m_pHead = m_pTail = pNode;
  101.     }
  102.     else
  103.     {
  104.         m_pTail->pNext = pNode;
  105.         m_pTail = m_pTail->pNext;           
  106.     }
  107.     m_ulItems++;
  108. }
  109.  
  110. ///////////////////////////////////////////////////////////////////////////////
  111. //    CMsgQueue::Delete()
  112. //
  113. //    Parameters                        
  114. //      None.
  115. //
  116. //    Purpose
  117. //      Dequeues a node from the message queue. The caller is responsible for
  118. //      freeing the node after use.
  119. //
  120. //    Return Value
  121. //      The pointer to the dequeued node on success, NULL otherwise
  122. //
  123. PLIST_NODE WINAPI CMsgQueue::Delete()
  124. {   
  125.     if (!m_ulItems)
  126.     {
  127.         return NULL;
  128.     }
  129.     PLIST_NODE pNode = m_pHead;
  130.     if (m_pHead->pNext == NULL)
  131.     {
  132.         m_pHead = m_pTail = NULL;
  133.     }
  134.     else
  135.     {
  136.         m_pHead = m_pHead->pNext;
  137.     }
  138.     m_ulItems--;
  139.     pNode->pNext = NULL;
  140.     return pNode;
  141. }
  142.  
  143. ///////////////////////////////////////////////////////////////////////////////
  144. //    CList::CList()
  145. //
  146. //    Parameters                        
  147. //      None
  148. //
  149. //    Purpose
  150. //      Class constructor for the CList class, the list of downloaded
  151. //      messages. Encapsulates the message queue and methods for manipulating
  152. //      in the context of the CXPLogon object.
  153. //
  154. //    Return Value
  155. //      None.
  156. //
  157. CList::CList()
  158. {
  159.     m_pToDownload = NULL;
  160.     m_pDownloaded = NULL;
  161.     m_pLogon      = NULL;
  162. }
  163.  
  164. ///////////////////////////////////////////////////////////////////////////////
  165. //    CList::~CList()
  166. //
  167. //    Parameters                        
  168. //      None.
  169. //
  170. //    Purpose
  171. //      CList class destructor. Frees the TO DO and DONE queues
  172. //
  173. //    Return Value
  174. //      None.
  175. //
  176. CList::~CList()
  177. {
  178.     // free the msg queues
  179.     delete m_pToDownload;
  180.     m_pToDownload = NULL;
  181.     delete m_pDownloaded;
  182.     m_pDownloaded = NULL;
  183. }
  184.  
  185. ///////////////////////////////////////////////////////////////////////////////
  186. //    PropToMID()
  187. //
  188. //    Parameters                        
  189. //      lProp       the MAPI MSGSTATUS_REMOTExx flag
  190. //
  191. //    Purpose
  192. //      Translate the MAPI MSGSTATUS_REMOTExx flag bimask into an internal
  193. //      message ID. This utility function is useful for walking the contents
  194. //      table and checking which messages have been marked for downloading,
  195. //      moving, etc.
  196. //
  197. //    Return Value
  198. //      The message ID if lProp indicates the message was marked for a remote
  199. //      operation, UNMARKED otherwise.
  200. //
  201. MID WINAPI PropToMID (long lProp)
  202. {
  203.     if (lProp & MSGSTATUS_REMOTE_DOWNLOAD)
  204.     {
  205.         if (lProp & MSGSTATUS_REMOTE_DELETE)
  206.         {
  207.             return MSG_MOVE;
  208.         }
  209.         else
  210.         {
  211.             return MSG_DOWNLOAD;
  212.         }
  213.     }
  214.     else
  215.     {
  216.         if (lProp & MSGSTATUS_REMOTE_DELETE)
  217.         {
  218.             return MSG_DELETE;
  219.         }
  220.     }
  221.     return UNMARKED;        
  222. }
  223.  
  224. ///////////////////////////////////////////////////////////////////////////////
  225. //    CList::UpdateTableRow()
  226. //
  227. //    Parameters                        
  228. //      pEID        Pointer to a message entry ID
  229. //      midAction   Action taken on a row of the remote folder contents table
  230. //
  231. //    Purpose
  232. //      Update a message's row in the contents table. Call this
  233. //      method after a remote DOWNLOAD, MOVE or DELETE to synch the local
  234. //      header contents table with what's really on the server.
  235. //
  236. //    Return Value
  237. //      TRUE on success, FALSE otherwise
  238. //
  239. BOOL WINAPI CList::UpdateTableRow (LPBYTE pEID, MID midAction)
  240. {
  241.     SPropValue spvEID = { 0 };
  242.     spvEID.ulPropTag = PR_ENTRYID;
  243.     spvEID.Value.bin.cb = TRANSPORT_MESSAGE_EID_SIZE;
  244.     spvEID.Value.bin.lpb = pEID;
  245.     LPTABLEDATA pTableData = m_pLogon->GetRemoteFolderTableData();
  246.     HRESULT hResult;
  247.     if (midAction == MSG_DOWNLOAD)
  248.     {
  249.         LPSRow pRow;
  250.         ULONG i;
  251.         long lOldFlags;
  252.         hResult = pTableData->HrQueryRow (&spvEID, &pRow, NULL);
  253.         TraceResult ("CList::UpdateTableRow: Failed to query the table row", hResult);
  254.         if (!hResult)
  255.         {
  256.             for (i=0; i<pRow->cValues; i++)
  257.             {
  258.                 if (PR_MESSAGE_FLAGS == pRow->lpProps[i].ulPropTag)
  259.                 {
  260.                     lOldFlags = pRow->lpProps[i].Value.l;
  261.                     pRow->lpProps[i].Value.l |= MSGFLAG_READ;
  262.                     if (pRow->lpProps[i].Value.l != lOldFlags)
  263.                     {
  264.                         hResult = pTableData->HrModifyRow (pRow);
  265.                     }
  266.                     break; // Out of the FOR() loop
  267.                 }
  268.             }
  269.             gpfnFreeBuffer (pRow);
  270.         }
  271.     }
  272.     else
  273.     {
  274.         hResult = pTableData->HrDeleteRow (&spvEID);
  275.     }
  276.     TraceResult ("CList::UpdateTableRow: Failed to update the table row", hResult);
  277.     return hResult;
  278. }
  279.  
  280. ///////////////////////////////////////////////////////////////////////////////
  281. //    CList::Init()
  282. //
  283. //    Parameters                        
  284. //      none.
  285. //
  286. //    Purpose
  287. //      Initializes the CList object. Creates the 'TO DO' and "DONE'
  288. //      message queues.
  289. //
  290. //    Return Value
  291. //      TRUE on success, FALSE otherwise
  292. //
  293. BOOL WINAPI CList::Init()
  294. {
  295.     if (m_pToDownload)
  296.     {
  297.         delete m_pToDownload;
  298.     }
  299.     m_pToDownload = new CMsgQueue;
  300.     if (m_pDownloaded)
  301.     {
  302.         delete m_pDownloaded;
  303.     }
  304.     m_pDownloaded = new CMsgQueue;
  305.  
  306.     if (!m_pToDownload || !m_pDownloaded)
  307.     {
  308.         delete m_pToDownload;
  309.         delete m_pDownloaded;
  310.         TraceMessage ("CList::Init: Failed to create download queues");
  311.         return FALSE;
  312.     }
  313.     return TRUE;
  314. }
  315.  
  316. ///////////////////////////////////////////////////////////////////////////////
  317. //    CList::DownLoadMsgs()
  318. //
  319. //    Parameters                        
  320. //      pTable          Pointer to the header contents table
  321. //      ulRowCount      Count of entries in the contents table
  322. //      hPipe           Handle to the named pipe opened to the server
  323. //                      where the messages in being downloaded through.
  324. //
  325. //    Purpose
  326. //      Walks through the contents table and checks the PR_MSG_STATUS of each
  327. //      entry. Messages marked for download, move, or delete are allocated a
  328. //      node for queuing the "TO DO' queue. Messages marked for download or
  329. //      move additionally have a temporary filename created for them to hold
  330. //      the downloaded message. 
  331. //
  332. //      If the resulting 'TO DO' queue is non-empty, the download worker
  333. //      thread is started and the CList object passed to it. This starts the
  334. //      background processing of messages in the 'TO DO' queue.
  335. //
  336. //    Return Value
  337. //      An HRESULT
  338. //
  339. HRESULT WINAPI CList::DownLoadMsgs (LPMAPITABLE pTable, ULONG ulRowCount, HANDLE hPipe)
  340. {
  341.     HRESULT hResult = pTable->SetColumns ((LPSPropTagArray)&sptProps, 0);
  342.     if (hResult)
  343.     {
  344.         TraceResult ("CList::DownLoadMsgs: Failed to set the columns", hResult);
  345.         return hResult;
  346.     }
  347.  
  348.     LPSRowSet pRows;
  349.     hResult = pTable->QueryRows (ulRowCount, 0, &pRows);
  350.     if (hResult)
  351.     {
  352.         TraceResult ("CList::DownLoadMsgs: Failed to get the rows", hResult);
  353.         return hResult;
  354.     }
  355.  
  356.     // construct the list of msgs to download/delete/move
  357.     ASSERT (pRows->cRows);
  358.     MID midMsgAction;
  359.     for (ULONG i=0; i<pRows->cRows; i++)
  360.     {
  361.         ASSERT (PR_MSG_STATUS == pRows->aRow[i].lpProps[STAT].ulPropTag);
  362.         ASSERT (PR_ENTRYID == pRows->aRow[i].lpProps[EID].ulPropTag);
  363.         if (pRows->aRow[i].lpProps[STAT].ulPropTag == PR_MSG_STATUS &&
  364.             pRows->aRow[i].lpProps[EID].ulPropTag  == PR_ENTRYID)
  365.         {
  366.             midMsgAction= PropToMID (pRows->aRow[i].lpProps[STAT].Value.l);
  367.             // Queue message only if marked for remote processing
  368.             if (UNMARKED != midMsgAction)
  369.             {
  370.                 PLIST_NODE pNode= new LIST_NODE;
  371.                 if (NULL == pNode)
  372.                 {
  373.                     TraceMessage ("CList::DownLoadMsgs: Failed to allocate new node");
  374.                     hResult = E_OUTOFMEMORY;
  375.                     break;
  376.                 }
  377.                 ZeroMemory (pNode, sizeof(LIST_NODE));
  378.                 pNode->fRetry = FALSE;
  379.  
  380.                 // Only downloaded/moved msgs need the tempfile
  381.                 if (midMsgAction == MSG_DOWNLOAD || midMsgAction == MSG_MOVE)                
  382.                 {
  383.                     m_pLogon->GetMsgTempFileName (pNode->szFileName);
  384.                 }
  385.                 ASSERT (TRANSPORT_MESSAGE_EID_SIZE == pRows->aRow[i].lpProps[EID].Value.bin.cb);
  386.                 CopyMemory (pNode->Hdr.Info.EID, pRows->aRow[i].lpProps[EID].Value.bin.lpb, TRANSPORT_MESSAGE_EID_SIZE);
  387.                 pNode->Hdr.ulMID = pNode->OpStat = midMsgAction;
  388.                 // insert in 'TO DO' queue
  389.                 m_pToDownload->Insert (pNode);
  390.             }
  391.         }
  392.     }
  393.     FreeProws(pRows);
  394.     if (!hResult)
  395.     {
  396.         ASSERT (!m_pToDownload->Empty());
  397.         DownLoadNow (hPipe);
  398.     }
  399.     return hResult;
  400. }
  401.  
  402. ///////////////////////////////////////////////////////////////////////////////
  403. //    CList::DownLoadNow()
  404. //
  405. //    Parameters                        
  406. //      hPipe           Handle to the named pipe opened to the server
  407. //                      where the messages in being downloaded through.
  408. //
  409. //    Purpose
  410. //      For each message in the 'TO DO' queue, check its requested
  411. //      operation and format a control command to send over the pipe to
  412. //      the server. 
  413. //
  414. //      The control command will contain the requested operation code and
  415. //      the remote entry ID of the message. The server will ACK the control
  416. //      command if the operation can be performed on the server side. The 
  417. //      ACK message sent back by the server contains the size in bytes of
  418. //      the data stream that the server will next write to the pipe.
  419. //
  420. //      If the server can't perform the requested operation (e.g. the message
  421. //      can't be opened), it sends back an OP_FAILED command (NAK) and the 
  422. //      dequeued TO DO message is discarded. This is considered a non-fatal error.
  423. //      and the rest of the queue is processed.
  424. //   
  425. //      Downloaded and moved messages have a temporary file created for them before
  426. //      the data transfer to the message store is started. A failure creating the
  427. //      file is also non-fatal, the current message is discarded but processing
  428. //      continues. After the temp file is created, data transfer is started on
  429. //      the pipe. The stream is copied from the pipe into the temp file.
  430. //
  431. //      If the MOVE/DOWNLOAD operation succeeds, the 'TO DO' message is inserted
  432. //      into the 'DONE' queue which is then passed to StartMessage for processing
  433. //      in the spooler Poll() loop.
  434. //
  435. //      On exit, we send the server a 'HANG UP' command.
  436. //
  437. //    Return Value
  438. //      None
  439. //
  440. void WINAPI CList::DownLoadNow (HANDLE hPipe)
  441. {
  442.     PLIST_NODE pNode;
  443.     MSG_HDR CtrlMsg, InMsg;
  444.     DWORD dwBytes;
  445.     m_ulMsgCount = m_pToDownload->m_ulItems;
  446.     for (ULONG i=0; i<m_ulMsgCount; i++)
  447.     {
  448.         pNode = m_pToDownload->Delete();
  449.         if (NULL == pNode)
  450.         {
  451.             TraceMessage ("CList::DownLoadNow: Failed to get node from internal queue");
  452.             continue; // The FOR() loop
  453.         }
  454.         CtrlMsg = pNode->Hdr;
  455.         switch (pNode->Hdr.ulMID)
  456.         {
  457.             case MSG_MOVE :
  458.             case MSG_DOWNLOAD :
  459.                 // We need a temp file where we download the server message temporeraly
  460.                 // until it gets picked up in IXPLogon::StartMessage()
  461.                 pNode->hFile = CreateFile (pNode->szFileName,
  462.                                            GENERIC_READ | GENERIC_WRITE,
  463.                                            FILE_SHARE_READ,
  464.                                            NULL,
  465.                                            CREATE_ALWAYS,
  466.                                            FILE_ATTRIBUTE_TEMPORARY,
  467.                                            NULL);
  468.                 if (INVALID_HANDLE_VALUE == pNode->hFile)
  469.                 {
  470.                     pNode->OpStat = OP_FAILED;
  471.                     TraceResult ("CList::DownLoadNow: Failed to create local file", GetLastError());
  472.                     goto ProcessNextMessage;
  473.                 }
  474.                 // The node has a header structure with the command action to instruct
  475.                 // the server on what we need to do on this message
  476.                 if (!WriteFile (hPipe, &CtrlMsg, sizeof(MSG_HDR), &dwBytes, NULL))
  477.                 {
  478.                     TraceResult ("CList::DownLoadNow: (MOVE/COPY) Failed to send control message to host", GetLastError());
  479.                     goto ProcessNextMessage;
  480.                 }
  481.                 // Upon receiving the control header, the remote host sent a response
  482.                 if (!ReadFile (hPipe, &InMsg, sizeof(MSG_HDR), &dwBytes, NULL))
  483.                 {
  484.                     TraceResult ("CList::DownLoadNow: (MOVE/COPY) Failed to receive message to data", GetLastError());
  485.                     goto ProcessNextMessage;
  486.                 }
  487.                 // Check the result of the host and if successful, copy the message to a local file
  488.                 if (OP_FAILED == InMsg.ulMID ||
  489.                     NO_ERROR != FileCopy (pNode->hFile, hPipe, InMsg.Info.ulMsgLen))
  490.                 {
  491.                     goto ProcessNextMessage;
  492.                 }
  493.                 // If the message was moved from the remote server, we must remote the
  494.                 // row in the remote folder viewer.
  495.                 UpdateTableRow (pNode->Hdr.Info.EID, pNode->Hdr.ulMID);
  496.                 m_pDownloaded->Insert (pNode);
  497.                 pNode = NULL;
  498.                 break;
  499.  
  500.             case MSG_DELETE :
  501.                 if (!WriteFile (hPipe, &CtrlMsg, sizeof(MSG_HDR), &dwBytes, NULL))
  502.                 {
  503.                     TraceResult ("CList::DownLoadNow: (DELETE) Failed to send control message to host", GetLastError());
  504.                     goto ProcessNextMessage;
  505.                 }
  506.                 if (!ReadFile (hPipe, &InMsg, sizeof(MSG_HDR), &dwBytes, NULL))
  507.                 {
  508.                     TraceResult ("CList::DownLoadNow: (DELETE) Failed to receive message to data", GetLastError());
  509.                     goto ProcessNextMessage;
  510.                 }
  511.                 if (InMsg.ulMID != OP_FAILED)
  512.                 {
  513.                     UpdateTableRow (pNode->Hdr.Info.EID, MSG_DELETE);
  514.                 }
  515.                 break;
  516.             default :
  517.                 TraceMessage ("CList::DownLoadNow: Unknown action on this node");
  518.                 break;
  519.         }
  520. ProcessNextMessage:
  521.         m_pLogon->UpdateProgress ((((i + 1) * 100) / m_ulMsgCount), REMOTE_ACTION_DOWNLOADING_MSGS);
  522.         if (pNode)
  523.         {
  524.             if (pNode->hFile)
  525.             {
  526.                 CloseHandle (pNode->hFile);
  527.                 DeleteFile (pNode->szFileName);
  528.             }
  529.             delete pNode;
  530.         }
  531.     }
  532.     CtrlMsg.ulMID = GOODBYE;
  533.     WriteFile (hPipe, &CtrlMsg, sizeof(MSG_HDR), &dwBytes, NULL);
  534.     m_ulMsgLeft = m_ulMsgCount; 
  535. }
  536.  
  537. ///////////////////////////////////////////////////////////////////////////////
  538. //    CList::UpdateProgress()
  539. //
  540. //    Parameters
  541. //      None
  542. //
  543. //    Purpose
  544. //      Update the progress percentage in the status row after a message is
  545. //      processed in IXPLogon::StartMessage()
  546. //
  547. //    Return Value
  548. //      None
  549. //
  550. void WINAPI CList::UpdateProgress()
  551. {
  552.     if (m_ulMsgCount)
  553.     {
  554.         m_ulMsgLeft--;
  555.         m_pLogon->UpdateProgress ((((m_ulMsgCount - m_ulMsgLeft) * 100) / m_ulMsgCount),
  556.                                     REMOTE_ACTION_PROCESSING_MSGS);
  557.     }
  558. }
  559.  
  560. ///////////////////////////////////////////////////////////////////////////////
  561. //    FileCopy()
  562. //
  563. //    Parameters                        
  564. //      hDest       Handle of stream object receiving the data
  565. //      hSrc        Handle of stream object whence data
  566. //      dwMsgLen    Number of bytes to transfer
  567. //
  568. //    Purpose
  569. //      Transfers dwMsgLen bytes from hSrc to hDest. The objects can be
  570. //      open file handles, pipes, or any stream oriented object that uses
  571. //      the Read/WriteFile semantics.      
  572. //
  573. //    Return Value
  574. //      NO_ERROR on success, ERROR_READ/WRITE_FAULT otherwise
  575. //
  576. long WINAPI FileCopy (HANDLE hDest, HANDLE hSrc, DWORD dwMsgLen)
  577. {
  578.     BYTE abBuffer[IO_BUFFERSIZE]; 
  579.     DWORD dwRead, dwRemaining = dwMsgLen;
  580.     BOOL bSuccess;
  581.     long lResult = NO_ERROR;
  582.     for (DWORD dwWritten=0 ; dwRemaining>0; dwRemaining -= dwWritten)
  583.     {
  584.         bSuccess = ReadFile (hSrc, abBuffer, min(dwRemaining, IO_BUFFERSIZE), &dwRead, NULL);
  585.         if (!dwRead || !bSuccess)
  586.         {
  587.             lResult = ERROR_READ_FAULT;
  588.             break;
  589.         }
  590.         bSuccess = WriteFile (hDest, abBuffer, dwRead, &dwWritten, NULL);
  591.         if (!dwWritten || !bSuccess)
  592.         {
  593.             lResult = ERROR_WRITE_FAULT;
  594.             break;
  595.         }
  596.     }
  597.     if (lResult)
  598.     {
  599.         TraceResult ("CList::FileCopy: IO operation failed", GetLastError());
  600.     }
  601.     else
  602.     {
  603.         FlushFileBuffers (hDest);
  604.         SetFilePointer (hDest, 0, NULL, FILE_BEGIN);
  605.     }
  606.     return lResult;
  607. }
  608.  
  609. // End of file for XPLIST.CPP
  610.