Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

863 lines
25 KiB

  1. #define INCL_INETSRV_INCS
  2. #include "smtpinc.h"
  3. #include "remoteq.hxx"
  4. #include "dropdir.hxx"
  5. DWORD g_dwDropFileCounter = 0;
  6. CPool CDropDir::m_Pool(DROPDIR_SIG);
  7. //////////////////////////////////////////////////////////////////////////////
  8. VOID
  9. DropDirWriteCompletion(
  10. PVOID pvContext,
  11. DWORD cbWritten,
  12. DWORD dwCompletionStatus,
  13. OVERLAPPED * lpo
  14. )
  15. {
  16. DECL_TRACE((LPARAM) 0xC0DEC0DE, "DropDirWriteCompletion");
  17. BOOL WasProcessed = TRUE;
  18. CDropDir *pCC = (CDropDir *) pvContext;
  19. _ASSERT(pCC);
  20. pCC->SetHr( pCC->OnIoWriteCompletion(cbWritten, dwCompletionStatus, lpo) );
  21. SAFE_RELEASE( pCC );
  22. }
  23. //////////////////////////////////////////////////////////////////////////////
  24. VOID
  25. DropDirReadCompletion(
  26. PFIO_CONTEXT pContext,
  27. PFH_OVERLAPPED lpo,
  28. DWORD cbRead,
  29. DWORD dwCompletionStatus
  30. )
  31. {
  32. DECL_TRACE((LPARAM) 0xC0DEC0DE, "DropDirReadCompletion");
  33. BOOL WasProcessed = TRUE;
  34. DROPDIR_READ_OVERLAPPED *p = (DROPDIR_READ_OVERLAPPED *) lpo;
  35. CDropDir *pCC = (CDropDir*) p->ThisPtr;
  36. _ASSERT(pCC);
  37. pCC->SetHr( pCC->OnIoReadCompletion(cbRead, dwCompletionStatus, (LPOVERLAPPED)lpo) );
  38. SAFE_RELEASE( pCC );
  39. }
  40. //////////////////////////////////////////////////////////////////////////////
  41. CDropDir::CDropDir()
  42. {
  43. DECL_TRACE((LPARAM) this, "CDropDir::CDropDir");
  44. m_dwSig = DROPDIR_SIG;
  45. m_cRef = 1;
  46. m_NumRcpts = 0;
  47. m_hDrop = INVALID_HANDLE_VALUE;
  48. m_hMail = NULL;
  49. m_AdvContext = NULL;
  50. m_rgRcptIndexList = NULL;
  51. m_pIMsg = NULL;
  52. m_pBindInterface = NULL;
  53. m_pAtqContext = NULL;
  54. CopyMemory( m_acCrLfDotCrLf, "\r\n.\r\n", 5 );
  55. ZeroMemory( m_acLastBytes, 5 );
  56. m_pIMsgRecips = NULL;
  57. m_hr = S_OK;
  58. m_cbWriteOffset = 0;
  59. m_cbReadOffset = 0;
  60. m_cbMsgWritten = 0;
  61. m_idxRecips = 0;
  62. ZeroMemory( &m_WriteOverlapped, sizeof( m_WriteOverlapped ) );
  63. ZeroMemory( &m_ReadOverlapped, sizeof( m_ReadOverlapped ) );
  64. m_szDropDir[0] = '\0';
  65. m_pISMTPConnection = NULL;
  66. m_ReadState = DROPDIR_READ_NULL;
  67. m_WriteState = DROPDIR_WRITE_NULL;
  68. m_pParentInst = NULL;
  69. }
  70. //////////////////////////////////////////////////////////////////////////////
  71. CDropDir::~CDropDir()
  72. {
  73. DECL_TRACE((LPARAM) this, "CDropDir::~CDropDir");
  74. SAFE_RELEASE( m_pIMsgRecips );
  75. if(m_pBindInterface)
  76. {
  77. m_pBindInterface->ReleaseContext();
  78. SAFE_RELEASE(m_pBindInterface);
  79. }
  80. m_MsgAck.pIMailMsgProperties = m_pIMsg;
  81. m_MsgAck.pvMsgContext = (DWORD *) m_AdvContext;
  82. if(SUCCEEDED(m_hr))
  83. {
  84. m_MsgAck.dwMsgStatus = MESSAGE_STATUS_ALL_DELIVERED;
  85. }
  86. else
  87. {
  88. m_MsgAck.dwMsgStatus = MESSAGE_STATUS_RETRY_ALL;
  89. }
  90. m_MsgAck.dwStatusCode = 0;
  91. m_pISMTPConnection->AckMessage(&m_MsgAck);
  92. SAFE_RELEASE(m_pIMsg);
  93. if(SUCCEEDED(m_hr))
  94. {
  95. BUMP_COUNTER( m_pParentInst, DirectoryDrops);
  96. }
  97. if(SUCCEEDED(m_hr))
  98. {
  99. m_pISMTPConnection->AckConnection(CONNECTION_STATUS_OK);
  100. }
  101. else
  102. {
  103. DeleteFile(m_szDropFile);
  104. SetLastError( 0x0000FFFF & m_hr );
  105. m_pISMTPConnection->AckConnection(CONNECTION_STATUS_FAILED);
  106. }
  107. PATQ_CONTEXT pAtqContext = (PATQ_CONTEXT)InterlockedExchangePointer( (PVOID *)&m_pAtqContext, NULL);
  108. if ( pAtqContext != NULL )
  109. {
  110. if(m_hDrop != INVALID_HANDLE_VALUE)
  111. AtqCloseFileHandle( pAtqContext );
  112. AtqFreeContext( pAtqContext, TRUE );
  113. }
  114. //
  115. // Now get the next message on this connection and
  116. // copy it to drop directory.
  117. //
  118. if (m_szDropDir[0])
  119. AsyncCopyMailToDropDir( m_pISMTPConnection, m_szDropDir, m_pParentInst );
  120. SAFE_RELEASE( m_pISMTPConnection );
  121. m_dwSig = DROPDIR_SIG_FREE;
  122. }
  123. //---[ CDropDir::CopyMailToDropDir ]-------------------------------------------
  124. //
  125. //
  126. // Description:
  127. //
  128. // Entry point to the Async writing to the mail file. When this is called
  129. // it sets up a series of alternating reads and writes (read from the queue
  130. // and write to the mail file). The (rough description of the) algorithm is
  131. // as follows:
  132. //
  133. // WriteHeaders()
  134. // Post Async Read from MailFile
  135. // return
  136. //
  137. // Read Completion function()
  138. // Post Async Write of Data to DropDir
  139. // return
  140. //
  141. // Write Completion function()
  142. // Post Async Read from MailFile
  143. // return
  144. //
  145. // Called by
  146. // AsyncCopyMailToDropDir()
  147. //
  148. // Parameters:
  149. //
  150. // Returns:
  151. //
  152. // History:
  153. // Created by PGOPI
  154. // 3/25/99 - MikeSwa modified for checkin
  155. //
  156. //-----------------------------------------------------------------------------
  157. HRESULT CDropDir::CopyMailToDropDir(
  158. ISMTPConnection *pISMTPConnection,
  159. const char *DropDirectory,
  160. IMailMsgProperties *pIMsg,
  161. PVOID AdvContext,
  162. DWORD NumRcpts,
  163. DWORD *rgRcptIndexList,
  164. SMTP_SERVER_INSTANCE *pParentInst)
  165. {
  166. DECL_TRACE((LPARAM) this, "CDropDir::CopyMailToDropDir");
  167. HRESULT hr = S_OK;
  168. m_pISMTPConnection = pISMTPConnection;
  169. SAFE_ADDREF( m_pISMTPConnection );
  170. m_pIMsg = pIMsg;
  171. SAFE_ADDREF( m_pIMsg );
  172. m_pParentInst = pParentInst;
  173. m_AdvContext = AdvContext;
  174. m_NumRcpts = NumRcpts;
  175. m_rgRcptIndexList = rgRcptIndexList;
  176. if (!DropDirectory)
  177. {
  178. hr = E_INVALIDARG;
  179. return ( hr );
  180. }
  181. //Copy drop directory now... so we have a copy in the destructor
  182. //even if this message cannot be dropped
  183. lstrcpy(m_szDropDir, DropDirectory);
  184. hr = m_pIMsg->QueryInterface(IID_IMailMsgBind, (void **)&m_pBindInterface);
  185. if(FAILED(hr))
  186. {
  187. ErrorTrace( (LPARAM)this, "m_pIMsg->QueryInterface for IID_IMailMsgBind return hr = 0x%x", hr );
  188. return( hr );
  189. }
  190. hr = m_pBindInterface->GetBinding(&m_hMail, NULL);
  191. if(FAILED(hr))
  192. {
  193. ErrorTrace( (LPARAM)this, "GetBinding return hr = 0x%x", hr );
  194. return( hr );
  195. }
  196. hr = m_pIMsg->QueryInterface( IID_IMailMsgRecipients, (PVOID *) &m_pIMsgRecips);
  197. if( FAILED( hr ) )
  198. {
  199. ErrorTrace( (LPARAM)this, "QueryInterface for IID_IMailMsgRecipients failed hr = 0x%x", hr );
  200. return( hr );
  201. }
  202. if( CheckIfAllRcptsHandled() )
  203. {
  204. return( S_OK );
  205. }
  206. DebugTrace((LPARAM)this, "Dropping file to: %s", DropDirectory);
  207. m_hDrop = CreateDropFile(DropDirectory);
  208. if (m_hDrop == INVALID_HANDLE_VALUE)
  209. {
  210. hr = HRESULT_FROM_WIN32( GetLastError() );
  211. ErrorTrace( (LPARAM)this, "Unable to create drop directory (%s) : 0x%x", DropDirectory, hr);
  212. return( hr );
  213. }
  214. if (!AtqAddAsyncHandle( &m_pAtqContext,
  215. NULL,
  216. (LPVOID) this,
  217. DropDirWriteCompletion,
  218. INFINITE,
  219. m_hDrop))
  220. {
  221. hr = HRESULT_FROM_WIN32( GetLastError() );
  222. ErrorTrace((LPARAM)this, "Error 0x%x while adding async handle to ATQ", hr);
  223. return( hr );
  224. }
  225. // Output the x-headers
  226. if (FAILED( hr = CreateXHeaders() ) )
  227. {
  228. ErrorTrace( (LPARAM)this, " Error while creating x-headers hr = 0x%x", hr);
  229. return( hr );
  230. }
  231. return( hr );
  232. }
  233. //////////////////////////////////////////////////////////////////////////////
  234. HANDLE CDropDir::CreateDropFile(const char * DropDir)
  235. {
  236. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  237. DWORD dwStrLen;
  238. FILETIME ftTime;
  239. DWORD Error = 0;
  240. DECL_TRACE((LPARAM)this, "CDropDir::CreateDropFile");
  241. dwStrLen = lstrlen(DropDir);
  242. lstrcpy(m_szDropFile, DropDir);
  243. do
  244. {
  245. GetSystemTimeAsFileTime(&ftTime);
  246. wsprintf(&m_szDropFile[dwStrLen],
  247. "%08x%08x%08x%s",
  248. ftTime.dwLowDateTime,
  249. ftTime.dwHighDateTime,
  250. InterlockedIncrement((PLONG)&g_dwDropFileCounter),
  251. ".eml");
  252. FileHandle = CreateFile(m_szDropFile,
  253. GENERIC_READ | GENERIC_WRITE,
  254. FILE_SHARE_READ | FILE_SHARE_WRITE,
  255. NULL,
  256. CREATE_NEW,
  257. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED,
  258. NULL);
  259. if (FileHandle != INVALID_HANDLE_VALUE)
  260. break;
  261. if((Error = GetLastError()) != ERROR_FILE_EXISTS)
  262. {
  263. FileHandle = INVALID_HANDLE_VALUE;
  264. ErrorTrace( (LPARAM)this, "CreateFile returns err = %u", Error );
  265. break;
  266. }
  267. } while( (FileHandle == INVALID_HANDLE_VALUE) && !m_pParentInst->IsShuttingDown());
  268. return (FileHandle);
  269. }
  270. //////////////////////////////////////////////////////////////////////////////
  271. HRESULT CDropDir::ReadFile(
  272. IN LPVOID pBuffer,
  273. IN DWORD cbSize
  274. )
  275. {
  276. DECL_TRACE((LPARAM) this, "CDropDir::ReadFile");
  277. HRESULT hr = S_OK;
  278. _ASSERT(pBuffer != NULL);
  279. m_ReadOverlapped.Overlapped.Offset = LODWORD( m_cbReadOffset );
  280. m_ReadOverlapped.Overlapped.OffsetHigh = HIDWORD( m_cbReadOffset );
  281. m_ReadOverlapped.Overlapped.pfnCompletion = DropDirReadCompletion;
  282. m_ReadOverlapped.ThisPtr = (PVOID)this;
  283. AddRef();
  284. BOOL fRet = FIOReadFile( m_hMail,
  285. pBuffer, // Buffer
  286. cbSize, // BytesToRead
  287. (PFH_OVERLAPPED)&m_ReadOverlapped) ;
  288. if(!fRet)
  289. {
  290. DWORD err = GetLastError();
  291. if( err != ERROR_IO_PENDING )
  292. {
  293. hr = HRESULT_FROM_WIN32( err );
  294. ErrorTrace( (LPARAM)this, "FIOReadFile return hr = 0x%x", hr );
  295. }
  296. }
  297. if( FAILED( hr ) )
  298. {
  299. Release();
  300. }
  301. return hr;
  302. }
  303. //////////////////////////////////////////////////////////////////////////////
  304. HRESULT CDropDir::WriteFile(
  305. IN LPVOID pBuffer,
  306. IN DWORD cbSize
  307. )
  308. {
  309. DECL_TRACE((LPARAM) this, "CDropDir::WriteFile");
  310. CDropDir *p = this;
  311. _ASSERT(pBuffer != NULL);
  312. _ASSERT(cbSize > 0);
  313. HRESULT hr = S_OK;
  314. m_WriteOverlapped.Offset = LODWORD( m_cbWriteOffset );
  315. m_WriteOverlapped.OffsetHigh = HIDWORD( m_cbWriteOffset );
  316. AddRef();
  317. BOOL fRet = AtqWriteFile( m_pAtqContext, // Atq context
  318. pBuffer, // Buffer
  319. cbSize, // BytesToRead
  320. (OVERLAPPED *) &m_WriteOverlapped) ;
  321. if(!fRet)
  322. {
  323. DWORD err = GetLastError();
  324. if( err != ERROR_IO_PENDING )
  325. {
  326. hr = HRESULT_FROM_WIN32( err );
  327. ErrorTrace( (LPARAM)this, "AtqWriteFile return hr = 0x%x", hr );
  328. }
  329. }
  330. if( FAILED( hr ) )
  331. {
  332. Release();
  333. }
  334. return( hr );
  335. }
  336. //////////////////////////////////////////////////////////////////////////////
  337. HRESULT CDropDir::OnIoWriteCompletion( DWORD cbSize, DWORD dwErr, OVERLAPPED *lpo )
  338. {
  339. DECL_TRACE((LPARAM) this, "CDropDir::OnIoWriteCompletion");
  340. HRESULT hr = S_OK;
  341. if( dwErr != NO_ERROR )
  342. {
  343. ErrorTrace( (LPARAM)this, "OnIoWriteCompletion got err = %u", dwErr );
  344. return( HRESULT_FROM_WIN32( dwErr ) );
  345. }
  346. m_cbWriteOffset += cbSize;
  347. switch( m_ReadState )
  348. {
  349. case DROPDIR_READ_X_SENDER:
  350. _ASSERT(!"Invalid State READ_X_SENDER");
  351. hr = E_INVALIDARG;
  352. break;
  353. case DROPDIR_READ_X_RECEIVER:
  354. hr = CreateXRecvHeaders();
  355. break;
  356. case DROPDIR_READ_DATA:
  357. hr = OnCopyMessageWrite( cbSize);
  358. break;
  359. default:
  360. _ASSERT(!"INVALID read STATE");
  361. hr = E_INVALIDARG;
  362. ErrorTrace( (LPARAM)this, "Invalid ReadState" );
  363. break;
  364. }
  365. return( hr );
  366. }
  367. //////////////////////////////////////////////////////////////////////////////
  368. HRESULT CDropDir::OnIoReadCompletion( DWORD cbSize, DWORD dwErr, OVERLAPPED *lpo )
  369. {
  370. DECL_TRACE((LPARAM) this, "CDropDir::OnIoReadCompletion");
  371. HRESULT hr = S_OK;
  372. if( ( dwErr != NO_ERROR ) && ( dwErr != ERROR_HANDLE_EOF ) )
  373. {
  374. hr = HRESULT_FROM_WIN32( dwErr );
  375. ErrorTrace( (LPARAM)this, "OnIOReadCompletion got hr = 0x%x", hr );
  376. }
  377. else
  378. {
  379. m_cbReadOffset += cbSize;
  380. hr = OnCopyMessageRead( cbSize, dwErr );
  381. }
  382. return( hr );
  383. }
  384. ///////////////////////////////////////////////////////////////////////////
  385. HRESULT CDropDir::CreateXHeaders()
  386. {
  387. DECL_TRACE(((LPARAM) this), "CDropDir::CreateXHeaders");
  388. HRESULT hr = S_OK;
  389. DebugTrace( (LPARAM)this, "Setting m_ReadState = DROPDIR_READ_X_SENDER" );
  390. m_ReadState = DROPDIR_READ_X_SENDER;
  391. strcpy( m_szBuffer, X_SENDER_HEADER );
  392. hr = m_pIMsg->GetStringA( IMMPID_MP_SENDER_ADDRESS_SMTP, MAX_INTERNET_NAME, &m_szBuffer[ sizeof(X_SENDER_HEADER) - 1] );
  393. if(SUCCEEDED(hr))
  394. {
  395. strcat( m_szBuffer, X_HEADER_EOLN);
  396. //
  397. // this WriteFile will complete async, so set the next read state.
  398. // Make Sure that no class member variables are acessed after calling
  399. // WriteFile or ReadFile as they complete async & there is no critical
  400. // section protecting access.To do this trace through all paths
  401. // thru which ReadFile and WriteFile are called and make sure no member
  402. // variables are accessed.
  403. m_ReadState = DROPDIR_READ_X_RECEIVER;
  404. DebugTrace( (LPARAM)this, "Setting m_ReadState = DROPDIR_READ_X_RECEIVER" );
  405. //
  406. if( FAILED( hr = WriteFile( m_szBuffer, strlen( m_szBuffer ) ) ) )
  407. {
  408. ErrorTrace((LPARAM)this, "Error %d writing x-sender line %s", hr, m_szBuffer);
  409. return( hr );
  410. }
  411. }
  412. else
  413. {
  414. hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  415. ErrorTrace((LPARAM)this, "Could not get Sender Address returning hr = 0x%x", hr);
  416. return( hr );
  417. }
  418. return( hr );
  419. }
  420. ///////////////////////////////////////////////////////////////////////////
  421. HRESULT CDropDir::CreateXRecvHeaders()
  422. {
  423. DECL_TRACE((LPARAM) this, "CDropDir::CreateXRecvHeaders");
  424. HRESULT hr = S_OK;
  425. DWORD dwRecipientFlags = 0;
  426. BOOL fPendingWrite = FALSE;
  427. strcpy( m_szBuffer, X_RECEIVER_HEADER );
  428. while( m_idxRecips < m_NumRcpts )
  429. {
  430. hr = m_pIMsgRecips->GetDWORD( m_rgRcptIndexList[m_idxRecips], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  431. if( SUCCEEDED( hr ) )
  432. {
  433. if( RP_HANDLED != ( dwRecipientFlags & RP_HANDLED ) )
  434. {
  435. hr = m_pIMsgRecips->GetStringA( m_rgRcptIndexList[m_idxRecips],
  436. IMMPID_RP_ADDRESS_SMTP,
  437. MAX_INTERNET_NAME,
  438. &m_szBuffer[ sizeof(X_RECEIVER_HEADER) - 1 ]);
  439. if (SUCCEEDED(hr))
  440. {
  441. strcat(m_szBuffer, X_HEADER_EOLN);
  442. // Make Sure that no class member variables are acessed after calling
  443. // WriteFile or ReadFile as they complete async & there is no critical
  444. // section protecting access.To do this trace through all paths
  445. // thru which ReadFile and WriteFile are called and make sure no member
  446. // variables are accessed.
  447. m_idxRecips++;
  448. if(FAILED( hr = WriteFile(m_szBuffer, strlen(m_szBuffer) ) ) )
  449. {
  450. ErrorTrace( (LPARAM)this, "WriteFile return hr = 0x%x", hr );
  451. return( hr );
  452. }
  453. fPendingWrite = TRUE;
  454. break;
  455. }
  456. else
  457. {
  458. ErrorTrace( (LPARAM)this, "GetStringA for IMMPID_RP_ADDRESS_SMTP returns hr = 0x%x", hr );
  459. return( hr );
  460. }
  461. }
  462. else
  463. {
  464. m_idxRecips++;
  465. }
  466. }
  467. else
  468. {
  469. ErrorTrace( (LPARAM)this, "GetDWORD for IMMPID_RP_RECIPIENT_FLAGS returns hr = 0x%x", hr );
  470. return( hr );
  471. }
  472. }
  473. if( !fPendingWrite )
  474. {
  475. m_ReadState = DROPDIR_READ_DATA;
  476. m_WriteState = DROPDIR_WRITE_DATA;
  477. DebugTrace( (LPARAM)this, "Setting m_WriteState & m_ReadState to WRITE_DATA & READ_DATA respectively" );
  478. return( CopyMessage() );
  479. }
  480. return( hr );
  481. }
  482. ///////////////////////////////////////////////////////////////////////////
  483. HRESULT CDropDir::CopyMessage()
  484. {
  485. DECL_TRACE((LPARAM) this, "CDropDir::CopyMessage");
  486. // Make Sure that no class member variables are acessed after calling
  487. // WriteFile or ReadFile as they complete async & there is no critical
  488. // section protecting access.To do this trace through all paths
  489. // thru which ReadFile and WriteFile are called and make sure no member
  490. // variables are accessed.
  491. HRESULT hr = ReadFile( m_szBuffer, sizeof( m_szBuffer ) );
  492. if( hr == HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ) )
  493. {
  494. m_WriteState = DROPDIR_WRITE_CRLF;
  495. DebugTrace( (LPARAM)this, "ReadFile returns err = ERROR_HANDLE_EOF, Setting m_WriteState = DROPDIR_WRITE_CRLF" );
  496. return( DoLastFileOp() );
  497. }
  498. return( hr );
  499. }
  500. ///////////////////////////////////////////////////////////////////////////
  501. HRESULT CDropDir::OnCopyMessageRead( DWORD dwBytesRead, DWORD dwErr )
  502. {
  503. DECL_TRACE((LPARAM) this, "CDropDir::OnCopyMessageRead");
  504. HRESULT hr = S_OK;
  505. if (dwBytesRead )
  506. {
  507. if (dwBytesRead > 4)
  508. {
  509. CopyMemory(m_acLastBytes, &m_szBuffer[dwBytesRead-5], 5);
  510. }
  511. else
  512. {
  513. MoveMemory(m_acLastBytes, &m_acLastBytes[dwBytesRead], 5-dwBytesRead);
  514. CopyMemory(&m_acLastBytes[5-dwBytesRead], m_szBuffer, dwBytesRead);
  515. }
  516. //
  517. // the write file will complete async & then we'll do the
  518. // last fileop.
  519. //
  520. if( dwErr == ERROR_HANDLE_EOF )
  521. {
  522. DebugTrace( (LPARAM)this, " Interesting case err = ERROR_HANDLE_EOF, Setting m_WriteState = DROPDIR_WRITE_CRLF" );
  523. m_WriteState = DROPDIR_WRITE_CRLF;
  524. }
  525. // Make Sure that no class member variables are acessed after calling
  526. // WriteFile or ReadFile as they complete async & there is no critical
  527. // section protecting access.To do this trace through all paths
  528. // thru which ReadFile and WriteFile are called and make sure no member
  529. // variables are accessed.
  530. if ( FAILED( hr = WriteFile(m_szBuffer, dwBytesRead ) ) )
  531. {
  532. ErrorTrace( (LPARAM)this, "WriteFile return hr = 0x%x", hr );
  533. return( hr );
  534. }
  535. }
  536. else
  537. {
  538. m_WriteState = DROPDIR_WRITE_CRLF;
  539. DebugTrace( (LPARAM)this, "Setting m_WriteState = DROPDIR_WRITE_CRLF" );
  540. return( DoLastFileOp() );
  541. }
  542. return( hr );
  543. }
  544. ///////////////////////////////////////////////////////////////////////////
  545. HRESULT CDropDir::OnCopyMessageWrite( DWORD cbWritten )
  546. {
  547. DECL_TRACE((LPARAM) this, "CDropDir::OnCopyMessageWrite");
  548. HRESULT hr = S_OK;
  549. m_cbMsgWritten += cbWritten;
  550. switch( m_WriteState )
  551. {
  552. case DROPDIR_WRITE_DATA:
  553. return( CopyMessage() );
  554. break;
  555. case DROPDIR_WRITE_CRLF:
  556. return( DoLastFileOp() );
  557. break;
  558. case DROPDIR_WRITE_SETPOS:
  559. return( AdjustFilePos() );
  560. break;
  561. default:
  562. _ASSERT(!"Invalid WriteState");
  563. ErrorTrace( (LPARAM)this, "Invalid WriteState" );
  564. hr = E_INVALIDARG;
  565. break;
  566. }
  567. return( hr );
  568. }
  569. ///////////////////////////////////////////////////////////////////////////
  570. HRESULT CDropDir::DoLastFileOp()
  571. {
  572. DECL_TRACE((LPARAM) this, "CDropDir::DoLastFileOp");
  573. HRESULT hr = S_OK;
  574. DebugTrace( (LPARAM)this, "DoLastFileOp called" );
  575. // Now, see if the file ends with a CRLF, if not, add it
  576. if ((m_cbMsgWritten > 1) && memcmp(&m_acLastBytes[3], &m_acCrLfDotCrLf[3], 2))
  577. {
  578. //
  579. // the write will complete async. set next write state
  580. //
  581. DebugTrace( (LPARAM)this, "Setting m_WriteState = DROPDIR_WRITE_SETPOS" );
  582. m_WriteState = DROPDIR_WRITE_SETPOS;
  583. // Make Sure that no class member variables are acessed after calling
  584. // WriteFile or ReadFile as they complete async & there is no critical
  585. // section protecting access.To do this trace through all paths
  586. // thru which ReadFile and WriteFile are called and make sure no member
  587. // variables are accessed.
  588. // Add the trailing CRLF
  589. if(FAILED( hr = WriteFile(m_acCrLfDotCrLf, 2 ) ) )
  590. {
  591. ErrorTrace( (LPARAM)this, "WriteFile returns hr = 0x%x", hr );
  592. return(hr);
  593. }
  594. }
  595. else
  596. {
  597. DebugTrace( (LPARAM)this, "Setting m_WriteState = DROPDIR_WRITE_SETPOS" );
  598. m_WriteState = DROPDIR_WRITE_SETPOS;
  599. return( AdjustFilePos() );
  600. }
  601. return( hr );
  602. }
  603. ///////////////////////////////////////////////////////////////////////////
  604. HRESULT CDropDir::AdjustFilePos()
  605. {
  606. DECL_TRACE((LPARAM) this, "CDropDir::AdjustFilePos");
  607. HRESULT hr = S_OK;
  608. DebugTrace( (LPARAM)this, "AdjustFilePos called" );
  609. //If file ends with CRLF.CRLF, remove the trailing .CRLF
  610. //Do not modify the file otherwise
  611. DWORD dwLo = LODWORD( m_cbWriteOffset );
  612. DWORD dwHi = HIDWORD( m_cbWriteOffset );
  613. if ((m_cbMsgWritten > 4) && !memcmp(m_acLastBytes, m_acCrLfDotCrLf, 5))
  614. {
  615. DebugTrace( (LPARAM)this, "Removing the trailing CRLF-DOT-CRLF" );
  616. // Remove the trailing .CRLF
  617. if ((SetFilePointer(m_hDrop, dwLo-3, (LONG*)&dwHi, FILE_BEGIN) == 0xffffffff) ||
  618. !SetEndOfFile(m_hDrop))
  619. {
  620. hr = HRESULT_FROM_WIN32( GetLastError() );
  621. ErrorTrace( (LPARAM)this, "SetFilePointer or SetEndofFile return hr = 0x%x", hr );
  622. return( hr );
  623. }
  624. }
  625. //We need to flush the context before ACK'ing the message
  626. if (!FlushFileBuffers(m_hDrop))
  627. {
  628. hr = HRESULT_FROM_WIN32( GetLastError() );
  629. if (SUCCEEDED(hr))
  630. hr = E_FAIL;
  631. ErrorTrace( (LPARAM)this, "FlushFileBuffers failed with hr = 0x%x", hr );
  632. return( hr );
  633. }
  634. hr = SetAllRcptsHandled();
  635. if( FAILED( hr ) )
  636. {
  637. ErrorTrace( (LPARAM)this, "SetAllRcptsHandledreturn hr = 0x%x", hr );
  638. return( hr );
  639. }
  640. return( hr );
  641. }
  642. //////////////////////////////////////////////////////////////////////////////
  643. BOOL CDropDir::CheckIfAllRcptsHandled()
  644. {
  645. DECL_TRACE((LPARAM) this, "CDropDir::CheckIfAllRcptsHandled");
  646. BOOL fRet = TRUE;
  647. for( DWORD i = 0; i < m_NumRcpts; i++ )
  648. {
  649. if ( m_rgRcptIndexList[i] != INVALID_RCPT_IDX_VALUE)
  650. {
  651. DWORD dwRecipientFlags = 0;
  652. HRESULT hr = m_pIMsgRecips->GetDWORD(m_rgRcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  653. if (FAILED(hr))
  654. {
  655. fRet = FALSE;
  656. break;
  657. }
  658. if( RP_HANDLED != ( dwRecipientFlags & RP_HANDLED ) )
  659. {
  660. fRet = FALSE;
  661. break;
  662. }
  663. }
  664. }
  665. return( fRet );
  666. }
  667. //////////////////////////////////////////////////////////////////////////////
  668. HRESULT CDropDir::SetAllRcptsHandled()
  669. {
  670. DECL_TRACE((LPARAM) this, "CDropDir::SetAllRcptsHandled");
  671. HRESULT hr = S_OK;
  672. for( DWORD i = 0; i < m_NumRcpts; i++ )
  673. {
  674. if (m_rgRcptIndexList[i] != INVALID_RCPT_IDX_VALUE)
  675. {
  676. DWORD dwRecipientFlags = 0;
  677. hr = m_pIMsgRecips->GetDWORD(m_rgRcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,&dwRecipientFlags);
  678. if (FAILED(hr))
  679. {
  680. break;
  681. }
  682. if( RP_HANDLED != ( dwRecipientFlags & RP_HANDLED ) )
  683. {
  684. dwRecipientFlags |= RP_DELIVERED;
  685. hr = m_pIMsgRecips->PutDWORD(m_rgRcptIndexList[i], IMMPID_RP_RECIPIENT_FLAGS,dwRecipientFlags);
  686. if (FAILED(hr))
  687. {
  688. break;
  689. }
  690. }
  691. }
  692. }
  693. return( hr );
  694. }