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.

2913 lines
88 KiB

  1. #define INCL_INETSRV_INCS
  2. #include "smtpinc.h"
  3. #include "dirnot.hxx"
  4. #include "headers.hxx"
  5. #include "timeconv.h"
  6. #include "smtpcli.hxx"
  7. //
  8. // ProgID for IMsg - this needs to be published in an SDK
  9. //
  10. #define TIMEOUT_INTERVAL 30
  11. #define DIRNOT_IP_ADDRESS "127.0.0.1"
  12. #define IMSG_PROGID L"Exchange.IMsg"
  13. #define MAILMSG_PROGID L"Exchange.MailMsg"
  14. extern void GenerateMessageId (char * Buffer, DWORD BuffLen);
  15. extern DWORD GetIncreasingMsgId();
  16. extern BOOL FindNextUnquotedOccurrence(char *lpszString,DWORD dwStringLength, char cSearch,char **ppszLocation);
  17. static char * Daynames[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  18. //Event used make write files blocking
  19. HANDLE g_hFileWriteEvent;
  20. //
  21. // provide memory for static declared in SMTP_DIRNOT
  22. //
  23. CPool CIoBuffer::Pool( DIRNOT_BUFFER_SIGNATURE );
  24. CPool CBuffer::Pool( DIRNOT_IO_BUFFER_SIGNATURE );
  25. int strcasecmp(char *s1, char *s2);
  26. int strncasecmp(char *s1, char *s2, int n);
  27. //+---------------------------------------------------------------
  28. //
  29. // Function: CBuffer
  30. //
  31. // Synopsis: constructor
  32. //
  33. // Arguments: void
  34. //
  35. // Returns: void
  36. //
  37. //----------------------------------------------------------------
  38. CBuffer::CBuffer( BOOL bEncrypted ) :
  39. m_dwSignature( DIRNOT_BUFFER_SIGNATURE ),
  40. m_bEncrypted( bEncrypted )
  41. {
  42. TraceFunctEnterEx( (LPARAM)this, "CBuffer::CBuffer" );
  43. //
  44. // allocate the IO Buffer for this CBuffer
  45. // allocator needs to call GetData to ensure m_pIoBuffer is not NULL
  46. //
  47. ZeroMemory (&m_Overlapped, sizeof(m_Overlapped));
  48. m_pIoBuffer = new CIoBuffer;
  49. m_Overlapped.pBuffer = this;
  50. m_cCount = 0;
  51. }
  52. //+---------------------------------------------------------------
  53. //
  54. // Function: CBuffer::~CBuffer
  55. //
  56. // Synopsis: frees associated IO buffer
  57. //
  58. // Arguments: void
  59. //
  60. // Returns: void
  61. //
  62. //----------------------------------------------------------------
  63. CBuffer::~CBuffer( void )
  64. {
  65. TraceFunctEnterEx( (LPARAM)this, "CBuffer::~CBuffer" );
  66. //
  67. // delete the IO Buffer for this CBuffer
  68. //
  69. if ( m_pIoBuffer != NULL )
  70. {
  71. delete m_pIoBuffer;
  72. m_pIoBuffer = NULL;
  73. }
  74. TraceFunctLeaveEx((LPARAM)this);
  75. }
  76. /*++
  77. Name:
  78. SMTP_DIRNOT::SMTP_DIRNOT
  79. Constructs a new SMTP connection object for the client
  80. connection given the client connection socket and socket
  81. address. This constructor is private. Only the Static
  82. member funtion, declared below, can call it.
  83. --*/
  84. SMTP_DIRNOT::SMTP_DIRNOT(SMTP_SERVER_INSTANCE * pInstance)
  85. {
  86. TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::SMTP_DIRNOT" );
  87. _ASSERT(pInstance != NULL);
  88. m_hDir = INVALID_HANDLE_VALUE;
  89. m_pAtqContext = NULL;
  90. m_cPendingIoCount = 0;
  91. m_cDirChangeIoCount = 0;
  92. m_cActiveThreads = 0;
  93. m_pInstance = pInstance;
  94. //m_pRetryQ = NULL;
  95. m_Signature = SMTP_DIRNOT_SIGNATURE_VALID;
  96. InitializeCriticalSection (&m_CritFindLock);
  97. g_hFileWriteEvent = INVALID_HANDLE_VALUE;
  98. m_FindThreads = 0;
  99. m_FindFirstHandle = INVALID_HANDLE_VALUE;
  100. m_bDelayedFind = FALSE;
  101. TraceFunctLeaveEx((LPARAM)this);
  102. }
  103. SMTP_DIRNOT::~SMTP_DIRNOT (void)
  104. {
  105. PATQ_CONTEXT pAtqContext = NULL;
  106. HANDLE hTemp = INVALID_HANDLE_VALUE;
  107. TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::~SMTP_DIRNOT" );
  108. _ASSERT(GetThreadCount() == 0);
  109. //release the context from Atq
  110. pAtqContext = (PATQ_CONTEXT)InterlockedExchangePointer( (PVOID *)&m_pAtqContext, (PVOID) NULL);
  111. if ( pAtqContext != NULL )
  112. {
  113. pAtqContext->hAsyncIO = NULL;
  114. AtqFreeContext( pAtqContext, TRUE );
  115. }
  116. // Invalidate the signature. since this connection is trashed.
  117. m_Signature = SMTP_DIRNOT_SIGNATURE_FREE;
  118. hTemp = (HANDLE)InterlockedExchangePointer( (PVOID *)&g_hFileWriteEvent, (PVOID) INVALID_HANDLE_VALUE);
  119. if ( hTemp != INVALID_HANDLE_VALUE )
  120. {
  121. CloseHandle(hTemp);
  122. }
  123. DeleteCriticalSection (&m_CritFindLock);
  124. TraceFunctLeaveEx((LPARAM)this);
  125. }
  126. void SMTP_DIRNOT::SetPickupRetryQueueEvent(void)
  127. {
  128. TraceFunctEnterEx((LPARAM)this, "SMTP_SERVER_INSTANCE::SetPickupRetryQueueEvent");
  129. //if(m_pRetryQ)
  130. //{
  131. // m_pRetryQ->SetQueueEvent();
  132. //}
  133. TraceFunctLeaveEx((LPARAM)this);
  134. }
  135. BOOL SMTP_DIRNOT::InitializeObject (char *DirPickupName, ATQ_COMPLETION pfnCompletion)
  136. {
  137. DWORD error = 0;
  138. //PATQ_CONT pContext;
  139. HANDLE StopHandle;
  140. DWORD i;
  141. TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::InitializeObject" );
  142. _ASSERT(m_pInstance != NULL);
  143. //open the directory
  144. m_hDir = CreateFile (DirPickupName, FILE_LIST_DIRECTORY,
  145. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  146. NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
  147. NULL);
  148. if(m_hDir == INVALID_HANDLE_VALUE)
  149. {
  150. ErrorTrace((LPARAM) this, "CreateFile on %s failed with error %d ", DirPickupName, GetLastError());
  151. TraceFunctLeaveEx((LPARAM) this);
  152. return FALSE;
  153. }
  154. g_hFileWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  155. if(g_hFileWriteEvent == INVALID_HANDLE_VALUE)
  156. {
  157. ErrorTrace((LPARAM) this, "CreateEvent() failed for FileWriteEvent with error %d ", GetLastError());
  158. TraceFunctLeaveEx((LPARAM) this);
  159. return FALSE;
  160. }
  161. StopHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
  162. if(StopHandle == NULL)
  163. {
  164. ErrorTrace((LPARAM) this, "CreateEvent() failed with error %d ", GetLastError());
  165. TraceFunctLeaveEx((LPARAM) this);
  166. return FALSE;
  167. }
  168. //add it to our Gibraltar interface
  169. if(!AtqAddAsyncHandle( &m_pAtqContext, NULL, this, pfnCompletion,
  170. INFINITE, m_hDir))
  171. {
  172. error = GetLastError();
  173. ErrorTrace((LPARAM) this, "AtqAddAsyncHandle on %s failed with error %d ", DirPickupName, GetLastError());
  174. CloseHandle (m_hDir);
  175. CloseHandle (StopHandle);
  176. m_hDir = INVALID_HANDLE_VALUE;
  177. StopHandle = NULL;
  178. TraceFunctLeaveEx((LPARAM) this);
  179. return FALSE;
  180. }
  181. QuerySmtpInstance()->SetDirnotStopHandle(StopHandle);
  182. //
  183. // pend a set of outstanding directory change notifications, at all times, we want
  184. // at least one notification outstanding, so pend 3 or 4
  185. //
  186. for (i = 0; i < OUTSTANDING_NOTIFICATIONS; i++)
  187. {
  188. if(!PendDirChangeNotification ())
  189. {
  190. ErrorTrace((LPARAM) this, "PendDirChangeNotification on failed with error %d ", GetLastError());
  191. ErrorTrace((LPARAM) this, "Setting stop handle because PendDirChangeNotification () failed");
  192. SetEvent(StopHandle);
  193. TraceFunctLeaveEx((LPARAM) this);
  194. return FALSE;
  195. }
  196. }
  197. TraceFunctLeaveEx((LPARAM) this);
  198. return TRUE;
  199. }
  200. void SMTP_DIRNOT::CloseDirHandle (void)
  201. {
  202. HANDLE hDir = NULL;
  203. int i = 0;
  204. int Count = 0;
  205. int AfterSleepCount = 0;
  206. DWORD dwLastAllocCount = 0;
  207. DWORD dwAllocCount = 0;
  208. DWORD dwStopHint = 2;
  209. DWORD dwTickCount = 0;
  210. TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::CloseDirHandle" );
  211. _ASSERT(m_pInstance != NULL);
  212. hDir = (HANDLE) InterlockedExchangePointer((PVOID *) &m_hDir, NULL);
  213. if(hDir != NULL)
  214. {
  215. CloseHandle (hDir);
  216. }
  217. //
  218. // need to check Pool.GetAllocCount instead of InUseList.Empty
  219. // because alloc goes to zero during the delete operator
  220. // instead of during the destructor
  221. //
  222. //
  223. dwTickCount = GetTickCount();
  224. for( i = 0; i < 240; i++ )
  225. {
  226. dwAllocCount = (DWORD) QuerySmtpInstance()->GetCBufferAllocCount ();
  227. if ( dwAllocCount == 0)
  228. {
  229. DebugTrace((LPARAM)this, "All pickup CBuffers are gone!");
  230. break;
  231. }
  232. Sleep( 1000 );
  233. // Update the stop hint checkpoint when we get within 1 second (1000 ms), of the timeout...
  234. if ((SERVICE_STOP_WAIT_HINT - 1000) < (GetTickCount() - dwTickCount) && g_pInetSvc &&
  235. (g_pInetSvc->QueryCurrentServiceState() == SERVICE_STOP_PENDING))
  236. {
  237. DebugTrace((LPARAM)this, "Updating stop hint in pickup, checkpoint = %u", dwStopHint);
  238. g_pInetSvc->UpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, dwStopHint,
  239. SERVICE_STOP_WAIT_HINT ) ;
  240. dwStopHint++ ;
  241. dwTickCount = GetTickCount();
  242. }
  243. DebugTrace((LPARAM)this, "Alloc counts: current = %u, last = %u;",
  244. dwAllocCount, dwLastAllocCount);
  245. if (dwAllocCount < dwLastAllocCount)
  246. {
  247. DebugTrace((LPARAM)this, "Pickup CBuffers are going away, reseting i");
  248. i = 0;
  249. }
  250. dwLastAllocCount = dwAllocCount;
  251. }
  252. DebugTrace((LPARAM)this, "Waiting for QuerySmtpInstance()->GetDirnotStopHandle()!");
  253. WaitForSingleObject(QuerySmtpInstance()->GetDirnotStopHandle(), INFINITE);
  254. DebugTrace((LPARAM)this, "End waiting for QuerySmtpInstance()->GetDirnotStopHandle()!");
  255. QuerySmtpInstance()->SetStopHint(2);
  256. TraceFunctLeaveEx((LPARAM)this);
  257. }
  258. /*++
  259. Name :
  260. SMTP_DIRNOT::CreateSmtpDirNotification
  261. Description:
  262. This is the static member function than is the only
  263. entity that is allowed to create an SMTP_CONNOUT
  264. class. This class cannot be allocated on the stack.
  265. Arguments:
  266. Returns:
  267. A pointer to an SMTP_DIRNOT class or NULL
  268. --*/
  269. SMTP_DIRNOT * SMTP_DIRNOT::CreateSmtpDirNotification (char * DirPickupName,
  270. ATQ_COMPLETION pfnCompletion,
  271. SMTP_SERVER_INSTANCE * pInstance)
  272. {
  273. SMTP_DIRNOT * pSmtpDirNotObj;
  274. TraceFunctEnterEx((LPARAM) 0, "SMTP_CONNOUT::CreateSmtpConnection");
  275. pSmtpDirNotObj = new SMTP_DIRNOT (pInstance);
  276. if(pSmtpDirNotObj == NULL)
  277. {
  278. ErrorTrace(0, "new SMTP_DIRNOT () failed");
  279. TraceFunctLeaveEx((LPARAM)NULL);
  280. return NULL;
  281. }
  282. if(!pSmtpDirNotObj->InitializeObject(pSmtpDirNotObj->QuerySmtpInstance()->GetMailPickupDir(), SMTP_DIRNOT::ReadDirectoryCompletion))
  283. {
  284. TraceFunctLeaveEx((LPARAM)NULL);
  285. return NULL;
  286. }
  287. TraceFunctLeaveEx((LPARAM)NULL);
  288. return pSmtpDirNotObj;
  289. }
  290. BOOL SMTP_DIRNOT::DoFindFirstFile(BOOL bIISThread)
  291. {
  292. //
  293. // re-entrent FindFirst... the first thread does the FindFirst, all other threads up to
  294. // MAXFIND_THREADS do the FindNext.
  295. //
  296. char Buffer [MAX_PATH + 1];
  297. HANDLE hFindFile = INVALID_HANDLE_VALUE;
  298. DWORD BytesRead = 0;
  299. DWORD NumFiles = 0;
  300. WIN32_FIND_DATA find;
  301. BOOL bClosed;
  302. PSMTP_IIS_SERVICE pService;
  303. TraceFunctEnterEx((LPARAM)this, "DoFindFirstFile");
  304. _ASSERT(m_pInstance != NULL);
  305. if (!QuerySmtpInstance()->GetAcceptConnBool())
  306. {
  307. return TRUE;
  308. }
  309. pService = (PSMTP_IIS_SERVICE) g_pInetSvc;
  310. //
  311. // ensure only one thread gets in here at a time. we can only have one thread either setting
  312. // the find first at a time.
  313. //
  314. LockFind();
  315. hFindFile = GetFindFirstHandle();
  316. if (hFindFile == INVALID_HANDLE_VALUE)
  317. {
  318. //make up the file spec we want to find
  319. lstrcpy(Buffer, QuerySmtpInstance()->GetMailPickupDir());
  320. lstrcat(Buffer, "*.*");
  321. hFindFile = FindFirstFile(Buffer, &find);
  322. if (hFindFile == INVALID_HANDLE_VALUE)
  323. {
  324. // should not fail as we look for *.* and the directory root should be there.
  325. // setting the flag will make sure that next drop posts a findfirst.
  326. SetDelayedFindNotification(TRUE);
  327. ErrorTrace((LPARAM) this, "FindFirst failed for %s. Error %d", Buffer, GetLastError());
  328. UnLockFind();
  329. TraceFunctLeaveEx((LPARAM)this);
  330. return TRUE;
  331. }
  332. else
  333. {
  334. IncFindThreads(); // there should be not find threads running at this point.
  335. //
  336. // We have no IIS threads available for the single findfirst... we must create a thread.
  337. // hopefull this will happen seldom.
  338. SetFindFirstHandle(hFindFile);
  339. SetDelayedFindNotification(FALSE);
  340. }
  341. }
  342. else
  343. {
  344. SetDelayedFindNotification(TRUE);
  345. if (!IncFindThreads())
  346. {
  347. UnLockFind();
  348. DebugTrace((LPARAM)this, "Have hit the max num Find Threads.");
  349. TraceFunctLeaveEx((LPARAM)this);
  350. return TRUE;
  351. }
  352. if (!FindNextFile(hFindFile, &find))
  353. {
  354. if (GetLastError() != ERROR_NO_MORE_FILES)
  355. {
  356. SetDelayedFindNotification(TRUE);
  357. ErrorTrace((LPARAM) this,"FindNextFile() failed with error %d", GetLastError());
  358. }
  359. CloseFindHandle(); // will DecFindThreads.
  360. //
  361. // In the case below, it is possible that some files were missed by FindFirst.
  362. // Create an ATQ thread for a final findfirst iteration to make sure.
  363. //
  364. if ((GetNumFindThreads() == 0) && GetDelayedFindNotification())
  365. {
  366. IncPendingIoCount();
  367. // AtqContext with buffer size of zero to get a FindFirst Going.
  368. if(!AtqPostCompletionStatus(QueryAtqContext(), 0))
  369. {
  370. DecPendingIoCount();
  371. ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError());
  372. }
  373. }
  374. UnLockFind();
  375. TraceFunctLeaveEx((LPARAM)this);
  376. return TRUE;
  377. }
  378. }
  379. UnLockFind();
  380. bClosed = FALSE;
  381. do
  382. {
  383. //format the name of the stream and then open the file.
  384. BytesRead = wsprintf(Buffer, "%s%s",QuerySmtpInstance()->GetMailPickupDir(), find.cFileName);
  385. if (!(find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  386. {
  387. HRESULT hr = S_OK;
  388. IMailMsgProperties *pIMsg = NULL;
  389. hr = CoCreateInstance(CLSID_MsgImp, NULL, CLSCTX_INPROC_SERVER,
  390. IID_IMailMsgProperties, (LPVOID *)&pIMsg);
  391. // Next, check if we are over the inbound cutoff limit. If so, we will release the message
  392. // and not proceed.
  393. if (SUCCEEDED(hr))
  394. {
  395. DWORD dwCreationFlags;
  396. hr = pIMsg->GetDWORD(
  397. IMMPID_MPV_MESSAGE_CREATION_FLAGS,
  398. &dwCreationFlags);
  399. if (FAILED(hr) ||
  400. (dwCreationFlags & MPV_INBOUND_CUTOFF_EXCEEDED))
  401. {
  402. // If we fail to get this property of if the inbound cutoff
  403. // exceeded flag is set, discard the message and return failure
  404. if (SUCCEEDED(hr))
  405. {
  406. DebugTrace((LPARAM)this, "Failing because inbound cutoff reached");
  407. hr = E_OUTOFMEMORY;
  408. }
  409. pIMsg->Release();
  410. pIMsg = NULL;
  411. }
  412. }
  413. DebugTrace((LPARAM)this,"Found file %s", find.cFileName);
  414. if ((pIMsg == NULL) || FAILED(hr))
  415. {
  416. // We are out of resources, there is absolutely nothing
  417. // we can do: can't NDR, can't retry ...
  418. ErrorTrace((LPARAM) this, "new MAILQ_ENTRY failed for file: %s",
  419. find.cFileName);
  420. //
  421. // Will want to run a findfirst when things free up a little. Flag the post-processing findfirst.
  422. //
  423. SetDelayedFindNotification(TRUE);
  424. IncPendingIoCount ();
  425. AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_TIMEOUT, TIMEOUT_INTERVAL); //retry after a while
  426. ErrorTrace((LPARAM)this, "Failed to create message will retry later.");
  427. break;
  428. }
  429. else
  430. {
  431. // We are in faith that upon delivery, the allocated
  432. // MailQEntry structure will be freed
  433. NumFiles++;
  434. pIMsg->PutStringA(IMMPID_MP_PICKUP_FILE_NAME, find.cFileName);
  435. if(!ProcessFile(pIMsg))
  436. {
  437. // will be mail left in pickup. queue a findfirst to take care of it.
  438. SetDelayedFindNotification(TRUE);
  439. }
  440. }
  441. }
  442. LockFind();
  443. if (!FindNextFile(hFindFile, &find))
  444. {
  445. if (GetLastError() != ERROR_NO_MORE_FILES)
  446. {
  447. SetDelayedFindNotification(TRUE);
  448. ErrorTrace((LPARAM) this,"FindNextFile() failed with error %d", GetLastError());
  449. }
  450. CloseFindHandle();
  451. //
  452. // In the case below, it is possible that some files were missed by FindFirst.
  453. // Create an ATQ thread for a final findfirst iteration to make sure.
  454. //
  455. if ((GetNumFindThreads() == 0) && GetDelayedFindNotification())
  456. {
  457. IncPendingIoCount();
  458. // AtqContext with buffer size of zero to get a FindFirst Going.
  459. if(!AtqPostCompletionStatus(QueryAtqContext(), 0))
  460. {
  461. DecPendingIoCount();
  462. ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError());
  463. }
  464. }
  465. UnLockFind();
  466. bClosed = TRUE;
  467. break;
  468. }
  469. UnLockFind();
  470. }
  471. while ((!QuerySmtpInstance()->IsShuttingDown())
  472. && (QuerySmtpInstance()->QueryServerState( ) != MD_SERVER_STATE_STOPPED)
  473. && (QuerySmtpInstance()->QueryServerState( ) != MD_SERVER_STATE_INVALID));
  474. if (!bClosed) // termination by the while condition above.
  475. {
  476. LockFind();
  477. CloseFindHandle();
  478. UnLockFind();
  479. }
  480. TraceFunctLeaveEx((LPARAM)this);
  481. return TRUE;
  482. }
  483. DWORD WINAPI SMTP_DIRNOT::CreateNonIISFindThread(void * ClassPtr)
  484. {
  485. //
  486. // Called by a CreateThread when we have run out of IIS threads.
  487. //
  488. SMTP_DIRNOT * ThisPtr = (SMTP_DIRNOT *) ClassPtr;
  489. TraceFunctEnterEx((LPARAM) ThisPtr,"CreateNonIISFindThread");
  490. _ASSERT(ThisPtr != NULL);
  491. _ASSERT(ThisPtr->QuerySmtpInstance() != NULL);
  492. //
  493. // Build the initial list - THE FLAG bIISThread IS SET TO FALSE.
  494. // We IncCBufferAllocCount and DecCBufferAllocCount to make sure that we clean up properly in CloseDirHandle.
  495. // We don't want to destroy the Dirnot Object before this thread finishes.
  496. //
  497. ThisPtr->QuerySmtpInstance()->IncCBufferObjs();
  498. ThisPtr->DoFindFirstFile(FALSE);
  499. ThisPtr->QuerySmtpInstance()->DecCBufferObjs();
  500. TraceFunctLeaveEx((LPARAM)ThisPtr);
  501. return TRUE;
  502. }
  503. DWORD WINAPI SMTP_DIRNOT::PickupInitialFiles(void * ClassPtr)
  504. {
  505. SMTP_DIRNOT * ThisPtr = (SMTP_DIRNOT *) ClassPtr;
  506. TraceFunctEnterEx((LPARAM) ThisPtr,"PickupInitialFiles");
  507. _ASSERT(ThisPtr != NULL);
  508. _ASSERT(ThisPtr->QuerySmtpInstance() != NULL);
  509. // Just quit if we are suhtting down already
  510. if (ThisPtr->QuerySmtpInstance()->IsShuttingDown())
  511. return TRUE;
  512. // Build the initial list
  513. ThisPtr->DoFindFirstFile();
  514. TraceFunctLeaveEx((LPARAM)ThisPtr);
  515. return TRUE;
  516. }
  517. #define PRIVATE_OPTIMAL_BUFFER_SIZE 4096
  518. #define PRIVATE_LINE_BUFFER_SIZE 1024
  519. #define IS_SPACE_OR_TAB(ch) (((ch) == ' ') || ((ch) == '\t'))
  520. #define IS_WHITESPACE_OR_CRLF(ch) (((ch) == ' ') || ((ch) == '\t') || ((ch) == '\n') || ((ch) == '\r'))
  521. static void pReplaceCrLfWithSpaces(CHAR *szIn, CHAR *szOut)
  522. {
  523. while (*szIn)
  524. {
  525. if ((*szIn == '\r') || (*szIn == '\n'))
  526. *szOut++ = ' ';
  527. else
  528. *szOut++ = *szIn;
  529. szIn++;
  530. }
  531. *szOut = '\0';
  532. }
  533. BOOL SMTP_DIRNOT::CreateToList (char *AddrsList, IMailMsgRecipientsAdd *pIMsgRecips, IMailMsgProperties *pIMsgProps)
  534. {
  535. char *p = NULL; //points to the ',' or '\0'
  536. char * StartOfAddress = NULL; //start of recipient address
  537. char * EndOfAddress = NULL; // end of recipient address
  538. char * ThisAddress = NULL;
  539. CAddr * NewAddress = NULL; //new CAddr to add to our list
  540. char szAddress[MAX_INTERNET_NAME + 1], *pszAddress = szAddress;
  541. DWORD dwPropId = IMMPID_RP_ADDRESS_SMTP;
  542. DWORD dwNewRecipIndex = 0;
  543. HRESULT hr = S_OK;
  544. BOOL fNotFound = FALSE;
  545. TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::CreateToList");
  546. _ASSERT(m_pInstance != NULL);
  547. //start at the top of the list
  548. p = AddrsList;
  549. //get rid of leading white space, newlines, and commas
  550. while ((*p == ',') || IS_WHITESPACE_OR_CRLF(*p))
  551. p++;
  552. while((p != NULL) && (*p != '\0'))
  553. {
  554. char LastChar;
  555. StartOfAddress = p;
  556. // Find the ending delimiter of the address
  557. //while((*p != '\0') && (*p != ','))
  558. // p++;
  559. // The first unquoted comma indicates the end of the address
  560. if(!FindNextUnquotedOccurrence(p,strlen(p),',',&EndOfAddress))
  561. {
  562. SetLastError(ERROR_INVALID_DATA);
  563. NewAddress = NULL;
  564. ErrorTrace((LPARAM) this, "Failed to parse out the address");
  565. return FALSE;
  566. }
  567. else if(!EndOfAddress)
  568. EndOfAddress = p + strlen(p);
  569. p = EndOfAddress;
  570. _ASSERT(EndOfAddress != NULL);
  571. // We don't like trailing spaces either, so walk backwards
  572. // to get rid of them
  573. //EndOfAddress = p;
  574. while (EndOfAddress > StartOfAddress)
  575. {
  576. EndOfAddress--;
  577. if (!IS_WHITESPACE_OR_CRLF(*EndOfAddress))
  578. {
  579. EndOfAddress++;
  580. break;
  581. }
  582. }
  583. // Save the character we are about to overrite
  584. LastChar = *EndOfAddress;
  585. // NULL terminate the address
  586. *EndOfAddress = '\0';
  587. if(lstrlen(StartOfAddress) > (MAX_INTERNET_NAME + 1))
  588. {
  589. SetLastError(ERROR_INVALID_DATA);
  590. NewAddress = NULL;
  591. ErrorTrace((LPARAM) this, "Address too long : %d bytes",lstrlen(StartOfAddress));
  592. return FALSE;
  593. }
  594. pReplaceCrLfWithSpaces(StartOfAddress, szAddress);
  595. DebugTrace((LPARAM) this, "found address [%s]", szAddress);
  596. //
  597. // Run it through the addr821 library
  598. //
  599. NewAddress = CAddr::CreateAddress(szAddress);
  600. BOOL fValidAddress = FALSE;
  601. if(NewAddress)
  602. {
  603. if(!NewAddress->IsDomainOffset())
  604. {
  605. CAddr * TempAddress = NULL;
  606. TempAddress = QuerySmtpInstance()->AppendLocalDomain (NewAddress);
  607. if (TempAddress)
  608. {
  609. delete NewAddress;
  610. NewAddress = TempAddress;
  611. TempAddress = NULL;
  612. } else if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
  613. ErrorTrace((LPARAM) this, "CAddr::CreateAddress (StartOfAddress) failed . err: %u", ERROR_NOT_ENOUGH_MEMORY);
  614. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  615. return FALSE;
  616. }
  617. }
  618. DebugTrace((LPARAM) this, "CreateAddress returned %s", NewAddress->GetAddress());
  619. ThisAddress = NewAddress->GetAddress();
  620. DWORD dwLen = strlen(ThisAddress);
  621. if(Validate821Address(
  622. ThisAddress,
  623. dwLen)) {
  624. LPSTR pszDomain;
  625. if(Get821AddressDomain(
  626. ThisAddress,
  627. dwLen,
  628. &pszDomain) && pszDomain)
  629. {
  630. DWORD dwDomain = strlen(pszDomain);
  631. if (Validate821Domain(pszDomain, dwDomain)) {
  632. // everything is valid
  633. fValidAddress = TRUE;
  634. }
  635. } else {
  636. ErrorTrace((LPARAM)0, "Detected legal address without a domain: %s",
  637. ThisAddress);
  638. }
  639. } else {
  640. ErrorTrace((LPARAM)0, "Detected ILLEGAL address: %s",
  641. ThisAddress);
  642. }
  643. } else {
  644. if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
  645. ErrorTrace((LPARAM) this, "CAddr::CreateAddress (StartOfAddress) failed . err: %u", ERROR_NOT_ENOUGH_MEMORY);
  646. return FALSE;
  647. }
  648. ThisAddress = szAddress;
  649. fValidAddress = FALSE;
  650. }
  651. hr = pIMsgRecips->AddPrimary(1, (LPCTSTR *) &ThisAddress, &dwPropId, &dwNewRecipIndex, NULL, 0);
  652. if (FAILED(hr)) {
  653. SetLastError(hr);
  654. return FALSE;
  655. }
  656. if (NewAddress) delete NewAddress;
  657. if (!fValidAddress) {
  658. // AQ will look for this special domain and won't add it to the
  659. // DMT. This stops us from putting corrupted domains into the
  660. // DMT
  661. hr = pIMsgRecips->PutStringA(dwNewRecipIndex,
  662. IMMPID_RP_DOMAIN,
  663. "==NDR==");
  664. if (FAILED(hr)) {
  665. DebugTrace((LPARAM) 0, "PutString(RP_DOMAIN) failed 0x%x", hr);
  666. SetLastError(hr);
  667. return FALSE;
  668. }
  669. // set the recipient to NDR
  670. hr = pIMsgRecips->PutDWORD(dwNewRecipIndex,
  671. IMMPID_RP_RECIPIENT_FLAGS,
  672. (RP_ERROR_CONTEXT_CAT | RP_UNRESOLVED));
  673. if (FAILED(hr)) {
  674. DebugTrace((LPARAM) 0, "PutDWORD(RP_FLAGS) failed 0x%x", hr);
  675. SetLastError(hr);
  676. return FALSE;
  677. }
  678. // tell AQ why it is NDRing
  679. hr = pIMsgRecips->PutDWORD(dwNewRecipIndex,
  680. IMMPID_RP_ERROR_CODE,
  681. CAT_E_ILLEGAL_ADDRESS);
  682. if (FAILED(hr)) {
  683. DebugTrace((LPARAM) 0, "PutDWORD(RP_ERROR) failed 0x%x", hr);
  684. SetLastError(hr);
  685. return FALSE;
  686. }
  687. // tell AQ that some recips are NDRing
  688. hr = pIMsgProps->PutDWORD(IMMPID_MP_HR_CAT_STATUS,
  689. CAT_W_SOME_UNDELIVERABLE_MSGS);
  690. if (FAILED(hr)) {
  691. DebugTrace((LPARAM) 0, "SetMailMsgCatStatus failed 0x%x", hr);
  692. SetLastError(hr);
  693. return FALSE;
  694. }
  695. }
  696. // Go find the start of the next address
  697. *EndOfAddress = LastChar;
  698. if(*p == ',')
  699. {
  700. p++;
  701. while(IS_WHITESPACE_OR_CRLF(*p))
  702. p++;
  703. }
  704. }
  705. TraceFunctLeave();
  706. return (TRUE);
  707. }
  708. static HANDLE pOpenPickupFile(LPSTR szFileName)
  709. {
  710. DWORD dwError;
  711. HANDLE hFile;
  712. TraceFunctEnterEx((LPARAM)NULL, "pOpenPickupFile");
  713. // Open the file
  714. hFile = CreateFile(szFileName,
  715. GENERIC_READ,
  716. FILE_SHARE_DELETE,
  717. NULL,
  718. OPEN_EXISTING,
  719. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  720. NULL );
  721. if (hFile == INVALID_HANDLE_VALUE)
  722. {
  723. dwError = GetLastError();
  724. ErrorTrace((LPARAM)NULL,
  725. "Error: Can't open source file %s (err=%d)...skipping it",
  726. szFileName, dwError);
  727. }
  728. TraceFunctLeaveEx((LPARAM)NULL);
  729. return(hFile);
  730. }
  731. static inline BOOL pFetchFileBuffer( HANDLE hFile,
  732. CHAR *lpBuffer,
  733. LPDWORD lpdwSize)
  734. {
  735. BOOL fRet = TRUE;
  736. DWORD Error = 0;
  737. fRet = ReadFile(hFile, lpBuffer, *lpdwSize, lpdwSize, NULL);
  738. if(!fRet)
  739. {
  740. Error = GetLastError();
  741. if(Error == 998)
  742. {
  743. _ASSERT(FALSE);
  744. }
  745. }
  746. return fRet;
  747. }
  748. static CHAR *pGetLineBuffer(CHAR *lpOldBuffer,
  749. DWORD *lpdwLength)
  750. {
  751. DWORD dwLength = *lpdwLength;
  752. CHAR *lpBuffer;
  753. if (lpOldBuffer)
  754. {
  755. // We have an old buffer, we double the size of the old buffer
  756. _ASSERT(lpdwLength);
  757. dwLength <<= 1;
  758. }
  759. else
  760. dwLength = PRIVATE_LINE_BUFFER_SIZE;
  761. // Allocate the new buffer
  762. lpBuffer = (CHAR *)HeapAlloc(GetProcessHeap(), 0, dwLength);
  763. if (!lpBuffer)
  764. return(NULL);
  765. if (lpOldBuffer)
  766. {
  767. // Copy the info over and free the old buffer
  768. CopyMemory((LPVOID)lpBuffer, (LPVOID)lpOldBuffer, *lpdwLength);
  769. _VERIFY( HeapFree(GetProcessHeap(), 0, lpOldBuffer) );
  770. }
  771. *lpdwLength = dwLength;
  772. return(lpBuffer);
  773. }
  774. static BOOL pFreeLineBuffer(CHAR *lpBuffer)
  775. {
  776. return( HeapFree(GetProcessHeap(), 0, lpBuffer) );
  777. }
  778. static CHAR *pGetValueFromHeader(CHAR *szHeader)
  779. {
  780. while (*szHeader && (*szHeader++ != ':'))
  781. ;
  782. while (*szHeader)
  783. {
  784. if (!IS_SPACE_OR_TAB(*szHeader))
  785. return(szHeader);
  786. else
  787. szHeader++;
  788. }
  789. return(NULL);
  790. }
  791. static CHAR *pReadNextLineFromBuffer( HANDLE hFile,
  792. CHAR *lpBuffer,
  793. LPDWORD lpdwSize,
  794. CHAR *lpStart,
  795. LPSTR *ppszLine,
  796. DWORD *lpdwMaxLineLen,
  797. LPSTR *ppOriginalBuffer,
  798. DWORD *lpdwOriginalBufferLen)
  799. {
  800. DWORD dwLineLen = 0;
  801. DWORD dwMaxLineLen = *lpdwMaxLineLen;
  802. BOOL fThisIsCR = FALSE;
  803. BOOL fLastIsCR = FALSE;
  804. BOOL fThisIsLF = FALSE;
  805. BOOL fLastIsLF = FALSE;
  806. CHAR *lpEnd;
  807. CHAR *lpszLine = *ppszLine;
  808. CHAR ch;
  809. BOOL bEndOfFile;
  810. TraceFunctEnter("pReadNextLineFromBuffer");
  811. _ASSERT(hFile != INVALID_HANDLE_VALUE);
  812. _ASSERT(!IsBadWritePtr(lpdwSize, sizeof(DWORD)));
  813. _ASSERT(!IsBadWritePtr(lpBuffer, *lpdwSize));
  814. _ASSERT(!IsBadWritePtr(*ppszLine, *lpdwMaxLineLen));
  815. // raid 181922/88855 - replace recusive loop with while loop.
  816. do {
  817. dwLineLen = 0;
  818. dwMaxLineLen = *lpdwMaxLineLen;
  819. fThisIsCR = FALSE;
  820. fLastIsCR = FALSE;
  821. fThisIsLF = FALSE;
  822. fLastIsLF = FALSE;
  823. bEndOfFile = FALSE;
  824. // Now, make sure the supplied start pointer is within
  825. // buffer supplied (We allow it to be one byte past the
  826. // end of the buffer, but we will never dereference it).
  827. if ((lpStart < lpBuffer) || (lpStart > (lpBuffer + *lpdwSize)))
  828. {
  829. _ASSERT(0);
  830. return(NULL);
  831. }
  832. // Now, keep copying until we hit one of the following scenarios:
  833. // i) We hit CRLF
  834. // ii) We hit the end of the buffer, which we reload more data
  835. // or if it is the end of the mail, we postpend a CRLF.
  836. lpEnd = lpBuffer + *lpdwSize;
  837. do
  838. {
  839. // See if this is past the buffer
  840. if (lpStart == lpEnd)
  841. {
  842. DWORD dwNewLength;
  843. dwNewLength = *lpdwSize;
  844. if (!pFetchFileBuffer(hFile, lpBuffer, &dwNewLength))
  845. {
  846. return(NULL);
  847. }
  848. // Done!
  849. if (!dwNewLength)
  850. {
  851. bEndOfFile = TRUE;
  852. break;
  853. }
  854. // Get the new buffer length
  855. *lpdwSize = dwNewLength;
  856. // Reset the start and end pointers
  857. lpStart = lpBuffer;
  858. lpEnd = lpBuffer + *lpdwSize;
  859. }
  860. ch = *lpszLine++ = *lpStart++;
  861. // Too long?
  862. if (++dwLineLen >= dwMaxLineLen)
  863. {
  864. CHAR *lpTemp;
  865. DWORD dwUsedPortion = (DWORD)(lpszLine - *ppOriginalBuffer);
  866. // Yep, get a bigger buffer, all the existing stuff is copied over
  867. DebugTrace((LPARAM)*ppOriginalBuffer, "Growing buffer at %u bytes", *lpdwOriginalBufferLen);
  868. lpTemp = pGetLineBuffer(*ppOriginalBuffer, lpdwOriginalBufferLen);
  869. if (!lpTemp)
  870. {
  871. DebugTrace((LPARAM)NULL, "Failed to obtain buffer (%u)", GetLastError());
  872. TraceFunctLeave();
  873. return(NULL);
  874. }
  875. // Got it, adjust all associated pointers and lengths
  876. DebugTrace((LPARAM)lpTemp, "Obtained buffer at %u bytes", *lpdwOriginalBufferLen);
  877. // New beginning of line buffer
  878. *ppOriginalBuffer = lpTemp;
  879. // New beginning of line
  880. *ppszLine = lpTemp;
  881. // New pointer to next character
  882. lpszLine = lpTemp + dwUsedPortion;
  883. // New maximum length for current string before re-growing
  884. dwMaxLineLen = *lpdwOriginalBufferLen - dwUsedPortion;
  885. }
  886. fLastIsCR = fThisIsCR;
  887. if (ch == '\r')
  888. fThisIsCR = TRUE;
  889. else
  890. fThisIsCR = FALSE;
  891. fLastIsLF = fThisIsLF;
  892. if (ch == '\n')
  893. fThisIsLF = TRUE;
  894. else
  895. fThisIsLF = FALSE;
  896. // If we have CRLF or LFCR we leave
  897. if ((fLastIsCR && fThisIsLF) || (fLastIsLF && fThisIsCR))
  898. break;
  899. } while (1);
  900. *lpszLine = '\0';
  901. // Calculate remaining buffer size
  902. *lpdwMaxLineLen = dwMaxLineLen - dwLineLen;
  903. // raid 178234 - If we have CRLF or LFCR and no more continue line we leave
  904. if (((fLastIsCR && fThisIsLF) || (fLastIsLF && fThisIsCR)) && !IS_SPACE_OR_TAB(*lpStart))
  905. {
  906. // raid 166777 - Make sure we do leave if we find a line.
  907. break;
  908. }
  909. } while (!bEndOfFile && (IS_SPACE_OR_TAB(*lpStart) || (lpStart == lpEnd)));
  910. // We always return the start of line to be the start of our buffer
  911. // Note that the buffer could have changed during recursion due to growth
  912. *ppszLine = *ppOriginalBuffer;
  913. TraceFunctLeave();
  914. return(lpStart);
  915. }
  916. BOOL inline WriteToSpooledFile(PFIO_CONTEXT hDstFile, CHAR *lpszLine, DWORD &DestOffset)
  917. {
  918. DWORD dwBytesToWrite;
  919. DWORD dwBytesWritten;
  920. BOOL fResult = FALSE;
  921. FH_OVERLAPPED ov;
  922. DWORD err = NO_ERROR;
  923. HANDLE HackedHandle = NULL;
  924. BOOL fRet = FALSE;
  925. ZeroMemory(&ov, sizeof(ov));
  926. ov.Offset = DestOffset;
  927. dwBytesToWrite = lstrlen(lpszLine);
  928. if (!dwBytesToWrite)
  929. {
  930. fRet = TRUE; //This is not really a failure
  931. goto Exit;
  932. }
  933. //HackedHandle = ((DWORD)g_hFileWriteEvent | 1);
  934. HackedHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
  935. if(HackedHandle == NULL)
  936. {
  937. return FALSE;
  938. }
  939. ov.hEvent = (HANDLE) ((ULONG_PTR) HackedHandle | 1);
  940. //ov.hEvent = (HANDLE)HackedHandle;
  941. fResult = FIOWriteFile(hDstFile, lpszLine, dwBytesToWrite, &ov);
  942. if (!fResult) err = GetLastError();
  943. if((err == ERROR_IO_PENDING) || (((DWORD)STATUS_PENDING == ov.Internal) && ( err == NO_ERROR )))
  944. {
  945. // 12/16/98 - MikeSwa Modified
  946. // OK... this is the theory... we are getting random AV, from what
  947. // looks like async completion to I/O. It looks like
  948. // GetOverlappedResult cannot be called to wait for a hacked handle,
  949. // and it will not use the first argument unless the event in the
  950. // overlapped structure is NULL. The solution is to call WaitForSingleObject
  951. // if we detect that the IO is still pending
  952. WaitForSingleObject(HackedHandle, INFINITE);
  953. _ASSERT((DWORD)STATUS_PENDING != ov.Internal);
  954. }
  955. else if (NO_ERROR != err)
  956. {
  957. SetLastError (err); //preserve the last error
  958. if(err == 998)
  959. {
  960. _ASSERT(FALSE);
  961. }
  962. goto Exit;
  963. }
  964. DestOffset += dwBytesToWrite;
  965. fRet = TRUE;
  966. Exit:
  967. if(HackedHandle)
  968. {
  969. CloseHandle(HackedHandle);
  970. }
  971. //TraceFunctLeaveEx((LPARAM)NULL);
  972. return fRet;
  973. }
  974. BOOL CopyRestOfMessage(HANDLE hSrcFile, PFIO_CONTEXT hDstFile, DWORD &DestOffset)
  975. {
  976. CHAR acBuffer[PRIVATE_OPTIMAL_BUFFER_SIZE];
  977. DWORD dwBytesRead;
  978. DWORD dwBytesWritten;
  979. DWORD dwTotalBytes = 0;
  980. DWORD err = NO_ERROR;
  981. BOOL fResult = FALSE;
  982. BOOL fRet = FALSE;
  983. HANDLE HackedHandle;
  984. FH_OVERLAPPED ov;
  985. CHAR acCrLfDotCrLf[5] = { '\r', '\n', '.', '\r', '\n' };
  986. CHAR acLastBytes[5] = { '\0', '\0', '\0', '\0', '\0' };
  987. // Copies from the current file pointer to the end of hSrcFile
  988. // and appends to the current file pointer of hDstFile.
  989. _ASSERT(hSrcFile != INVALID_HANDLE_VALUE);
  990. _ASSERT(hDstFile != NULL);
  991. ZeroMemory(&ov, sizeof(ov));
  992. HackedHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
  993. if(HackedHandle == NULL)
  994. {
  995. return FALSE;
  996. }
  997. ov.hEvent = (HANDLE) ((ULONG_PTR) HackedHandle | 1);
  998. do
  999. {
  1000. if (!ReadFile(hSrcFile, acBuffer,
  1001. PRIVATE_OPTIMAL_BUFFER_SIZE,
  1002. &dwBytesRead,
  1003. NULL))
  1004. {
  1005. err = GetLastError();
  1006. if(err == 998)
  1007. {
  1008. _ASSERT(FALSE);
  1009. }
  1010. goto Exit;
  1011. }
  1012. if (dwBytesRead)
  1013. {
  1014. ov.Offset = DestOffset;
  1015. //
  1016. // Save the last two bytes ever read/written. the buffer after
  1017. // writing could be modified due to dot-stripping.
  1018. //
  1019. if (dwBytesRead > 4)
  1020. {
  1021. CopyMemory(acLastBytes, &acBuffer[dwBytesRead-5], 5);
  1022. }
  1023. else
  1024. {
  1025. MoveMemory(acLastBytes, &acLastBytes[dwBytesRead], 5-dwBytesRead);
  1026. CopyMemory(&acLastBytes[5-dwBytesRead], acBuffer, dwBytesRead);
  1027. }
  1028. fResult = FIOWriteFile(hDstFile, acBuffer, dwBytesRead, &ov);
  1029. if (!fResult) err = GetLastError();
  1030. if((err == ERROR_IO_PENDING) || (((DWORD)STATUS_PENDING == ov.Internal) && ( err == NO_ERROR )))
  1031. {
  1032. // 12/16/98 - MikeSwa Modified
  1033. // OK... this is the theory... we are getting random AV, from what
  1034. // looks like async completion to I/O. It looks like
  1035. // GetOverlappedResult cannot be called to wait for a hacked handle,
  1036. // and it will not use the first argument unless the event in the
  1037. // overlapped structure is NULL. The solution is to call
  1038. // WaitForSingleObject and pass in the un-munged handle (only if
  1039. // we know that IO is still pending).
  1040. WaitForSingleObject(HackedHandle, INFINITE);
  1041. _ASSERT((DWORD)STATUS_PENDING != ov.Internal);
  1042. }
  1043. else if (NO_ERROR != err)
  1044. {
  1045. SetLastError (err); //preserve the last error
  1046. if(err == 998)
  1047. {
  1048. _ASSERT(FALSE);
  1049. }
  1050. goto Exit;
  1051. }
  1052. //
  1053. // this is because Fcache keeps track of the offset were we need
  1054. // to write. So we update dwBytesWritten to what we actually read.
  1055. //
  1056. dwBytesWritten = dwBytesRead;
  1057. }
  1058. else
  1059. {
  1060. dwBytesWritten = 0;
  1061. }
  1062. if (dwBytesWritten)
  1063. {
  1064. dwTotalBytes += dwBytesWritten;
  1065. DestOffset += dwBytesWritten;
  1066. }
  1067. } while (dwBytesRead);
  1068. // Now, see if the file ends with a CRLF, if not, add it
  1069. if ((dwTotalBytes > 1) && memcmp(&acLastBytes[3], &acCrLfDotCrLf[3], 2))
  1070. {
  1071. // Add the trailing CRLF
  1072. if (!WriteToSpooledFile(hDstFile, "\r\n", DestOffset))
  1073. {
  1074. goto Exit;
  1075. }
  1076. dwTotalBytes+=2;
  1077. }
  1078. //If file ends with CRLF.CRLF, remove the trailing .CRLF
  1079. //NimishK ** : this was decided per the bug 63394
  1080. if ((dwTotalBytes > 4) && !memcmp(acLastBytes, acCrLfDotCrLf, 5))
  1081. {
  1082. DWORD dwFileSizeHigh = 0;
  1083. DWORD dwFileSizeLow = GetFileSizeFromContext( hDstFile, &dwFileSizeHigh );
  1084. DWORD Offset = SetFilePointer(hDstFile->m_hFile, dwFileSizeLow, NULL, FILE_BEGIN);
  1085. // Remove the trailing CRLF only as the <DOT> would have been removed by
  1086. // dot-stripping by file handle cache.
  1087. if ((SetFilePointer(hDstFile->m_hFile, -2, NULL, FILE_CURRENT) == 0xffffffff) ||
  1088. !SetEndOfFile(hDstFile->m_hFile))
  1089. {
  1090. _ASSERT(0 && "SetFilePointerFailed");
  1091. goto Exit;
  1092. }
  1093. }
  1094. fRet = TRUE;
  1095. Exit:
  1096. if(HackedHandle)
  1097. {
  1098. CloseHandle(HackedHandle);
  1099. }
  1100. return fRet;
  1101. }
  1102. BOOL SMTP_DIRNOT::ProcessFile(IMailMsgProperties *pIMsg)
  1103. {
  1104. LONGLONG LastAccessTime = (LONGLONG) 0;
  1105. CHAR* acCrLf = "\r\n";
  1106. DWORD AbOffset = 0;
  1107. DWORD DestWriteOffset = 0;
  1108. DWORD HeaderFlags = 0;
  1109. DWORD dwPickupFileSize = 0;
  1110. BOOL fIsStartOfFile = TRUE;
  1111. BOOL fIsXSenderRead = FALSE;
  1112. BOOL fAreXRcptsRead = FALSE;
  1113. BOOL fIsSenderSeen = FALSE;
  1114. BOOL fInvalidAddresses = FALSE;
  1115. BOOL fDateExists = FALSE;
  1116. BOOL fMessageIdExists = FALSE;
  1117. BOOL fXOriginalArrivalTime = FALSE;
  1118. BOOL fFromSeen = FALSE;
  1119. BOOL fRcptSeen = FALSE;
  1120. BOOL fSeenRFC822FromAddress = FALSE;
  1121. BOOL fSeenRFC822ToAddress = FALSE;
  1122. BOOL fSeenRFC822CcAddress = FALSE;
  1123. BOOL fSeenRFC822BccAddress = FALSE;
  1124. BOOL fSeenRFC822Subject = FALSE;
  1125. BOOL fSeenRFC822SenderAddress = FALSE;
  1126. BOOL fSeenXPriority = FALSE;
  1127. BOOL fSeenContentType = FALSE;
  1128. BOOL fSetContentType = FALSE;
  1129. HANDLE hSource = INVALID_HANDLE_VALUE;
  1130. PFIO_CONTEXT hDest = NULL;
  1131. SYSTEMTIME SysTime;
  1132. DWORD dwLineBufferSize = 0;
  1133. DWORD dwMaxLineSize = 0;
  1134. CHAR* szLineBuffer = NULL;
  1135. CHAR* szLine = NULL;
  1136. CHAR* szValue = NULL;
  1137. CHAR szScratch[512];
  1138. CHAR szMsgId[512];
  1139. CHAR acReadBuffer[PRIVATE_OPTIMAL_BUFFER_SIZE];
  1140. CHAR szPickupFilePath[MAX_PATH + 1];
  1141. CHAR szDateBuf [cMaxArpaDate];
  1142. DWORD dwBufferSize = 0;
  1143. CHAR FileName[MAX_PATH + 1];
  1144. CHAR* lpStart = NULL;
  1145. CHAR* lpXMarker = NULL;
  1146. DWORD dwBytesToRewind = 0;
  1147. LONG lOffset = 0;
  1148. CAddr* Sender = NULL;
  1149. PSMTP_IIS_SERVICE pService = NULL;
  1150. IMailMsgRecipientsAdd* pIMsgRecips = NULL;
  1151. IMailMsgRecipients* pIMsgOrigRecips = NULL;
  1152. IMailMsgBind* pBindInterface = NULL;
  1153. HRESULT hr = S_OK;
  1154. DWORD dwTotalRecips = 0;
  1155. SMTP_ALLOC_PARAMS AllocParams;
  1156. BOOL fResult = FALSE;
  1157. BOOL DeleteIMsg = TRUE;
  1158. TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::ProcessFile" );
  1159. _ASSERT(m_pInstance != NULL);
  1160. _ASSERT(pIMsg);
  1161. if (!pIMsg)
  1162. {
  1163. ErrorTrace((LPARAM)this, "Internal error: pIMsg is NULL.");
  1164. SetLastError(ERROR_INVALID_PARAMETER);
  1165. TraceFunctLeaveEx((LPARAM)this);
  1166. return FALSE;
  1167. }
  1168. // Compose the pickup path
  1169. hr = pIMsg->GetStringA(IMMPID_MP_PICKUP_FILE_NAME, sizeof(FileName), FileName);
  1170. if (FAILED(hr))
  1171. {
  1172. ErrorTrace((LPARAM)this, "MailQEntry->GetFileName() is NULL.");
  1173. goto RetryPickup;
  1174. }
  1175. hr = pIMsg->QueryInterface(IID_IMailMsgBind, (void **)&pBindInterface);
  1176. if(FAILED(hr))
  1177. {
  1178. ErrorTrace((LPARAM)this, "IMsg->QueryInterface(IID_IMailMsgBindATQ) failed.");
  1179. goto RetryPickup;
  1180. }
  1181. //Get recipient list
  1182. hr = pIMsg->QueryInterface(IID_IMailMsgRecipients, (void **) &pIMsgOrigRecips);
  1183. if (FAILED(hr))
  1184. {
  1185. ErrorTrace((LPARAM)this, "IMsg->QueryInterface(IID_IMailMsgRecipients) failed.");
  1186. goto RetryPickup;
  1187. }
  1188. hr = pIMsgOrigRecips->AllocNewList(&pIMsgRecips);
  1189. if (FAILED(hr))
  1190. {
  1191. ErrorTrace((LPARAM)this, "pIMsgOrigRecips->AllocNewList");
  1192. goto RetryPickup;
  1193. }
  1194. lstrcpy(szPickupFilePath, QuerySmtpInstance()->GetMailPickupDir());
  1195. lstrcat(szPickupFilePath, FileName);
  1196. // Open the pickup file, if this failsas a sharing violation,
  1197. // we would like to retry it ...
  1198. hSource = pOpenPickupFile(szPickupFilePath);
  1199. if (hSource == INVALID_HANDLE_VALUE)
  1200. { //
  1201. // this is probably caused by the file still being written, wait a small amount of time
  1202. // we manage our IO compl. threads to ensure that there are enough, so this shouldn't be
  1203. // a big problem. It avoids the retry queue.
  1204. //
  1205. WaitForSingleObject(QuerySmtpInstance()->GetQStopEvent(), g_PickupWait);
  1206. hSource = pOpenPickupFile(szPickupFilePath);
  1207. if (hSource == INVALID_HANDLE_VALUE)
  1208. {
  1209. if (GetLastError() == ERROR_FILE_NOT_FOUND)
  1210. {
  1211. //delete MailQEntry;
  1212. ErrorTrace((LPARAM)this, "File %s is deleted from pickup dir", szPickupFilePath);
  1213. goto RetryPickup;
  1214. }
  1215. // Schedule pickup mail for retry ...
  1216. goto RetryPickup;
  1217. }
  1218. }
  1219. AllocParams.BindInterfacePtr = (PVOID) pBindInterface;
  1220. AllocParams.IMsgPtr = (PVOID)pIMsg;
  1221. AllocParams.hContent = NULL;
  1222. AllocParams.pAtqClientContext = QuerySmtpInstance();
  1223. AllocParams.m_pNotify = NULL;
  1224. fResult = QuerySmtpInstance()->AllocNewMessage (&AllocParams);
  1225. //Get the context and the atq context
  1226. hDest = AllocParams.hContent;
  1227. if((!fResult) || (hDest == NULL))
  1228. {
  1229. goto RetryPickup;
  1230. }
  1231. // Get the handle of the spooled file
  1232. _ASSERT(hDest != NULL);
  1233. //
  1234. // Winse:13699 and X5:174038
  1235. // Remove Dot stuffing based on the metabase key.
  1236. // Default behavior is that pickup directory messages are dot stuffed.
  1237. // If the metabase key DisablePickupDotStuff is set then the pickup directory
  1238. // messages should not be dot stuffed.
  1239. //
  1240. if( !SetDotStuffingOnWrites( hDest, !m_pInstance->DisablePickupDotStuff(), TRUE ) )
  1241. {
  1242. ErrorTrace((LPARAM)this, "SetDotStuffingonWrites failed" );
  1243. goto RetryPickup;
  1244. }
  1245. // Spit a Received: line to the spooled file
  1246. GetArpaDate(szDateBuf);
  1247. GetLocalTime(&SysTime);
  1248. // Allocate the needed line buffer
  1249. szLineBuffer = pGetLineBuffer(NULL, &dwLineBufferSize);
  1250. if (!szLineBuffer)
  1251. {
  1252. // Schedule pickup mail for retry ...
  1253. ErrorTrace((LPARAM)this, "Unable to allocate line buffer (%u)", GetLastError());
  1254. goto RetryPickup;
  1255. }
  1256. DebugTrace((LPARAM)this, "Allocated line buffer at %p, %u bytes", szLineBuffer, dwLineBufferSize);
  1257. // Set up the line pointers
  1258. szLine = szLineBuffer;
  1259. dwMaxLineSize = dwLineBufferSize;
  1260. // Prefetch a buffer-full of text
  1261. dwBufferSize = PRIVATE_OPTIMAL_BUFFER_SIZE;
  1262. if (!pFetchFileBuffer(hSource, acReadBuffer, &dwBufferSize))
  1263. {
  1264. // Schedule pickup mail for retry ...
  1265. ErrorTrace((LPARAM)this, "Retrying because cannot fetch file buffer (%u)",GetLastError());
  1266. goto RetryPickup;
  1267. }
  1268. m_pInstance->LockGenCrit();
  1269. wsprintf( szScratch,
  1270. "Received: from mail pickup service by %s with Microsoft SMTPSVC;\r\n\t %s, %s\r\n",
  1271. QuerySmtpInstance()->GetFQDomainName(),
  1272. Daynames[SysTime.wDayOfWeek],
  1273. szDateBuf);
  1274. m_pInstance->UnLockGenCrit();
  1275. //We will copy out to the temp out buffer
  1276. //NK** : This is a safe assumption that this will not result in IO
  1277. #if 0
  1278. if ((SetFilePointer(hDest, 0, NULL, FILE_BEGIN) == 0xffffffff) ||
  1279. !WriteToSpooledFile(hDest, szScratch, DestWriteOffset))
  1280. #else
  1281. if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset))
  1282. #endif
  1283. goto RetryPickup;
  1284. lpStart = acReadBuffer;
  1285. while (1)
  1286. {
  1287. HeaderFlags = 0;
  1288. // The X marker is used to mark the start of the
  1289. // non-X-headers if X-headers are used
  1290. if (fIsStartOfFile)
  1291. lpXMarker = lpStart;
  1292. // This function will read a complete header line,
  1293. // into a single NULL-terminated string.
  1294. // Raid 181922 - The function no longer unfolds the line.
  1295. if (!(lpStart = pReadNextLineFromBuffer( hSource,
  1296. acReadBuffer,
  1297. &dwBufferSize,
  1298. lpStart,
  1299. &szLine,
  1300. &dwMaxLineSize,
  1301. &szLineBuffer,
  1302. &dwLineBufferSize)))
  1303. {
  1304. // We failed a file operation, we will retry later
  1305. goto RetryPickup;
  1306. }
  1307. // See if we are still in the header portion of the body
  1308. if (!IsHeader(szLine))
  1309. break;
  1310. // Get the set of headers that we know about.
  1311. ChompHeader(szLine, HeaderFlags);
  1312. szValue = pGetValueFromHeader(szLine);
  1313. if(!szValue)
  1314. {
  1315. continue;
  1316. }
  1317. // Strip away the CRLF at the end of the value string
  1318. lOffset = lstrlen(szValue);
  1319. if (lOffset >= 2)
  1320. {
  1321. if (((szValue[lOffset-2] == '\r') && (szValue[lOffset-1] == '\n')) ||
  1322. ((szValue[lOffset-2] == '\n') && (szValue[lOffset-1] == '\r')))
  1323. szValue[lOffset-2] = '\0';
  1324. }
  1325. hr = GetAndPersistRFC822Headers( szLine,
  1326. szValue,
  1327. pIMsg,
  1328. fSeenRFC822FromAddress,
  1329. fSeenRFC822ToAddress,
  1330. fSeenRFC822BccAddress,
  1331. fSeenRFC822CcAddress,
  1332. fSeenRFC822Subject,
  1333. fSeenRFC822SenderAddress,
  1334. fSeenXPriority,
  1335. fSeenContentType,
  1336. fSetContentType );
  1337. if( FAILED( hr ) )
  1338. {
  1339. ErrorTrace((LPARAM)this, "Retrying because cannot getAndPersistRFC822 headers(%x)", hr);
  1340. goto RetryPickup;
  1341. }
  1342. // Check for leading X-Sender and X-Receiver header lines ...
  1343. if (fIsStartOfFile)
  1344. {
  1345. if (HeaderFlags & H_X_SENDER)
  1346. {
  1347. // NOTE: if we have more than one sender,
  1348. // we always use the first one encountered
  1349. // Yes, we found an X-header
  1350. fIsXSenderRead = TRUE;
  1351. }
  1352. else if (HeaderFlags & H_X_RECEIVER)
  1353. {
  1354. // We don't check for sender here, since if we don't
  1355. // have one, we will barf downstream
  1356. // Yes, we found an X-header
  1357. fAreXRcptsRead = TRUE;
  1358. }
  1359. else
  1360. {
  1361. // We are done with our X-headers, now we have
  1362. // normal headers!
  1363. fIsStartOfFile = FALSE;
  1364. }
  1365. }
  1366. if (HeaderFlags & H_FROM)
  1367. {
  1368. fFromSeen = TRUE;
  1369. }
  1370. if (HeaderFlags & H_RCPT)
  1371. {
  1372. fRcptSeen = TRUE;
  1373. }
  1374. // Handle senders and recipients
  1375. if (!fIsSenderSeen && ((HeaderFlags & H_X_SENDER) || (HeaderFlags & H_FROM)))
  1376. {
  1377. // Selectively do so
  1378. if (fIsStartOfFile || !fIsXSenderRead)
  1379. {
  1380. char *Address = NULL;
  1381. DWORD cbText = 0;
  1382. DWORD dwlen = strlen(szValue);
  1383. _ASSERT(dwlen < 512 );
  1384. Sender = CAddr::CreateAddress (szValue, FROMADDR);
  1385. if (Sender)
  1386. {
  1387. Address = Sender->GetAddress();
  1388. if(!Sender->IsDomainOffset() && !ISNULLADDRESS(Address))
  1389. {
  1390. CAddr * TempAddress = NULL;
  1391. TempAddress = QuerySmtpInstance()->AppendLocalDomain (Sender);
  1392. delete Sender;
  1393. if (TempAddress)
  1394. {
  1395. Sender = TempAddress;
  1396. Address = Sender->GetAddress();
  1397. TempAddress = NULL;
  1398. }
  1399. else
  1400. {
  1401. ErrorTrace((LPARAM) this,"Retrying because cannot to append local domain (%u)",GetLastError());
  1402. goto RetryPickup;
  1403. }
  1404. }
  1405. // Set the size of the FromName so that we can
  1406. // read it out of the queue file if we need it.
  1407. //cbText = lstrlen(Address) + 1;
  1408. //MailQEntry->SetFromNameSize (cbText);
  1409. //MailQEntry->SetSenderToStream(Address);
  1410. hr = pIMsg->PutStringA(IMMPID_MP_SENDER_ADDRESS_SMTP, Address);
  1411. fIsSenderSeen = TRUE;
  1412. }
  1413. else
  1414. {
  1415. // Undo the flag
  1416. if (fIsXSenderRead)
  1417. fIsXSenderRead = FALSE;
  1418. // If the sender is not invalid, it is likely to be a
  1419. // resource constraint, so we go and retry it
  1420. if (GetLastError() != ERROR_INVALID_DATA)
  1421. {
  1422. ErrorTrace((LPARAM)this, "Retrying because cannot allocate sender (%u)", GetLastError());
  1423. goto RetryPickup;
  1424. }
  1425. }
  1426. }
  1427. }
  1428. else if ((HeaderFlags & H_X_RECEIVER) || (HeaderFlags & H_RCPT))
  1429. {
  1430. // Selectively do so
  1431. if (fIsStartOfFile || !fAreXRcptsRead)
  1432. {
  1433. DebugTrace((LPARAM)szLine, "To: line read <%s>", szValue);
  1434. if (!CreateToList(szValue, pIMsgRecips, pIMsg))
  1435. {
  1436. // If we're out of memory, we retry.
  1437. // If it is an invalid address, we remember the fact and
  1438. // remember to throw a copy into badmail
  1439. if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
  1440. {
  1441. ErrorTrace((LPARAM)this, "Retrying because cannot allocate recipient (%u)", GetLastError());
  1442. goto RetryPickup;
  1443. }
  1444. else
  1445. fInvalidAddresses = TRUE;
  1446. }
  1447. if (fIsStartOfFile)
  1448. {
  1449. // X-headers!
  1450. lpXMarker = lpStart;
  1451. }
  1452. }
  1453. }
  1454. else if (HeaderFlags & H_MID)
  1455. {
  1456. // Message-ID already exists
  1457. fMessageIdExists = TRUE;
  1458. hr = pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_ID , szValue );
  1459. if (FAILED(hr))
  1460. {
  1461. ErrorTrace((LPARAM) this, "PutStringA of IMMPID_MP_RFC822_MSG_ID failed - hr 0x%08X", hr);
  1462. goto RetryPickup;
  1463. }
  1464. }
  1465. else if (HeaderFlags & H_DATE)
  1466. {
  1467. // Date line already exists
  1468. fDateExists = TRUE;
  1469. }
  1470. // 10/15/98 - MikeSwa Added supersedes functionality
  1471. else if (HeaderFlags & H_X_MSGGUID)
  1472. {
  1473. hr = pIMsg->PutStringA(IMMPID_MP_MSG_GUID, szValue);
  1474. if (FAILED(hr))
  1475. {
  1476. ErrorTrace((LPARAM) this, "PutStringA of MSG_GUID failed - hr 0x%08X", hr);
  1477. goto RetryPickup;
  1478. }
  1479. }
  1480. else if (HeaderFlags & H_X_SUPERSEDES_MSGGUID)
  1481. {
  1482. hr = pIMsg->PutStringA(IMMPID_MP_SUPERSEDES_MSG_GUID, szValue);
  1483. if (FAILED(hr))
  1484. {
  1485. ErrorTrace((LPARAM) this, "PutStringA of SUPERSEDES_MSG_GUID failed - hr 0x%08X", hr);
  1486. goto RetryPickup;
  1487. }
  1488. }
  1489. else if( HeaderFlags & H_X_ORIGINAL_ARRIVAL_TIME )
  1490. {
  1491. fXOriginalArrivalTime = TRUE;
  1492. hr = pIMsg->PutStringA(IMMPID_MP_ORIGINAL_ARRIVAL_TIME, szValue);
  1493. if (FAILED(hr))
  1494. {
  1495. ErrorTrace((LPARAM) this, "PutStringA of ORIGINAL_ARRIVAL_TIME failed - hr 0x%08X", hr);
  1496. goto RetryPickup;
  1497. }
  1498. }
  1499. // If this is an ordinary header, we will dump this to
  1500. // the spooled file
  1501. if (!fIsStartOfFile)
  1502. {
  1503. // Non-x-header; dump the line!
  1504. lstrcat(szLine, acCrLf);
  1505. if (!WriteToSpooledFile(hDest, szLine, DestWriteOffset))
  1506. {
  1507. ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError());
  1508. goto RetryPickup;
  1509. }
  1510. }
  1511. }
  1512. hr = pIMsg->PutStringA(IMMPID_MP_HELO_DOMAIN, m_pInstance->GetDefaultDomain());
  1513. if(FAILED(hr))
  1514. {
  1515. ErrorTrace((LPARAM)this, "Retrying because pIMsg->PutString(helo domain) failed (%x)", hr);
  1516. goto RetryPickup;
  1517. }
  1518. hr = pIMsg->PutStringA(IMMPID_MP_CONNECTION_IP_ADDRESS, DIRNOT_IP_ADDRESS);
  1519. if(FAILED(hr))
  1520. {
  1521. ErrorTrace((LPARAM)this, "Retrying because pIMsg->PutString(IP address) failed (%x)", hr);
  1522. goto RetryPickup;
  1523. }
  1524. hr = SetAvailableMailMsgProperties( pIMsg );
  1525. if(FAILED(hr))
  1526. {
  1527. ErrorTrace((LPARAM)this, "Retrying because pIMsg->PutString( for all available props) failed (%x)", hr);
  1528. goto RetryPickup;
  1529. }
  1530. wsprintf( szScratch,
  1531. "%s, %s",
  1532. Daynames[SysTime.wDayOfWeek],
  1533. szDateBuf);
  1534. hr = pIMsg->PutStringA(IMMPID_MP_ARRIVAL_TIME, szScratch);
  1535. if( FAILED( hr ) )
  1536. {
  1537. ErrorTrace((LPARAM)this,"Retrying because cannot putString IMPPID_MP_ARRIVAL_TIME (%x)", hr);
  1538. goto RetryPickup;
  1539. }
  1540. // Now, before we go on and waste time on copying the rest of the message, etc.
  1541. // we want to make sure we saw the sender and at least one valid recipient.
  1542. // If not, we will just move the message to bad mail and be done with it.
  1543. hr = pIMsgRecips->Count(&dwTotalRecips);
  1544. if (!fIsSenderSeen || FAILED(hr) || (dwTotalRecips == 0))
  1545. {
  1546. // If no recipients but sender is seen, we must not leak the sender
  1547. // object
  1548. if (fIsSenderSeen)
  1549. {
  1550. delete Sender;
  1551. Sender = NULL;
  1552. }
  1553. _VERIFY( pFreeLineBuffer(szLineBuffer) );
  1554. szLineBuffer = NULL;
  1555. if (hSource != INVALID_HANDLE_VALUE)
  1556. {
  1557. _VERIFY( CloseHandle(hSource) );
  1558. hSource = INVALID_HANDLE_VALUE;
  1559. }
  1560. if(FAILED(hr))
  1561. {
  1562. goto RetryPickup;
  1563. }
  1564. else
  1565. {
  1566. // No choice but move to bad mail directory
  1567. if( !QuerySmtpInstance()->MoveToBadMail( pIMsg, FALSE, FileName, QuerySmtpInstance()->GetMailPickupDir()) )
  1568. {
  1569. if (!DeleteFile(szPickupFilePath))
  1570. {
  1571. DWORD dwError = GetLastError();
  1572. ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError);
  1573. }
  1574. }
  1575. goto RetryPickup;
  1576. }
  1577. }
  1578. hr = pIMsg->PutDWORD( IMMPID_MP_NUM_RECIPIENTS, dwTotalRecips );
  1579. if( FAILED( hr ) )
  1580. {
  1581. ErrorTrace((LPARAM)this,"Retrying because cannot putDWORD IMPPID_MP_NUM_RECIPIENTS (%x)", hr);
  1582. goto RetryPickup;
  1583. }
  1584. // if we did not read a From Line, create a From: line from the xSender.
  1585. // if we did not read a To, CC, BCC line, create a To: line from the xReceiver.
  1586. if (!fFromSeen)
  1587. {
  1588. char *Address = NULL;
  1589. char szUserName[MAX_INTERNET_NAME + 5];
  1590. Address = Sender->GetAddress();
  1591. if (!Address)
  1592. {
  1593. _VERIFY( pFreeLineBuffer(szLineBuffer) );
  1594. szLineBuffer = NULL;
  1595. if (hSource != INVALID_HANDLE_VALUE)
  1596. {
  1597. _VERIFY( CloseHandle(hSource) );
  1598. hSource = INVALID_HANDLE_VALUE;
  1599. }
  1600. // No choice but move to bad mail directory
  1601. if( !QuerySmtpInstance()->MoveToBadMail( pIMsg, FALSE, FileName, QuerySmtpInstance()->GetMailPickupDir()) )
  1602. {
  1603. if (!DeleteFile(szPickupFilePath))
  1604. {
  1605. DWORD dwError = GetLastError();
  1606. ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError);
  1607. }
  1608. }
  1609. goto RetryPickup;
  1610. }
  1611. wsprintf(szUserName, "From: %s\r\n", Address);
  1612. if (!WriteToSpooledFile(hDest, szUserName, DestWriteOffset))
  1613. {
  1614. ErrorTrace((LPARAM)this,"Retrying because cannot write to file (%u)", GetLastError());
  1615. goto RetryPickup;
  1616. }
  1617. }
  1618. if (!fRcptSeen)
  1619. {
  1620. //
  1621. // fix for bug: 78275
  1622. // add an emty Bcc line:
  1623. //
  1624. static char * endRcpt = "Bcc:\r\n";
  1625. if (!WriteToSpooledFile(hDest, endRcpt, DestWriteOffset))
  1626. {
  1627. ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError());
  1628. goto RetryPickup;
  1629. }
  1630. }
  1631. if (fIsSenderSeen)
  1632. {
  1633. delete Sender;
  1634. }
  1635. // We still have to dump the current line to the spool
  1636. // file, then we copy the rest over.
  1637. // Since we use buffered read, the current file position
  1638. // really doesn't mean much. So we calculate how many bytes
  1639. // we have to rewind
  1640. dwBytesToRewind = dwBufferSize - (DWORD)(lpStart - acReadBuffer);
  1641. // Negative, since we are going backwards
  1642. _ASSERT(dwBytesToRewind <= PRIVATE_OPTIMAL_BUFFER_SIZE);
  1643. lOffset = -(long)dwBytesToRewind;
  1644. if (SetFilePointer(hSource, lOffset, NULL, FILE_CURRENT)
  1645. == 0xffffffff)
  1646. {
  1647. ErrorTrace((LPARAM)this,
  1648. "Retrying because cannot move file pointer (%u)",
  1649. GetLastError());
  1650. goto RetryPickup;
  1651. }
  1652. // See if we have to add a Date: or Message-ID: line
  1653. if (!fMessageIdExists)
  1654. {
  1655. char MessageId[MAX_PATH];
  1656. MessageId[0] = '\0';
  1657. GenerateMessageId (MessageId, sizeof(MessageId));
  1658. m_pInstance->LockGenCrit();
  1659. wsprintf( szMsgId, "<%s%8.8x@%s>", MessageId, GetIncreasingMsgId(),QuerySmtpInstance()->GetFQDomainName());
  1660. m_pInstance->UnLockGenCrit();
  1661. wsprintf(szScratch, "Message-ID: %s\r\n",szMsgId );
  1662. hr = pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_ID, szMsgId );
  1663. if( FAILED( hr ) )
  1664. {
  1665. ErrorTrace((LPARAM)this, "Retrying because Put string of IMMPID_MP_RFC822 failed (%x)", hr );
  1666. goto RetryPickup;
  1667. }
  1668. if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset))
  1669. {
  1670. ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError());
  1671. goto RetryPickup;
  1672. }
  1673. }
  1674. //
  1675. // see if we have to add Original Arrival time
  1676. //
  1677. if (!fXOriginalArrivalTime )
  1678. {
  1679. char szOriginalArrivalTime[64];
  1680. szOriginalArrivalTime[0] = '\0';
  1681. GetSysAndFileTimeAsString( szOriginalArrivalTime );
  1682. wsprintf(szScratch, "X-OriginalArrivalTime: %s\r\n",szOriginalArrivalTime );
  1683. hr = pIMsg->PutStringA( IMMPID_MP_ORIGINAL_ARRIVAL_TIME, szOriginalArrivalTime );
  1684. if( FAILED( hr ) )
  1685. {
  1686. ErrorTrace((LPARAM)this, "Retrying because Put string of IMMPID_MP_ORIGINAL_ARRIVAL_TIME failed (%x)", hr );
  1687. goto RetryPickup;
  1688. }
  1689. if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset))
  1690. {
  1691. ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError());
  1692. goto RetryPickup;
  1693. }
  1694. }
  1695. //
  1696. // see if we have to add the date
  1697. //
  1698. if (!fDateExists)
  1699. {
  1700. lstrcpy(szScratch, "Date: ");
  1701. lstrcat(szScratch, szDateBuf);
  1702. lstrcat(szScratch, acCrLf);
  1703. if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset))
  1704. {
  1705. ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError());
  1706. goto RetryPickup;
  1707. }
  1708. }
  1709. // Write out the currently buffered line, then copy the
  1710. // pickup file to the spooler
  1711. if (!WriteToSpooledFile(hDest, szLine, DestWriteOffset) ||
  1712. !CopyRestOfMessage(hSource, hDest, DestWriteOffset))
  1713. {
  1714. ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError());
  1715. goto RetryPickup;
  1716. }
  1717. // We will no longer read headers at this point, so we free our
  1718. // line buffer here ...
  1719. DebugTrace((LPARAM)this, "Freeing line buffer at %p, %u bytes", szLineBuffer, dwLineBufferSize);
  1720. _VERIFY( pFreeLineBuffer(szLineBuffer) );
  1721. szLineBuffer = NULL;
  1722. // Get the size of the spooled file before we close it
  1723. DWORD dwFileSizeHigh;
  1724. dwPickupFileSize = GetFileSizeFromContext(hDest, &dwFileSizeHigh);
  1725. _ASSERT(dwFileSizeHigh == 0); // why??
  1726. if (dwPickupFileSize == 0xffffffff)
  1727. {
  1728. dwPickupFileSize = 0;
  1729. }
  1730. pIMsg->PutDWORD( IMMPID_MP_MSG_SIZE_HINT, dwPickupFileSize );
  1731. // Now, we see if there are any invalid addresses; if so, we will
  1732. // throw a copy og the original message into the badmail directory
  1733. if (fInvalidAddresses)
  1734. {
  1735. // Close the pickup file
  1736. if (hSource != INVALID_HANDLE_VALUE)
  1737. {
  1738. _VERIFY( CloseHandle(hSource) );
  1739. hSource = INVALID_HANDLE_VALUE;
  1740. }
  1741. // Move it to badmail instead of deleting it
  1742. DebugTrace((LPARAM)this, "Saving a copy in badmail due to bad recipients");
  1743. // No choice but move to bad mail directory
  1744. if( !QuerySmtpInstance()->MoveToBadMail( pIMsg, FALSE, FileName, QuerySmtpInstance()->GetMailPickupDir() ) )
  1745. {
  1746. if (!DeleteFile(szPickupFilePath))
  1747. {
  1748. DWORD dwError = GetLastError();
  1749. ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError);
  1750. }
  1751. }
  1752. }
  1753. else
  1754. {
  1755. // Delete the file
  1756. if (!DeleteFile(szPickupFilePath))
  1757. {
  1758. DWORD dwError = GetLastError();
  1759. ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError);
  1760. }
  1761. // Close the pickup file
  1762. if (hSource != INVALID_HANDLE_VALUE)
  1763. {
  1764. _VERIFY( CloseHandle(hSource) );
  1765. hSource = INVALID_HANDLE_VALUE;
  1766. }
  1767. }
  1768. //
  1769. // put the file into the local queue.
  1770. //
  1771. pService = (PSMTP_IIS_SERVICE) g_pInetSvc;
  1772. hr = pIMsgOrigRecips->WriteList(pIMsgRecips);
  1773. if(FAILED(hr))
  1774. {
  1775. ErrorTrace((LPARAM)this, "Retrying because pIMsgOrigRecips->WriteList failed (%x)", hr);
  1776. goto RetryPickup;
  1777. }
  1778. //
  1779. // we scan for dots & strip them away while storing, so set these two properties
  1780. // so we can get the dot stuffed context while sending it out.
  1781. //
  1782. pIMsg->PutDWORD( IMMPID_MP_SCANNED_FOR_CRLF_DOT_CRLF, TRUE );
  1783. pIMsg->PutDWORD( IMMPID_MP_FOUND_EMBEDDED_CRLF_DOT_CRLF, TRUE );
  1784. CompleteDotStuffingOnWrites( hDest, TRUE );
  1785. hr = pIMsg->Commit(NULL);
  1786. if(FAILED(hr))
  1787. {
  1788. ErrorTrace((LPARAM)this, "Retrying because pIMsg->Commit(NULL) failed (%u)",
  1789. hr);
  1790. goto RetryPickup;
  1791. }
  1792. pIMsgRecips->Release();
  1793. pIMsgOrigRecips->Release();
  1794. pIMsgRecips = NULL;
  1795. pIMsgOrigRecips = NULL;
  1796. if(m_pInstance->InsertIntoQueue(pIMsg))
  1797. {
  1798. DeleteIMsg = FALSE;
  1799. // Up the msgs received and avg bytes per message counters
  1800. ADD_BIGCOUNTER(QuerySmtpInstance(), BytesRcvdMsg, (dwPickupFileSize));
  1801. BUMP_COUNTER(QuerySmtpInstance(), NumMsgsForwarded);
  1802. }
  1803. RetryPickup:
  1804. IMailMsgQueueMgmt * pMgmt = NULL;
  1805. if(pBindInterface)
  1806. {
  1807. pBindInterface->Release();
  1808. }
  1809. if(pIMsgRecips)
  1810. {
  1811. pIMsgRecips->Release();
  1812. pIMsgRecips = NULL;
  1813. }
  1814. if(pIMsgOrigRecips)
  1815. {
  1816. pIMsgOrigRecips->Release();
  1817. pIMsgOrigRecips = NULL;
  1818. }
  1819. if(DeleteIMsg)
  1820. {
  1821. hr = pIMsg->QueryInterface(IID_IMailMsgQueueMgmt, (void **)&pMgmt);
  1822. if(!FAILED(hr))
  1823. {
  1824. pMgmt->Delete(NULL);
  1825. pMgmt->Release();
  1826. }
  1827. }
  1828. if(pIMsg)
  1829. {
  1830. pIMsg->Release();
  1831. pIMsg = NULL;
  1832. }
  1833. // Retry message
  1834. if (szLineBuffer)
  1835. {
  1836. _VERIFY( pFreeLineBuffer(szLineBuffer) );
  1837. szLineBuffer = NULL;
  1838. }
  1839. if (hSource != INVALID_HANDLE_VALUE)
  1840. {
  1841. _VERIFY( CloseHandle( hSource ) );
  1842. hSource = INVALID_HANDLE_VALUE;
  1843. }
  1844. TraceFunctLeaveEx((LPARAM)this);
  1845. return FALSE;
  1846. }
  1847. //////////////////////////////////////////////////////////////////////////////
  1848. HRESULT SMTP_DIRNOT::GetAndPersistRFC822Headers(
  1849. char* InputLine,
  1850. char* pszValueBuf,
  1851. IMailMsgProperties* pIMsg,
  1852. BOOL & fSeenRFC822FromAddress,
  1853. BOOL & fSeenRFC822ToAddress,
  1854. BOOL & fSeenRFC822BccAddress,
  1855. BOOL & fSeenRFC822CcAddress,
  1856. BOOL & fSeenRFC822Subject,
  1857. BOOL & fSeenRFC822SenderAddress,
  1858. BOOL & fSeenXPriority,
  1859. BOOL & fSeenContentType,
  1860. BOOL & fSetContentType
  1861. )
  1862. {
  1863. HRESULT hr = S_OK;
  1864. //
  1865. // get the Subject: & persist it
  1866. //
  1867. if( !fSeenRFC822Subject &&
  1868. ( !strncasecmp( InputLine, "Subject:", strlen("Subject:") ) ||
  1869. !strncasecmp( InputLine, "Subject :", strlen("Subject :") ) ) )
  1870. {
  1871. fSeenRFC822Subject = TRUE;
  1872. if( pszValueBuf )
  1873. {
  1874. if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_SUBJECT, pszValueBuf ) ) )
  1875. {
  1876. return( hr );
  1877. }
  1878. }
  1879. }
  1880. //
  1881. // get the To: address & persist it
  1882. //
  1883. if( !fSeenRFC822ToAddress &&
  1884. ( !strncasecmp( InputLine, "To:", strlen("To:") ) ||
  1885. !strncasecmp( InputLine, "To :", strlen("To :") ) ) )
  1886. {
  1887. fSeenRFC822ToAddress = TRUE;
  1888. if( pszValueBuf )
  1889. {
  1890. if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_TO_ADDRESS, pszValueBuf ) ) )
  1891. {
  1892. return( hr );
  1893. }
  1894. }
  1895. }
  1896. //
  1897. // get the Cc: address & persist it
  1898. //
  1899. if( !fSeenRFC822CcAddress &&
  1900. ( !strncasecmp( InputLine, "Cc:", strlen("Cc:") ) ||
  1901. !strncasecmp( InputLine, "Cc :", strlen("Cc :") ) ) )
  1902. {
  1903. fSeenRFC822CcAddress = TRUE;
  1904. if( pszValueBuf )
  1905. {
  1906. if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_CC_ADDRESS, pszValueBuf ) ) )
  1907. {
  1908. return( hr );
  1909. }
  1910. }
  1911. }
  1912. //
  1913. // get the Bcc: address & persist it
  1914. //
  1915. if( !fSeenRFC822BccAddress &&
  1916. ( !strncasecmp( InputLine, "Bcc:", strlen("Bcc:") ) ||
  1917. !strncasecmp( InputLine, "Bcc :", strlen("Bcc :") ) ) )
  1918. {
  1919. fSeenRFC822BccAddress = TRUE;
  1920. if( pszValueBuf )
  1921. {
  1922. if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_BCC_ADDRESS, pszValueBuf ) ) )
  1923. {
  1924. return( hr );
  1925. }
  1926. }
  1927. }
  1928. //
  1929. // get the From: address & persist it
  1930. //
  1931. if( !fSeenRFC822FromAddress &&
  1932. ( !strncasecmp( InputLine, "From:", strlen("From:") ) ||
  1933. !strncasecmp( InputLine, "From :", strlen("From :") ) ) )
  1934. {
  1935. fSeenRFC822FromAddress = TRUE;
  1936. if( pszValueBuf )
  1937. {
  1938. if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_FROM_ADDRESS, pszValueBuf ) ) )
  1939. {
  1940. return( hr );
  1941. }
  1942. }
  1943. char szSmtpFromAddress[MAX_INTERNET_NAME + 6] = "smtp:";
  1944. char *pszDomainOffset;
  1945. DWORD cAddr;
  1946. if (CAddr::ExtractCleanEmailName(szSmtpFromAddress + 5, &pszDomainOffset, &cAddr, pszValueBuf)) {
  1947. if (FAILED(hr = pIMsg->PutStringA(IMMPID_MP_FROM_ADDRESS, szSmtpFromAddress))) {
  1948. return hr;
  1949. }
  1950. }
  1951. }
  1952. if( !fSeenRFC822SenderAddress &&
  1953. ( !strncasecmp( InputLine, "Sender:", strlen("Sender:") ) ||
  1954. !strncasecmp( InputLine, "Sender :", strlen("Sender :") ) ) )
  1955. {
  1956. fSeenRFC822SenderAddress = TRUE;
  1957. if( pszValueBuf )
  1958. {
  1959. char szSmtpSenderAddress[MAX_INTERNET_NAME + 6] = "smtp:";
  1960. char *pszDomainOffset;
  1961. DWORD cAddr;
  1962. if (CAddr::ExtractCleanEmailName(szSmtpSenderAddress + 5, &pszDomainOffset, &cAddr, pszValueBuf)) {
  1963. if (FAILED(hr = pIMsg->PutStringA(IMMPID_MP_SENDER_ADDRESS, szSmtpSenderAddress))) {
  1964. return hr;
  1965. }
  1966. }
  1967. }
  1968. }
  1969. //
  1970. // get the X-Priority & persist it
  1971. //
  1972. if( !fSeenXPriority &&
  1973. ( !strncasecmp( InputLine, "X-Priority:", strlen("X-Priority:") ) ||
  1974. !strncasecmp( InputLine, "X-Priority :", strlen("X-Priority :") ) ) )
  1975. {
  1976. fSeenXPriority = TRUE;
  1977. if( pszValueBuf )
  1978. {
  1979. DWORD dwPri = (DWORD)atoi( pszValueBuf );
  1980. if( FAILED( hr = pIMsg->PutDWORD( IMMPID_MP_X_PRIORITY, dwPri ) ) )
  1981. {
  1982. return( hr );
  1983. }
  1984. }
  1985. }
  1986. //
  1987. // get the content type & persist it
  1988. //
  1989. if( !fSeenContentType &&
  1990. ( !strncasecmp( InputLine, "Content-Type:", strlen("Content-Type:") ) ||
  1991. !strncasecmp( InputLine, "Content-Type :", strlen("Content-Type :") ) ) )
  1992. {
  1993. fSeenContentType = TRUE;
  1994. fSetContentType = TRUE;
  1995. DWORD dwContentType = 0;
  1996. if( pszValueBuf )
  1997. {
  1998. if( !strncasecmp( pszValueBuf, "multipart/signed", strlen("multipart/signed") ) )
  1999. {
  2000. dwContentType = 1;
  2001. }
  2002. else if( !strncasecmp( pszValueBuf, "application/x-pkcs7-mime", strlen("application/x-pkcs7-mime") ) ||
  2003. !strncasecmp( pszValueBuf, "application/pkcs7-mime", strlen("application/pkcs7-mime") ) )
  2004. {
  2005. dwContentType = 2;
  2006. }
  2007. if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_CONTENT_TYPE, pszValueBuf ) ) )
  2008. {
  2009. return( hr );
  2010. }
  2011. }
  2012. if( FAILED( hr = pIMsg->PutDWORD( IMMPID_MP_ENCRYPTION_TYPE, dwContentType ) ) )
  2013. {
  2014. return( hr );
  2015. }
  2016. }
  2017. if( !fSetContentType )
  2018. {
  2019. fSetContentType = TRUE;
  2020. if( FAILED( hr = pIMsg->PutDWORD( IMMPID_MP_ENCRYPTION_TYPE, 0 ) ) )
  2021. {
  2022. return( hr );
  2023. }
  2024. }
  2025. return( hr );
  2026. }
  2027. //////////////////////////////////////////////////////////////////////////////
  2028. HRESULT SMTP_DIRNOT::SetAvailableMailMsgProperties( IMailMsgProperties *pIMsg )
  2029. {
  2030. //set IPaddress that is already available
  2031. HRESULT hr = S_OK;
  2032. hr = pIMsg->PutStringA(IMMPID_MP_CONNECTION_SERVER_IP_ADDRESS, DIRNOT_IP_ADDRESS);
  2033. if(FAILED(hr))
  2034. {
  2035. return( hr );
  2036. }
  2037. hr = pIMsg->PutStringA(IMMPID_MP_SERVER_NAME, g_ComputerName);
  2038. if(FAILED(hr))
  2039. {
  2040. return( hr );
  2041. }
  2042. hr = pIMsg->PutStringA(IMMPID_MP_SERVER_VERSION, g_VersionString);
  2043. if(FAILED(hr))
  2044. {
  2045. return( hr );
  2046. }
  2047. return( hr );
  2048. }
  2049. //////////////////////////////////////////////////////////////////////////////
  2050. void SMTP_DIRNOT::CreateLocalDeliveryThread (void)
  2051. {
  2052. TraceFunctEnterEx((LPARAM)this, "SMTP_DIRNOT::CreateLocalDeliveryThread");
  2053. //
  2054. // Add a delivery thread with the ATQPorts Overlapped structure. For the dir notifications,
  2055. // our overlapped structure is used... this is how we tell the difference in Process Client.
  2056. //
  2057. IncPendingIoCount();
  2058. if(!AtqPostCompletionStatus(QueryAtqContext(), 50))
  2059. {
  2060. DecPendingIoCount();
  2061. ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError());
  2062. }
  2063. TraceFunctLeaveEx((LPARAM)this);
  2064. }
  2065. //////////////////////////////////////////////////////////////////////////////
  2066. BOOL SMTP_DIRNOT::PendDirChangeNotification (void)
  2067. {
  2068. CBuffer * pBuffer = NULL;
  2069. DWORD nBytes = 0;
  2070. DWORD Error = 0;
  2071. TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::PendDirChangeNotification" );
  2072. _ASSERT(m_pInstance != NULL);
  2073. _ASSERT(m_hDir != INVALID_HANDLE_VALUE);
  2074. //get a new buffer
  2075. pBuffer = new CBuffer();
  2076. if(pBuffer == NULL)
  2077. {
  2078. ErrorTrace((LPARAM) this, "pBuffer = new CBuffer()failed . err: %u", ERROR_NOT_ENOUGH_MEMORY);
  2079. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  2080. TraceFunctLeaveEx((LPARAM)this);
  2081. return FALSE;
  2082. }
  2083. if(pBuffer->GetData() == NULL)
  2084. {
  2085. ErrorTrace((LPARAM) this, "pBuffer = new CBuffer()->GetData failed . err: %u", ERROR_NOT_ENOUGH_MEMORY);
  2086. delete pBuffer;
  2087. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  2088. TraceFunctLeaveEx((LPARAM)this);
  2089. return FALSE;
  2090. }
  2091. QuerySmtpInstance()->IncCBufferObjs ();
  2092. IncPendingIoCount();
  2093. IncDirChangeIoCount();
  2094. if(!AtqReadDirChanges(QueryAtqContext(),pBuffer->GetData(),QuerySmtpInstance()->GetDirBufferSize(),
  2095. FALSE,FILE_NOTIFY_CHANGE_FILE_NAME,
  2096. (OVERLAPPED *) &pBuffer->m_Overlapped.SeoOverlapped.Overlapped))
  2097. {
  2098. Error = GetLastError();
  2099. ErrorTrace((LPARAM) this, "ReadDirectoryChangesW failed . err: %u", Error);
  2100. delete pBuffer;
  2101. QuerySmtpInstance()->DecCBufferObjs ();
  2102. //decrement over all I/O count
  2103. DecPendingIoCount();
  2104. DecDirChangeIoCount();
  2105. TraceFunctLeaveEx((LPARAM)this);
  2106. return FALSE;
  2107. }
  2108. TraceFunctLeaveEx((LPARAM)this);
  2109. return TRUE;
  2110. }
  2111. BOOL SMTP_DIRNOT::ProcessDirNotification( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  2112. {
  2113. CHAR FileName[MAX_PATH + 1];
  2114. PFILE_NOTIFY_INFORMATION Info = NULL;
  2115. DWORD FileNameLength = 0;
  2116. DWORD ServerState;
  2117. CBuffer* pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer;
  2118. HRESULT hr;
  2119. TraceFunctEnterEx((LPARAM)this, "SMTP_DIRNOT::ProcessDirNotification" );
  2120. _ASSERT(m_pInstance != NULL);
  2121. //
  2122. // pend a new notification to replace the one we are currently using... there will still be
  2123. // a couple outstanding unless we are shutting down.
  2124. //
  2125. if (!PendDirChangeNotification())
  2126. ErrorTrace((LPARAM)this, "PendDirChangeNotification() failed");
  2127. if (!QuerySmtpInstance()->GetAcceptConnBool())
  2128. {
  2129. delete pBuffer;
  2130. QuerySmtpInstance()->DecCBufferObjs ();
  2131. TraceFunctLeaveEx((LPARAM)this);
  2132. return TRUE;
  2133. }
  2134. if(dwCompletionStatus != NO_ERROR)
  2135. {
  2136. ErrorTrace((LPARAM) this, "SMTP_DIRNOT::ProcessDirNotification received error %d", dwCompletionStatus);
  2137. delete pBuffer;
  2138. QuerySmtpInstance()->DecCBufferObjs ();
  2139. TraceFunctLeaveEx((LPARAM)this);
  2140. return TRUE;
  2141. }
  2142. ServerState = QuerySmtpInstance()->QueryServerState( );
  2143. //if we are not running, just delete
  2144. if( (ServerState == MD_SERVER_STATE_STOPPED ) || (ServerState == MD_SERVER_STATE_INVALID))
  2145. {
  2146. delete pBuffer;
  2147. QuerySmtpInstance()->DecCBufferObjs ();
  2148. TraceFunctLeaveEx((LPARAM)this);
  2149. return TRUE;
  2150. }
  2151. if(InputBufferLen)
  2152. {
  2153. Info = (PFILE_NOTIFY_INFORMATION) pBuffer->GetData();
  2154. while (1)
  2155. {
  2156. //we only care about files added to this
  2157. //directory
  2158. //if(Info->Action == FILE_ACTION_MODIFIED)
  2159. if(Info->Action == FILE_ACTION_ADDED)
  2160. {
  2161. IMailMsgProperties *pIMsg = NULL;
  2162. //convert the name to ascii
  2163. FileNameLength = WideCharToMultiByte(CP_ACP, 0, &Info->FileName[0], Info->FileNameLength,
  2164. FileName, sizeof(FileName), NULL, NULL);
  2165. FileName[FileNameLength/2] = '\0';
  2166. DebugTrace((LPARAM) this, "File %s was detected", FileName);
  2167. hr = CoCreateInstance(CLSID_MsgImp, NULL, CLSCTX_INPROC_SERVER,
  2168. IID_IMailMsgProperties, (LPVOID *)&pIMsg);
  2169. // Next, check if we are over the inbound cutoff limit. If so, we will release the message
  2170. // and not proceed.
  2171. if (SUCCEEDED(hr))
  2172. {
  2173. DWORD dwCreationFlags;
  2174. hr = pIMsg->GetDWORD(
  2175. IMMPID_MPV_MESSAGE_CREATION_FLAGS,
  2176. &dwCreationFlags);
  2177. if (FAILED(hr) ||
  2178. (dwCreationFlags & MPV_INBOUND_CUTOFF_EXCEEDED))
  2179. {
  2180. // If we fail to get this property of if the inbound cutoff
  2181. // exceeded flag is set, discard the message and return failure
  2182. if (SUCCEEDED(hr))
  2183. {
  2184. DebugTrace((LPARAM)this, "Failing because inbound cutoff reached");
  2185. hr = E_OUTOFMEMORY;
  2186. }
  2187. pIMsg->Release();
  2188. pIMsg = NULL;
  2189. }
  2190. }
  2191. if( (pIMsg == NULL) || FAILED(hr))
  2192. {
  2193. // We are out of resources, there is absolutely nothing
  2194. // we can do: can't NDR, can't retry ...
  2195. // Just go on with the next mail ...
  2196. ErrorTrace((LPARAM) this, "new MAILQ_ENTRY failed for file: %s",
  2197. FileName);
  2198. // will be mail left in pickup. queue a findfirst to take care of it.
  2199. SetDelayedFindNotification(TRUE);
  2200. IncPendingIoCount ();
  2201. AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_TIMEOUT, TIMEOUT_INTERVAL); //retry after a while
  2202. ErrorTrace((LPARAM)this, "Failed to create message will retry later.");
  2203. goto Exit;
  2204. }
  2205. else
  2206. {
  2207. pIMsg->PutStringA(IMMPID_MP_PICKUP_FILE_NAME, FileName);
  2208. if(!ProcessFile(pIMsg))
  2209. {
  2210. // will be mail left in pickup. queue a findfirst to take care of it.
  2211. SetDelayedFindNotification(TRUE);
  2212. }
  2213. }
  2214. }
  2215. if(Info->NextEntryOffset == 0)
  2216. {
  2217. DebugTrace((LPARAM) this, "No more entries in this notification");
  2218. break;
  2219. }
  2220. //get the next entry in the list to process
  2221. Info = (PFILE_NOTIFY_INFORMATION)( (PCHAR)Info + Info->NextEntryOffset);
  2222. }
  2223. if (GetDelayedFindNotification())
  2224. {
  2225. DebugTrace((LPARAM) this, "Doing FindFirstFile because max mail objects was hit in notification");
  2226. DoFindFirstFile();
  2227. }
  2228. }
  2229. else
  2230. {
  2231. DebugTrace((LPARAM) this, "Doing FindFirstFile because InputBufferLen == 0");
  2232. DoFindFirstFile();
  2233. }
  2234. Exit:
  2235. if(pBuffer)
  2236. {
  2237. delete pBuffer;
  2238. pBuffer = NULL;
  2239. QuerySmtpInstance()->DecCBufferObjs ();
  2240. }
  2241. TraceFunctLeaveEx((LPARAM)this);
  2242. return TRUE;
  2243. }
  2244. /*++
  2245. Name :
  2246. SMTP_DIRNOT::ProcessClient
  2247. Description:
  2248. Main function for this class. Processes the connection based
  2249. on current state of the connection.
  2250. It may invoke or be invoked by ATQ functions.
  2251. Arguments:
  2252. cbWritten count of bytes written
  2253. dwCompletionStatus Error Code for last IO operation
  2254. lpo Overlapped stucture
  2255. Returns:
  2256. TRUE when processing is incomplete.
  2257. FALSE when the connection is completely processed and this
  2258. object may be deleted.
  2259. --*/
  2260. BOOL SMTP_DIRNOT::ProcessClient( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  2261. {
  2262. PFILE_NOTIFY_INFORMATION Info = NULL;
  2263. DWORD FileNameLength = 0;
  2264. BOOL fRet;
  2265. PSMTP_IIS_SERVICE pService;
  2266. TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::ProcessClient" );
  2267. _ASSERT(m_pInstance != NULL);
  2268. //
  2269. // increment the number of threads processing this client
  2270. //
  2271. IncThreadCount();
  2272. if(!QuerySmtpInstance()->IsShuttingDown())
  2273. {
  2274. //
  2275. // dhowell - if a gilbraltar thread, and size > 0 then it is a delivery thread we have created
  2276. // the SMTP_CONNECTION code will ensure that only the correct number run.
  2277. //
  2278. if (lpo == &(QueryAtqContext()->Overlapped) || dwCompletionStatus == ERROR_SEM_TIMEOUT)
  2279. {
  2280. if(InputBufferLen == 0)
  2281. {
  2282. DebugTrace( (LPARAM)this,"FindFirst called internally.");
  2283. //reset timeout to infinity in anticipation of having memory; DoFindFirstFile
  2284. //will set it to TIMEOUT_INTERVAL if it runs out of memory
  2285. AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_TIMEOUT, INFINITE);
  2286. DoFindFirstFile();
  2287. //since this was a timeout completion, the atq context has been "disabled" from
  2288. //further IO processing. Restart the timeout processing.
  2289. AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_RESUME_IO, 1);
  2290. }
  2291. }
  2292. //
  2293. // A legit notification .
  2294. //
  2295. else
  2296. {
  2297. DecDirChangeIoCount();
  2298. DebugTrace( (LPARAM)this,"ReadDirectoryChange Notification");
  2299. fRet = ProcessDirNotification(InputBufferLen, dwCompletionStatus, lpo);
  2300. }
  2301. }
  2302. else
  2303. {
  2304. if (lpo != &(QueryAtqContext()->Overlapped))
  2305. {
  2306. //just cleanup up shop if we are shutting down.
  2307. CBuffer* pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer;
  2308. delete pBuffer;
  2309. pBuffer = NULL;
  2310. QuerySmtpInstance()->DecCBufferObjs ();
  2311. DecDirChangeIoCount();
  2312. }
  2313. //
  2314. // only applies to shutdown case
  2315. // these will not be decremented via SMTP_CONNECTION::ProcessMailQIO so we have to do it here.
  2316. //
  2317. if ((lpo == &(QueryAtqContext()->Overlapped)) && InputBufferLen)
  2318. {
  2319. pService = (PSMTP_IIS_SERVICE) g_pInetSvc;
  2320. QuerySmtpInstance()->DecRoutingThreads();
  2321. pService->DecSystemRoutingThreads();
  2322. }
  2323. }
  2324. //
  2325. // decrement the number of threads processing this client
  2326. //
  2327. DecThreadCount();
  2328. if ( DecPendingIoCount() == 0 )
  2329. {
  2330. DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() shutting down - Pending IOs: %d",m_cPendingIoCount);
  2331. DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() shutting down - ActiveThreads: %d",m_cActiveThreads);
  2332. _ASSERT( m_cActiveThreads == 0 );
  2333. fRet = FALSE;
  2334. }
  2335. else
  2336. {
  2337. DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() - Pending IOs: %d",m_cPendingIoCount);
  2338. DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() - ActiveThreads: %d",m_cActiveThreads);
  2339. fRet = TRUE;
  2340. }
  2341. TraceFunctLeaveEx((LPARAM)this);
  2342. return fRet;
  2343. }
  2344. /*++
  2345. Name :
  2346. ReadDirectoryCompletion
  2347. Description:
  2348. Handles a completed I/O for ReadDirectoryChanges
  2349. Arguments:
  2350. pvContext: the context pointer specified in the initial IO
  2351. cbWritten: the number of bytes sent
  2352. dwCompletionStatus: the status of the completion (usually NO_ERROR)
  2353. lpo: the overlapped structure associated with the IO
  2354. Returns:
  2355. nothing.
  2356. --*/
  2357. VOID SMTP_DIRNOT::ReadDirectoryCompletion(PVOID pvContext, DWORD cbWritten,
  2358. DWORD dwCompletionStatus, OVERLAPPED * lpo)
  2359. {
  2360. BOOL WasProcessed;
  2361. SMTP_DIRNOT *pCC = (SMTP_DIRNOT *) pvContext;
  2362. TraceFunctEnterEx((LPARAM) pCC, "SMTP_DIRNOT::ReadDirectoryCompletion");
  2363. _ASSERT(pCC);
  2364. _ASSERT(pCC->IsValid());
  2365. _ASSERT(pCC->QuerySmtpInstance() != NULL);
  2366. if (dwCompletionStatus != NO_ERROR && dwCompletionStatus != ERROR_SEM_TIMEOUT)
  2367. ErrorTrace((LPARAM)pCC, "Error in Atq operation: %d\r\n", dwCompletionStatus);
  2368. WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, lpo);
  2369. if(!WasProcessed)
  2370. {
  2371. SetEvent(pCC->QuerySmtpInstance()->GetDirnotStopHandle());
  2372. }
  2373. TraceFunctLeaveEx((LPARAM) pCC);
  2374. }