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.

519 lines
14 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name :
  4. buildq.cxx
  5. Abstract:
  6. Builds the initial queue during service startup
  7. Author:
  8. KeithLau 10/9/96
  9. Project:
  10. SMTP Server DLL
  11. Functions Exported:
  12. Revision History:
  13. dhowell 26/5/97 Added MCIS to K2 upgrade... rewrite envelope if required logic.
  14. --*/
  15. /************************************************************
  16. * Include Headers
  17. ************************************************************/
  18. #define INCL_INETSRV_INCS
  19. #include "smtpinc.h"
  20. #include "buildq.hxx"
  21. #include "headers.hxx"
  22. //#include "pkuprtyq.hxx"
  23. #include "timeconv.h"
  24. #define MAX_INITIAL_THREADS 8
  25. /************************************************************
  26. * Threadprocs
  27. ************************************************************/
  28. //
  29. // Threadproc which builds up the initial queue of messages
  30. //
  31. DWORD BuildInitialQueueProc(void *lpThis);
  32. //
  33. // Threadproc which delivers the queued messages
  34. //
  35. VOID ProcessInitialQueueObjects(PVOID pvContext,
  36. DWORD cbWritten,
  37. DWORD dwCompletionStatus,
  38. OVERLAPPED *lpo);
  39. /************************************************************
  40. * Globals
  41. ************************************************************/
  42. /************************************************************
  43. * Implementation of SMTP_BUILDQ class
  44. ************************************************************/
  45. /*++
  46. Name:
  47. SMTP_BUILDQ::SMTP_BUILDQ
  48. Constructs a new SMTP_BUILDQ object
  49. --*/
  50. SMTP_BUILDQ::SMTP_BUILDQ(SMTP_SERVER_INSTANCE * pInst)
  51. {
  52. TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::SMTP_BUILDQ");
  53. m_hAtq = INVALID_HANDLE_VALUE;
  54. m_pAtqContext = NULL;
  55. m_cPendingIoCount = 0;
  56. m_cActiveThreads = 0;
  57. m_Entries = 0;
  58. m_Signature = SMTP_BUILDQ_SIGNATURE_VALID;
  59. m_pInstance = pInst;
  60. InitializeListHead(&m_InitalQListHead);
  61. InitializeCriticalSection (&m_CritSec);
  62. TraceFunctLeaveEx((LPARAM)this);
  63. }
  64. SMTP_BUILDQ::~SMTP_BUILDQ(void)
  65. {
  66. PATQ_CONTEXT pAtqContext = NULL;
  67. TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::~SMTP_BUILDQ");
  68. _ASSERT(GetThreadCount() == 0);
  69. // Release the context from Atq
  70. pAtqContext = (PATQ_CONTEXT)InterlockedExchangePointer((PVOID *)&m_pAtqContext, (PVOID)NULL);
  71. if (pAtqContext != NULL)
  72. {
  73. pAtqContext->hAsyncIO = NULL;
  74. AtqFreeContext( pAtqContext, TRUE );
  75. }
  76. // Close the directory handle if for some reason it is
  77. // not already closed
  78. if (m_hAtq != INVALID_HANDLE_VALUE)
  79. {
  80. _VERIFY(CloseHandle(m_hAtq));
  81. m_hAtq = INVALID_HANDLE_VALUE;
  82. }
  83. // Invalidate the signature. since this connection is trashed.
  84. m_Signature = SMTP_BUILDQ_SIGNATURE_FREE;
  85. DeleteCriticalSection (&m_CritSec);
  86. TraceFunctLeaveEx((LPARAM)this);
  87. }
  88. BOOL SMTP_BUILDQ::InitializeObject(ATQ_COMPLETION pfnCompletion)
  89. {
  90. //PATQ_CONT pContext;
  91. BOOL fReturn = FALSE;
  92. HANDLE hStop;
  93. TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::InitializeObject");
  94. // Open the queue directory, this is just to keep AtqAddAsyncHandle happy
  95. m_hAtq = CreateFile(QuerySmtpInstance()->GetMailQueueDir(),
  96. FILE_LIST_DIRECTORY,
  97. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  98. NULL,
  99. OPEN_EXISTING,
  100. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
  101. NULL);
  102. if(m_hAtq == INVALID_HANDLE_VALUE)
  103. {
  104. ErrorTrace((LPARAM)this,
  105. "CreateFile on %s failed with error %d ",
  106. QuerySmtpInstance()->GetMailQueueDir(),
  107. GetLastError());
  108. goto ExitFunc;
  109. }
  110. // Create an event to fire when we stop
  111. hStop = CreateEvent(NULL, TRUE, FALSE, NULL);
  112. if(hStop == NULL)
  113. {
  114. ErrorTrace((LPARAM)this,
  115. "CreateEvent() for hStop failed with error %d ",
  116. GetLastError());
  117. goto ExitFunc;
  118. }
  119. QuerySmtpInstance()->SetBuildQStopHandle(hStop);
  120. // Add it to our Gibraltar interface
  121. if (!AtqAddAsyncHandle(&m_pAtqContext, NULL, this, pfnCompletion,
  122. INFINITE, m_hAtq))
  123. {
  124. ErrorTrace((LPARAM)this,
  125. "AtqAddAsyncHandle failed with error %d ",
  126. GetLastError());
  127. goto ExitFunc;
  128. }
  129. // Hack to get around ATQ assert
  130. //pContext = (PATQ_CONT)m_pAtqContext;
  131. //pContext->lSyncTimeout = AtqProcessingIo;
  132. // Indicate success
  133. fReturn = TRUE;
  134. ExitFunc:
  135. if (!fReturn)
  136. {
  137. if (m_hAtq != INVALID_HANDLE_VALUE)
  138. {
  139. _VERIFY(CloseHandle(m_hAtq));
  140. m_hAtq = INVALID_HANDLE_VALUE;
  141. }
  142. QuerySmtpInstance()->CloseBuildQStopHandle();
  143. }
  144. TraceFunctLeaveEx((LPARAM)this);
  145. return(fReturn);
  146. }
  147. void SMTP_BUILDQ::CloseAtqHandleAndFlushQueue (void)
  148. {
  149. HANDLE hAtq = INVALID_HANDLE_VALUE;
  150. PQUEUE_ENTRY pQEntry;
  151. TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::CloseAtqHandle");
  152. // We close the Atq (directory) handle.
  153. hAtq = (HANDLE)InterlockedExchangePointer((PVOID *)&m_hAtq,
  154. (PVOID)INVALID_HANDLE_VALUE);
  155. if (hAtq != INVALID_HANDLE_VALUE)
  156. _VERIFY(CloseHandle(hAtq));
  157. QuerySmtpInstance()->SetStopHint(2);
  158. WaitForSingleObject(QuerySmtpInstance()->QueryBuildQStopHandle(), INFINITE);
  159. QuerySmtpInstance()->CloseBuildQStopHandle();
  160. // Flush the queue, since we are all shuttin gdown
  161. LockQ();
  162. while(!IsListEmpty(&m_InitalQListHead))
  163. {
  164. QuerySmtpInstance()->StopHint();
  165. pQEntry = PopQEntry();
  166. delete pQEntry;
  167. }
  168. UnLockQ();
  169. QuerySmtpInstance()->SetStopHint(2);
  170. TraceFunctLeaveEx((LPARAM)this);
  171. }
  172. BOOL SMTP_BUILDQ::InsertIntoInitialQueue(IN OUT PERSIST_QUEUE_ENTRY * pEntry)
  173. {
  174. return(TRUE);
  175. }
  176. PQUEUE_ENTRY SMTP_BUILDQ::PopQEntry(void)
  177. {
  178. return NULL;
  179. }
  180. BOOL SMTP_BUILDQ::PostCompletionStatus(DWORD BytesTransferred)
  181. {
  182. BOOL fRet;
  183. TraceFunctEnterEx((LPARAM)this, "PostCompletionStatus");
  184. _ASSERT(QueryAtqContext() != NULL);
  185. // Ask for more threads only if we are less than the preset
  186. // maximum number of threads
  187. IncPendingIoCount();
  188. if(!(fRet = AtqPostCompletionStatus(QueryAtqContext(),
  189. BytesTransferred)))
  190. {
  191. DecPendingIoCount();
  192. ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError());
  193. }
  194. TraceFunctLeaveEx((LPARAM) this);
  195. return(fRet);
  196. }
  197. /*++
  198. Name :
  199. DWORD BuildInitialQueueProc
  200. Description:
  201. Wrapper for the SMTP_BUILDQ::BuildInitialQueue workhorse
  202. funciton.
  203. Arguments:
  204. lpThis - points to the SMTP_BUILDQ object
  205. Returns:
  206. NO_ERROR
  207. --*/
  208. DWORD BuildInitialQueueProc(void *lpThis)
  209. {
  210. SMTP_BUILDQ *pBuildQ = (SMTP_BUILDQ *)lpThis;
  211. _ASSERT(pBuildQ);
  212. _ASSERT(pBuildQ->IsValid());
  213. // Call the main workhorse function
  214. pBuildQ->BuildInitialQueue();
  215. return(NO_ERROR);
  216. }
  217. /*++
  218. Name :
  219. SMTP_BUILDQ::BuildInitialQueue
  220. Description:
  221. This function reads all the mail files
  222. that were not sent, for what ever reason,
  223. opens the NTFS stream that holds the envelope,
  224. and builds up the internal send queue. The
  225. envelope for each message resides in an NTFS
  226. stream in that message file. The envelope has
  227. a header that looks like the following :
  228. struct ENVELOPE_HEADER
  229. {
  230. DWORD Version;
  231. DWORD Dummy1; // To be used later
  232. DWORD Dummy2; // To be used later
  233. DWORD LocalOffset; // Local rcpt. list offset
  234. DWORD RemoteOffset; // Remote rcpt. list offset
  235. DWORD LocalSize; // Size of local rcpt list
  236. DWORD RemoteSize; // Size of remote rcpt list
  237. };
  238. Right after the envelope header is the address
  239. that was in the "Mail From" line. This address
  240. is stored like "[email protected]\n". The "S"
  241. stands for SENDER. In the code below, the first
  242. byte is always removed when reading the address.
  243. The '\n' is also replaced with a '\0';
  244. In this version the Local recipient list, if any,
  245. comes right after the senders' address. You can
  246. also find it by seeking LocalOffset bytes from the
  247. beginning of the file. Once LocalOffset is reached,
  248. the code reads LocalSize bytes of data. This is the
  249. total size in bytes of the local recipient list.
  250. Each recipient address is stored on a line by itself,
  251. with the first letter "R" as in the example below:
  252. Rrohanp@microsoft.com\n
  253. Rtoddch@microsoft.com\n
  254. etc.
  255. The remote addresses have the same format. The first byte,
  256. 'R' stands for recipient and is always removed when building
  257. the address. The '\n' is also removed.
  258. Arguments:
  259. A pointer to a PERSIST_QUEUE
  260. Returns:
  261. --*/
  262. DWORD SMTP_BUILDQ::BuildInitialQueue(void)
  263. {
  264. SMTP_SERVER_INSTANCE * pInst;
  265. pInst = QuerySmtpInstance();
  266. SetEvent(QuerySmtpInstance()->GetBuildQStopHandle());
  267. // Log an event saying that the server is ready to accept connections,
  268. // only if we are not shutting down.
  269. if (!pInst->IsShuttingDown())
  270. {
  271. char IntBuffer [20];
  272. pInst->SetAcceptConnBool();
  273. _itoa(pInst->QueryInstanceId(), IntBuffer, 10);
  274. SmtpLogEventEx(SMTP_EVENT_ACCEPTING_CONNECTIONS, IntBuffer, 0);
  275. }
  276. return(NO_ERROR);
  277. }
  278. BOOL SMTP_BUILDQ::ProcessMailQIO(DWORD InputBufferLen,
  279. DWORD dwCompletionStatus,
  280. OVERLAPPED * lpo)
  281. {
  282. return(TRUE);
  283. }
  284. /*++
  285. Name :
  286. SMTP_BUILDQ::ProcessQueueObject
  287. Description:
  288. Main function for this class. Processes the objects in the
  289. initial queue
  290. Arguments:
  291. cbWritten count of bytes written
  292. dwCompletionStatus Error Code for last IO operation
  293. lpo Overlapped stucture
  294. Returns:
  295. FALSE when processing is incomplete.
  296. TRUE when the connection is completely processed and this
  297. object may be deleted.
  298. --*/
  299. BOOL SMTP_BUILDQ::ProcessQueueObject(IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
  300. {
  301. BOOL fRet;
  302. TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::ProcessQueueObject" );
  303. //
  304. // Increment the number of threads processing this client
  305. //
  306. IncThreadCount();
  307. if(!QuerySmtpInstance()->IsShuttingDown())
  308. {
  309. // We want to make sure we are called from the Gibraltar
  310. // context since that should be the only case ...
  311. if (lpo == &(QueryAtqContext()->Overlapped))
  312. {
  313. DebugTrace((LPARAM)this,"About to process queue object");
  314. fRet = ProcessMailQIO (InputBufferLen, dwCompletionStatus, lpo);
  315. }
  316. else
  317. {
  318. // We are not called with the correct context, we can't
  319. // do much about it; so we skip in in retail and break
  320. // in debug.
  321. FatalTrace((LPARAM)this,"Bad overlapped context");
  322. _ASSERT(0);
  323. }
  324. }
  325. //
  326. // Decrement the number of threads processing this client
  327. //
  328. DecThreadCount();
  329. // If our pending IO count ever hits zero, we know we have handled
  330. // all initial mail messages. If this is the case, we will return
  331. // FALSE, and the calling function will then fire the Stop event.
  332. if (DecPendingIoCount() == 0)
  333. {
  334. DebugTrace((LPARAM)this,
  335. "SMTP_BUILDQ::ProcessQueueObject() shutting down - Pending IOs: %d",
  336. m_cPendingIoCount);
  337. DebugTrace((LPARAM)this,
  338. "SMTP_BUILDQ::ProcessQueueObject() shutting down - ActiveThreads: %d",
  339. m_cActiveThreads);
  340. _ASSERT(m_cActiveThreads == 0);
  341. fRet = TRUE;
  342. }
  343. else
  344. {
  345. DebugTrace((LPARAM)this,
  346. "SMTP_BUILDQ::ProcessClient() - Pending IOs: %d",
  347. m_cPendingIoCount);
  348. DebugTrace((LPARAM)this,
  349. "SMTP_BUILDQ::ProcessClient() - ActiveThreads: %d",
  350. m_cActiveThreads);
  351. fRet = FALSE;
  352. }
  353. TraceFunctLeaveEx((LPARAM)this);
  354. return(fRet);
  355. }
  356. /*++
  357. Name :
  358. ProcessInitialQueueObjects
  359. Description:
  360. Handles a completed I/O for delivering queued mail objects
  361. Arguments:
  362. pvContext: the context pointer specified in the initial IO
  363. cbWritten: the number of bytes sent
  364. dwCompletionStatus: the status of the completion (usually NO_ERROR)
  365. lpo: the overlapped structure associated with the IO
  366. Returns:
  367. nothing.
  368. --*/
  369. VOID ProcessInitialQueueObjects(PVOID pvContext,
  370. DWORD cbWritten,
  371. DWORD dwCompletionStatus,
  372. OVERLAPPED *lpo)
  373. {
  374. BOOL fCompleted;
  375. SMTP_BUILDQ *pBuildQ = (SMTP_BUILDQ *)pvContext;
  376. TraceFunctEnterEx((LPARAM)pBuildQ, "ProcessInitialQueueObjects");
  377. _ASSERT(pBuildQ);
  378. _ASSERT(pBuildQ->IsValid());
  379. // Call the in-context function to process the object
  380. fCompleted =
  381. pBuildQ->ProcessQueueObject(cbWritten, dwCompletionStatus, lpo);
  382. if (fCompleted)
  383. {
  384. // If fCompleted is TRUE, we know that we either finished
  385. // delivering the entire initial queue, or that we are already
  386. // signalled to shut down, and the last pending IO has just
  387. // been serviced. We fire the Stop event to allow CloseAtqHandle
  388. // to proceed through the wait.
  389. _VERIFY(SetEvent(pBuildQ->QuerySmtpInstance()->GetBuildQStopHandle()));
  390. }
  391. TraceFunctLeaveEx((LPARAM)pBuildQ);
  392. }
  393. BOOL SMTP_BUILDQ::MakeAllAddrsLocal (HANDLE hFile, char * szBuffer, ENVELOPE_HEADER * pMyHeader)
  394. {
  395. return TRUE;
  396. }