Leaked source code of windows server 2003
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.

5615 lines
180 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: dsnsink.cpp
  5. //
  6. // Description: Implementation of default DSN Generation sink
  7. //
  8. // Author: Mike Swafford (MikeSwa)
  9. //
  10. // History:
  11. // 6/30/98 - MikeSwa Created
  12. //
  13. // Copyright (C) 1998 Microsoft Corporation
  14. //
  15. //-----------------------------------------------------------------------------
  16. #include "precomp.h"
  17. //
  18. // This length is inspired by the other protocols that we deal with. The
  19. // default address limit is 1024, but the MTA can allow 1024 + 834 for the
  20. // OR address. We'll define out default buffer size to allow this large
  21. // of an address.
  22. //
  23. #define PROP_BUFFER_SIZE 1860
  24. #ifdef DEBUG
  25. #define DEBUG_DO_IT(x) x
  26. #else
  27. #define DEBUG_DO_IT(x)
  28. #endif //DEBUG
  29. //min sizes for valid status strings
  30. #define MIN_CHAR_FOR_VALID_RFC2034 10
  31. #define MIN_CHAR_FOR_VALID_RFC821 3
  32. #define MAX_RFC822_DATE_SIZE 35
  33. BOOL FileTimeToLocalRFC822Date(const FILETIME & ft, char achReturn[MAX_RFC822_DATE_SIZE]);
  34. static char *s_rgszMonth[ 12 ] =
  35. {
  36. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  37. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  38. };
  39. static char *s_rgszWeekDays[7] =
  40. {
  41. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  42. };
  43. #define MAX_RFC_DOMAIN_SIZE 64
  44. //String used in generation of MsgID
  45. static char g_szBoundaryChars [] = "0123456789abcdefghijklmnopqrstuvwxyz"
  46. "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  47. static LONG g_cDSNMsgID = 0;
  48. //Address types to check for, and their corresponding address types.
  49. const DWORD g_rgdwSenderPropIDs[] = {
  50. IMMPID_MP_SENDER_ADDRESS_SMTP,
  51. IMMPID_MP_SENDER_ADDRESS_X400,
  52. IMMPID_MP_SENDER_ADDRESS_LEGACY_EX_DN,
  53. IMMPID_MP_SENDER_ADDRESS_X500,
  54. IMMPID_MP_SENDER_ADDRESS_OTHER};
  55. const DWORD g_rgdwRecipPropIDs[] = {
  56. IMMPID_RP_ADDRESS_SMTP,
  57. IMMPID_RP_ADDRESS_X400,
  58. IMMPID_RP_LEGACY_EX_DN,
  59. IMMPID_RP_ADDRESS_X500,
  60. IMMPID_RP_ADDRESS_OTHER};
  61. const DWORD NUM_DSN_ADDRESS_PROPERTIES = 5;
  62. const CHAR *g_rgszAddressTypes[] = {
  63. "rfc822",
  64. "x-x400",
  65. "x-ex",
  66. "x-x500",
  67. "unknown"};
  68. CPool CDSNPool::sm_Pool;
  69. //---[ fLanguageAvailable ]----------------------------------------------------
  70. //
  71. //
  72. // Description:
  73. // Checks to see if resources for a given language are available.
  74. // Parameters:
  75. // LangId Language to check for
  76. // Returns:
  77. // TRUE If localized resources for requested language are available
  78. // FALSE If resources for that language are not available.
  79. // History:
  80. // 10/26/98 - MikeSwa Created
  81. //
  82. //-----------------------------------------------------------------------------
  83. BOOL fLanguageAvailable(LANGID LangId)
  84. {
  85. TraceFunctEnterEx((LPARAM) LangId, "fLanguageAvailable");
  86. HINSTANCE hModule = GetModuleHandle(DSN_RESOUCE_MODULE_NAME);
  87. HRSRC hResInfo = NULL;
  88. BOOL fResult = FALSE;
  89. if (NULL == hModule)
  90. {
  91. _ASSERT( 0 && "Cannot get resource module handle");
  92. return FALSE;
  93. }
  94. //Find handle to string table segment
  95. hResInfo = FindResourceEx(hModule, RT_STRING,
  96. MAKEINTRESOURCE(((WORD)((USHORT)GENERAL_SUBJECT >> 4) + 1)),
  97. LangId);
  98. if (NULL != hResInfo)
  99. fResult = TRUE;
  100. else
  101. ErrorTrace((LPARAM) LangId, "Unable to load DSN resources for language");
  102. TraceFunctLeave();
  103. return fResult;
  104. }
  105. //---[ fIsValidMIMEBoundaryChar ]----------------------------------------------
  106. //
  107. //
  108. // Description:
  109. //
  110. // Checks to see if the given character is a valid as described by the
  111. // RFC2046 BNF for MIME Boundaries:
  112. // boundary := 0*69<bchars> bcharsnospace
  113. // bchars := bcharsnospace / " "
  114. // bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
  115. // "+" / "_" / "," / "-" / "." /
  116. // "/" / ":" / "=" / "?"
  117. // Parameters:
  118. // ch Char to check
  119. // Returns:
  120. // TRUE if VALID
  121. // FALSE otherwise
  122. // History:
  123. // 7/6/98 - MikeSwa Created
  124. //
  125. //-----------------------------------------------------------------------------
  126. BOOL fIsValidMIMEBoundaryChar(CHAR ch)
  127. {
  128. if (isalnum((UCHAR)ch))
  129. return TRUE;
  130. //check to see if it is one of the special case characters
  131. if (('\'' == ch) || ('(' == ch) || (')' == ch) || ('+' == ch) ||
  132. ('_' == ch) || (',' == ch) || ('_' == ch) || ('.' == ch) ||
  133. ('/' == ch) || (':' == ch) || ('=' == ch) || ('?' == ch))
  134. return TRUE;
  135. else
  136. return FALSE;
  137. }
  138. //---[ GenerateDSNMsgID ]------------------------------------------------------
  139. //
  140. //
  141. // Description:
  142. // Generates a unique MsgID string
  143. //
  144. // The format is:
  145. // <random-unique-string>@<domain>
  146. // Parameters:
  147. // IN szDomain Domain to generate MsgID for
  148. // IN cbDomain Domain to generate MsgID for
  149. // IN OUT szBuffer Buffer to write MsgID in
  150. // IN cbBuffer Size of buffer to write MsgID in
  151. // Returns:
  152. // TRUE on success
  153. // FALSE otherwise
  154. // History:
  155. // 3/2/99 - MikeSwa Created
  156. //
  157. //-----------------------------------------------------------------------------
  158. BOOL fGenerateDSNMsgID(LPSTR szDomain,DWORD cbDomain,
  159. LPSTR szBuffer, DWORD cbBuffer)
  160. {
  161. TraceFunctEnterEx((LPARAM) NULL, "fGenerateDSNMsgID");
  162. _ASSERT(szDomain);
  163. _ASSERT(cbDomain);
  164. _ASSERT(szBuffer);
  165. _ASSERT(cbBuffer);
  166. // insert the leading <
  167. if (cbBuffer >= 1) {
  168. *szBuffer = '<';
  169. szBuffer++;
  170. cbBuffer--;
  171. }
  172. const CHAR szSampleFormat[] = "00000000@"; // sample format string
  173. const DWORD cbMsgIdLen = 20; //default size of random string
  174. LPSTR szStop = szBuffer + cbMsgIdLen;
  175. LPSTR szCurrent = szBuffer;
  176. DWORD cbCurrent = 0;
  177. //minimize size for *internal* static buffer
  178. _ASSERT(cbBuffer > MAX_RFC_DOMAIN_SIZE + cbMsgIdLen);
  179. if (!szDomain || !cbDomain || !szBuffer || !cbBuffer ||
  180. (cbBuffer <= MAX_RFC_DOMAIN_SIZE + cbMsgIdLen))
  181. return FALSE;
  182. //We want to figure how much room we have for random characters
  183. //We will need to fit the domain name, the '@', and the 8 character unique
  184. //number
  185. // awetmore - add 1 for the trailing >
  186. if(cbBuffer < cbDomain + cbMsgIdLen + 1)
  187. {
  188. //Fall through an allow for 20 characaters and part of domain name
  189. //We want to catch this in debug builds
  190. _ASSERT(0 && "Buffer too small for MsgID");
  191. }
  192. //this should have been caught in parameter checking
  193. _ASSERT(cbBuffer > cbMsgIdLen);
  194. szStop -= (sizeof(szSampleFormat) + 1);
  195. while (szCurrent < szStop)
  196. {
  197. *szCurrent = g_szBoundaryChars[rand() % (sizeof(g_szBoundaryChars) - 1)];
  198. szCurrent++;
  199. }
  200. //Add unique number
  201. cbCurrent = sprintf(szCurrent, "%8.8x@", InterlockedIncrement(&g_cDSNMsgID));
  202. _ASSERT(sizeof(szSampleFormat) - 1 == cbCurrent);
  203. //Figure out how much room we have and add domain name
  204. szCurrent += cbCurrent;
  205. cbCurrent = (DWORD) (szCurrent-szBuffer);
  206. //unless I've messed up the logic this is always true
  207. _ASSERT(cbCurrent < cbBuffer);
  208. //Add domain part to message id
  209. strncat(szCurrent-1, szDomain, cbBuffer - cbCurrent - 1);
  210. _ASSERT(cbCurrent + cbDomain < cbBuffer);
  211. // Add the trailing >. we accounted for the space above check for
  212. // cbBuffer size
  213. strncat(szCurrent, ">", cbBuffer - cbCurrent - cbDomain - 1);
  214. DebugTrace((LPARAM) NULL, "Generating DSN Message ID %s", szCurrent);
  215. TraceFunctLeave();
  216. return TRUE;
  217. }
  218. //---[ fIsMailMsgDSN ]---------------------------------------------------------
  219. //
  220. //
  221. // Description:
  222. // Determines if a mailmsg is a DSN.
  223. // Parameters:
  224. // IN pIMailMsgProperties
  225. // Returns:
  226. // TRUE if the orinal message is a DSN
  227. // FALSE if it is not a DSN
  228. // History:
  229. // 2/11/99 - MikeSwa Created
  230. //
  231. //-----------------------------------------------------------------------------
  232. BOOL fIsMailMsgDSN(IMailMsgProperties *pIMailMsgProperties)
  233. {
  234. CHAR szSenderBuffer[sizeof(DSN_MAIL_FROM)];
  235. DWORD cbSender = 0;
  236. HRESULT hr = S_OK;
  237. BOOL fIsDSN = FALSE; //unless proven otherwise... it is not a DSN
  238. _ASSERT(pIMailMsgProperties);
  239. szSenderBuffer[0] = '\0';
  240. //Get the sender of the original message
  241. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_SENDER_ADDRESS_SMTP,
  242. sizeof(szSenderBuffer), &cbSender, (BYTE *) szSenderBuffer);
  243. if (SUCCEEDED(hr) &&
  244. ('\0' == szSenderBuffer[0] || !strcmp(DSN_MAIL_FROM, szSenderBuffer)))
  245. {
  246. //If the sender is a NULL string... or "<>"... then it is a DSN
  247. fIsDSN = TRUE;
  248. }
  249. return fIsDSN;
  250. }
  251. #ifdef DEBUG
  252. #define _ASSERT_RECIP_FLAGS AssertRecipFlagsFn
  253. #define _ASSERT_MIME_BOUNDARY(szMimeBoundary) AssertMimeBoundary(szMimeBoundary)
  254. //---[ AssertRecipFlagsFn ]----------------------------------------------------
  255. //
  256. //
  257. // Description:
  258. // ***DEBUG ONLY***
  259. // Asserts that the recipient flags defined in mailmsgprops.h are correct
  260. // Parameters:
  261. // -
  262. // Returns:
  263. // -
  264. // History:
  265. // 7/2/98 - MikeSwa Created
  266. //
  267. //-----------------------------------------------------------------------------
  268. void AssertRecipFlagsFn()
  269. {
  270. DWORD i, j;
  271. DWORD rgdwFlags[] = {RP_DSN_NOTIFY_SUCCESS, RP_DSN_NOTIFY_FAILURE,
  272. RP_DSN_NOTIFY_DELAY, RP_DSN_NOTIFY_NEVER, RP_DELIVERED,
  273. RP_DSN_SENT_NDR, RP_FAILED, RP_UNRESOLVED, RP_EXPANDED,
  274. RP_DSN_SENT_DELAYED, RP_DSN_SENT_EXPANDED, RP_DSN_SENT_RELAYED,
  275. RP_DSN_SENT_DELIVERED, RP_REMOTE_MTA_NO_DSN, RP_ERROR_CONTEXT_STORE,
  276. RP_ERROR_CONTEXT_CAT, RP_ERROR_CONTEXT_MTA};
  277. DWORD cFlags = sizeof(rgdwFlags)/sizeof(DWORD);
  278. for (i = 0; i < cFlags;i ++)
  279. {
  280. for (j = i+1; j < cFlags; j++)
  281. {
  282. //make sure all have some unique bits
  283. if (rgdwFlags[i] & rgdwFlags[j])
  284. {
  285. _ASSERT((rgdwFlags[i] & rgdwFlags[j]) != rgdwFlags[j]);
  286. _ASSERT((rgdwFlags[i] & rgdwFlags[j]) != rgdwFlags[i]);
  287. }
  288. }
  289. }
  290. //Verify that handled bit is used correctly
  291. _ASSERT(RP_HANDLED & RP_DELIVERED);
  292. _ASSERT(RP_HANDLED & RP_DSN_SENT_NDR);
  293. _ASSERT(RP_HANDLED & RP_FAILED);
  294. _ASSERT(RP_HANDLED & RP_UNRESOLVED);
  295. _ASSERT(RP_HANDLED & RP_EXPANDED);
  296. _ASSERT(RP_HANDLED ^ RP_DELIVERED);
  297. _ASSERT(RP_HANDLED ^ RP_DSN_SENT_NDR);
  298. _ASSERT(RP_HANDLED ^ RP_FAILED);
  299. _ASSERT(RP_HANDLED ^ RP_UNRESOLVED);
  300. _ASSERT(RP_HANDLED ^ RP_EXPANDED);
  301. //Verify that DSN-handled bit is used correctly
  302. _ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_NDR);
  303. _ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_EXPANDED);
  304. _ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_RELAYED);
  305. _ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_DELIVERED);
  306. _ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_NDR);
  307. _ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_EXPANDED);
  308. _ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_RELAYED);
  309. _ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_DELIVERED);
  310. //Verify that general failure bit is used correctly
  311. _ASSERT(RP_GENERAL_FAILURE & RP_FAILED);
  312. _ASSERT(RP_GENERAL_FAILURE & RP_UNRESOLVED);
  313. _ASSERT(RP_GENERAL_FAILURE ^ RP_FAILED);
  314. _ASSERT(RP_GENERAL_FAILURE ^ RP_UNRESOLVED);
  315. }
  316. //---[ AssertMimeBoundary ]----------------------------------------------------
  317. //
  318. // ***DEBUG ONLY***
  319. // Description:
  320. // Asserts that the given MIME boundary is NULL-terminated and has only
  321. // Valid characters
  322. // Parameters:
  323. // szMimeBoundary NULL-terminated MIME Boundary string
  324. // Returns:
  325. // -
  326. // History:
  327. // 7/6/98 - MikeSwa Created
  328. //
  329. //-----------------------------------------------------------------------------
  330. void AssertMimeBoundary(LPSTR szMimeBoundary)
  331. {
  332. CHAR *pcharCurrent = szMimeBoundary;
  333. DWORD cChars = 0;
  334. while ('\0' != *pcharCurrent)
  335. {
  336. cChars++;
  337. _ASSERT(cChars <= MIME_BOUNDARY_RFC2046_LIMIT);
  338. _ASSERT(fIsValidMIMEBoundaryChar(*pcharCurrent));
  339. pcharCurrent++;
  340. }
  341. }
  342. #else //not DEBUG
  343. #define _ASSERT_RECIP_FLAGS()
  344. #define _VERIFY_MARKED_RECIPS(a, b, c)
  345. #define _ASSERT_MIME_BOUNDARY(szMimeBoundary)
  346. #endif //DEBUG
  347. //---[ CDSNGenerator::CDSNGenerator ]--------------------------------------
  348. //
  349. //
  350. // Description:
  351. // CDSNGenerator constructor
  352. // Parameters:
  353. // -
  354. // Returns:
  355. //
  356. // History:
  357. // 6/30/98 - MikeSwa Created
  358. //
  359. //-----------------------------------------------------------------------------
  360. CDSNGenerator::CDSNGenerator(
  361. IUnknown *pUnk) :
  362. m_CDefaultDSNSink(pUnk)
  363. {
  364. m_dwSignature = DSN_SINK_SIG;
  365. }
  366. //---[ CDSNGenerator::~CDSNGenerator ]-------------------------------------
  367. //
  368. //
  369. // Description:
  370. //
  371. // Parameters:
  372. //
  373. // Returns:
  374. //
  375. // History:
  376. // 2/11/99 - MikeSwa Created
  377. //
  378. //-----------------------------------------------------------------------------
  379. CDSNGenerator::~CDSNGenerator()
  380. {
  381. m_dwSignature = DSN_SINK_SIG_FREED;
  382. }
  383. //---[ CDSNGenerator::GenerateDSN ]------------------------------------------
  384. //
  385. //
  386. // Description:
  387. // Implements GenerateDSN. Generates a DSN
  388. // IMailMsgProperties and
  389. // Parameters:
  390. // pIServerEvent Interface for triggering events
  391. // dwVSID VSID for this server
  392. // pISMTPServer Interface used to generate DSN
  393. // pIMailMsgProperties IMailMsg to generate DSN for
  394. // dwStartDomain Domain to start recip context
  395. // dwDSNActions DSN action to perform
  396. // dwRFC821Status Global RFC821 status DWORD
  397. // hrStatus Global HRESULT status
  398. // szDefaultDomain Default domain (used to create FROM address)
  399. // szReportingMTA Name of MTA requesting DSN generation
  400. // szReportingMTAType Type of MTA requestiong DSN (SMTP is "dns"
  401. // PreferredLangId Language to generate DSN in
  402. // dwDSNOptions Options flags as defined in aqueue.idl
  403. // szCopyNDRTo SMTP Address to copy NDR to
  404. // pIDSNSubmission Interface for submitting DSNs
  405. // dwMaxDSNSize Return HDRS by default for messages
  406. // larger than this
  407. //
  408. // Returns:
  409. // S_OK on success
  410. // AQUEUE_E_NDR_OF_DSN if attempting to NDR a DSN
  411. // E_OUTOFMEMORY
  412. // error from mailmsg
  413. // History:
  414. // 6/30/98 - MikeSwa Created
  415. // 12/14/98 - MikeSwa Modified (Added pcIterationsLeft)
  416. // 10/13/1999 - MikeSwa Modified (Added szDefaultDomain)
  417. // 5/10/2001 - jstamerj Modified for server events
  418. //
  419. //
  420. //-----------------------------------------------------------------------------
  421. STDMETHODIMP CDSNGenerator::GenerateDSN(
  422. IAQServerEvent *pIServerEvent,
  423. DWORD dwVSID,
  424. ISMTPServer *pISMTPServer,
  425. IMailMsgProperties *pIMailMsgProperties,
  426. DWORD dwStartDomain,
  427. DWORD dwDSNActions,
  428. DWORD dwRFC821Status,
  429. HRESULT hrStatus,
  430. LPSTR szDefaultDomain,
  431. LPSTR szReportingMTA,
  432. LPSTR szReportingMTAType,
  433. LPSTR szDSNContext,
  434. DWORD dwPreferredLangId,
  435. DWORD dwDSNOptions,
  436. LPSTR szCopyNDRTo,
  437. FILETIME *pftExpireTime,
  438. IDSNSubmission *pIAQDSNSubmission,
  439. DWORD dwMaxDSNSize)
  440. {
  441. HRESULT hr = S_OK;
  442. HRESULT hrReturn = S_OK;
  443. DWORD dwCount = 0;
  444. DWORD fBadmailMsg = FALSE;
  445. IDSNRecipientIterator *pIRecipIter = NULL;
  446. CDSNPool *pDSNPool = NULL;
  447. CDefaultDSNRecipientIterator *pDefaultRecipIter = NULL;
  448. CPostDSNHandler *pPostDSNHandler = NULL;
  449. CMailMsgPropertyBag *pPropBag = NULL;
  450. TraceFunctEnterEx((LPARAM) this, "CDSNGenerator::GenerateDSN");
  451. //
  452. // Parameter -> Property mapping tables
  453. //
  454. struct _tagDWORDProps
  455. {
  456. DWORD dwPropId;
  457. DWORD dwValue;
  458. } DsnDwordProps[] =
  459. {
  460. { DSNPROP_DW_DSNACTIONS, dwDSNActions },
  461. { DSNPROP_DW_DSNOPTIONS, dwDSNOptions },
  462. { DSNPROP_DW_RFC821STATUS, dwRFC821Status },
  463. { DSNPROP_DW_HRSTATUS, (DWORD) hrStatus },
  464. { DSNPROP_DW_LANGID, dwPreferredLangId },
  465. };
  466. struct _tagStringProps
  467. {
  468. DWORD dwPropId;
  469. LPSTR psz;
  470. } DsnStringProps[] =
  471. {
  472. { DSNPROP_SZ_DEFAULTDOMAIN, szDefaultDomain },
  473. { DSNPROP_SZ_REPORTINGMTA, szReportingMTA },
  474. { DSNPROP_SZ_REPORTINGMTATYPE, szReportingMTAType },
  475. { DSNPROP_SZ_DSNCONTEXT, szDSNContext },
  476. { DSNPROP_SZ_COPYNDRTO, szCopyNDRTo },
  477. };
  478. pDSNPool = new CDSNPool(
  479. this,
  480. pIServerEvent,
  481. dwVSID,
  482. pISMTPServer,
  483. pIMailMsgProperties,
  484. pIAQDSNSubmission,
  485. &m_CDefaultDSNSink);
  486. if(pDSNPool == NULL)
  487. {
  488. hr = E_OUTOFMEMORY;
  489. goto CLEANUP;
  490. }
  491. pDefaultRecipIter = pDSNPool->GetDefaultIter();
  492. pPostDSNHandler = pDSNPool->GetPostDSNHandler();
  493. pPropBag = pDSNPool->GetDSNProperties();
  494. pPostDSNHandler->SetPropInterface(
  495. pPropBag);
  496. for(dwCount = 0;
  497. dwCount < ( sizeof(DsnDwordProps) / sizeof(DsnDwordProps[0]));
  498. dwCount++)
  499. {
  500. hr = pPropBag->PutDWORD(
  501. DsnDwordProps[dwCount].dwPropId,
  502. DsnDwordProps[dwCount].dwValue);
  503. if(FAILED(hr))
  504. goto CLEANUP;
  505. }
  506. for(dwCount = 0;
  507. dwCount < ( sizeof(DsnStringProps) / sizeof(DsnStringProps[0]));
  508. dwCount++)
  509. {
  510. if(DsnStringProps[dwCount].psz)
  511. {
  512. hr = pPropBag->PutStringA(
  513. DsnStringProps[dwCount].dwPropId,
  514. DsnStringProps[dwCount].psz);
  515. if(FAILED(hr))
  516. goto CLEANUP;
  517. }
  518. }
  519. //
  520. //Set MsgExpire Time
  521. //
  522. if(pftExpireTime)
  523. {
  524. hr = pPropBag->PutProperty(
  525. DSNPROP_FT_EXPIRETIME,
  526. sizeof(FILETIME),
  527. (PBYTE) pftExpireTime);
  528. if(FAILED(hr))
  529. goto CLEANUP;
  530. }
  531. //
  532. //Set the return type
  533. //
  534. hr = HrSetRetType(
  535. dwMaxDSNSize,
  536. pIMailMsgProperties,
  537. pPropBag);
  538. if(FAILED(hr))
  539. goto CLEANUP;
  540. hr = pDefaultRecipIter->HrInit(
  541. pIMailMsgProperties,
  542. dwStartDomain,
  543. dwDSNActions);
  544. if(FAILED(hr))
  545. {
  546. ErrorTrace((LPARAM)this, "DefaultRecipIter.HrInit failed hr %08lx", hr);
  547. goto CLEANUP;
  548. }
  549. pIRecipIter = pDefaultRecipIter;
  550. pIRecipIter->AddRef();
  551. //
  552. // Trigger events
  553. //
  554. hr = HrTriggerGetDSNRecipientIterator(
  555. pIServerEvent,
  556. dwVSID,
  557. pISMTPServer,
  558. pIMailMsgProperties,
  559. pPropBag,
  560. dwStartDomain,
  561. dwDSNActions,
  562. &pIRecipIter);
  563. if(FAILED(hr))
  564. goto CLEANUP;
  565. hr = HrTriggerGenerateDSN(
  566. pIServerEvent,
  567. dwVSID,
  568. pISMTPServer,
  569. pPostDSNHandler,
  570. pIMailMsgProperties,
  571. pPropBag,
  572. pIRecipIter);
  573. if(FAILED(hr))
  574. goto CLEANUP;
  575. //
  576. // Check sink's indicated return status
  577. //
  578. hr = pPropBag->GetDWORD(
  579. DSNPROP_DW_HR_RETURN_STATUS,
  580. (DWORD *) &hrReturn);
  581. if(hr == MAILMSG_E_PROPNOTFOUND)
  582. {
  583. //
  584. // If the property is not set, this indicates no error
  585. //
  586. hrReturn = S_OK;
  587. } else if(FAILED(hr))
  588. goto CLEANUP;
  589. DebugTrace((LPARAM)this, "Sink return status: %08lx", hrReturn);
  590. hr = hrReturn;
  591. if(FAILED(hr))
  592. goto CLEANUP;
  593. hr = pPropBag->GetBool(
  594. DSNPROP_F_BADMAIL_MSG,
  595. &fBadmailMsg);
  596. if(hr == MAILMSG_E_PROPNOTFOUND)
  597. {
  598. fBadmailMsg = FALSE;
  599. hr = S_OK;
  600. }
  601. else if(FAILED(hr))
  602. goto CLEANUP;
  603. if(fBadmailMsg)
  604. hr = AQUEUE_E_NDR_OF_DSN;
  605. CLEANUP:
  606. //
  607. // Sinks should not be submitting DSNs after the event has been
  608. // completed. This is not supported and would be bad because the
  609. // object that implements pIAQDSNSubmission is allocated on the
  610. // stack. Release this interface pointer here.
  611. //
  612. if(pPostDSNHandler)
  613. pPostDSNHandler->ReleaseAQDSNSubmission();
  614. if(pIRecipIter)
  615. pIRecipIter->Release();
  616. if(pDSNPool)
  617. pDSNPool->Release();
  618. DebugTrace((LPARAM)this, "returning hr %08lx", hr);
  619. TraceFunctLeave();
  620. return SUCCEEDED(hr) ? S_OK : hr;
  621. }
  622. //+------------------------------------------------------------
  623. //
  624. // Function: CDSNGenerator::HrSetRetType
  625. //
  626. // Synopsis: Set the (default) return type property in the DSN
  627. // property bag
  628. //
  629. // Arguments:
  630. // dwMaxDSNSize: Messages larger than this will default to RET=HDRS
  631. // pIMsg: Mailmsg ptr
  632. // pDSNProps: DSN property bag
  633. //
  634. // Returns:
  635. // S_OK: Success
  636. // error from mailmsg
  637. //
  638. // History:
  639. // jstamerj 2001/06/14 17:12:50: Created.
  640. //
  641. //-------------------------------------------------------------
  642. HRESULT CDSNGenerator::HrSetRetType(
  643. IN DWORD dwMaxDSNSize,
  644. IN IMailMsgProperties *pIMsg,
  645. IN IMailMsgPropertyBag *pDSNProps)
  646. {
  647. HRESULT hr = S_OK;
  648. DWORD dwRetType = 0;
  649. DWORD dwMsgSize = 0;
  650. CHAR szRET[] = "FULL";
  651. TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrSetRetType");
  652. //
  653. //Determine if we want to return the full message or minimal headers.
  654. //The logic for this is:
  655. // - Obey explicit RET (IMMPID_MP_DSN_RET_VALUE) values
  656. // - Default to HDRS for all DSNs greater than a specified size
  657. // - Do not set the property otherwise (let the sinks decide)
  658. //
  659. hr = pIMsg->GetStringA(
  660. IMMPID_MP_DSN_RET_VALUE,
  661. sizeof(szRET),
  662. szRET);
  663. if (SUCCEEDED(hr))
  664. {
  665. if(!_strnicmp(szRET, (char * )"FULL", 4))
  666. dwRetType = DSN_RET_FULL;
  667. else if (!_strnicmp(szRET, (char * )"HDRS", 4))
  668. dwRetType = DSN_RET_HDRS;
  669. }
  670. else if(hr != MAILMSG_E_PROPNOTFOUND)
  671. goto CLEANUP;
  672. if(dwRetType)
  673. {
  674. DebugTrace((LPARAM)this, "DSN Return value specified: %s", szRET);
  675. goto CLEANUP;
  676. }
  677. if(dwMaxDSNSize)
  678. {
  679. //
  680. // Check the original message size
  681. //
  682. hr = pIMsg->GetDWORD(
  683. IMMPID_MP_MSG_SIZE_HINT,
  684. &dwMsgSize);
  685. if(hr == MAILMSG_E_PROPNOTFOUND)
  686. {
  687. hr = pIMsg->GetContentSize(
  688. &dwMsgSize,
  689. NULL);
  690. if(FAILED(hr))
  691. {
  692. //
  693. // Assume a failure here means we don't have the resources
  694. // to get the original message content.
  695. // Rather than badmailing, generate a DSN with headers only
  696. //
  697. ErrorTrace((LPARAM)this, "GetContentSize failed hr %08lx", hr);
  698. hr = pDSNProps->PutDWORD(
  699. DSNPROP_DW_CONTENT_FAILURE,
  700. hr);
  701. if(FAILED(hr))
  702. goto CLEANUP;
  703. dwRetType = DSN_RET_PARTIAL_HDRS;
  704. goto CLEANUP;
  705. }
  706. }
  707. else if(FAILED(hr))
  708. goto CLEANUP;
  709. if(dwMsgSize > dwMaxDSNSize)
  710. {
  711. //
  712. // Return a subset of the headers (so that we do not have to
  713. // generate the original message
  714. //
  715. dwRetType = DSN_RET_PARTIAL_HDRS;
  716. }
  717. }
  718. else
  719. {
  720. //
  721. // MaxDSNSize is zero. Always default to header subset.
  722. //
  723. dwRetType = DSN_RET_PARTIAL_HDRS;
  724. }
  725. hr = S_OK;
  726. CLEANUP:
  727. if(dwRetType)
  728. {
  729. DebugTrace((LPARAM)this, "dwRetType: %08lx", dwRetType);
  730. hr = pDSNProps->PutDWORD(
  731. DSNPROP_DW_RET_TYPE,
  732. dwRetType);
  733. }
  734. DebugTrace((LPARAM)this, "returning %08lx", hr);
  735. TraceFunctLeaveEx((LPARAM)this);
  736. return SUCCEEDED(hr) ? S_OK : hr;
  737. } // CDSNGenerator::HrSetRetType
  738. //+------------------------------------------------------------
  739. //
  740. // Function: CDSNGenerator::HrTriggerGetDSNRecipientIterator
  741. //
  742. // Synopsis: Trigger the server event
  743. //
  744. // Arguments: see ptntintf.idl
  745. //
  746. // Returns:
  747. // S_OK: Success
  748. //
  749. // History:
  750. // jstamerj 2000/12/11 17:01:12: Created.
  751. //
  752. //-------------------------------------------------------------
  753. HRESULT CDSNGenerator::HrTriggerGetDSNRecipientIterator(
  754. IAQServerEvent *pIServerEvent,
  755. DWORD dwVSID,
  756. ISMTPServer *pISMTPServer,
  757. IMailMsgProperties *pIMsg,
  758. IMailMsgPropertyBag *pIDSNProperties,
  759. DWORD dwStartDomain,
  760. DWORD dwDSNActions,
  761. IDSNRecipientIterator **ppIRecipIterator)
  762. {
  763. HRESULT hr = S_OK;
  764. EVENTPARAMS_GET_DSN_RECIPIENT_ITERATOR EventParams;
  765. TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrTriggerGetDSNRecipientIterator");
  766. EventParams.dwVSID = dwVSID;
  767. EventParams.pISMTPServer = pISMTPServer;
  768. EventParams.pIMsg = pIMsg;
  769. EventParams.pDSNProperties = pIDSNProperties;
  770. EventParams.dwStartDomain = dwStartDomain;
  771. EventParams.dwDSNActions = dwDSNActions;
  772. EventParams.pRecipIter = *ppIRecipIterator;
  773. hr = pIServerEvent->TriggerServerEvent(
  774. SMTP_GET_DSN_RECIPIENT_ITERATOR_EVENT,
  775. &EventParams);
  776. if(FAILED(hr))
  777. goto CLEANUP;
  778. *ppIRecipIterator = EventParams.pRecipIter;
  779. CLEANUP:
  780. DebugTrace((LPARAM)this, "returning %08lx", hr);
  781. TraceFunctLeaveEx((LPARAM)this);
  782. return hr;
  783. } // CDSNGenerator::HrTriggerGetDSNRecipientIterator
  784. //+------------------------------------------------------------
  785. //
  786. // Function: CDSNGenerator::HrTriggerGenerateDSN
  787. //
  788. // Synopsis: Trigger the server event
  789. //
  790. // Arguments: See ptntintf.idl
  791. //
  792. // Returns:
  793. // S_OK: Success
  794. //
  795. // History:
  796. // jstamerj 2000/12/11 17:10:26: Created.
  797. //
  798. //-------------------------------------------------------------
  799. HRESULT CDSNGenerator::HrTriggerGenerateDSN(
  800. IAQServerEvent *pIServerEvent,
  801. DWORD dwVSID,
  802. ISMTPServer *pISMTPServer,
  803. IDSNSubmission *pIDSNSubmission,
  804. IMailMsgProperties *pIMsg,
  805. IMailMsgPropertyBag *pIDSNProperties,
  806. IDSNRecipientIterator *pIRecipIterator)
  807. {
  808. HRESULT hr = S_OK;
  809. EVENTPARAMS_GENERATE_DSN EventParams;
  810. TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrTriggerGenerateDSN");
  811. EventParams.dwVSID = dwVSID;
  812. EventParams.pDefaultSink = &m_CDefaultDSNSink;
  813. EventParams.pISMTPServer = pISMTPServer;
  814. EventParams.pIDSNSubmission = pIDSNSubmission;
  815. EventParams.pIMsg = pIMsg;
  816. EventParams.pDSNProperties = pIDSNProperties;
  817. EventParams.pRecipIter = pIRecipIterator;
  818. hr = pIServerEvent->TriggerServerEvent(
  819. SMTP_GENERATE_DSN_EVENT,
  820. &EventParams);
  821. if(FAILED(hr))
  822. {
  823. ErrorTrace((LPARAM)this, "HrTriggerServerEvent failed hr %08lx", hr);
  824. goto CLEANUP;
  825. }
  826. CLEANUP:
  827. DebugTrace((LPARAM)this, "returning %08lx", hr);
  828. TraceFunctLeaveEx((LPARAM)this);
  829. return hr;
  830. } // CDSNGenerator::HrTriggerGenerateDSN
  831. //+------------------------------------------------------------
  832. //
  833. // Function: CDSNGenerator::HrTriggerPostGenerateDSN
  834. //
  835. // Synopsis: Triggers the server event
  836. //
  837. // Arguments: See ptntintf.idl
  838. //
  839. // Returns:
  840. // S_OK: Success
  841. //
  842. // History:
  843. // jstamerj 2000/12/11 17:18:17: Created.
  844. //
  845. //-------------------------------------------------------------
  846. HRESULT CDSNGenerator::HrTriggerPostGenerateDSN(
  847. IAQServerEvent *pIServerEvent,
  848. DWORD dwVSID,
  849. ISMTPServer *pISMTPServer,
  850. IMailMsgProperties *pIMsgOrig,
  851. DWORD dwDSNAction,
  852. DWORD cRecipsDSNd,
  853. IMailMsgProperties *pIMsgDSN,
  854. IMailMsgPropertyBag *pIDSNProperties)
  855. {
  856. HRESULT hr = S_OK;
  857. EVENTPARAMS_POST_GENERATE_DSN EventParams;
  858. TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrTriggerPostGenerateDSN");
  859. EventParams.dwVSID = dwVSID;
  860. EventParams.pISMTPServer = pISMTPServer;
  861. EventParams.pIMsgOrig = pIMsgOrig;
  862. EventParams.dwDSNAction = dwDSNAction;
  863. EventParams.cRecipsDSNd = cRecipsDSNd;
  864. EventParams.pIMsgDSN = pIMsgDSN;
  865. EventParams.pIDSNProperties = pIDSNProperties;
  866. hr = pIServerEvent->TriggerServerEvent(
  867. SMTP_POST_DSN_EVENT,
  868. &EventParams);
  869. if(FAILED(hr))
  870. {
  871. ErrorTrace((LPARAM)this, "HrTriggerServerEvent failed hr %08lx", hr);
  872. goto CLEANUP;
  873. }
  874. CLEANUP:
  875. DebugTrace((LPARAM)this, "returning %08lx", hr);
  876. TraceFunctLeaveEx((LPARAM)this);
  877. return hr;
  878. } // CDSNGenerator::HrTriggerPostGenerateDSN
  879. //---[ FileTimeToLocalRFC822Date ]---------------------------------------------
  880. //
  881. //
  882. // Description:
  883. // Converts filetime to RFC822 compliant date
  884. // Parameters:
  885. // ft Filetime to generate date for
  886. // achReturn Buffer for filetime
  887. // Returns:
  888. // BOOL - success or not
  889. // History:
  890. // 8/19/98 - MikeSwa Modified from various timeconv.cxx functions written
  891. // by Lindsay Harris - lindasyh
  892. // Carl Kadie [carlk]
  893. //
  894. //-----------------------------------------------------------------------------
  895. BOOL FileTimeToLocalRFC822Date(const FILETIME & ft, char achReturn[MAX_RFC822_DATE_SIZE])
  896. {
  897. TraceFunctEnterEx((LPARAM)0, "FileTimeToLocalRFC822Date");
  898. FILETIME ftLocal;
  899. SYSTEMTIME st;
  900. char chSign; // Sign to print.
  901. DWORD dwResult;
  902. int iBias; // Offset relative to GMT.
  903. TIME_ZONE_INFORMATION tzi; // Local time zone data.
  904. BOOL bReturn = FALSE;
  905. dwResult = GetTimeZoneInformation( &tzi );
  906. _ASSERT(achReturn); //real assert
  907. achReturn[0]='\0';
  908. if (!FileTimeToLocalFileTime(&ft, &ftLocal))
  909. {
  910. ErrorTrace((LPARAM)0, "FileTimeToLocalFileTime failed - %x", GetLastError());
  911. bReturn = FALSE;
  912. goto Exit;
  913. }
  914. if (!FileTimeToSystemTime(&ftLocal, &st))
  915. {
  916. ErrorTrace((LPARAM)0, "FileTimeToSystemTime failed - %x", GetLastError());
  917. bReturn = FALSE;
  918. goto Exit;
  919. }
  920. // Calculate the time zone offset.
  921. iBias = tzi.Bias;
  922. if( dwResult == TIME_ZONE_ID_DAYLIGHT )
  923. iBias += tzi.DaylightBias;
  924. /*
  925. * We always want to print the sign for the time zone offset, so
  926. * we decide what it is now and remember that when converting.
  927. * The convention is that west of the 0 degree meridian has a
  928. * negative offset - i.e. add the offset to GMT to get local time.
  929. */
  930. if( iBias > 0 )
  931. {
  932. chSign = '-'; // Yes, I do mean negative.
  933. }
  934. else
  935. {
  936. iBias = -iBias;
  937. chSign = '+';
  938. }
  939. /*
  940. * No major trickery here. We have all the data, so simply
  941. * format it according to the rules on how to do this.
  942. */
  943. wsprintf( achReturn, "%s, %d %s %04d %02d:%02d:%02d %c%02d%02d",
  944. s_rgszWeekDays[st.wDayOfWeek],
  945. st.wDay, s_rgszMonth[ st.wMonth - 1 ],
  946. st.wYear,
  947. st.wHour, st.wMinute, st.wSecond, chSign,
  948. (iBias / 60) % 100, iBias % 60 );
  949. _ASSERT(lstrlen(achReturn) < MAX_RFC822_DATE_SIZE);
  950. bReturn = TRUE;
  951. Exit:
  952. TraceFunctLeaveEx((LPARAM)0);
  953. return bReturn;
  954. }
  955. //+------------------------------------------------------------
  956. //
  957. // Function: CDefaultDSNRecipientIterator::Init
  958. //
  959. // Synopsis: Constructor. Initializes member variables.
  960. //
  961. // Arguments:
  962. // pIMsg: Mailmsg interface
  963. // dwStartDomain: First domain of recipient enumeration
  964. // dwDSNActions: The DSN actions to perform
  965. //
  966. // Returns:
  967. // S_OK: Success
  968. //
  969. // History:
  970. // jstamerj 2000/11/09 14:18:45: Created.
  971. //
  972. //-------------------------------------------------------------
  973. HRESULT CDefaultDSNRecipientIterator::HrInit(
  974. IN IMailMsgProperties *pIMsg,
  975. IN DWORD dwStartDomain,
  976. IN DWORD dwDSNActions)
  977. {
  978. HRESULT hr = S_OK;
  979. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::Init");
  980. TerminateFilter();
  981. if(m_pIRecips)
  982. {
  983. m_pIRecips->Release();
  984. m_pIRecips = NULL;
  985. }
  986. m_dwStartDomain = dwStartDomain;
  987. m_dwDSNActions = dwDSNActions;
  988. hr = pIMsg->QueryInterface(
  989. IID_IMailMsgRecipients,
  990. (LPVOID *) &m_pIRecips);
  991. _ASSERT(SUCCEEDED(hr));
  992. if(FAILED(hr))
  993. {
  994. ErrorTrace((LPARAM)this, "QI failed %08lx", hr);
  995. goto CLEANUP;
  996. }
  997. hr = HrReset();
  998. if(FAILED(hr))
  999. {
  1000. ErrorTrace((LPARAM)this, "HrReset failed %08lx", hr);
  1001. }
  1002. CLEANUP:
  1003. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1004. TraceFunctLeaveEx((LPARAM)this);
  1005. return hr;
  1006. } // CDefaultDSNRecipientIterator::HrInit
  1007. //+------------------------------------------------------------
  1008. //
  1009. // Function: CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator
  1010. //
  1011. // Synopsis: Destructor. Cleanup
  1012. //
  1013. // Arguments: NONE
  1014. //
  1015. // Returns: NOTHING
  1016. //
  1017. // History:
  1018. // jstamerj 2000/11/09 18:42:32: Created.
  1019. //
  1020. //-------------------------------------------------------------
  1021. CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator()
  1022. {
  1023. HRESULT hr = S_OK;
  1024. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator");
  1025. TerminateFilter();
  1026. if(m_pIRecips)
  1027. m_pIRecips->Release();
  1028. _ASSERT(m_dwSig == RECIPITER_SIG);
  1029. m_dwSig = RECIPITER_SIG_INVALID;
  1030. TraceFunctLeaveEx((LPARAM)this);
  1031. } // CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator
  1032. //+------------------------------------------------------------
  1033. //
  1034. // Function: CDefaultDSNRecipientIterator::QueryInterface
  1035. //
  1036. // Synopsis: Return requested interface
  1037. //
  1038. // Arguments:
  1039. // riid: Interface ID
  1040. // ppvObj: Out paramter for itnerface
  1041. //
  1042. // Returns:
  1043. // S_OK: Success
  1044. //
  1045. // History:
  1046. // jstamerj 2000/11/09 14:25:39: Created.
  1047. //
  1048. //-------------------------------------------------------------
  1049. HRESULT CDefaultDSNRecipientIterator::QueryInterface(
  1050. IN REFIID riid,
  1051. OUT LPVOID *ppvObj)
  1052. {
  1053. HRESULT hr = S_OK;
  1054. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::QueryInterface");
  1055. _ASSERT(ppvObj);
  1056. *ppvObj = NULL;
  1057. if(riid == IID_IUnknown)
  1058. {
  1059. *ppvObj = (IUnknown *)this;
  1060. }
  1061. else if(riid == IID_IDSNRecipientIterator)
  1062. {
  1063. *ppvObj = (IDSNRecipientIterator *)this;
  1064. }
  1065. else
  1066. {
  1067. hr = E_NOINTERFACE;
  1068. }
  1069. if(SUCCEEDED(hr))
  1070. AddRef();
  1071. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1072. TraceFunctLeaveEx((LPARAM)this);
  1073. return hr;
  1074. } // CDefaultDSNRecipientIterator::QueryInterface
  1075. //+------------------------------------------------------------
  1076. //
  1077. // Function: CDefaultDSNRecipientIterator::HrReset
  1078. //
  1079. // Synopsis: Reset recipent iteration
  1080. //
  1081. // Arguments: None
  1082. //
  1083. // Returns:
  1084. // S_OK: Success
  1085. //
  1086. // History:
  1087. // jstamerj 2000/11/09 14:32:04: Created.
  1088. //
  1089. //-------------------------------------------------------------
  1090. HRESULT CDefaultDSNRecipientIterator::HrReset()
  1091. {
  1092. HRESULT hr = S_OK;
  1093. DWORD dwRecipFilterMask = 0;
  1094. DWORD dwRecipFilterFlags = 0;
  1095. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::HrReset");
  1096. GetFilterMaskAndFlags(
  1097. m_dwDSNActions,
  1098. &dwRecipFilterMask,
  1099. &dwRecipFilterFlags);
  1100. TerminateFilter();
  1101. hr = m_pIRecips->InitializeRecipientFilterContext(
  1102. &m_rpfctxt,
  1103. m_dwStartDomain,
  1104. dwRecipFilterFlags,
  1105. dwRecipFilterMask);
  1106. if (FAILED(hr))
  1107. goto CLEANUP;
  1108. m_fFilterInit = TRUE;
  1109. CLEANUP:
  1110. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1111. TraceFunctLeaveEx((LPARAM)this);
  1112. return hr;
  1113. } // CDefaultDSNRecipientIterator::HrReset
  1114. //---[ CDefaultDSNRecipientIterator::GetFilterMaskAndFlags ]-------------------
  1115. //
  1116. //
  1117. // Description:
  1118. // Determines what the appropriate mask and flags for a recip serch filter
  1119. // are based on the given actions.
  1120. //
  1121. // It may not be possible to constuct a perfectly optimal search (ie Failed
  1122. // and delivered).... this function will attempt to find the "most optimal"
  1123. // search possible.
  1124. // Parameters:
  1125. // dwDSNActions Requested DSN generation operations
  1126. // pdwRecipMask Mask to pass to InitializeRecipientFilterContext
  1127. // pdwRecipFlags Flags to pass to InitializeRecipientFilterContext
  1128. // Returns: Nothing
  1129. // History:
  1130. // 7/1/98 - MikeSwa Created
  1131. //
  1132. //-----------------------------------------------------------------------------
  1133. VOID CDefaultDSNRecipientIterator::GetFilterMaskAndFlags(
  1134. IN DWORD dwDSNActions,
  1135. OUT DWORD *pdwRecipMask,
  1136. OUT DWORD *pdwRecipFlags)
  1137. {
  1138. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNRecipientIterator::HrGetFilterMaskAndFlags");
  1139. _ASSERT(pdwRecipMask);
  1140. _ASSERT(pdwRecipFlags);
  1141. //in general we are only interested in un-DSN'd recipients
  1142. *pdwRecipFlags = 0x00000000;
  1143. *pdwRecipMask = RP_DSN_HANDLED | RP_DSN_NOTIFY_NEVER;
  1144. //Note these searches are just optimizations... so we don't look at
  1145. //recipients we don't need to. However, it may not be possible to
  1146. //limit the search precisely
  1147. if (DSN_ACTION_FAILURE == dwDSNActions)
  1148. {
  1149. //We are interested in hard failures
  1150. *pdwRecipMask |= RP_GENERAL_FAILURE;
  1151. *pdwRecipFlags |= RP_GENERAL_FAILURE;
  1152. }
  1153. if (!((DSN_ACTION_DELIVERED | DSN_ACTION_RELAYED) & dwDSNActions))
  1154. {
  1155. //are not interested in delivered
  1156. if ((DSN_ACTION_FAILURE_ALL | DSN_ACTION_DELAYED) & dwDSNActions)
  1157. {
  1158. //it is safe to check only undelivered
  1159. *pdwRecipMask |= (RP_DELIVERED ^ RP_HANDLED); //must be un-set
  1160. _ASSERT(!(*pdwRecipFlags & (RP_DELIVERED ^ RP_HANDLED)));
  1161. }
  1162. }
  1163. else
  1164. {
  1165. //$$TODO - can narrow this search more
  1166. //we are interested in delivered
  1167. if (!((DSN_ACTION_FAILURE_ALL | DSN_ACTION_FAILURE| DSN_ACTION_DELAYED)
  1168. & dwDSNActions))
  1169. {
  1170. //it is safe to check only delivered
  1171. *pdwRecipMask |= RP_DELIVERED;
  1172. *pdwRecipFlags |= RP_DELIVERED;
  1173. }
  1174. }
  1175. DebugTrace((LPARAM) this,
  1176. "DSN Action 0x%08X, Recip mask 0x%08X, Recip flags 0x%08X",
  1177. dwDSNActions, *pdwRecipMask, *pdwRecipFlags);
  1178. TraceFunctLeave();
  1179. }
  1180. //+------------------------------------------------------------
  1181. //
  1182. // Function: CDefaultDSNRecipientIterator::HrGetNextRecipient
  1183. //
  1184. // Synopsis: Returns the next recipient for wich a DSN action should
  1185. // be taken.
  1186. //
  1187. // Arguments:
  1188. // piRecipient: Receives next recipient index
  1189. // pdwDSNAction: Receives DSN Action(s) that should be taken
  1190. //
  1191. // Returns:
  1192. // S_OK: Success
  1193. // HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)
  1194. // E_UNEXPECTED: Need to call HrReset first.
  1195. //
  1196. // History:
  1197. // jstamerj 2000/11/09 15:30:17: Created.
  1198. //
  1199. //-------------------------------------------------------------
  1200. HRESULT CDefaultDSNRecipientIterator::HrGetNextRecipient(
  1201. OUT DWORD *piRecipient,
  1202. OUT DWORD *pdwDSNAction)
  1203. {
  1204. HRESULT hr = S_OK;
  1205. DWORD iCurrentRecip = 0;
  1206. DWORD dwCurrentRecipFlags = 0;
  1207. DWORD dwCurrentDSNAction = 0;
  1208. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::HrGetNextRecipient");
  1209. if((piRecipient == NULL) ||
  1210. (pdwDSNAction == NULL))
  1211. {
  1212. hr = E_INVALIDARG;
  1213. goto CLEANUP;
  1214. }
  1215. if(! m_fFilterInit)
  1216. {
  1217. hr = E_UNEXPECTED;
  1218. goto CLEANUP;
  1219. }
  1220. while(SUCCEEDED(hr) && (dwCurrentDSNAction == 0))
  1221. {
  1222. hr = m_pIRecips->GetNextRecipient(&m_rpfctxt, &iCurrentRecip);
  1223. if(FAILED(hr))
  1224. goto CLEANUP;
  1225. hr = m_pIRecips->GetDWORD(
  1226. iCurrentRecip,
  1227. IMMPID_RP_RECIPIENT_FLAGS,
  1228. &dwCurrentRecipFlags);
  1229. if(hr == MAILMSG_E_PROPNOTFOUND)
  1230. {
  1231. dwCurrentRecipFlags = 0;
  1232. }
  1233. else if (FAILED(hr))
  1234. {
  1235. ErrorTrace((LPARAM) this,
  1236. "Failure 0x%08X to get flags for recip %d",
  1237. hr, iCurrentRecip);
  1238. goto CLEANUP;
  1239. }
  1240. DebugTrace((LPARAM) this,
  1241. "Recipient %d with flags 0x%08X found",
  1242. iCurrentRecip, dwCurrentRecipFlags);
  1243. GetDSNAction(
  1244. m_dwDSNActions,
  1245. dwCurrentRecipFlags,
  1246. &dwCurrentDSNAction);
  1247. }
  1248. if(SUCCEEDED(hr))
  1249. {
  1250. *pdwDSNAction = dwCurrentDSNAction;
  1251. *piRecipient = iCurrentRecip;
  1252. }
  1253. CLEANUP:
  1254. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1255. TraceFunctLeaveEx((LPARAM)this);
  1256. return hr;
  1257. } // CDefaultDSNRecipientIterator::HrGetNextRecipient
  1258. //---[ CDefaultDSNRecipientIterator::GetDSNAction ]-------------------------
  1259. //
  1260. //
  1261. // Description:
  1262. // Determines what DSN action needs to happen on a recipient based on
  1263. // the requested DSN actions and the recipient flags
  1264. // Parameters:
  1265. // IN dwDSNAction The requested DSN actions
  1266. // IN dwCurrentRecipFlags The flags for current recipient...
  1267. // OUT pdwCurrentDSNAction The DSN action that needs to be performed
  1268. // On this recipient (DSN_ACTION_FAILURE is
  1269. // used to denote sending a NDR)
  1270. // Returns: Nothing
  1271. // History:
  1272. // 7/2/98 - MikeSwa Created
  1273. //
  1274. //-----------------------------------------------------------------------------
  1275. VOID CDefaultDSNRecipientIterator::GetDSNAction(
  1276. IN DWORD dwDSNAction,
  1277. IN DWORD dwCurrentRecipFlags,
  1278. OUT DWORD *pdwCurrentDSNAction)
  1279. {
  1280. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNRecipientIterator::fdwGetDSNAction");
  1281. _ASSERT(pdwCurrentDSNAction);
  1282. DWORD dwOriginalRecipFlags = dwCurrentRecipFlags;
  1283. DWORD dwRecipFlagsForAction = 0;
  1284. DWORD dwFlags = 0;
  1285. //This should never be hit because of the filter
  1286. _ASSERT(!(dwCurrentRecipFlags & (RP_DSN_HANDLED | RP_DSN_NOTIFY_NEVER)));
  1287. *pdwCurrentDSNAction = 0;
  1288. if (DSN_ACTION_FAILURE & dwDSNAction)
  1289. {
  1290. if ((RP_GENERAL_FAILURE & dwCurrentRecipFlags) &&
  1291. ((RP_DSN_NOTIFY_FAILURE & dwCurrentRecipFlags) ||
  1292. (!(RP_DSN_NOTIFY_MASK & dwCurrentRecipFlags))))
  1293. {
  1294. DebugTrace((LPARAM) this, "Recipient matched for FAILURE DSN");
  1295. // Recip flags will be updated in HrNotifyActionHandled
  1296. // dwCurrentRecipFlags |= RP_DSN_SENT_NDR;
  1297. *pdwCurrentDSNAction = DSN_ACTION_FAILURE;
  1298. goto Exit;
  1299. }
  1300. }
  1301. if (DSN_ACTION_FAILURE_ALL & dwDSNAction)
  1302. {
  1303. //Fail all non-delivered that we haven't sent notifications for
  1304. if (((!((RP_DSN_HANDLED | (RP_DELIVERED ^ RP_HANDLED)) & dwCurrentRecipFlags))) &&
  1305. ((RP_DSN_NOTIFY_FAILURE & dwCurrentRecipFlags) ||
  1306. (!(RP_DSN_NOTIFY_MASK & dwCurrentRecipFlags))))
  1307. {
  1308. //Don't send failures for expanded DL;s
  1309. if (RP_EXPANDED != (dwCurrentRecipFlags & RP_EXPANDED))
  1310. {
  1311. DebugTrace((LPARAM) this, "Recipient matched for FAILURE (all) DSN");
  1312. // dwCurrentRecipFlags |= RP_DSN_SENT_NDR;
  1313. *pdwCurrentDSNAction = DSN_ACTION_FAILURE_ALL;
  1314. goto Exit;
  1315. }
  1316. }
  1317. }
  1318. if (DSN_ACTION_DELAYED & dwDSNAction)
  1319. {
  1320. //send at most 1 delay DSN
  1321. //Also send only if DELAY was requested or no specific instructions were
  1322. //specified
  1323. if ((!((RP_DSN_SENT_DELAYED | RP_HANDLED) & dwCurrentRecipFlags)) &&
  1324. ((RP_DSN_NOTIFY_DELAY & dwCurrentRecipFlags) ||
  1325. (!(RP_DSN_NOTIFY_MASK & dwCurrentRecipFlags))))
  1326. {
  1327. DebugTrace((LPARAM) this, "Recipient matched for DELAYED DSN");
  1328. // dwCurrentRecipFlags |= RP_DSN_SENT_DELAYED;
  1329. *pdwCurrentDSNAction = DSN_ACTION_DELAYED;
  1330. goto Exit;
  1331. }
  1332. }
  1333. if (DSN_ACTION_RELAYED & dwDSNAction)
  1334. {
  1335. //send relay if it was delivered *and* DSN not supported by remote MTA
  1336. //*and* notification of success was explicitly requested
  1337. dwFlags = (RP_DELIVERED ^ RP_HANDLED) |
  1338. RP_REMOTE_MTA_NO_DSN |
  1339. RP_DSN_NOTIFY_SUCCESS;
  1340. if ((dwFlags & dwCurrentRecipFlags) == dwFlags)
  1341. {
  1342. DebugTrace((LPARAM) this, "Recipient matched for RELAYED DSN");
  1343. // dwCurrentRecipFlags |= RP_DSN_SENT_RELAYED;
  1344. *pdwCurrentDSNAction = DSN_ACTION_RELAYED;
  1345. goto Exit;
  1346. }
  1347. }
  1348. if (DSN_ACTION_DELIVERED & dwDSNAction)
  1349. {
  1350. //send delivered if it was delivered *and* no DSN sent yet
  1351. dwFlags = (RP_DELIVERED ^ RP_HANDLED) | RP_DSN_NOTIFY_SUCCESS;
  1352. _ASSERT(!(dwCurrentRecipFlags & RP_DSN_HANDLED)); //should be filtered out
  1353. if ((dwFlags & dwCurrentRecipFlags) == dwFlags)
  1354. {
  1355. DebugTrace((LPARAM) this, "Recipient matched for SUCCESS DSN");
  1356. // dwCurrentRecipFlags |= RP_DSN_SENT_DELIVERED;
  1357. *pdwCurrentDSNAction = DSN_ACTION_DELIVERED;
  1358. goto Exit;
  1359. }
  1360. }
  1361. if (DSN_ACTION_EXPANDED & dwDSNAction)
  1362. {
  1363. //Send expanded if the recipient is marked as expanded and
  1364. //NOTIFY=SUCCESS was requested
  1365. if ((RP_EXPANDED == (dwCurrentRecipFlags & RP_EXPANDED)) &&
  1366. (dwCurrentRecipFlags & RP_DSN_NOTIFY_SUCCESS) &&
  1367. !(dwCurrentRecipFlags & RP_DSN_SENT_EXPANDED))
  1368. {
  1369. DebugTrace((LPARAM) this, "Recipient matched for EXPANDED DSN");
  1370. // dwCurrentRecipFlags |= RP_DSN_SENT_EXPANDED;
  1371. *pdwCurrentDSNAction = DSN_ACTION_EXPANDED;
  1372. goto Exit;
  1373. }
  1374. }
  1375. Exit:
  1376. GetRecipientFlagsForActions(
  1377. *pdwCurrentDSNAction,
  1378. &dwRecipFlagsForAction);
  1379. TraceFunctLeave();
  1380. }
  1381. //+------------------------------------------------------------
  1382. //
  1383. // Function: CDefaultDSNRecipientIterator::HrNotifyActionHandled
  1384. //
  1385. // Synopsis: Notifies that particular DSN(s) have been generated.
  1386. // Sets recipient flags so that recipient will not be enumerated again.
  1387. //
  1388. // Arguments:
  1389. // iRecipient: Recip index
  1390. // dwDSNAction: The action(s) performed
  1391. //
  1392. // Returns:
  1393. // S_OK: Success
  1394. // error from mailmsg
  1395. //
  1396. // History:
  1397. // jstamerj 2000/11/09 18:26:50: Created.
  1398. //
  1399. //-------------------------------------------------------------
  1400. HRESULT CDefaultDSNRecipientIterator::HrNotifyActionHandled(
  1401. IN DWORD iRecipient,
  1402. IN DWORD dwDSNAction)
  1403. {
  1404. HRESULT hr = S_OK;
  1405. DWORD dwRecipFlags = 0;
  1406. DWORD dwNewRecipFlags = 0;
  1407. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::HrNotifyActionHandled");
  1408. hr = m_pIRecips->GetDWORD(
  1409. iRecipient,
  1410. IMMPID_RP_RECIPIENT_FLAGS,
  1411. &dwRecipFlags);
  1412. if(hr == MAILMSG_E_PROPNOTFOUND)
  1413. {
  1414. dwRecipFlags = 0;
  1415. }
  1416. else if(FAILED(hr))
  1417. {
  1418. ErrorTrace((LPARAM)this, "m_pIRecips->GetDWORD failed");
  1419. goto CLEANUP;
  1420. }
  1421. GetRecipientFlagsForActions(
  1422. dwDSNAction,
  1423. &dwNewRecipFlags);
  1424. DebugTrace((LPARAM)this, "Orig recip flags: %08lx", dwRecipFlags);
  1425. dwRecipFlags |= dwNewRecipFlags;
  1426. hr = m_pIRecips->PutDWORD(
  1427. iRecipient,
  1428. IMMPID_RP_RECIPIENT_FLAGS,
  1429. dwRecipFlags);
  1430. if(FAILED(hr))
  1431. {
  1432. ErrorTrace((LPARAM)this, "m_pIRecips->PutDWORD failed hr %08lx", hr);
  1433. goto CLEANUP;
  1434. }
  1435. DebugTrace((LPARAM)this, "New recip flags: %08lx", dwRecipFlags);
  1436. CLEANUP:
  1437. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1438. TraceFunctLeaveEx((LPARAM)this);
  1439. return hr;
  1440. } // CDefaultDSNRecipientIterator::HrNotifyActionHandled
  1441. //+------------------------------------------------------------
  1442. //
  1443. // Function: CDefaultDSNRecipientIterator::GetRecipientFlagsForActions
  1444. //
  1445. // Synopsis: Get mailmsg recipient flags corresponding to an action
  1446. //
  1447. // Arguments:
  1448. // dwDSNAction: Action in question
  1449. // pdwRecipientFlags: Recip flags
  1450. //
  1451. // Returns: Nothing
  1452. //
  1453. // History:
  1454. // jstamerj 2000/11/09 16:18:34: Created.
  1455. //
  1456. //-------------------------------------------------------------
  1457. VOID CDefaultDSNRecipientIterator::GetRecipientFlagsForActions(
  1458. IN DWORD dwDSNAction,
  1459. OUT DWORD *pdwRecipientFlags)
  1460. {
  1461. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::GetRecipientFlagsForActions");
  1462. *pdwRecipientFlags = 0;
  1463. if(dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
  1464. {
  1465. *pdwRecipientFlags |= RP_DSN_SENT_NDR;
  1466. }
  1467. if(dwDSNAction & DSN_ACTION_DELAYED)
  1468. {
  1469. *pdwRecipientFlags |= RP_DSN_SENT_DELAYED;
  1470. }
  1471. if(dwDSNAction & DSN_ACTION_RELAYED)
  1472. {
  1473. *pdwRecipientFlags |= RP_DSN_SENT_RELAYED;
  1474. }
  1475. if(dwDSNAction & DSN_ACTION_DELIVERED)
  1476. {
  1477. *pdwRecipientFlags |= RP_DSN_SENT_DELIVERED;
  1478. }
  1479. if(dwDSNAction & DSN_ACTION_EXPANDED)
  1480. {
  1481. *pdwRecipientFlags |= RP_DSN_SENT_EXPANDED;
  1482. }
  1483. DebugTrace((LPARAM)this, "dwDSNAction: %08lx", dwDSNAction);
  1484. DebugTrace((LPARAM)this, "*pdwRecipientFlags: %08lx", *pdwRecipientFlags);
  1485. TraceFunctLeaveEx((LPARAM)this);
  1486. } // CDefaultDSNRecipientIterator::GetRecipientFlagsForActions
  1487. //+------------------------------------------------------------
  1488. //
  1489. // Function: CDefaultDSNRecipientIterator::TermianteFilter
  1490. //
  1491. // Synopsis: Terminate the mailmsg filter
  1492. //
  1493. // Arguments: None
  1494. //
  1495. // Returns: Nothing
  1496. //
  1497. // History:
  1498. // jstamerj 2000/11/14 14:14:59: Created.
  1499. //
  1500. //-------------------------------------------------------------
  1501. VOID CDefaultDSNRecipientIterator::TerminateFilter()
  1502. {
  1503. HRESULT hr = S_OK;
  1504. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::TermianteFilter");
  1505. if(m_fFilterInit)
  1506. {
  1507. //recycle context
  1508. m_fFilterInit = FALSE;
  1509. hr = m_pIRecips->TerminateRecipientFilterContext(&m_rpfctxt);
  1510. _ASSERT(SUCCEEDED(hr) && "TerminateRecipientFilterContext FAILED!!!!");
  1511. }
  1512. TraceFunctLeaveEx((LPARAM)this);
  1513. } // CDefaultDSNRecipientIterator::TermianteFilter
  1514. //+------------------------------------------------------------
  1515. //
  1516. // Function: CDefaultDSNSink::CDefaultDSNSink
  1517. //
  1518. // Synopsis: Constructor; initialize member data
  1519. //
  1520. // Arguments: None
  1521. //
  1522. // Returns: Nothing
  1523. //
  1524. // History:
  1525. // jstamerj 2000/12/04 17:41:35: Created.
  1526. //
  1527. //-------------------------------------------------------------
  1528. CDefaultDSNSink::CDefaultDSNSink(
  1529. IUnknown *pUnk)
  1530. {
  1531. FILETIME ftStartTime;
  1532. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::CDefaultDSNSink");
  1533. m_dwSig = SIGNATURE_CDEFAULTDSNSINK;
  1534. m_pUnk = pUnk;
  1535. _ASSERT_RECIP_FLAGS();
  1536. m_fInit = FALSE;
  1537. m_cDSNsRequested = 0;
  1538. //Init string for MIME headers
  1539. GetSystemTimeAsFileTime(&ftStartTime);
  1540. wsprintf(m_szPerInstanceMimeBoundary, "%08X%08X",
  1541. ftStartTime.dwHighDateTime, ftStartTime.dwLowDateTime);
  1542. TraceFunctLeaveEx((LPARAM)this);
  1543. } // CDefaultDSNSink::CDefaultDSNSink
  1544. //+------------------------------------------------------------
  1545. //
  1546. // Function: CDefaultDSNSink::QueryInterface
  1547. //
  1548. // Synopsis: Return a requested interface
  1549. //
  1550. // Arguments:
  1551. // riid: Interface ID
  1552. // ppvObj: Return place for interface
  1553. //
  1554. // Returns:
  1555. // S_OK: Success
  1556. // E_NOINTERFACE: Not a supported interface
  1557. //
  1558. // History:
  1559. // jstamerj 2000/12/08 20:05:46: Created.
  1560. //
  1561. //-------------------------------------------------------------
  1562. HRESULT CDefaultDSNSink::QueryInterface(
  1563. REFIID riid,
  1564. LPVOID *ppvObj)
  1565. {
  1566. HRESULT hr = S_OK;
  1567. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::QueryInterface");
  1568. *ppvObj = NULL;
  1569. if(riid == IID_IUnknown)
  1570. {
  1571. *ppvObj = (IUnknown *)this;
  1572. }
  1573. else if(riid == IID_IDSNGenerationSink)
  1574. {
  1575. *ppvObj = (IDSNGenerationSink *)this;
  1576. }
  1577. else
  1578. {
  1579. hr = E_NOINTERFACE;
  1580. }
  1581. if(SUCCEEDED(hr))
  1582. AddRef();
  1583. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1584. TraceFunctLeaveEx((LPARAM)this);
  1585. return hr;
  1586. } // CDefaultDSNSink::QueryInterface
  1587. //+------------------------------------------------------------
  1588. //
  1589. // Function: CDefaultDSNSink::OnSyncSinkInit
  1590. //
  1591. // Synopsis: Initialize the sink.
  1592. //
  1593. // Arguments:
  1594. // dwVSID: Virtual server ID
  1595. //
  1596. // Returns:
  1597. // S_OK: Success
  1598. //
  1599. // History:
  1600. // jstamerj 2000/11/14 13:58:32: Created.
  1601. //
  1602. //-------------------------------------------------------------
  1603. HRESULT CDefaultDSNSink::OnSyncSinkInit(
  1604. IN DWORD dwVSID)
  1605. {
  1606. HRESULT hr = S_OK;
  1607. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::OnSyncSinkInit");
  1608. DebugTrace((LPARAM)this, "VSID: %d", dwVSID);
  1609. m_dwVSID = dwVSID;
  1610. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1611. TraceFunctLeaveEx((LPARAM)this);
  1612. return hr;
  1613. } // CDefaultDSNSink::OnSyncSinkInit
  1614. //+------------------------------------------------------------
  1615. //
  1616. // Function: CDefaultDSNSink::OnSyncGetDSNRecipientIterator
  1617. //
  1618. // Synopsis: Not implemented
  1619. //
  1620. // Arguments: see smtpevent.idl
  1621. //
  1622. // Returns:
  1623. // S_OK: Success
  1624. // E_OUTOFMEMORY
  1625. //
  1626. // History:
  1627. // jstamerj 2000/11/14 14:00:00: Created.
  1628. //
  1629. //-------------------------------------------------------------
  1630. HRESULT CDefaultDSNSink::OnSyncGetDSNRecipientIterator(
  1631. IN ISMTPServer *pISMTPServer,
  1632. IN IMailMsgProperties *pIMsg,
  1633. IN IMailMsgPropertyBag *pDSNProperties,
  1634. IN DWORD dwStartDomain,
  1635. IN DWORD dwDSNActions,
  1636. IN IDSNRecipientIterator *pRecipIterIN,
  1637. OUT IDSNRecipientIterator **ppRecipIterOUT)
  1638. {
  1639. return E_NOTIMPL;
  1640. } // CDefaultDSNSink::OnSyncGetDSNRecipientIterator
  1641. //+------------------------------------------------------------
  1642. //
  1643. // Function: CDefaultDSNSink::OnSyncGenerateDSN
  1644. //
  1645. // Synopsis: Implements the default DSN generation sink
  1646. //
  1647. // Arguments: see smtpevent.idl
  1648. //
  1649. // Returns:
  1650. // S_OK: Success
  1651. //
  1652. // History:
  1653. // jstamerj 2000/11/14 14:30:45: Created.
  1654. //
  1655. //-------------------------------------------------------------
  1656. HRESULT CDefaultDSNSink::OnSyncGenerateDSN(
  1657. IN ISMTPServer *pISMTPServer,
  1658. IN IDSNSubmission *pIDSNSubmission,
  1659. IN IMailMsgProperties *pIMsg,
  1660. IN IMailMsgPropertyBag *pDSNProperties,
  1661. IN IDSNRecipientIterator *pRecipIter)
  1662. {
  1663. HRESULT hr = S_OK;
  1664. DWORD dwCount = 0;
  1665. //
  1666. // Initialize parameters to default values
  1667. //
  1668. DWORD dwDSNActions = 0;
  1669. DWORD dwDSNOptions = 0;
  1670. DWORD dwRFC821Status = 0;
  1671. HRESULT hrStatus = S_OK;
  1672. DWORD dwPreferredLangId = 0;
  1673. LPSTR szDefaultDomain = NULL;
  1674. LPSTR szReportingMTA = NULL;
  1675. LPSTR szReportingMTAType = NULL;
  1676. LPSTR szDSNContext = NULL;
  1677. LPSTR szCopyNDRTo = NULL;
  1678. LPSTR szTopCustomText = NULL;
  1679. LPSTR szBottomCustomText = NULL;
  1680. LPWSTR wszTopCustomText = NULL;
  1681. LPWSTR wszBottomCustomText = NULL;
  1682. DWORD cIterationsLeft = 0;
  1683. IMailMsgProperties *pDSNMsg = NULL;
  1684. DWORD cbCurrentSize = 0; //used to get size of returned property
  1685. FILETIME ftExpireTime;
  1686. FILETIME *pftExpireTime = NULL;
  1687. DWORD dwDSNRetType = 0;
  1688. HRESULT hrContentFailure = S_OK;
  1689. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::OnSyncGenerateDSN");
  1690. struct _tagDWORDProps
  1691. {
  1692. DWORD dwPropId;
  1693. DWORD *pdwValue;
  1694. } DsnDwordProps[] =
  1695. {
  1696. { DSNPROP_DW_DSNACTIONS, & dwDSNActions },
  1697. { DSNPROP_DW_DSNOPTIONS, & dwDSNOptions },
  1698. { DSNPROP_DW_RFC821STATUS, & dwRFC821Status },
  1699. { DSNPROP_DW_HRSTATUS, (DWORD *) & hrStatus },
  1700. { DSNPROP_DW_LANGID, & dwPreferredLangId },
  1701. { DSNPROP_DW_RET_TYPE, & dwDSNRetType },
  1702. { DSNPROP_DW_CONTENT_FAILURE, (DWORD *) & hrContentFailure },
  1703. };
  1704. struct _tagStringProps
  1705. {
  1706. DWORD dwPropId;
  1707. LPSTR *ppsz;
  1708. } DsnStringProps[] =
  1709. {
  1710. { DSNPROP_SZ_DEFAULTDOMAIN, & szDefaultDomain },
  1711. { DSNPROP_SZ_REPORTINGMTA, & szReportingMTA },
  1712. { DSNPROP_SZ_REPORTINGMTATYPE, & szReportingMTAType },
  1713. { DSNPROP_SZ_DSNCONTEXT, & szDSNContext },
  1714. { DSNPROP_SZ_COPYNDRTO, & szCopyNDRTo },
  1715. { DSNPROP_SZ_HR_TOP_CUSTOM_TEXT_A, & szTopCustomText },
  1716. { DSNPROP_SZ_HR_BOTTOM_CUSTOM_TEXT_A, & szBottomCustomText },
  1717. };
  1718. struct _tagWideStringProps
  1719. {
  1720. DWORD dwPropId;
  1721. LPWSTR *ppwsz;
  1722. } DsnWideStringProps[] =
  1723. {
  1724. { DSNPROP_SZ_HR_TOP_CUSTOM_TEXT_W, & wszTopCustomText },
  1725. { DSNPROP_SZ_HR_BOTTOM_CUSTOM_TEXT_W, & wszBottomCustomText },
  1726. };
  1727. //
  1728. // Get DWORDs
  1729. //
  1730. for(dwCount = 0;
  1731. dwCount < ( sizeof(DsnDwordProps) / sizeof(DsnDwordProps[0]));
  1732. dwCount++)
  1733. {
  1734. hr = pDSNProperties->GetDWORD(
  1735. DsnDwordProps[dwCount].dwPropId,
  1736. DsnDwordProps[dwCount].pdwValue);
  1737. if(FAILED(hr) && (hr != MAILMSG_E_PROPNOTFOUND))
  1738. goto CLEANUP;
  1739. }
  1740. //
  1741. // Get Strings
  1742. //
  1743. for(dwCount = 0;
  1744. dwCount < ( sizeof(DsnStringProps) / sizeof(DsnStringProps[0]));
  1745. dwCount++)
  1746. {
  1747. BYTE bStupid = 0;
  1748. DWORD dwcb = 0;
  1749. hr = pDSNProperties->GetProperty(
  1750. DsnStringProps[dwCount].dwPropId,
  1751. 0, // Length
  1752. &dwcb, // pcbLength
  1753. &bStupid);
  1754. if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
  1755. {
  1756. *(DsnStringProps[dwCount].ppsz) = new CHAR[dwcb+1];
  1757. if( (*(DsnStringProps[dwCount].ppsz)) == NULL)
  1758. {
  1759. hr = E_OUTOFMEMORY;
  1760. goto CLEANUP;
  1761. }
  1762. hr = pDSNProperties->GetStringA(
  1763. DsnStringProps[dwCount].dwPropId,
  1764. dwcb+1,
  1765. *(DsnStringProps[dwCount].ppsz));
  1766. if(FAILED(hr))
  1767. {
  1768. ErrorTrace((LPARAM)this, "GetStringA failed hr %08lx", hr);
  1769. goto CLEANUP;
  1770. }
  1771. }
  1772. else if(FAILED(hr) && (hr != MAILMSG_E_PROPNOTFOUND))
  1773. {
  1774. ErrorTrace((LPARAM)this, "GetProperty failed hr %08lx", hr);
  1775. goto CLEANUP;
  1776. }
  1777. }
  1778. //
  1779. // Get Wide Strings
  1780. //
  1781. for(dwCount = 0;
  1782. dwCount < ( sizeof(DsnWideStringProps) / sizeof(DsnWideStringProps[0]));
  1783. dwCount++)
  1784. {
  1785. BYTE bStupid = 0;
  1786. DWORD dwcb = 0;
  1787. hr = pDSNProperties->GetProperty(
  1788. DsnWideStringProps[dwCount].dwPropId,
  1789. 0, // Length
  1790. &dwcb, // pcbLength
  1791. &bStupid);
  1792. if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
  1793. {
  1794. *(DsnWideStringProps[dwCount].ppwsz) = new WCHAR[(dwcb / sizeof(WCHAR))+1];
  1795. if( (*(DsnWideStringProps[dwCount].ppwsz)) == NULL)
  1796. {
  1797. hr = E_OUTOFMEMORY;
  1798. goto CLEANUP;
  1799. }
  1800. hr = pDSNProperties->GetStringW(
  1801. DsnWideStringProps[dwCount].dwPropId,
  1802. (dwcb / sizeof(WCHAR))+1,
  1803. *(DsnWideStringProps[dwCount].ppwsz));
  1804. if(FAILED(hr))
  1805. {
  1806. ErrorTrace((LPARAM)this, "GetStringA failed hr %08lx", hr);
  1807. goto CLEANUP;
  1808. }
  1809. }
  1810. else if(FAILED(hr) && (hr != MAILMSG_E_PROPNOTFOUND))
  1811. {
  1812. ErrorTrace((LPARAM)this, "GetProperty failed hr %08lx", hr);
  1813. goto CLEANUP;
  1814. }
  1815. }
  1816. //Get MsgExpire Time
  1817. //Write DSN_RP_HEADER_RETRY_UNTIL using expire FILETIME
  1818. hr = pDSNProperties->GetProperty(
  1819. DSNPROP_FT_EXPIRETIME,
  1820. sizeof(FILETIME),
  1821. &cbCurrentSize,
  1822. (BYTE *) &ftExpireTime);
  1823. if (SUCCEEDED(hr))
  1824. {
  1825. _ASSERT(sizeof(FILETIME) == cbCurrentSize);
  1826. if (sizeof(FILETIME) == cbCurrentSize)
  1827. pftExpireTime = &ftExpireTime;
  1828. }
  1829. else if (MAILMSG_E_PROPNOTFOUND == hr)
  1830. hr = S_OK;
  1831. else
  1832. goto CLEANUP;
  1833. do
  1834. {
  1835. DWORD dwDSNActionsGenerated = 0;
  1836. DWORD cRecipsDSNd = 0;
  1837. hr = HrGenerateDSNInternal(
  1838. pISMTPServer,
  1839. pIMsg,
  1840. pRecipIter,
  1841. pIDSNSubmission,
  1842. dwDSNActions,
  1843. dwRFC821Status,
  1844. hrStatus,
  1845. szDefaultDomain,
  1846. szDefaultDomain ? lstrlen(szDefaultDomain) : 0,
  1847. szReportingMTA,
  1848. szReportingMTA ? lstrlen(szReportingMTA) : 0,
  1849. szReportingMTAType,
  1850. szReportingMTAType ? lstrlen(szReportingMTAType) : 0,
  1851. szDSNContext,
  1852. szDSNContext ? lstrlen(szDSNContext) : 0,
  1853. dwPreferredLangId,
  1854. dwDSNOptions,
  1855. szCopyNDRTo,
  1856. szCopyNDRTo ? lstrlen(szCopyNDRTo) : 0,
  1857. pftExpireTime,
  1858. szTopCustomText,
  1859. wszTopCustomText,
  1860. szBottomCustomText,
  1861. wszBottomCustomText,
  1862. &pDSNMsg,
  1863. &dwDSNActionsGenerated,
  1864. &cRecipsDSNd,
  1865. &cIterationsLeft,
  1866. dwDSNRetType,
  1867. hrContentFailure);
  1868. if(FAILED(hr))
  1869. {
  1870. _ASSERT(pDSNMsg == NULL);
  1871. ErrorTrace((LPARAM)this, "HrGenerateDSNInternal failed hr %08lx", hr);
  1872. goto CLEANUP;
  1873. }
  1874. else if(pDSNMsg)
  1875. {
  1876. //
  1877. // Submit the DSN to the system
  1878. //
  1879. hr = pIDSNSubmission->HrSubmitDSN(
  1880. dwDSNActionsGenerated,
  1881. cRecipsDSNd,
  1882. pDSNMsg);
  1883. if(FAILED(hr))
  1884. {
  1885. ErrorTrace((LPARAM)this, "HrSubmitDSN failed hr %08lx", hr);
  1886. goto CLEANUP;
  1887. }
  1888. pDSNMsg->Release();
  1889. pDSNMsg = NULL;
  1890. }
  1891. } while(SUCCEEDED(hr) && cIterationsLeft);
  1892. if(hr == AQUEUE_E_NDR_OF_DSN)
  1893. {
  1894. DebugTrace((LPARAM)this, "NDR of DSN; setting badmail flag");
  1895. hr = pDSNProperties->PutBool(
  1896. DSNPROP_F_BADMAIL_MSG,
  1897. TRUE);
  1898. }
  1899. CLEANUP:
  1900. for(dwCount = 0;
  1901. dwCount < ( sizeof(DsnStringProps) / sizeof(DsnStringProps[0]));
  1902. dwCount++)
  1903. {
  1904. if(*(DsnStringProps[dwCount].ppsz))
  1905. {
  1906. delete [] (*(DsnStringProps[dwCount].ppsz));
  1907. *(DsnStringProps[dwCount].ppsz) = NULL;
  1908. }
  1909. }
  1910. for(dwCount = 0;
  1911. dwCount < ( sizeof(DsnWideStringProps) / sizeof(DsnWideStringProps[0]));
  1912. dwCount++)
  1913. {
  1914. if(*(DsnWideStringProps[dwCount].ppwsz))
  1915. {
  1916. delete [] (*(DsnWideStringProps[dwCount].ppwsz));
  1917. *(DsnWideStringProps[dwCount].ppwsz) = NULL;
  1918. }
  1919. }
  1920. if(pDSNMsg)
  1921. pDSNMsg->Release();
  1922. //
  1923. // Return failures in a property
  1924. //
  1925. if(FAILED(hr))
  1926. {
  1927. HRESULT hrProp;
  1928. hrProp = pDSNProperties->PutDWORD(
  1929. DSNPROP_DW_HR_RETURN_STATUS,
  1930. hr);
  1931. _ASSERT(SUCCEEDED(hrProp));
  1932. ErrorTrace((LPARAM)this, "Unable to generate DSN: %08lx", hr);
  1933. //
  1934. // Return S_OK to dispatcher
  1935. //
  1936. hr = S_OK;
  1937. }
  1938. DebugTrace((LPARAM)this, "returning %08lx", hr);
  1939. TraceFunctLeaveEx((LPARAM)this);
  1940. return SUCCEEDED(hr) ? S_OK : hr;
  1941. } // CDefaultDSNSink::OnSyncGenerateDSN
  1942. //+------------------------------------------------------------
  1943. //
  1944. // Function: CDefaultDSNSink::HrGenerateDSNInternal
  1945. //
  1946. // Synopsis: Generates one DSN message
  1947. //
  1948. // Arguments:
  1949. // pISMTPServer Interface used to generate DSN
  1950. // pIMailMsgProperties IMailMsg to generate DSN for
  1951. // pIRecipIter Controls which recipients to DSN
  1952. // dwDSNActions DSN Actions requested
  1953. // dwRFC821Status Global RFC821 status DWORD
  1954. // hrStatus Global HRESULT status
  1955. // szDefaultDomain Default domain (used to create FROM address)
  1956. // cbDefaultDomain string length of szDefaultDomain
  1957. // szReportingMTA Name of MTA requesting DSN generation
  1958. // cbReportingMTA string length of szReportingMTA
  1959. // szReportingMTAType Type of MTA requestiong DSN (SMTP is "dns"
  1960. // cbReportingMTAType string length of szReportingMTAType
  1961. // PreferredLangId Language to generate DSN in
  1962. // dwDSNOptions Options flags as defined in aqueue.idl
  1963. // szCopyNDRTo SMTP Address to copy NDR to
  1964. // cbCopyNDRTo string lengtt of szCopyNDRTo
  1965. // ppIMailMsgPeropertiesDSN Generated DSN.
  1966. // pdwDSNTypesGenerated Describes the type(s) of DSN's generated
  1967. // pcRecipsDSNd # of recipients that were DSN'd for this message
  1968. // pcIterationsLeft # of times remaining that this function needs
  1969. // to be called to generate all requested DSNs.
  1970. // First-time caller should initialize to
  1971. // zero
  1972. // dwDSNRetType Return type for DSN
  1973. // hrContentFailure Value for X-Content-Failure header
  1974. //
  1975. // Returns:
  1976. // S_OK on success
  1977. // AQUEUE_E_NDR_OF_DSN if attempting to NDR a DSN
  1978. // History:
  1979. // 6/30/98 - MikeSwa Created
  1980. // 12/14/98 - MikeSwa Modified (Added pcIterationsLeft)
  1981. // 10/13/1999 - MikeSwa Modified (Added szDefaultDomain)
  1982. //
  1983. // Returns:
  1984. // S_OK: Success
  1985. //
  1986. // History:
  1987. // jstamerj 2000/11/16 10:53:30: Created.
  1988. //
  1989. //-------------------------------------------------------------
  1990. HRESULT CDefaultDSNSink::HrGenerateDSNInternal(
  1991. ISMTPServer *pISMTPServer,
  1992. IMailMsgProperties *pIMailMsgProperties,
  1993. IDSNRecipientIterator *pIRecipIter,
  1994. IDSNSubmission *pIDSNSubmission,
  1995. DWORD dwDSNActions,
  1996. DWORD dwRFC821Status,
  1997. HRESULT hrStatus,
  1998. LPSTR szDefaultDomain,
  1999. DWORD cbDefaultDomain,
  2000. LPSTR szReportingMTA,
  2001. DWORD cbReportingMTA,
  2002. LPSTR szReportingMTAType,
  2003. DWORD cbReportingMTAType,
  2004. LPSTR szDSNContext,
  2005. DWORD cbDSNContext,
  2006. DWORD dwPreferredLangId,
  2007. DWORD dwDSNOptions,
  2008. LPSTR szCopyNDRTo,
  2009. DWORD cbCopyNDRTo,
  2010. FILETIME *pftExpireTime,
  2011. LPSTR szHRTopCustomText,
  2012. LPWSTR wszHRTopCustomText,
  2013. LPSTR szHRBottomCustomText,
  2014. LPWSTR wszHRBottomCustomText,
  2015. IMailMsgProperties **ppIMailMsgPropertiesDSN,
  2016. DWORD *pdwDSNTypesGenerated,
  2017. DWORD *pcRecipsDSNd,
  2018. DWORD *pcIterationsLeft,
  2019. DWORD dwDSNRetTypeIN,
  2020. HRESULT hrContentFailure)
  2021. {
  2022. HRESULT hr = S_OK;
  2023. HRESULT hrTmp = S_OK;
  2024. DWORD iCurrentRecip = 0;
  2025. DWORD dwCurrentDSNAction = 0;
  2026. DWORD dwDSNActionsNeeded = 0; //the type of DSNs that will actually be sent
  2027. IMailMsgRecipients *pIMailMsgRecipients = NULL;
  2028. IMailMsgProperties *pIMailMsgPropertiesDSN = NULL;
  2029. PFIO_CONTEXT pDSNBody = NULL;
  2030. CDSNBuffer dsnbuff;
  2031. CHAR szMimeBoundary[MIME_BOUNDARY_SIZE];
  2032. DWORD cbMimeBoundary = 0;
  2033. CHAR szExpireTimeBuffer[MAX_RFC822_DATE_SIZE];
  2034. LPSTR szExpireTime = NULL; //will point to szExpireTimeBuffer if found
  2035. DWORD cbExpireTime = 0;
  2036. DWORD iRecip = 0;
  2037. DWORD dwDSNAction = 0;
  2038. DWORD dwDSNRetType = dwDSNRetTypeIN;
  2039. DWORD dwOrigMsgSize = 0;
  2040. HRESULT hrContent = hrContentFailure;
  2041. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::HrGenerateDSNInternal");
  2042. _ASSERT(ppIMailMsgPropertiesDSN);
  2043. _ASSERT(pISMTPServer);
  2044. _ASSERT(pIMailMsgProperties);
  2045. _ASSERT(pdwDSNTypesGenerated);
  2046. _ASSERT(pcRecipsDSNd);
  2047. _ASSERT(pcIterationsLeft);
  2048. *pcRecipsDSNd = 0;
  2049. *ppIMailMsgPropertiesDSN = NULL;
  2050. *pdwDSNTypesGenerated = 0;
  2051. GetCurrentMimeBoundary(szReportingMTA, cbReportingMTA, szMimeBoundary, &cbMimeBoundary);
  2052. //Get Recipients interface
  2053. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
  2054. (PVOID *) &pIMailMsgRecipients);
  2055. if (FAILED(hr))
  2056. goto Exit;
  2057. hr = pIRecipIter->HrReset();
  2058. if (FAILED(hr))
  2059. goto Exit;
  2060. //Loop over recipients to make sure we can need to allocate a message
  2061. hr = pIRecipIter->HrGetNextRecipient(
  2062. &iRecip,
  2063. &dwDSNAction);
  2064. while (SUCCEEDED(hr))
  2065. {
  2066. DebugTrace((LPARAM) this,
  2067. "Recipient %d with DSN Action 0x%08X found",
  2068. iRecip, dwDSNAction);
  2069. //keep track of the types of DSN's we will be generating
  2070. dwDSNActionsNeeded |= dwDSNAction;
  2071. hr = pIRecipIter->HrGetNextRecipient(
  2072. &iRecip,
  2073. &dwDSNAction);
  2074. }
  2075. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
  2076. hr = S_OK; //we just reached the end of the context
  2077. else if (FAILED(hr))
  2078. ErrorTrace((LPARAM) this, "GetNextRecipient failed with 0x%08X",hr);
  2079. if (dwDSNActionsNeeded == 0)
  2080. {
  2081. DebugTrace((LPARAM) this,
  2082. "Do not need to generate a 0x%08X DSN",
  2083. dwDSNActions, pIMailMsgProperties);
  2084. *pcIterationsLeft = 0;
  2085. goto Exit; //don't create a message object if we don't have to
  2086. }
  2087. //Check if message is a DSN (we will not genrate a DSN of a DSN)
  2088. //This must be checked after we run through the recipients, because
  2089. //we need to check them to keep from badmailing this message
  2090. //multiple times.
  2091. if (fIsMailMsgDSN(pIMailMsgProperties))
  2092. {
  2093. DebugTrace((LPARAM) pIMailMsgProperties, "Message is a DSN");
  2094. *pcIterationsLeft = 0;
  2095. if (dwDSNActions & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
  2096. {
  2097. //NDR of DSN... return special error code
  2098. hr = AQUEUE_E_NDR_OF_DSN;
  2099. //mark all the appropriate recipient flags so we don't
  2100. //generate 2 badmails
  2101. HrMarkAllRecipFlags(
  2102. dwDSNActions,
  2103. pIRecipIter);
  2104. }
  2105. goto Exit;
  2106. }
  2107. //if we can generate a failure DSN and the orginal request was for
  2108. //fail *all* make sure dwDSNActionNeeded reflects this
  2109. if ((DSN_ACTION_FAILURE & dwDSNActionsNeeded) &&
  2110. (DSN_ACTION_FAILURE_ALL & dwDSNActions))
  2111. dwDSNActionsNeeded |= DSN_ACTION_FAILURE_ALL;
  2112. GetCurrentIterationsDSNAction(&dwDSNActionsNeeded, pcIterationsLeft);
  2113. if (!dwDSNActionsNeeded)
  2114. {
  2115. *pcIterationsLeft = 0;
  2116. goto Exit; //don't create a message object if we don't have to
  2117. }
  2118. hr = pIDSNSubmission->HrAllocBoundMessage(
  2119. &pIMailMsgPropertiesDSN,
  2120. &pDSNBody);
  2121. if (FAILED(hr))
  2122. goto Exit;
  2123. //workaround to handle AllocBoundMessage on shutdown
  2124. if (NULL == pDSNBody)
  2125. {
  2126. hr = HRESULT_FROM_WIN32(ERROR_NO_SYSTEM_RESOURCES);
  2127. ErrorTrace((LPARAM) this, "ERROR: AllocBoundMessage failed silently");
  2128. goto Exit;
  2129. }
  2130. //Associate file handle with CDSNBuffer
  2131. hr = dsnbuff.HrInitialize(pDSNBody);
  2132. if (FAILED(hr))
  2133. goto Exit;
  2134. //Get MsgExpire Time
  2135. //Write DSN_RP_HEADER_RETRY_UNTIL using expire FILETIME
  2136. if (pftExpireTime)
  2137. {
  2138. //convert to internet standard
  2139. if (FileTimeToLocalRFC822Date(*pftExpireTime, szExpireTimeBuffer))
  2140. {
  2141. szExpireTime = szExpireTimeBuffer;
  2142. cbExpireTime = lstrlen(szExpireTime);
  2143. }
  2144. }
  2145. //
  2146. // If the RET type has not yet been specified, default to:
  2147. // FULL for Failure/Delay DSNs
  2148. // HDRS for other types of DSNs (Expanded/Delivered/Relayed)
  2149. //
  2150. if(dwDSNRetType == 0)
  2151. {
  2152. if ((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL | DSN_ACTION_DELAYED)
  2153. & dwDSNActionsNeeded)
  2154. dwDSNRetType = DSN_RET_FULL;
  2155. else
  2156. dwDSNRetType = DSN_RET_HDRS;
  2157. }
  2158. //
  2159. // If we're going to need the original content, get the size now.
  2160. //
  2161. if(SUCCEEDED(hrContent) && (dwDSNRetType != DSN_RET_PARTIAL_HDRS))
  2162. {
  2163. //Get the content size
  2164. hrContent = pIMailMsgProperties->GetContentSize(&dwOrigMsgSize, NULL);
  2165. }
  2166. //
  2167. // If we received EFNF on obtaining content size, we should simply skip this message
  2168. // without generating an NDR.
  2169. //
  2170. if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hrContent) {
  2171. DebugTrace((LPARAM)this, "GetContentSize failed with EFNF so not generating NDR or badmail.");
  2172. *pcIterationsLeft = 0;
  2173. hr = S_OK;
  2174. goto Exit;
  2175. }
  2176. if (FAILED(hrContent))
  2177. {
  2178. //
  2179. // Rather than badmailing, generate a DSN with only a header subset
  2180. //
  2181. ErrorTrace((LPARAM)this, "GetContentSize failed hr %08lx", hr);
  2182. dwDSNRetType = DSN_RET_PARTIAL_HDRS;
  2183. }
  2184. hr = HrWriteDSNP1AndP2Headers(dwDSNActionsNeeded,
  2185. pIMailMsgProperties, pIMailMsgPropertiesDSN,
  2186. &dsnbuff, szDefaultDomain, cbDefaultDomain,
  2187. szReportingMTA, cbReportingMTA,
  2188. szDSNContext, cbDSNContext,
  2189. szCopyNDRTo, hrStatus,
  2190. szMimeBoundary, cbMimeBoundary, dwDSNOptions,
  2191. hrContent);
  2192. if (FAILED(hr))
  2193. goto Exit;
  2194. hr = HrWriteDSNHumanReadable(pIMailMsgPropertiesDSN, pIMailMsgRecipients,
  2195. pIRecipIter,
  2196. dwDSNActionsNeeded,
  2197. &dsnbuff, dwPreferredLangId,
  2198. szMimeBoundary, cbMimeBoundary, hrStatus,
  2199. szHRTopCustomText, wszHRTopCustomText,
  2200. szHRBottomCustomText, wszHRBottomCustomText);
  2201. if (FAILED(hr))
  2202. goto Exit;
  2203. hr = HrWriteDSNReportPerMsgProperties(pIMailMsgProperties,
  2204. &dsnbuff, szReportingMTA, cbReportingMTA,
  2205. szMimeBoundary, cbMimeBoundary);
  2206. if (FAILED(hr))
  2207. goto Exit;
  2208. //recycle context again (may be used during generation of human readable)
  2209. hr = pIRecipIter->HrReset();
  2210. if (FAILED(hr))
  2211. goto Exit;
  2212. //$$REVIEW - Do we need to keep an "undo" list... or perhaps reverse
  2213. //engineer what the previous value was in case of a failure
  2214. hr = pIRecipIter->HrGetNextRecipient(&iCurrentRecip, &dwCurrentDSNAction);
  2215. while (SUCCEEDED(hr))
  2216. {
  2217. if(dwDSNActionsNeeded & dwCurrentDSNAction)
  2218. {
  2219. *pdwDSNTypesGenerated |= (dwCurrentDSNAction & DSN_ACTION_TYPE_MASK);
  2220. (*pcRecipsDSNd)++;
  2221. hr = HrWriteDSNReportPreRecipientProperties(
  2222. pIMailMsgRecipients, &dsnbuff,
  2223. iCurrentRecip, szExpireTime, cbExpireTime,
  2224. dwCurrentDSNAction, dwRFC821Status, hrStatus);
  2225. if (FAILED(hr))
  2226. goto Exit;
  2227. hr = pIRecipIter->HrNotifyActionHandled(
  2228. iCurrentRecip,
  2229. dwCurrentDSNAction);
  2230. _ASSERT(SUCCEEDED(hr) && "HrNotifyActionHandled failed on 2nd pass");
  2231. if (FAILED(hr))
  2232. goto Exit;
  2233. // DSN Logging
  2234. hr = HrLogDSNGenerationEvent(
  2235. pISMTPServer,
  2236. pIMailMsgProperties,
  2237. pIMailMsgRecipients,
  2238. iCurrentRecip,
  2239. dwCurrentDSNAction,
  2240. dwRFC821Status,
  2241. hrStatus);
  2242. if (FAILED(hr))
  2243. {
  2244. ErrorTrace((LPARAM) this, "Failed to log DSN generation with 0x%08X",hr);
  2245. hr = S_OK; // We can accept this failure ...
  2246. }
  2247. }
  2248. hr = pIRecipIter->HrGetNextRecipient(&iCurrentRecip, &dwCurrentDSNAction);
  2249. }
  2250. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
  2251. hr = S_OK;
  2252. if (0 == (*pcRecipsDSNd))
  2253. goto Exit; //no work to do
  2254. hr = HrWriteDSNClosingAndOriginalMessage(pIMailMsgProperties,
  2255. pIMailMsgPropertiesDSN, &dsnbuff, pDSNBody,
  2256. dwDSNActionsNeeded, szMimeBoundary, cbMimeBoundary,
  2257. dwDSNRetType, dwOrigMsgSize);
  2258. if (FAILED(hr))
  2259. goto Exit;
  2260. hr = pIMailMsgPropertiesDSN->Commit(NULL);
  2261. if (FAILED(hr))
  2262. {
  2263. ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: IMailMsg::Commit failed - hr 0x%08X", hr);
  2264. goto Exit;
  2265. }
  2266. *ppIMailMsgPropertiesDSN = pIMailMsgPropertiesDSN;
  2267. pIMailMsgPropertiesDSN = NULL;
  2268. Exit:
  2269. if (pIMailMsgRecipients)
  2270. {
  2271. pIMailMsgRecipients->Release();
  2272. }
  2273. if (pIMailMsgPropertiesDSN)
  2274. {
  2275. IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
  2276. //if non-NULL, then we should not be returning any value
  2277. _ASSERT(NULL == *ppIMailMsgPropertiesDSN);
  2278. //Check for alloc bound message failure
  2279. if (HRESULT_FROM_WIN32(ERROR_NO_SYSTEM_RESOURCES) != hr)
  2280. {
  2281. if (SUCCEEDED(pIMailMsgPropertiesDSN->QueryInterface(IID_IMailMsgQueueMgmt,
  2282. (void **) &pIMailMsgQueueMgmt)))
  2283. {
  2284. _ASSERT(pIMailMsgQueueMgmt);
  2285. pIMailMsgQueueMgmt->Delete(NULL);
  2286. pIMailMsgQueueMgmt->Release();
  2287. }
  2288. }
  2289. pIMailMsgPropertiesDSN->Release();
  2290. }
  2291. if (FAILED(hr))
  2292. *pcIterationsLeft = 0;
  2293. //workaround for alloc bound message
  2294. if (HRESULT_FROM_WIN32(ERROR_NO_SYSTEM_RESOURCES) == hr)
  2295. {
  2296. hr = S_OK;
  2297. }
  2298. DebugTrace((LPARAM)this, "returning %08lx", hr);
  2299. TraceFunctLeaveEx((LPARAM)this);
  2300. return hr;
  2301. } // CDefaultDSNSink::HrGenerateDSNInternal
  2302. //+------------------------------------------------------------
  2303. //
  2304. // Function: CDefaultDSNSink::HrMarkAllRecipFlags
  2305. //
  2306. // Synopsis:
  2307. // Marks all recipient according to the DSN action. Used when an NDR of
  2308. // an NDR is found and we will not be generating a DSN, but need to mark
  2309. // the recips so we can not generate 2 badmail events.
  2310. //
  2311. // Arguments:
  2312. // IN DWORD dwDSNActions: Actions to mark
  2313. // IN IDSNRecipientIterator *pIRecipIter: Recipient iterator
  2314. //
  2315. // Returns:
  2316. // S_OK: Success
  2317. //
  2318. // History:
  2319. // jstamerj 2000/11/20 17:13:12: Created.
  2320. //
  2321. //-------------------------------------------------------------
  2322. HRESULT CDefaultDSNSink::HrMarkAllRecipFlags(
  2323. IN DWORD dwDSNActions,
  2324. IN IDSNRecipientIterator *pIRecipIter)
  2325. {
  2326. HRESULT hr = S_OK;
  2327. DWORD iRecip = 0;
  2328. DWORD dwRecipDSNActions = 0;
  2329. TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::HrMarkAllRecipFlags");
  2330. hr = pIRecipIter->HrReset();
  2331. if(FAILED(hr))
  2332. goto CLEANUP;
  2333. hr = pIRecipIter->HrGetNextRecipient(
  2334. &iRecip,
  2335. &dwRecipDSNActions);
  2336. if(FAILED(hr))
  2337. goto CLEANUP;
  2338. while(SUCCEEDED(hr))
  2339. {
  2340. hr = pIRecipIter->HrNotifyActionHandled(
  2341. iRecip,
  2342. dwDSNActions);
  2343. if(FAILED(hr))
  2344. goto CLEANUP;
  2345. hr = pIRecipIter->HrGetNextRecipient(
  2346. &iRecip,
  2347. &dwRecipDSNActions);
  2348. if(FAILED(hr))
  2349. goto CLEANUP;
  2350. }
  2351. CLEANUP:
  2352. DebugTrace((LPARAM)this, "returning %08lx", hr);
  2353. TraceFunctLeaveEx((LPARAM)this);
  2354. return hr;
  2355. } // CDefaultDSNSink::HrMarkAllRecipFlags
  2356. //---[ CDefaultDSNSink::GetCurrentIterationsDSNAction ]------------------------
  2357. //
  2358. //
  2359. // Description:
  2360. // This function will select one of the pdwActualDSNAction to generate
  2361. // DSNs on during this call to the DSN generation sink.
  2362. // Parameters:
  2363. // IN OUT pdwActionDSNAction DSN Actions that can/will be used.
  2364. // IN OUT pcIterationsLeft Approximate # of times needed to call
  2365. // DSN generation
  2366. // Returns:
  2367. // -
  2368. // History:
  2369. // 12/14/98 - MikeSwa Created
  2370. //
  2371. //-----------------------------------------------------------------------------
  2372. void CDefaultDSNSink::GetCurrentIterationsDSNAction(
  2373. IN OUT DWORD *pdwActualDSNAction,
  2374. IN OUT DWORD *pcIterationsLeft)
  2375. {
  2376. _ASSERT(pdwActualDSNAction);
  2377. _ASSERT(pcIterationsLeft);
  2378. const DWORD MAX_DSN_ACTIONS = 6;
  2379. //In the following array FAILURE_ALL must come first or else we will
  2380. //generate separate failure DSNs for hard failures and undelivereds.
  2381. DWORD rgPossibleDSNActions[MAX_DSN_ACTIONS] = {DSN_ACTION_FAILURE_ALL,
  2382. DSN_ACTION_FAILURE,
  2383. DSN_ACTION_DELAYED,
  2384. DSN_ACTION_RELAYED,
  2385. DSN_ACTION_DELIVERED,
  2386. DSN_ACTION_EXPANDED};
  2387. DWORD i = 0;
  2388. DWORD iLastMatch = MAX_DSN_ACTIONS;
  2389. DWORD iFirstMatch = MAX_DSN_ACTIONS;
  2390. DWORD iStartIndex = 0;
  2391. //Since the possible DSNs to generate may change from call to call (because
  2392. //we are updating the pre-recipient flags), we need to generate and maintain
  2393. //pcIterationsLeft based on the possible Actions (which will not be changing
  2394. //from call to call).
  2395. _ASSERT(*pcIterationsLeft < MAX_DSN_ACTIONS);
  2396. //Figure out where we should start if this is not the
  2397. if (*pcIterationsLeft)
  2398. iStartIndex = MAX_DSN_ACTIONS-*pcIterationsLeft;
  2399. //Loop through possible DSN actions (that we haven't seen) and determine
  2400. //the first and last
  2401. for (i = iStartIndex; i < MAX_DSN_ACTIONS; i++)
  2402. {
  2403. if (rgPossibleDSNActions[i] & *pdwActualDSNAction)
  2404. {
  2405. iLastMatch = i;
  2406. if (MAX_DSN_ACTIONS == iFirstMatch)
  2407. iFirstMatch = i;
  2408. }
  2409. }
  2410. if (MAX_DSN_ACTIONS == iLastMatch)
  2411. {
  2412. //No matches... we are done
  2413. *pdwActualDSNAction = 0;
  2414. *pcIterationsLeft = 0;
  2415. return;
  2416. }
  2417. //If this is possible after the above check... then I've screwed up
  2418. _ASSERT(MAX_DSN_ACTIONS != iFirstMatch);
  2419. *pdwActualDSNAction = rgPossibleDSNActions[iFirstMatch];
  2420. if ((iLastMatch == iFirstMatch) ||
  2421. ((rgPossibleDSNActions[iFirstMatch] == DSN_ACTION_FAILURE_ALL) &&
  2422. (rgPossibleDSNActions[iLastMatch] == DSN_ACTION_FAILURE)))
  2423. {
  2424. //This is our last time through
  2425. *pcIterationsLeft = 0;
  2426. }
  2427. else
  2428. {
  2429. *pcIterationsLeft = MAX_DSN_ACTIONS-1-iFirstMatch;
  2430. }
  2431. }
  2432. //---[ CDefaultDSNSink::HrWriteDSNP1AndP2Headers ]-----------------------------
  2433. //
  2434. //
  2435. // Description:
  2436. // Writes global DSN P1 Properties to IMailMsgProperties
  2437. // Parameters:
  2438. // dwDSNAction DSN action specified for sink
  2439. // pIMailMsgProperties Msg that DSN is being generated for
  2440. // pIMailMsgPropertiesDSN DSN being generated
  2441. // psndbuff Buffer to write to
  2442. // szDefaultDomain Default domain - used from postmaster from address
  2443. // cbDefaultDomain strlen of szDefaultDomain
  2444. // szReportingMTA Reporting MTA as passed to event sink
  2445. // cbReportingMTA strlen of szReportingMTA
  2446. // szDSNConext Debug File and line number info passed in
  2447. // cbDSNConext strlen of szDSNContext
  2448. // szCopyNDRTo SMTP Address to copy NDR to
  2449. // hrStatus Status to record in DSN context
  2450. // szMimeBoundary MIME boundary string
  2451. // cbMimeBoundary strlen of MIME boundary
  2452. // dwDSNOptions DSN Options flags
  2453. // hrContent Content result
  2454. //
  2455. // Returns:
  2456. // S_OK on success
  2457. // History:
  2458. // 7/5/98 - MikeSwa Created
  2459. // 8/14/98 - MikeSwa Modified - Added DSN context headers
  2460. // 11/9/98 - MikeSwa Added copy NDR to functionality
  2461. //
  2462. //-----------------------------------------------------------------------------
  2463. HRESULT CDefaultDSNSink::HrWriteDSNP1AndP2Headers(
  2464. IN DWORD dwDSNAction,
  2465. IN IMailMsgProperties *pIMailMsgProperties,
  2466. IN IMailMsgProperties *pIMailMsgPropertiesDSN,
  2467. IN CDSNBuffer *pdsnbuff,
  2468. IN LPSTR szDefaultDomain,
  2469. IN DWORD cbDefaultDomain,
  2470. IN LPSTR szReportingMTA,
  2471. IN DWORD cbReportingMTA,
  2472. IN LPSTR szDSNContext,
  2473. IN DWORD cbDSNContext,
  2474. IN LPSTR szCopyNDRTo,
  2475. IN HRESULT hrStatus,
  2476. IN LPSTR szMimeBoundary,
  2477. IN DWORD cbMimeBoundary,
  2478. IN DWORD dwDSNOptions,
  2479. IN HRESULT hrContent)
  2480. {
  2481. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteDSNP1AndP2Headers");
  2482. HRESULT hr = S_OK;
  2483. BOOL bReturn = TRUE;
  2484. HRESULT hrTmp = S_OK;
  2485. CHAR szBuffer[512];
  2486. LPSTR szSender = (LPSTR) szBuffer; //tricks to avoid AV'ing in AddPrimary
  2487. IMailMsgRecipientsAdd *pIMailMsgRecipientsAdd = NULL;
  2488. IMailMsgRecipients *pIMailMsgRecipients = NULL;
  2489. DWORD dwRecipAddressProp = IMMPID_RP_ADDRESS_SMTP;
  2490. DWORD dwSMTPAddressProp = IMMPID_RP_ADDRESS_SMTP;
  2491. DWORD iCurrentAddressProp = 0;
  2492. DWORD dwDSNRecipient = 0;
  2493. DWORD cbPostMaster = 0;
  2494. CHAR szDSNAction[15];
  2495. FILETIME ftCurrentTime;
  2496. CHAR szCurrentTimeBuffer[MAX_RFC822_DATE_SIZE];
  2497. _ASSERT(pIMailMsgProperties);
  2498. _ASSERT(pIMailMsgPropertiesDSN);
  2499. _ASSERT(pdsnbuff);
  2500. szBuffer[0] = '\0';
  2501. //Get and write Message tracking properties
  2502. hr = pIMailMsgProperties->GetStringA(IMMPID_MP_SERVER_VERSION,
  2503. sizeof(szBuffer), szBuffer);
  2504. if (SUCCEEDED(hr))
  2505. {
  2506. hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_SERVER_VERSION, szBuffer);
  2507. if (FAILED(hr))
  2508. DebugTrace((LPARAM) this,
  2509. "Warning: Unable to write version to msg - 0x%08X", hr);
  2510. hr = S_OK;
  2511. }
  2512. else
  2513. {
  2514. DebugTrace((LPARAM) this,
  2515. "Warning: Unable to get server version from msg - 0x%08X", hr);
  2516. hr = S_OK; //ignore this non-fatal error
  2517. }
  2518. hr = pIMailMsgProperties->GetStringA(IMMPID_MP_SERVER_NAME,
  2519. sizeof(szBuffer), szBuffer);
  2520. if (SUCCEEDED(hr))
  2521. {
  2522. hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_SERVER_NAME, szBuffer);
  2523. if (FAILED(hr))
  2524. DebugTrace((LPARAM) this,
  2525. "Warning: Unable to write server name to msg - 0x%08X", hr);
  2526. hr = S_OK;
  2527. }
  2528. else
  2529. {
  2530. DebugTrace((LPARAM) this,
  2531. "Warning: Unable to get server name from msg - 0x%08X", hr);
  2532. hr = S_OK; //ignore this non-fatal error
  2533. }
  2534. //Set the type of message
  2535. if (dwDSNAction &
  2536. (DSN_ACTION_EXPANDED | DSN_ACTION_RELAYED | DSN_ACTION_DELIVERED)) {
  2537. hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSGCLASS,
  2538. MP_MSGCLASS_DELIVERY_REPORT);
  2539. } else if (dwDSNAction &
  2540. (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL | DSN_ACTION_DELAYED)) {
  2541. hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSGCLASS,
  2542. MP_MSGCLASS_NONDELIVERY_REPORT);
  2543. }
  2544. if (FAILED(hr)) {
  2545. DebugTrace((LPARAM) this,
  2546. "Warning: Unable to set msg class for dsn - 0x%08X", hr);
  2547. hr = S_OK;
  2548. }
  2549. for (iCurrentAddressProp = 0;
  2550. iCurrentAddressProp < NUM_DSN_ADDRESS_PROPERTIES;
  2551. iCurrentAddressProp++)
  2552. {
  2553. szBuffer[0] = '\0';
  2554. //Get the sender of the original message
  2555. hr = pIMailMsgProperties->GetStringA(
  2556. g_rgdwSenderPropIDs[iCurrentAddressProp],
  2557. sizeof(szBuffer), szBuffer);
  2558. if (FAILED(hr) && (MAILMSG_E_PROPNOTFOUND != hr))
  2559. {
  2560. ErrorTrace((LPARAM) this,
  2561. "ERROR: Unable to get 0x%X sender of IMailMsg %p",
  2562. g_rgdwSenderPropIDs[iCurrentAddressProp], pIMailMsgProperties);
  2563. goto Exit;
  2564. }
  2565. //
  2566. // If we have found an address break
  2567. //
  2568. if (SUCCEEDED(hr))
  2569. break;
  2570. }
  2571. //
  2572. // If we failed to get a property... bail
  2573. //
  2574. if (FAILED(hr))
  2575. {
  2576. ErrorTrace((LPARAM) this,
  2577. "ERROR: Unable to get any sender of IMailMsg 0x%08X",
  2578. pIMailMsgProperties);
  2579. goto Exit;
  2580. }
  2581. //write DSN Sender (P1)
  2582. hr = pIMailMsgPropertiesDSN->PutProperty(IMMPID_MP_SENDER_ADDRESS_SMTP,
  2583. sizeof(DSN_MAIL_FROM), (BYTE *) DSN_MAIL_FROM);
  2584. if (FAILED(hr))
  2585. {
  2586. ErrorTrace((LPARAM) this,
  2587. "ERROR: Unable to write P1 DSN sender for IMailMsg 0x%08X",
  2588. pIMailMsgProperties);
  2589. goto Exit;
  2590. }
  2591. //write DSN Recipient
  2592. hr = pIMailMsgPropertiesDSN->QueryInterface(IID_IMailMsgRecipients,
  2593. (void **) &pIMailMsgRecipients);
  2594. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgRecipients failed");
  2595. if (FAILED(hr))
  2596. goto Exit;
  2597. hr = pIMailMsgRecipients->AllocNewList(&pIMailMsgRecipientsAdd);
  2598. if (FAILED(hr))
  2599. {
  2600. ErrorTrace((LPARAM) this,
  2601. "ERROR: Unable to Alloc List for DSN generation of IMailMsg 0x%08X",
  2602. pIMailMsgProperties);
  2603. goto Exit;
  2604. }
  2605. dwRecipAddressProp = g_rgdwRecipPropIDs[iCurrentAddressProp];
  2606. hr = pIMailMsgRecipientsAdd->AddPrimary(
  2607. 1,
  2608. (LPCSTR *) &szSender,
  2609. &dwRecipAddressProp,
  2610. &dwDSNRecipient,
  2611. NULL,
  2612. 0);
  2613. if (FAILED(hr))
  2614. {
  2615. ErrorTrace((LPARAM) this,
  2616. "ERROR: Unable to write DSN recipient for IMailMsg 0x%p hr - 0x%08X",
  2617. pIMailMsgProperties, hr);
  2618. goto Exit;
  2619. }
  2620. //Write Address to copy NDR to (NDRs only)
  2621. if (szCopyNDRTo &&
  2622. (dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL)))
  2623. {
  2624. hr = pIMailMsgRecipientsAdd->AddPrimary(
  2625. 1,
  2626. (LPCSTR *) &szCopyNDRTo,
  2627. &dwSMTPAddressProp,
  2628. &dwDSNRecipient,
  2629. NULL,
  2630. 0);
  2631. if (FAILED(hr))
  2632. {
  2633. ErrorTrace((LPARAM) this,
  2634. "ERROR: Unable to write DSN recipient for IMailMsg 0x%08X",
  2635. pIMailMsgProperties);
  2636. goto Exit;
  2637. }
  2638. }
  2639. //write P2 DSN sender
  2640. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RFC822_SENDER, sizeof(DSN_RFC822_SENDER)-1);
  2641. if (FAILED(hr))
  2642. goto Exit;
  2643. hr = pdsnbuff->HrWriteBuffer((BYTE *) szDefaultDomain, cbDefaultDomain);
  2644. if (FAILED(hr))
  2645. goto Exit;
  2646. //
  2647. // If we do not have a SMTP address, write a blank BCC instead of
  2648. // a TO address (since we do not have a address we can write in the 822.
  2649. // This is similar to what we do with the pickup dir when we have no TO
  2650. // headers.
  2651. //
  2652. if (IMMPID_MP_SENDER_ADDRESS_SMTP == g_rgdwSenderPropIDs[iCurrentAddressProp])
  2653. {
  2654. //Write P2 "To:" header (using the szSender value we determined above)
  2655. hr = pdsnbuff->HrWriteBuffer((BYTE *) TO_HEADER, sizeof(TO_HEADER)-1);
  2656. if (FAILED(hr))
  2657. goto Exit;
  2658. hr = pdsnbuff->HrWriteBuffer((BYTE *) szSender, lstrlen(szSender));
  2659. if (FAILED(hr))
  2660. goto Exit;
  2661. //Save recipient (original sender) for Queue Admin/Message Tracking
  2662. hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_TO_ADDRESS, szSender);
  2663. if (FAILED(hr))
  2664. goto Exit;
  2665. }
  2666. else
  2667. {
  2668. hr = pdsnbuff->HrWriteBuffer((BYTE *) BCC_HEADER, sizeof(BCC_HEADER)-1);
  2669. if (FAILED(hr))
  2670. goto Exit;
  2671. }
  2672. //Use szBuffer to construct 822 from to set for Queue Admin/Msg Tracking
  2673. //"Postmaster@" + max of 64 characters should be less than 1/2 K!!
  2674. _ASSERT(sizeof(szBuffer) > sizeof(DSN_SENDER_ADDRESS_PREFIX) + cbReportingMTA);
  2675. memcpy(szBuffer, DSN_SENDER_ADDRESS_PREFIX, sizeof(DSN_SENDER_ADDRESS_PREFIX));
  2676. strncat(szBuffer, szDefaultDomain, sizeof(szBuffer) - sizeof(DSN_SENDER_ADDRESS_PREFIX));
  2677. hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_FROM_ADDRESS, szSender);
  2678. if (FAILED(hr))
  2679. goto Exit;
  2680. //Write P2 "Date:" header
  2681. hr = pdsnbuff->HrWriteBuffer((BYTE *) DATE_HEADER, sizeof(DATE_HEADER)-1);
  2682. if (FAILED(hr))
  2683. goto Exit;
  2684. //Get current time
  2685. GetSystemTimeAsFileTime(&ftCurrentTime);
  2686. bReturn = FileTimeToLocalRFC822Date(ftCurrentTime, szCurrentTimeBuffer);
  2687. // The only reason this function fails is a bad filetime. Since we get it from GetSystemTimeAsFileTime, there's no reason it fails.
  2688. _ASSERT(bReturn);
  2689. hr = pdsnbuff->HrWriteBuffer((BYTE *) szCurrentTimeBuffer, lstrlen(szCurrentTimeBuffer));
  2690. if (FAILED(hr))
  2691. goto Exit;
  2692. //Write the MIME header
  2693. hr = pdsnbuff->HrWriteBuffer( (BYTE *) MIME_HEADER, sizeof(MIME_HEADER)-1);
  2694. if (FAILED(hr))
  2695. goto Exit;
  2696. hr = pdsnbuff->HrWriteBuffer((BYTE *) "\"", sizeof(CHAR));
  2697. if (FAILED(hr))
  2698. goto Exit;
  2699. hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
  2700. if (FAILED(hr))
  2701. goto Exit;
  2702. hr = pdsnbuff->HrWriteBuffer((BYTE *) "\"", sizeof(CHAR));
  2703. if (FAILED(hr))
  2704. goto Exit;
  2705. //write x-DSNContext header
  2706. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CONTEXT_HEADER,
  2707. sizeof(DSN_CONTEXT_HEADER)-1);
  2708. if (FAILED(hr))
  2709. goto Exit;
  2710. hr = pdsnbuff->HrWriteBuffer((BYTE *) szDSNContext, cbDSNContext);
  2711. if (FAILED(hr))
  2712. goto Exit;
  2713. wsprintf(szDSNAction, " - %08X", dwDSNAction);
  2714. hr = pdsnbuff->HrWriteBuffer((BYTE *) szDSNAction, strlen(szDSNAction));
  2715. if (FAILED(hr))
  2716. goto Exit;
  2717. wsprintf(szDSNAction, " - %08X", hrStatus);
  2718. hr = pdsnbuff->HrWriteBuffer((BYTE *) szDSNAction, strlen(szDSNAction));
  2719. if (FAILED(hr))
  2720. goto Exit;
  2721. //Get and write the message ID
  2722. if (fGenerateDSNMsgID(szReportingMTA, cbReportingMTA, szBuffer, sizeof(szBuffer)))
  2723. {
  2724. hr = pdsnbuff->HrWriteBuffer((BYTE *) MSGID_HEADER, sizeof(MSGID_HEADER)-1);
  2725. if (FAILED(hr))
  2726. goto Exit;
  2727. hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, strlen(szBuffer));
  2728. if (FAILED(hr))
  2729. goto Exit;
  2730. hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_MSG_ID,
  2731. szBuffer);
  2732. if (FAILED(hr))
  2733. goto Exit;
  2734. }
  2735. //Write the X-Content-Failure DSN
  2736. if(FAILED(hrContent))
  2737. {
  2738. CHAR szHRESULT[11];
  2739. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CONTENT_FAILURE_HEADER,
  2740. sizeof(DSN_CONTENT_FAILURE_HEADER)-1);
  2741. if(FAILED(hr))
  2742. goto Exit;
  2743. wsprintf(szHRESULT, "0x%08lx", hrContent);
  2744. hr = pdsnbuff->HrWriteBuffer((PBYTE) szHRESULT, 10);
  2745. if(FAILED(hr))
  2746. goto Exit;
  2747. }
  2748. Exit:
  2749. if (pIMailMsgRecipients)
  2750. {
  2751. if (pIMailMsgRecipientsAdd)
  2752. {
  2753. hrTmp = pIMailMsgRecipients->WriteList( pIMailMsgRecipientsAdd );
  2754. _ASSERT(SUCCEEDED(hrTmp) && "Go Get Keith");
  2755. if (FAILED(hrTmp) && SUCCEEDED(hr))
  2756. hr = hrTmp;
  2757. }
  2758. pIMailMsgRecipients->Release();
  2759. }
  2760. if (pIMailMsgRecipientsAdd)
  2761. pIMailMsgRecipientsAdd->Release();
  2762. TraceFunctLeave();
  2763. return hr;
  2764. }
  2765. //---[ CDefaultDSNSink::HrWriteDSNHumanReadable ]--------------------
  2766. //
  2767. //
  2768. // Description:
  2769. // Write human readable portion of DSN (including subject header)
  2770. // Parameters:
  2771. // pIMailMsgProperties Message DSN is being generated for
  2772. // pIMailMsgREcipeints Recip Interface for Message
  2773. // prpfctxt Delivery context that DSN's are being generated for
  2774. // dwDSNActions DSN actions being taken (after looking at recips)
  2775. // So we can generate a reasonable subject
  2776. // pdsnbuff DSN Buffer to write content to
  2777. // PreferredLangId Preferred language to generate DSN in
  2778. // szMimeBoundary MIME boundary string
  2779. // cbMimeBoundary strlen of MIME boundary
  2780. // hrStatus Status to use to decide which text to display
  2781. // Returns:
  2782. // S_OK on success
  2783. // History:
  2784. // 7/5/98 - MikeSwa Created
  2785. // 12/15/98 - MikeSwa Added list of recipients & fancy human readable
  2786. //
  2787. //-----------------------------------------------------------------------------
  2788. HRESULT CDefaultDSNSink::HrWriteDSNHumanReadable(
  2789. IN IMailMsgProperties *pIMailMsgPropertiesDSN,
  2790. IN IMailMsgRecipients *pIMailMsgRecipients,
  2791. IN IDSNRecipientIterator *pIRecipIter,
  2792. IN DWORD dwDSNActions,
  2793. IN CDSNBuffer *pdsnbuff,
  2794. IN DWORD dwPreferredLangId,
  2795. IN LPSTR szMimeBoundary,
  2796. IN DWORD cbMimeBoundary,
  2797. IN HRESULT hrStatus,
  2798. IN LPSTR szHRTopCustomText,
  2799. IN LPWSTR wszHRTopCustomText,
  2800. IN LPSTR szHRBottomCustomText,
  2801. IN LPWSTR wszHRBottomCustomText)
  2802. {
  2803. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNGenerationSink::HrWriteDSNHumanReadable");
  2804. HRESULT hr = S_OK;
  2805. DWORD dwDSNType = (dwDSNActions & DSN_ACTION_TYPE_MASK);
  2806. LANGID LangID = (LANGID) dwPreferredLangId;
  2807. CUTF7ConversionContext utf7conv(FALSE);
  2808. CUTF7ConversionContext utf7convSubject(TRUE);
  2809. BOOL fWriteRecips = TRUE;
  2810. WORD wSubjectID = GENERAL_SUBJECT;
  2811. LPWSTR wszSubject = NULL;
  2812. LPWSTR wszStop = NULL;
  2813. DWORD cbSubject = 0;
  2814. LPSTR szSubject = NULL;
  2815. LPSTR szSubjectCurrent = NULL;
  2816. if (!fLanguageAvailable(LangID))
  2817. {
  2818. //Use default of server
  2819. LangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  2820. }
  2821. hr = pdsnbuff->HrWriteBuffer((BYTE *) SUBJECT_HEADER, sizeof(SUBJECT_HEADER)-1);
  2822. if (FAILED(hr))
  2823. goto Exit;
  2824. //Set conversion context to UTF7 for RFC1522 subject
  2825. pdsnbuff->SetConversionContext(&utf7convSubject);
  2826. //Write subject with useful info
  2827. if (((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL) & dwDSNType) == dwDSNType)
  2828. wSubjectID = FAILURE_SUBJECT;
  2829. else if (DSN_ACTION_RELAYED == dwDSNType)
  2830. wSubjectID = RELAY_SUBJECT;
  2831. else if (DSN_ACTION_DELAYED == dwDSNType)
  2832. wSubjectID = DELAY_SUBJECT;
  2833. else if (DSN_ACTION_DELIVERED == dwDSNType)
  2834. wSubjectID = DELIVERED_SUBJECT;
  2835. else if (DSN_ACTION_EXPANDED == dwDSNType)
  2836. wSubjectID = EXPANDED_SUBJECT;
  2837. hr = pdsnbuff->HrWriteResource(wSubjectID, LangID);
  2838. if (FAILED(hr))
  2839. goto Exit;
  2840. //Write *English* subject for Queue Admin/Message tracking
  2841. //Use english, becuase we return a ASCII string to queue admin
  2842. hr = pdsnbuff->HrLoadResourceString(wSubjectID,
  2843. MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  2844. &wszSubject, &cbSubject);
  2845. if (FAILED(hr))
  2846. {
  2847. ErrorTrace((LPARAM) this, "Unable to get resource for english subject 0x%08X", hr);
  2848. }
  2849. else
  2850. {
  2851. //We need to convert from UNICODE to ASCII... remember resource is not
  2852. //NULL terminated
  2853. szSubject = (LPSTR) pvMalloc(cbSubject/sizeof(WCHAR) + 1);
  2854. wszStop = wszSubject + (cbSubject/sizeof(WCHAR));
  2855. if (szSubject)
  2856. {
  2857. szSubjectCurrent = szSubject;
  2858. while ((wszSubject < wszStop) && *wszSubject)
  2859. {
  2860. wctomb(szSubjectCurrent, *wszSubject);
  2861. szSubjectCurrent++;
  2862. wszSubject++;
  2863. }
  2864. *szSubjectCurrent = '\0';
  2865. pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_MSG_SUBJECT, szSubject);
  2866. FreePv(szSubject);
  2867. }
  2868. }
  2869. pdsnbuff->ResetConversionContext();
  2870. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2871. if (FAILED(hr))
  2872. goto Exit;
  2873. //write summary saying that this is a MIME message
  2874. hr = pdsnbuff->HrWriteBuffer((BYTE *) MESSAGE_SUMMARY, sizeof(MESSAGE_SUMMARY)-1);
  2875. if (FAILED(hr))
  2876. goto Exit;
  2877. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2878. if (FAILED(hr))
  2879. goto Exit;
  2880. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
  2881. if (FAILED(hr))
  2882. goto Exit;
  2883. hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
  2884. if (FAILED(hr))
  2885. goto Exit;
  2886. //Write content type
  2887. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_CONTENT_TYPE, sizeof(MIME_CONTENT_TYPE)-1);
  2888. if (FAILED(hr))
  2889. goto Exit;
  2890. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HUMAN_READABLE_TYPE, sizeof(DSN_HUMAN_READABLE_TYPE)-1);
  2891. if (FAILED(hr))
  2892. goto Exit;
  2893. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_MIME_CHARSET_HEADER, sizeof(DSN_MIME_CHARSET_HEADER)-1);
  2894. if (FAILED(hr))
  2895. goto Exit;
  2896. //For now... we do our encoding as UTF7.... put that as the charset
  2897. hr = pdsnbuff->HrWriteBuffer((BYTE *) UTF7_CHARSET, sizeof(UTF7_CHARSET)-1);
  2898. if (FAILED(hr))
  2899. goto Exit;
  2900. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2901. if (FAILED(hr))
  2902. goto Exit;
  2903. //Set conversion context to UTF7
  2904. pdsnbuff->SetConversionContext(&utf7conv);
  2905. //
  2906. // Custom header text
  2907. //
  2908. if(szHRTopCustomText)
  2909. {
  2910. hr = pdsnbuff->HrWriteBuffer((BYTE *) szHRTopCustomText, lstrlenA(szHRTopCustomText));
  2911. if(FAILED(hr))
  2912. goto Exit;
  2913. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2914. if (FAILED(hr))
  2915. goto Exit;
  2916. }
  2917. if(wszHRTopCustomText)
  2918. {
  2919. hr = pdsnbuff->HrWriteModifiedUnicodeString(wszHRTopCustomText);
  2920. if(FAILED(hr))
  2921. goto Exit;
  2922. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2923. if (FAILED(hr))
  2924. goto Exit;
  2925. }
  2926. hr = pdsnbuff->HrWriteResource(DSN_SUMMARY, LangID);
  2927. if (FAILED(hr))
  2928. goto Exit;
  2929. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2930. if (FAILED(hr))
  2931. goto Exit;
  2932. //Describe the type of DSN
  2933. if (((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL) & dwDSNType) == dwDSNType)
  2934. {
  2935. //See if we have a failure-specific message
  2936. switch(hrStatus)
  2937. {
  2938. #ifdef NEVER
  2939. //CAT can generate errors other than unresolved recipeints
  2940. //We will use the generic DSN failure message rather than confuse
  2941. //recipients
  2942. case CAT_W_SOME_UNDELIVERABLE_MSGS:
  2943. hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY_MAILBOX, LangID);
  2944. break;
  2945. #endif //NEVER
  2946. case AQUEUE_E_MAX_HOP_COUNT_EXCEEDED:
  2947. hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY_HOP, LangID);
  2948. break;
  2949. case AQUEUE_E_MSG_EXPIRED:
  2950. case AQUEUE_E_HOST_NOT_RESPONDING:
  2951. case AQUEUE_E_CONNECTION_DROPPED:
  2952. hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY_EXPIRE, LangID);
  2953. break;
  2954. default:
  2955. hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY, LangID);
  2956. }
  2957. if (FAILED(hr))
  2958. goto Exit;
  2959. }
  2960. else if (DSN_ACTION_RELAYED == dwDSNType)
  2961. {
  2962. hr = pdsnbuff->HrWriteResource(RELAY_SUMMARY, LangID);
  2963. if (FAILED(hr))
  2964. goto Exit;
  2965. }
  2966. else if (DSN_ACTION_DELAYED == dwDSNType)
  2967. {
  2968. //UE want this three line warning.
  2969. hr = pdsnbuff->HrWriteResource(DELAY_WARNING, LangID);
  2970. if (FAILED(hr))
  2971. goto Exit;
  2972. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2973. if (FAILED(hr))
  2974. goto Exit;
  2975. hr = pdsnbuff->HrWriteResource(DELAY_DO_NOT_SEND, LangID);
  2976. if (FAILED(hr))
  2977. goto Exit;
  2978. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  2979. if (FAILED(hr))
  2980. goto Exit;
  2981. hr = pdsnbuff->HrWriteResource(DELAY_SUMMARY, LangID);
  2982. if (FAILED(hr))
  2983. goto Exit;
  2984. }
  2985. else if (DSN_ACTION_DELIVERED == dwDSNType)
  2986. {
  2987. hr = pdsnbuff->HrWriteResource(DELIVERED_SUMMARY, LangID);
  2988. if (FAILED(hr))
  2989. goto Exit;
  2990. }
  2991. else if (DSN_ACTION_EXPANDED == dwDSNType)
  2992. {
  2993. hr = pdsnbuff->HrWriteResource(EXPANDED_SUMMARY, LangID);
  2994. if (FAILED(hr))
  2995. goto Exit;
  2996. }
  2997. else
  2998. {
  2999. //In retail this will cause an extra blank line to appear in the DSN,
  3000. _ASSERT(0 && "Unsupported DSN Action");
  3001. fWriteRecips = FALSE;
  3002. }
  3003. //Write a list of recipients for this DSN
  3004. if (fWriteRecips)
  3005. {
  3006. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3007. if (FAILED(hr))
  3008. goto Exit;
  3009. hr = HrWriteHumanReadableListOfRecips(pIMailMsgRecipients, pIRecipIter,
  3010. dwDSNType, pdsnbuff);
  3011. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3012. if (FAILED(hr))
  3013. goto Exit;
  3014. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3015. if (FAILED(hr))
  3016. goto Exit;
  3017. }
  3018. //
  3019. // Custom trailer text
  3020. //
  3021. if(wszHRBottomCustomText)
  3022. {
  3023. hr = pdsnbuff->HrWriteModifiedUnicodeString(wszHRBottomCustomText);
  3024. if(FAILED(hr))
  3025. goto Exit;
  3026. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3027. if (FAILED(hr))
  3028. goto Exit;
  3029. }
  3030. if(szHRBottomCustomText)
  3031. {
  3032. hr = pdsnbuff->HrWriteBuffer((BYTE *) szHRBottomCustomText, lstrlenA(szHRBottomCustomText));
  3033. if(FAILED(hr))
  3034. goto Exit;
  3035. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3036. if (FAILED(hr))
  3037. goto Exit;
  3038. }
  3039. //Extra space to have nicer formatting in Outlook 97.
  3040. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3041. if (FAILED(hr))
  3042. goto Exit;
  3043. //Reset resource conversion context to default
  3044. pdsnbuff->ResetConversionContext();
  3045. Exit:
  3046. TraceFunctLeave();
  3047. return hr;
  3048. }
  3049. //---[ CDefaultDSNSink::HrWriteDSNReportPerMsgProperties ]-----------
  3050. //
  3051. //
  3052. // Description:
  3053. // Write the per-msg portion of the DSN Report
  3054. // Parameters:
  3055. // pIMailMsgProperties IMailMsgProperties to generate DSN for
  3056. // pdsnbuff CDSNBuffer to write content to
  3057. // szReportingMTA MTA requesting DSN
  3058. // cbReportingMTA String length of reporting MTA
  3059. // szMimeBoundary MIME boundary for this message
  3060. // cbMimeBoundary Length of MIME boundary
  3061. // Returns:
  3062. // S_OK on success
  3063. // History:
  3064. // 7/6/98 - MikeSwa Created
  3065. //
  3066. //-----------------------------------------------------------------------------
  3067. HRESULT CDefaultDSNSink::HrWriteDSNReportPerMsgProperties(
  3068. IN IMailMsgProperties *pIMailMsgProperties,
  3069. IN CDSNBuffer *pdsnbuff,
  3070. IN LPSTR szReportingMTA,
  3071. IN DWORD cbReportingMTA,
  3072. IN LPSTR szMimeBoundary,
  3073. IN DWORD cbMimeBoundary)
  3074. {
  3075. HRESULT hr = S_OK;
  3076. CHAR szPropBuffer[PROP_BUFFER_SIZE];
  3077. _ASSERT(szReportingMTA && cbReportingMTA);
  3078. //Write properly formatted MIME boundary and report type
  3079. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER,
  3080. sizeof(MIME_DELIMITER)-1);
  3081. if (FAILED(hr))
  3082. goto Exit;
  3083. hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
  3084. if (FAILED(hr))
  3085. goto Exit;
  3086. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_CONTENT_TYPE,
  3087. sizeof(MIME_CONTENT_TYPE)-1);
  3088. if (FAILED(hr))
  3089. goto Exit;
  3090. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_MIME_TYPE, sizeof(DSN_MIME_TYPE)-1);
  3091. if (FAILED(hr))
  3092. goto Exit;
  3093. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3094. if (FAILED(hr))
  3095. goto Exit;
  3096. //Write DSN_HEADER_ENVID if we have it
  3097. hr = pIMailMsgProperties->GetStringA(IMMPID_MP_DSN_ENVID_VALUE,
  3098. PROP_BUFFER_SIZE, szPropBuffer);
  3099. if (SUCCEEDED(hr))
  3100. {
  3101. //Prop found
  3102. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_ENVID,
  3103. sizeof(DSN_HEADER_ENVID)-1);
  3104. if (FAILED(hr))
  3105. goto Exit;
  3106. hr = pdsnbuff->HrWriteBuffer((BYTE *) szPropBuffer, lstrlen(szPropBuffer));
  3107. if (FAILED(hr))
  3108. goto Exit;
  3109. }
  3110. else
  3111. {
  3112. if (MAILMSG_E_PROPNOTFOUND == hr)
  3113. hr = S_OK;
  3114. else
  3115. goto Exit;
  3116. }
  3117. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_REPORTING_MTA,
  3118. sizeof(DSN_HEADER_REPORTING_MTA)-1);
  3119. if (FAILED(hr))
  3120. goto Exit;
  3121. hr = pdsnbuff->HrWriteBuffer((BYTE *) szReportingMTA, cbReportingMTA);
  3122. if (FAILED(hr))
  3123. goto Exit;
  3124. //Write DSN_HEADER_RECEIVED_FROM if we have it
  3125. hr = pIMailMsgProperties->GetStringA(IMMPID_MP_HELO_DOMAIN,
  3126. PROP_BUFFER_SIZE, szPropBuffer);
  3127. if (SUCCEEDED(hr))
  3128. {
  3129. //Prop found
  3130. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_RECEIVED_FROM,
  3131. sizeof(DSN_HEADER_RECEIVED_FROM)-1);
  3132. if (FAILED(hr))
  3133. goto Exit;
  3134. hr = pdsnbuff->HrWriteBuffer((BYTE *) szPropBuffer, lstrlen(szPropBuffer));
  3135. if (FAILED(hr))
  3136. goto Exit;
  3137. }
  3138. else
  3139. {
  3140. if (MAILMSG_E_PROPNOTFOUND == hr)
  3141. hr = S_OK;
  3142. else
  3143. goto Exit;
  3144. }
  3145. //Write DSN_HEADER_ARRIVAL_DATE if we have it
  3146. hr = pIMailMsgProperties->GetStringA(IMMPID_MP_ARRIVAL_TIME,
  3147. PROP_BUFFER_SIZE, szPropBuffer);
  3148. if (SUCCEEDED(hr))
  3149. {
  3150. //Prop found
  3151. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_ARRIVAL_DATE,
  3152. sizeof(DSN_HEADER_ARRIVAL_DATE)-1);
  3153. if (FAILED(hr))
  3154. goto Exit;
  3155. hr = pdsnbuff->HrWriteBuffer((BYTE *) szPropBuffer, lstrlen(szPropBuffer));
  3156. if (FAILED(hr))
  3157. goto Exit;
  3158. }
  3159. else
  3160. {
  3161. if (MAILMSG_E_PROPNOTFOUND == hr)
  3162. hr = S_OK;
  3163. else
  3164. goto Exit;
  3165. }
  3166. Exit:
  3167. return hr;
  3168. }
  3169. //---[ CDefaultDSNSink::HrWriteDSNReportPreRecipientProperties ]-----
  3170. //
  3171. //
  3172. // Description:
  3173. // Write a per-recipient portion of the DSN Report
  3174. // Parameters:
  3175. // pIMailMsgRecipients IMailMsgProperties that DSN is being generated for
  3176. // pdsnbuff CDSNBuffer to write content to
  3177. // iRecip Recipient to generate report for
  3178. // szExpireTime Time (if known) when message expires
  3179. // cbExpireTime size of string
  3180. // dwDSNAction DSN Action to take for this recipient
  3181. // dwRFC821Status Global RFC821 status DWORD
  3182. // hrStatus Global HRESULT status
  3183. // Returns:
  3184. // S_OK on success
  3185. // History:
  3186. // 7/6/98 - MikeSwa Created
  3187. //
  3188. //-----------------------------------------------------------------------------
  3189. HRESULT CDefaultDSNSink::HrWriteDSNReportPreRecipientProperties(
  3190. IN IMailMsgRecipients *pIMailMsgRecipients,
  3191. IN CDSNBuffer *pdsnbuff,
  3192. IN DWORD iRecip,
  3193. IN LPSTR szExpireTime,
  3194. IN DWORD cbExpireTime,
  3195. IN DWORD dwDSNAction,
  3196. IN DWORD dwRFC821Status,
  3197. IN HRESULT hrStatus)
  3198. {
  3199. HRESULT hr = S_OK;
  3200. CHAR szTempBuffer[PROP_BUFFER_SIZE * sizeof(WCHAR)]; // Multiply by sizeof(WCHAR) because
  3201. // we'll also use it for Unicode props
  3202. LPSTR szBuffer = szTempBuffer;
  3203. LPWSTR wszBuffer = (LPWSTR) szTempBuffer;
  3204. CUTF7ConversionContext utf7conv(TRUE);
  3205. CHAR szStatus[STATUS_STRING_SIZE];
  3206. BOOL fFoundDiagnostic = FALSE;
  3207. CHAR szAddressType[PROP_BUFFER_SIZE];
  3208. DWORD cbBuffer = 0;
  3209. //Write blank line between recipient reports (recip fields start with \n)
  3210. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3211. if (FAILED(hr))
  3212. goto Exit;
  3213. //Write DSN_RP_HEADER_ORCPT if we have it
  3214. hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_DSN_ORCPT_VALUE,
  3215. PROP_BUFFER_SIZE, szBuffer);
  3216. if (S_OK == hr) //prop was found
  3217. {
  3218. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ORCPT, sizeof(DSN_RP_HEADER_ORCPT)-1);
  3219. if (FAILED(hr))
  3220. goto Exit;
  3221. //write address value - type should be included in this property
  3222. hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
  3223. if (FAILED(hr))
  3224. goto Exit;
  3225. }
  3226. else if (FAILED(hr))
  3227. {
  3228. if (MAILMSG_E_PROPNOTFOUND == hr)
  3229. hr = S_OK;
  3230. else
  3231. goto Exit;
  3232. }
  3233. //Write DSN_RP_HEADER_FINAL_RECIP
  3234. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_FINAL_RECIP, sizeof(DSN_RP_HEADER_FINAL_RECIP)-1);
  3235. if (FAILED(hr))
  3236. goto Exit;
  3237. //Check for IMMPID_RP_DSN_PRE_CAT_ADDRESS first
  3238. hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_DSN_PRE_CAT_ADDRESS,
  3239. PROP_BUFFER_SIZE, szBuffer);
  3240. if (S_OK == hr) //prop was found
  3241. {
  3242. //write address value - type should be included in this property
  3243. hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
  3244. if (FAILED(hr))
  3245. goto Exit;
  3246. }
  3247. else //we need to use IMMPID_RP_ADDRESS_SMTP instead
  3248. {
  3249. hr = HrGetRecipAddressAndType(pIMailMsgRecipients, iRecip, PROP_BUFFER_SIZE,
  3250. szBuffer, sizeof(szAddressType), szAddressType);
  3251. if (SUCCEEDED(hr))
  3252. {
  3253. //write address type
  3254. hr = pdsnbuff->HrWriteBuffer((BYTE *) szAddressType, lstrlen(szAddressType));
  3255. if (FAILED(hr))
  3256. goto Exit;
  3257. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_TYPE_DELIMITER, sizeof(DSN_HEADER_TYPE_DELIMITER)-1);
  3258. if (FAILED(hr))
  3259. goto Exit;
  3260. //write address value
  3261. hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
  3262. if (FAILED(hr))
  3263. goto Exit;
  3264. }
  3265. else
  3266. {
  3267. _ASSERT(SUCCEEDED(hr) && "Recipient address *must* be present");
  3268. }
  3269. }
  3270. //Write DSN_RP_HEADER_ACTION
  3271. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION, sizeof(DSN_RP_HEADER_ACTION)-1);
  3272. if (FAILED(hr))
  3273. goto Exit;
  3274. if (dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
  3275. {
  3276. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_FAILURE,
  3277. sizeof(DSN_RP_HEADER_ACTION_FAILURE)-1);
  3278. if (FAILED(hr))
  3279. goto Exit;
  3280. }
  3281. else if (dwDSNAction & DSN_ACTION_DELAYED)
  3282. {
  3283. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_DELAYED,
  3284. sizeof(DSN_RP_HEADER_ACTION_DELAYED)-1);
  3285. if (FAILED(hr))
  3286. goto Exit;
  3287. }
  3288. else if (dwDSNAction & DSN_ACTION_RELAYED)
  3289. {
  3290. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_RELAYED,
  3291. sizeof(DSN_RP_HEADER_ACTION_RELAYED)-1);
  3292. if (FAILED(hr))
  3293. goto Exit;
  3294. }
  3295. else if (dwDSNAction & DSN_ACTION_DELIVERED)
  3296. {
  3297. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_DELIVERED,
  3298. sizeof(DSN_RP_HEADER_ACTION_DELIVERED)-1);
  3299. if (FAILED(hr))
  3300. goto Exit;
  3301. }
  3302. else if (dwDSNAction & DSN_ACTION_EXPANDED)
  3303. {
  3304. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_EXPANDED,
  3305. sizeof(DSN_RP_HEADER_ACTION_EXPANDED)-1);
  3306. if (FAILED(hr))
  3307. goto Exit;
  3308. }
  3309. else
  3310. {
  3311. _ASSERT(0 && "No DSN Action requested");
  3312. }
  3313. //Write DSN_RP_HEADER_STATUS
  3314. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_STATUS,
  3315. sizeof(DSN_RP_HEADER_STATUS)-1);
  3316. if (FAILED(hr))
  3317. goto Exit;
  3318. //Get status code
  3319. hr = HrGetStatusCode(pIMailMsgRecipients, iRecip, dwDSNAction,
  3320. dwRFC821Status, hrStatus,
  3321. PROP_BUFFER_SIZE, szBuffer, szStatus);
  3322. if (FAILED(hr))
  3323. goto Exit;
  3324. if (S_OK == hr)
  3325. {
  3326. //found diagnostic code
  3327. fFoundDiagnostic = TRUE;
  3328. }
  3329. hr = pdsnbuff->HrWriteBuffer((BYTE *) szStatus, lstrlen(szStatus));
  3330. if (FAILED(hr))
  3331. goto Exit;
  3332. if (fFoundDiagnostic)
  3333. {
  3334. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_DIAG_CODE,
  3335. sizeof(DSN_RP_HEADER_DIAG_CODE)-1);
  3336. if (FAILED(hr))
  3337. goto Exit;
  3338. //
  3339. // The SMTP response may be CRLF terminated, and we cannot put
  3340. // the CRLF in the DSN since CRLF is the header-separator. So we
  3341. // check for CRLF and strip it... actually since CRLF *must* be
  3342. // the last 2 bytes in the string (if present), and since CR isn't
  3343. // allowed to be part of the SMTP response, we cheat a little and
  3344. // only set the second last byte to NULL if it is CR.
  3345. //
  3346. cbBuffer = lstrlen(szBuffer);
  3347. if(szBuffer[cbBuffer-2] == '\r')
  3348. {
  3349. _ASSERT(szBuffer[cbBuffer-1] == '\n');
  3350. szBuffer[cbBuffer-2] = '\0';
  3351. cbBuffer -= 2; // We chomped the last 2 chars
  3352. }
  3353. hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, cbBuffer);
  3354. if (FAILED(hr))
  3355. goto Exit;
  3356. }
  3357. //Write DSN_RP_HEADER_RETRY_UNTIL using expire time if delay
  3358. if (szExpireTime && (DSN_ACTION_DELAYED & dwDSNAction))
  3359. {
  3360. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_RETRY_UNTIL,
  3361. sizeof(DSN_RP_HEADER_RETRY_UNTIL)-1);
  3362. if (FAILED(hr))
  3363. goto Exit;
  3364. hr = pdsnbuff->HrWriteBuffer((BYTE *) szExpireTime, cbExpireTime);
  3365. if (FAILED(hr))
  3366. goto Exit;
  3367. }
  3368. //Write the X-Display-Name header last
  3369. hr = pIMailMsgRecipients->GetStringW(iRecip, IMMPID_RP_DISPLAY_NAME,
  3370. PROP_BUFFER_SIZE, wszBuffer);
  3371. if ( (hr == S_OK) &&
  3372. (wszBuffer[0] != L'\0') )
  3373. {
  3374. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_DISPLAY_NAME, sizeof(DSN_HEADER_DISPLAY_NAME) - 1);
  3375. if (FAILED(hr))
  3376. goto Exit;
  3377. //
  3378. // Convert the X-Display-Name from UNICODE to RFC 1522. We also replace all
  3379. // whitespace characters in the input with Unicode whitespace (0x20). See
  3380. // documentation of HrWriteModifiedUnicodeBuffer for the reasons.
  3381. //
  3382. pdsnbuff->SetConversionContext(&utf7conv);
  3383. hr = pdsnbuff->HrWriteModifiedUnicodeString(wszBuffer);
  3384. pdsnbuff->ResetConversionContext();
  3385. if (FAILED(hr))
  3386. goto Exit;
  3387. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3388. if (FAILED(hr))
  3389. goto Exit;
  3390. }
  3391. else
  3392. {
  3393. // Not a fatal error if there is no display name...
  3394. hr = S_OK;
  3395. }
  3396. Exit:
  3397. return hr;
  3398. }
  3399. //---[ CDefaultDSNSink::HrLogDSNGenerationEvent ]------------------------------
  3400. //
  3401. //
  3402. // Description:
  3403. // Write a per-recipient portion of the DSN Report
  3404. // Parameters:
  3405. // pIMailMsgRecipients IMailMsgProperties that DSN is being generated for
  3406. // pdsnbuff CDSNBuffer to write content to
  3407. // iRecip Recipient to generate report for
  3408. // szExpireTime Time (if known) when message expires
  3409. // cbExpireTime size of string
  3410. // dwDSNAction DSN Action to take for this recipient
  3411. // dwRFC821Status Global RFC821 status DWORD
  3412. // hrStatus Global HRESULT status
  3413. // Returns:
  3414. // S_OK on success
  3415. // History:
  3416. // 6/12/2000 - dbraun created
  3417. //
  3418. //-----------------------------------------------------------------------------
  3419. HRESULT CDefaultDSNSink::HrLogDSNGenerationEvent(
  3420. ISMTPServer *pISMTPServer,
  3421. IMailMsgProperties *pIMailMsgProperties,
  3422. IN IMailMsgRecipients *pIMailMsgRecipients,
  3423. IN DWORD iRecip,
  3424. IN DWORD dwDSNAction,
  3425. IN DWORD dwRFC821Status,
  3426. IN HRESULT hrStatus)
  3427. {
  3428. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrLogDSNGenerationEvent");
  3429. HRESULT hr = S_OK;
  3430. CHAR szBuffer[PROP_BUFFER_SIZE * sizeof(WCHAR)];
  3431. CHAR szDiagBuffer[PROP_BUFFER_SIZE * sizeof(WCHAR)];
  3432. CHAR szStatus[STATUS_STRING_SIZE];
  3433. CHAR szRecipient[PROP_BUFFER_SIZE * sizeof(WCHAR)];
  3434. CHAR szMessageID[PROP_BUFFER_SIZE * sizeof(WCHAR)];
  3435. CHAR szAddressType[PROP_BUFFER_SIZE];
  3436. ISMTPServerEx *pISMTPServerEx = NULL;
  3437. DWORD cbPropSize = 0;
  3438. const char *rgszSubstrings[] = {
  3439. szStatus,
  3440. szRecipient,
  3441. szMessageID,
  3442. };
  3443. _ASSERT(pISMTPServer);
  3444. // If this is not an NDR, skip it
  3445. if (!(dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL)))
  3446. goto Exit;
  3447. // See if we can QI for ISMTPServerEx
  3448. hr = pISMTPServer->QueryInterface(
  3449. IID_ISMTPServerEx,
  3450. (LPVOID *)&pISMTPServerEx);
  3451. if (FAILED(hr))
  3452. {
  3453. ErrorTrace((LPARAM) pISMTPServer,
  3454. "Unable to QI for ISMTPServerEx 0x%08X",hr);
  3455. pISMTPServerEx = NULL;
  3456. goto Exit;
  3457. }
  3458. // Get the final recipient name
  3459. //Check for IMMPID_RP_DSN_PRE_CAT_ADDRESS first
  3460. hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_DSN_PRE_CAT_ADDRESS,
  3461. PROP_BUFFER_SIZE, szRecipient);
  3462. if (S_OK != hr) // S_OK = prop was found, otherwise ...
  3463. {
  3464. //we need to use IMMPID_RP_ADDRESS_SMTP instead
  3465. hr = HrGetRecipAddressAndType(pIMailMsgRecipients, iRecip, PROP_BUFFER_SIZE,
  3466. szBuffer, sizeof(szAddressType), szAddressType);
  3467. if (SUCCEEDED(hr))
  3468. {
  3469. // Construct the address string
  3470. sprintf(szRecipient, "%s%s%s", szAddressType, DSN_HEADER_TYPE_DELIMITER, szBuffer);
  3471. }
  3472. else
  3473. {
  3474. _ASSERT(SUCCEEDED(hr) && "Recipient address *must* be present");
  3475. goto Exit;
  3476. }
  3477. }
  3478. // Get status code
  3479. hr = HrGetStatusCode(pIMailMsgRecipients, iRecip, dwDSNAction,
  3480. dwRFC821Status, hrStatus,
  3481. PROP_BUFFER_SIZE, szDiagBuffer, szStatus);
  3482. if (FAILED(hr))
  3483. goto Exit;
  3484. // Get the message ID
  3485. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_MSG_ID,
  3486. sizeof(szMessageID), &cbPropSize, (PBYTE) szMessageID);
  3487. if (FAILED(hr))
  3488. goto Exit;
  3489. // Trigger Log Event
  3490. pISMTPServerEx->TriggerLogEvent(
  3491. AQUEUE_E_NDR_GENERATED_EVENT, // Event ID
  3492. TRAN_CAT_CONNECTION_MANAGER, // Category
  3493. 3, // Word count of substring
  3494. rgszSubstrings, // Substring
  3495. EVENTLOG_WARNING_TYPE, // Type of the message
  3496. hrStatus, // error code
  3497. LOGEVENT_LEVEL_MAXIMUM, // Logging level
  3498. szRecipient, // Key to identify this event
  3499. LOGEVENT_FLAG_ALWAYS, // Event logging option
  3500. 0xffffffff, // format string's index in substring
  3501. GetModuleHandle(AQ_MODULE_NAME) // module handle to format a message
  3502. );
  3503. Exit:
  3504. if (pISMTPServerEx) {
  3505. pISMTPServerEx->Release();
  3506. }
  3507. TraceFunctLeave();
  3508. return hr;
  3509. }
  3510. //---[ CDefaultDSNSink::HrWriteDSNClosingAndOriginalMessage ]--------
  3511. //
  3512. //
  3513. // Description:
  3514. // Writes the closing of the DSN as well as the end of the
  3515. // Parameters:
  3516. // pIMailMsgProperties IMailMsgProperties to generate DSN for
  3517. // pIMailMsgPropertiesDSN IMailMsgProperties for DSN
  3518. // pdsnbuff CDSNBuffer to write content to
  3519. // pDestFile PFIO_CONTEXT for destination file
  3520. // dwDSNAction DSN actions for this DSN
  3521. // szMimeBoundary MIME boundary for this message
  3522. // cbMimeBoundary Length of MIME boundary
  3523. // dwDSNRetTypeIN DSN return type
  3524. // dwOrigMsgSize Content size of the original message
  3525. //
  3526. // Returns:
  3527. //
  3528. // History:
  3529. // 7/6/98 - MikeSwa Created
  3530. // 1/6/2000 - MikeSwa Modified to add RET=HDRS support
  3531. //
  3532. //-----------------------------------------------------------------------------
  3533. HRESULT CDefaultDSNSink::HrWriteDSNClosingAndOriginalMessage(
  3534. IN IMailMsgProperties *pIMailMsgProperties,
  3535. IN IMailMsgProperties *pIMailMsgPropertiesDSN,
  3536. IN CDSNBuffer *pdsnbuff,
  3537. IN PFIO_CONTEXT pDestFile,
  3538. IN DWORD dwDSNAction,
  3539. IN LPSTR szMimeBoundary,
  3540. IN DWORD cbMimeBoundary,
  3541. IN DWORD dwDSNRetTypeIN,
  3542. IN DWORD dwOrigMsgSize)
  3543. {
  3544. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteDSNClosingAndOriginalMessage");
  3545. HRESULT hr = S_OK;
  3546. DWORD dwDSNRetType = dwDSNRetTypeIN;
  3547. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3548. if (FAILED(hr))
  3549. goto Exit;
  3550. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
  3551. if (FAILED(hr))
  3552. goto Exit;
  3553. hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
  3554. if (FAILED(hr))
  3555. goto Exit;
  3556. //Write Body content type MIME_CONTENT_TYPE = rfc822
  3557. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_CONTENT_TYPE, sizeof(MIME_CONTENT_TYPE)-1);
  3558. if (FAILED(hr))
  3559. goto Exit;
  3560. if (dwDSNRetType == DSN_RET_PARTIAL_HDRS)
  3561. {
  3562. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADERS_TYPE, sizeof(DSN_HEADERS_TYPE)-1);
  3563. if (FAILED(hr))
  3564. goto Exit;
  3565. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3566. if (FAILED(hr))
  3567. goto Exit;
  3568. hr = HrWriteOriginalMessagePartialHeaders(
  3569. pIMailMsgProperties, pIMailMsgPropertiesDSN,
  3570. pdsnbuff, pDestFile, szMimeBoundary, cbMimeBoundary);
  3571. }
  3572. else
  3573. {
  3574. //
  3575. //$$TODO: Check for DSN_RET_HDRS and implement a function that
  3576. // returns the original headers only
  3577. //
  3578. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RFC822_TYPE, sizeof(DSN_RFC822_TYPE)-1);
  3579. if (FAILED(hr))
  3580. goto Exit;
  3581. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3582. if (FAILED(hr))
  3583. goto Exit;
  3584. hr = HrWriteOriginalMessageFull(
  3585. pIMailMsgProperties, pIMailMsgPropertiesDSN,
  3586. pdsnbuff, pDestFile, szMimeBoundary, cbMimeBoundary,
  3587. dwOrigMsgSize);
  3588. }
  3589. if (FAILED(hr))
  3590. goto Exit;
  3591. Exit:
  3592. TraceFunctLeave();
  3593. return hr;
  3594. }
  3595. //---[ CDefaultDSNSink::HrInitialize ]-------------------------------
  3596. //
  3597. //
  3598. // Description:
  3599. // Performs initialization...
  3600. // - Sets init flag
  3601. // - Currently nothing else
  3602. // Parameters:
  3603. // -
  3604. // Returns:
  3605. // S_OK on SUCCESS
  3606. // History:
  3607. // 7/3/98 - MikeSwa Created
  3608. //
  3609. //-----------------------------------------------------------------------------
  3610. HRESULT CDefaultDSNSink::HrInitialize()
  3611. {
  3612. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrInitialize");
  3613. HRESULT hr = S_OK;
  3614. m_fInit = TRUE;
  3615. srand(GetTickCount());
  3616. TraceFunctLeave();
  3617. return hr;
  3618. }
  3619. //---[ CDefaultDSNSink::GetCurrentMimeBoundary ]---------------------
  3620. //
  3621. //
  3622. // Description:
  3623. // Creates unique MIME-boundary for message.
  3624. //
  3625. // Format we are using for boundary is string versions of the following:
  3626. // MIME_BOUNDARY_CONSTANT
  3627. // FILETIME at start
  3628. // DWORD count of DSNs Requested
  3629. // 16 bytes of our virtual server's domain name
  3630. // Parameters:
  3631. // IN szReportingMTA reporting MTA
  3632. // IN cbReportingMTA String length of reporting MTA
  3633. // IN OUT szMimeBoundary Buffer to put boundary in (size is MIME_BOUNDARY_SIZE)
  3634. // OUT cbMimeBoundary Amount of buffer used for MIME Boundary
  3635. // Returns:
  3636. // -
  3637. // History:
  3638. // 7/6/98 - MikeSwa Created
  3639. //
  3640. //-----------------------------------------------------------------------------
  3641. void CDefaultDSNSink::GetCurrentMimeBoundary(
  3642. IN LPSTR szReportingMTA,
  3643. IN DWORD cbReportingMTA,
  3644. IN OUT CHAR szMimeBoundary[MIME_BOUNDARY_SIZE],
  3645. OUT DWORD *pcbMimeBoundary)
  3646. {
  3647. _ASSERT(MIME_BOUNDARY_RFC2046_LIMIT >= MIME_BOUNDARY_SIZE);
  3648. DWORD iCurrentOffset = 0;
  3649. szMimeBoundary[MIME_BOUNDARY_SIZE-1] = '\0';
  3650. CHAR *pcharCurrent = NULL;
  3651. CHAR *pcharStop = NULL;
  3652. memcpy(szMimeBoundary+iCurrentOffset, MIME_BOUNDARY_CONSTANT,
  3653. sizeof(MIME_BOUNDARY_CONSTANT)-1);
  3654. iCurrentOffset += sizeof(MIME_BOUNDARY_CONSTANT)-1;
  3655. memcpy(szMimeBoundary+iCurrentOffset, m_szPerInstanceMimeBoundary,
  3656. MIME_BOUNDARY_START_TIME_SIZE);
  3657. iCurrentOffset += MIME_BOUNDARY_START_TIME_SIZE;
  3658. wsprintf(szMimeBoundary+iCurrentOffset, "%08X",
  3659. InterlockedIncrement((PLONG) &m_cDSNsRequested));
  3660. iCurrentOffset += 8;
  3661. if (cbReportingMTA >= MIME_BOUNDARY_SIZE-iCurrentOffset)
  3662. {
  3663. memcpy(szMimeBoundary+iCurrentOffset, szReportingMTA,
  3664. MIME_BOUNDARY_SIZE-iCurrentOffset - 1);
  3665. *pcbMimeBoundary = MIME_BOUNDARY_SIZE-1;
  3666. }
  3667. else
  3668. {
  3669. memcpy(szMimeBoundary+iCurrentOffset, szReportingMTA,
  3670. cbReportingMTA);
  3671. szMimeBoundary[iCurrentOffset + cbReportingMTA] = '\0';
  3672. *pcbMimeBoundary = iCurrentOffset + cbReportingMTA;
  3673. }
  3674. //Now we need to verify that the passed in string can be part of a valid
  3675. //MIME Header
  3676. pcharStop = szMimeBoundary + *pcbMimeBoundary;
  3677. for (pcharCurrent = szMimeBoundary + iCurrentOffset;
  3678. pcharCurrent < pcharStop;
  3679. pcharCurrent++)
  3680. {
  3681. if (!fIsValidMIMEBoundaryChar(*pcharCurrent))
  3682. *pcharCurrent = '?'; //turn it into a valid character
  3683. }
  3684. _ASSERT_MIME_BOUNDARY(szMimeBoundary);
  3685. _ASSERT('\0' == szMimeBoundary[MIME_BOUNDARY_SIZE-1]);
  3686. }
  3687. //---[ CDefaultDSNSink::HrWriteOriginalMessageFull ]-----------------
  3688. //
  3689. //
  3690. // Description:
  3691. // Writes the entire original message to the DSN
  3692. // Parameters:
  3693. // pIMailMsgProperties IMailMsgProperties to generate DSN for
  3694. // pIMailMsgPropertiesDSN IMailMsgProperties for DSN
  3695. // pdsnbuff CDSNBuffer to write content to
  3696. // pDestFile PFIO_CONTEXT for destination file
  3697. // szMimeBoundary MIME boundary for this message
  3698. // cbMimeBoundary Length of MIME boundary
  3699. // dwOrigMsgSize Size of original message
  3700. //
  3701. // Returns:
  3702. // S_OK on success
  3703. // History:
  3704. // 1/6/2000 - MikeSwa Created
  3705. //
  3706. //-----------------------------------------------------------------------------
  3707. HRESULT CDefaultDSNSink::HrWriteOriginalMessageFull(
  3708. IN IMailMsgProperties *pIMailMsgProperties,
  3709. IN IMailMsgProperties *pIMailMsgPropertiesDSN,
  3710. IN CDSNBuffer *pdsnbuff,
  3711. IN PFIO_CONTEXT pDestFile,
  3712. IN LPSTR szMimeBoundary,
  3713. IN DWORD cbMimeBoundary,
  3714. IN DWORD dwOrigMsgSize)
  3715. {
  3716. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteOriginalMessageFull");
  3717. HRESULT hr = S_OK;
  3718. DWORD dwFileSize = 0;
  3719. DWORD dwDontCare = 0;
  3720. hr = pdsnbuff->HrSeekForward(dwOrigMsgSize, &dwFileSize);
  3721. if (FAILED(hr))
  3722. goto Exit;
  3723. //Set size hint property on DSN for Queue Admin/Message Tracking
  3724. hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSG_SIZE_HINT,
  3725. dwOrigMsgSize + dwFileSize);
  3726. if (FAILED(hr))
  3727. {
  3728. //We really don't care too much about a failure with this
  3729. ErrorTrace((LPARAM) this, "Error writing size hint 0x%08X", hr);
  3730. hr = S_OK;
  3731. }
  3732. //Write at end of file - *before* file handle is lost to IMailMsg,
  3733. hr = HrWriteMimeClosing(pdsnbuff, szMimeBoundary, cbMimeBoundary, &dwDontCare);
  3734. if (FAILED(hr))
  3735. goto Exit;
  3736. //write body
  3737. hr = pIMailMsgProperties->CopyContentToFileAtOffset(pDestFile, dwFileSize, NULL);
  3738. if (FAILED(hr))
  3739. goto Exit;
  3740. Exit:
  3741. TraceFunctLeave();
  3742. return hr;
  3743. }
  3744. //---[ CDefaultDSNSink::HrWriteOriginalMessagePartialHeaders ]--------------
  3745. //
  3746. //
  3747. // Description:
  3748. // Writes only some headers of the original message to the DSN
  3749. // Parameters:
  3750. // pIMailMsgProperties IMailMsgProperties to generate DSN for
  3751. // pIMailMsgPropertiesDSN IMailMsgProperties for DSN
  3752. // pdsnbuff CDSNBuffer to write content to
  3753. // pDestFile PFIO_CONTEXT for destination file
  3754. // szMimeBoundary MIME boundary for this message
  3755. // cbMimeBoundary Length of MIME boundary
  3756. // Returns:
  3757. // S_OK on success
  3758. // History:
  3759. // 1/6/2000 - MikeSwa Created
  3760. //
  3761. //-----------------------------------------------------------------------------
  3762. HRESULT CDefaultDSNSink::HrWriteOriginalMessagePartialHeaders(
  3763. IN IMailMsgProperties *pIMailMsgProperties,
  3764. IN IMailMsgProperties *pIMailMsgPropertiesDSN,
  3765. IN CDSNBuffer *pdsnbuff,
  3766. IN PFIO_CONTEXT pDestFile,
  3767. IN LPSTR szMimeBoundary,
  3768. IN DWORD cbMimeBoundary)
  3769. {
  3770. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteOriginalMessagePartialHeaders");
  3771. HRESULT hr = S_OK;
  3772. IMailMsgRecipients *pRecips = NULL;
  3773. DWORD dwFileSize = 0;
  3774. DWORD cbPropSize = 0;
  3775. CHAR szPropBuffer[1026] = "";
  3776. //Loop through the 822 properties that we care about and write them
  3777. //to the message. A truely RFC-compliant version would re-parse the
  3778. //messages... and return all the headers
  3779. //
  3780. // From header
  3781. //
  3782. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_FROM_ADDRESS,
  3783. sizeof(szPropBuffer), &cbPropSize, (PBYTE) szPropBuffer);
  3784. if (SUCCEEDED(hr))
  3785. {
  3786. hr = pdsnbuff->HrWriteBuffer((PBYTE)DSN_FROM_HEADER_NO_CRLF,
  3787. sizeof(DSN_FROM_HEADER_NO_CRLF)-1);
  3788. if (FAILED(hr))
  3789. goto Exit;
  3790. hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
  3791. if (FAILED(hr))
  3792. goto Exit;
  3793. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3794. if (FAILED(hr))
  3795. goto Exit;
  3796. }
  3797. //
  3798. // To header
  3799. //
  3800. hr = pdsnbuff->HrWriteBuffer((PBYTE)TO_HEADER_NO_CRLF,
  3801. sizeof(TO_HEADER_NO_CRLF)-1);
  3802. if (FAILED(hr))
  3803. goto Exit;
  3804. hr = pIMailMsgProperties->GetProperty(
  3805. IMMPID_MP_RFC822_TO_ADDRESS,
  3806. sizeof(szPropBuffer),
  3807. &cbPropSize,
  3808. (PBYTE) szPropBuffer);
  3809. if(SUCCEEDED(hr))
  3810. {
  3811. hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
  3812. if (FAILED(hr))
  3813. goto Exit;
  3814. }
  3815. else
  3816. {
  3817. //
  3818. // Write the 821 recipients as 822 recipients
  3819. //
  3820. DWORD dwcRecips = 0;
  3821. DWORD dwCount = 0;
  3822. BOOL fPrintedFirstRecip = FALSE;
  3823. hr = pIMailMsgProperties->QueryInterface(
  3824. IID_IMailMsgRecipients,
  3825. (PVOID *) &pRecips);
  3826. if(FAILED(hr))
  3827. goto Exit;
  3828. hr = pRecips->Count(&dwcRecips);
  3829. if(FAILED(hr))
  3830. goto Exit;
  3831. for(dwCount = 0; dwCount < dwcRecips; dwCount++)
  3832. {
  3833. hr = pRecips->GetStringA(
  3834. dwCount, // Index
  3835. IMMPID_RP_ADDRESS_SMTP,
  3836. sizeof(szPropBuffer),
  3837. szPropBuffer);
  3838. if(SUCCEEDED(hr))
  3839. {
  3840. if(fPrintedFirstRecip)
  3841. {
  3842. hr = pdsnbuff->HrWriteBuffer(
  3843. (PBYTE) ADDRESS_SEPERATOR,
  3844. sizeof(ADDRESS_SEPERATOR)-1);
  3845. if(FAILED(hr))
  3846. goto Exit;
  3847. }
  3848. hr = pdsnbuff->HrWriteBuffer(
  3849. (PBYTE) szPropBuffer,
  3850. lstrlen(szPropBuffer));
  3851. if(FAILED(hr))
  3852. goto Exit;
  3853. fPrintedFirstRecip = TRUE;
  3854. }
  3855. }
  3856. }
  3857. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3858. if (FAILED(hr))
  3859. goto Exit;
  3860. //
  3861. // Message ID
  3862. //
  3863. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_MSG_ID,
  3864. sizeof(szPropBuffer), &cbPropSize, (PBYTE) szPropBuffer);
  3865. if (SUCCEEDED(hr))
  3866. {
  3867. hr = pdsnbuff->HrWriteBuffer((PBYTE)MSGID_HEADER_NO_CRLF,
  3868. sizeof(MSGID_HEADER_NO_CRLF)-1);
  3869. if (FAILED(hr))
  3870. goto Exit;
  3871. hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
  3872. if (FAILED(hr))
  3873. goto Exit;
  3874. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3875. if (FAILED(hr))
  3876. goto Exit;
  3877. }
  3878. //
  3879. // Subject header
  3880. //
  3881. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_MSG_SUBJECT,
  3882. sizeof(szPropBuffer), &cbPropSize, (PBYTE)szPropBuffer);
  3883. if (SUCCEEDED(hr))
  3884. {
  3885. hr = pdsnbuff->HrWriteBuffer((PBYTE)SUBJECT_HEADER_NO_CRLF,
  3886. sizeof(SUBJECT_HEADER_NO_CRLF)-1);
  3887. if (FAILED(hr))
  3888. goto Exit;
  3889. hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
  3890. if (FAILED(hr))
  3891. goto Exit;
  3892. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3893. if (FAILED(hr))
  3894. goto Exit;
  3895. }
  3896. hr = HrWriteMimeClosing(pdsnbuff, szMimeBoundary, cbMimeBoundary, &dwFileSize);
  3897. if (FAILED(hr))
  3898. goto Exit;
  3899. //Set size hint property on DSN for Queue Admin/Message Tracking
  3900. hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSG_SIZE_HINT, dwFileSize);
  3901. if (FAILED(hr))
  3902. {
  3903. //We really don't care too much about a failure with this
  3904. ErrorTrace((LPARAM) this, "Error writing size hint 0x%08X", hr);
  3905. hr = S_OK;
  3906. }
  3907. Exit:
  3908. if(pRecips)
  3909. pRecips->Release();
  3910. TraceFunctLeave();
  3911. return hr;
  3912. }
  3913. //---[ CDefaultDSNSink::HrWriteMimeClosing ]-------------------------
  3914. //
  3915. //
  3916. // Description:
  3917. // Write the MIME closing of the DSN after the 3rd MIME part.
  3918. // Parameters:
  3919. // pdsnbuff CDSNBuffer to write content to
  3920. // szReportingMTA MTA requesting DSN
  3921. // cbReportingMTA String length of reporting MTA
  3922. // szMimeBoundary MIME boundary for this message
  3923. // cbMimeBoundary Length of MIME boundary
  3924. // Returns:
  3925. // S_OK on success
  3926. // Failure code from CDSNBuffer on failure
  3927. // History:
  3928. // 1/6/2000 - MikeSwa Created
  3929. //
  3930. //-----------------------------------------------------------------------------
  3931. HRESULT CDefaultDSNSink::HrWriteMimeClosing(
  3932. IN CDSNBuffer *pdsnbuff,
  3933. IN LPSTR szMimeBoundary,
  3934. IN DWORD cbMimeBoundary,
  3935. OUT DWORD *pcbDSNSize)
  3936. {
  3937. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteMimeClosing");
  3938. HRESULT hr = S_OK;
  3939. hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
  3940. if (FAILED(hr))
  3941. goto Exit;
  3942. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
  3943. if (FAILED(hr))
  3944. goto Exit;
  3945. hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
  3946. if (FAILED(hr))
  3947. goto Exit;
  3948. hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
  3949. if (FAILED(hr))
  3950. goto Exit;
  3951. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
  3952. if (FAILED(hr))
  3953. goto Exit;
  3954. //flush buffers
  3955. hr = pdsnbuff->HrFlushBuffer(pcbDSNSize);
  3956. if (FAILED(hr))
  3957. goto Exit;
  3958. Exit:
  3959. TraceFunctLeave();
  3960. return hr;
  3961. }
  3962. //---[ CDefaultDSNSink::HrGetStatusCode ]----------------------------
  3963. //
  3964. //
  3965. // Description:
  3966. // Determines the status code (and diagnostic code) for a recipient. Will
  3967. // check the following (in order) to determine the status code to return:
  3968. // IMMPID_RP_SMTP_STATUS_STRING (per-recipient diagnostic code)
  3969. // Combination of:
  3970. // IMMPID_RP_RECIPIENT_FLAGS (determine who set the error)
  3971. // IMMPID_RP_ERROR_CODE (per-recipient HRESULT error code)
  3972. // dwDSNAction - kind of DSN being sent
  3973. // Combination of:
  3974. // IMMPID_RP_RECIPIENT_FLAGS (determine who set the error)
  3975. // dwRFC821Status - per message status code
  3976. // dwDSNAction - kind of DSN being sent
  3977. // Combination of:
  3978. // IMMPID_RP_RECIPIENT_FLAGS (determine who set the error)
  3979. // hrStatus - per message HRESULT failure
  3980. // dwDSNAction - kind of DSN being sent
  3981. // Status codes are defined in RFC 1893 as follows:
  3982. // status-code = class "." subject "." detail
  3983. // class = "2"/"4"/"5"
  3984. // subject = 1*3digit
  3985. // detail = 1*3digit
  3986. //
  3987. // Additionally, the class of "2", "4", and "5" correspond to success,
  3988. // transient failure, and hard failure respectively
  3989. // Parameters:
  3990. // pIMailMsgRecipients IMailMsgRecipients of message being DSN'd
  3991. // iRecip The index of the recipient we are looking at
  3992. // dwDSNAction The action code returned by fdwGetDSNAction
  3993. // dwRFC821Status RFC821 Status code returned by SMTP
  3994. // hrStatus HRESULT error if SMTP status is unavailable
  3995. // cbExtendedStatus Size of buffer for diagnostic code
  3996. // szExtendedStatus Buffer for diagnostic code
  3997. // szStatus Buffer for "n.n.n" formatted status code
  3998. // Returns:
  3999. // S_OK Success - found diagnostic code as well
  4000. // S_FALSE Success - but no diagnostic code
  4001. // History:
  4002. // 7/6/98 - MikeSwa Created
  4003. //
  4004. //-----------------------------------------------------------------------------
  4005. HRESULT CDefaultDSNSink::HrGetStatusCode(
  4006. IN IMailMsgRecipients *pIMailMsgRecipients,
  4007. IN DWORD iRecip,
  4008. IN DWORD dwDSNAction,
  4009. IN DWORD dwRFC821Status,
  4010. IN HRESULT hrStatus,
  4011. IN DWORD cbExtendedStatus,
  4012. IN OUT LPSTR szExtendedStatus,
  4013. IN OUT CHAR szStatus[STATUS_STRING_SIZE])
  4014. {
  4015. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrGetStatusCode");
  4016. HRESULT hr = S_OK;
  4017. HRESULT hrPerRecipStatus = S_OK;
  4018. BOOL fFoundDiagnostic = FALSE;
  4019. BOOL fTryToFindStatusCode = FALSE;
  4020. DWORD dwRecipFlags = 0;
  4021. //Check for IMMPID_RP_SMTP_STATUS_STRING on recipient and try to get
  4022. //status code from there
  4023. hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_SMTP_STATUS_STRING,
  4024. cbExtendedStatus, szExtendedStatus);
  4025. if (SUCCEEDED(hr)) //prop was found
  4026. {
  4027. fFoundDiagnostic = TRUE;
  4028. hr = HrGetStatusFromStatus(cbExtendedStatus, szExtendedStatus,
  4029. szStatus);
  4030. if (S_OK == hr)
  4031. goto Exit;
  4032. else if (S_FALSE == hr)
  4033. hr = S_OK; //not really an error... just get code from someplace else
  4034. else
  4035. goto Exit; //other failure
  4036. }
  4037. else if (MAILMSG_E_PROPNOTFOUND == hr)
  4038. {
  4039. //Not really a hard error
  4040. _ASSERT(!fFoundDiagnostic);
  4041. hr = S_OK;
  4042. }
  4043. else
  4044. {
  4045. goto Exit;
  4046. }
  4047. //Get the recipient flags
  4048. hr = pIMailMsgRecipients->GetDWORD(iRecip, IMMPID_RP_RECIPIENT_FLAGS, &dwRecipFlags);
  4049. if(FAILED(hr))
  4050. {
  4051. ErrorTrace((LPARAM)this, "Failure %08lx to get recipient flags for recip %d", hr, iRecip);
  4052. goto Exit;
  4053. }
  4054. //Get Per Recipient HRESULT
  4055. DEBUG_DO_IT(hrPerRecipStatus = 0xFFFFFFFF);
  4056. hr = pIMailMsgRecipients->GetDWORD(iRecip, IMMPID_RP_ERROR_CODE, (DWORD *) &hrPerRecipStatus);
  4057. if (SUCCEEDED(hr))
  4058. {
  4059. _ASSERT((0xFFFFFFFF != hrPerRecipStatus) && "Property not returned by MailMsg!!!");
  4060. hr = HrGetStatusFromContext(hrPerRecipStatus, dwRecipFlags, dwDSNAction, szStatus);
  4061. if (FAILED(hr))
  4062. goto Exit;
  4063. if (lstrcmp(szStatus, DSN_STATUS_FAILED))
  4064. goto Exit;
  4065. //
  4066. // We only found a generic status code (DSN_STATUS_FAILED), see if
  4067. // the global HRESULT or RFC821 status yield anything more specific.
  4068. //
  4069. fTryToFindStatusCode = TRUE;
  4070. }
  4071. else
  4072. {
  4073. if (MAILMSG_E_PROPNOTFOUND != hr)
  4074. goto Exit; // An error occurred getting the per-recip status
  4075. //
  4076. // There is no per-recip status. Fall back to global HRESULT or RFC821
  4077. // status string to try generate a status code.
  4078. //
  4079. fTryToFindStatusCode = TRUE;
  4080. }
  4081. if (fTryToFindStatusCode)
  4082. {
  4083. //
  4084. // We either couldn't generate a status string, or the status string
  4085. // wasn't good enough, try the global HRESULT and RFC822 status to
  4086. // generate a DSN status string.
  4087. //
  4088. hr = HrGetStatusFromRFC821Status(dwRFC821Status, szStatus);
  4089. if (FAILED(hr))
  4090. goto Exit;
  4091. if (S_OK == hr) //got status code from dwRFC821Status
  4092. goto Exit;
  4093. //If all else fails Get status code using global HRESULT & context
  4094. hr = HrGetStatusFromContext(hrStatus, dwRecipFlags, dwDSNAction, szStatus);
  4095. if (FAILED(hr))
  4096. goto Exit;
  4097. }
  4098. Exit:
  4099. if (SUCCEEDED(hr))
  4100. {
  4101. if (fFoundDiagnostic)
  4102. hr = S_OK;
  4103. else
  4104. hr = S_FALSE;
  4105. }
  4106. TraceFunctLeave();
  4107. return hr;
  4108. }
  4109. //---[ CDefaultDSNSink::HrGetStatusFromStatus ]----------------------
  4110. //
  4111. //
  4112. // Description:
  4113. // Parse status code from RFC2034 extended status code string
  4114. //
  4115. // If string is not a complete RFC2034 extended status string, this
  4116. // function will attempt to parse the RFC821 SMTP return code and
  4117. // turn it into an extended status string.
  4118. // Parameters:
  4119. // IN cbExtendedStatus Size of extended status buffer
  4120. // IN szExtendedStatus Extended status buffer
  4121. // IN OUT szStatus RFC1893 formatted status code
  4122. // Returns:
  4123. // S_OK on success
  4124. // S_FALSE if could not be parsed
  4125. // FAILED if other error occurs
  4126. // History:
  4127. // 7/7/98 - MikeSwa Created
  4128. //
  4129. //-----------------------------------------------------------------------------
  4130. HRESULT CDefaultDSNSink::HrGetStatusFromStatus(
  4131. IN DWORD cbExtendedStatus,
  4132. IN OUT LPSTR szExtendedStatus,
  4133. IN OUT CHAR szStatus[STATUS_STRING_SIZE])
  4134. {
  4135. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrGetStatusFromStatus");
  4136. HRESULT hr = S_OK;
  4137. DWORD dwRFC821Status = 0;
  4138. BOOL fFormattedCorrectly = FALSE;
  4139. CHAR *pchStatus = NULL;
  4140. CHAR *pchDiag = NULL; //ptr to status string supplied by SMTP
  4141. DWORD cNumDigits = 0;
  4142. int i = 0;
  4143. //copy status code from diagnostic string in to status code
  4144. pchStatus = szStatus;
  4145. pchDiag = szExtendedStatus;
  4146. //there must be at least 3 characters to attempt parsing
  4147. if (cbExtendedStatus < MIN_CHAR_FOR_VALID_RFC821)
  4148. {
  4149. hr = S_FALSE;
  4150. goto Exit;
  4151. }
  4152. //check RFC822
  4153. if (!((DSN_STATUS_CH_CLASS_SUCCEEDED == *pchDiag) ||
  4154. (DSN_STATUS_CH_CLASS_TRANSIENT == *pchDiag) ||
  4155. (DSN_STATUS_CH_CLASS_FAILED == *pchDiag)))
  4156. {
  4157. //Doesn't start with RFC822... can't be valid
  4158. hr = S_FALSE;
  4159. goto Exit;
  4160. }
  4161. //RFC2034 must have at least RFC822 + " " + "x.x.x" = 10 chanracters
  4162. if (cbExtendedStatus >= MIN_CHAR_FOR_VALID_RFC2034)
  4163. {
  4164. pchDiag += MIN_CHAR_FOR_VALID_RFC821; //format is "xxx x.x.x"
  4165. //Find first digit
  4166. while(isspace((unsigned char)*pchDiag) && pchDiag < (szExtendedStatus + cbExtendedStatus))
  4167. pchDiag++;
  4168. if ((DSN_STATUS_CH_CLASS_SUCCEEDED == *pchDiag) ||
  4169. (DSN_STATUS_CH_CLASS_TRANSIENT == *pchDiag) ||
  4170. (DSN_STATUS_CH_CLASS_FAILED == *pchDiag))
  4171. {
  4172. //copy status code class
  4173. *pchStatus = *pchDiag;
  4174. pchStatus++;
  4175. pchDiag++;
  4176. //Next character must be a DSN_STATUS_CH_DELIMITER
  4177. if (DSN_STATUS_CH_DELIMITER == *pchDiag)
  4178. {
  4179. *pchStatus = DSN_STATUS_CH_DELIMITER;
  4180. pchStatus++;
  4181. pchDiag++;
  4182. //now parse this 1*3digit "." 1*3digit part
  4183. for (i = 0; i < 3; i++)
  4184. {
  4185. *pchStatus = *pchDiag;
  4186. if (!isdigit((unsigned char)*pchDiag))
  4187. {
  4188. if (DSN_STATUS_CH_DELIMITER != *pchDiag)
  4189. {
  4190. fFormattedCorrectly = FALSE;
  4191. break;
  4192. }
  4193. //copy delimiter
  4194. *pchStatus = *pchDiag;
  4195. pchStatus++;
  4196. pchDiag++;
  4197. break;
  4198. }
  4199. pchStatus++;
  4200. pchDiag++;
  4201. fFormattedCorrectly = TRUE; //hace first digit
  4202. }
  4203. if (fFormattedCorrectly) //so far.. so good
  4204. {
  4205. fFormattedCorrectly = FALSE;
  4206. for (i = 0; i < 3; i++)
  4207. {
  4208. *pchStatus = *pchDiag;
  4209. if (!isdigit((unsigned char)*pchDiag))
  4210. {
  4211. if (!isspace((unsigned char)*pchDiag))
  4212. {
  4213. fFormattedCorrectly = FALSE;
  4214. break;
  4215. }
  4216. break;
  4217. }
  4218. pchStatus++;
  4219. pchDiag++;
  4220. fFormattedCorrectly = TRUE;
  4221. }
  4222. //If we have found a good status code... go to exit
  4223. if (fFormattedCorrectly)
  4224. {
  4225. *pchStatus = '\0'; //make sure last CHAR is a NULL
  4226. goto Exit;
  4227. }
  4228. }
  4229. }
  4230. }
  4231. }
  4232. //We haven't been able to parse the extended status code, but we
  4233. //know we have at least a valid RFC822 response string
  4234. //convert to DWORD
  4235. for (i = 0; i < MIN_CHAR_FOR_VALID_RFC821; i++)
  4236. {
  4237. dwRFC821Status *= 10;
  4238. dwRFC821Status += szExtendedStatus[i] - '0';
  4239. }
  4240. hr = HrGetStatusFromRFC821Status(dwRFC821Status, szStatus);
  4241. _ASSERT(S_OK == hr); //this cannot possibly fail
  4242. //The code *should* be valid at this point
  4243. _ASSERT((DSN_STATUS_CH_CLASS_SUCCEEDED == szStatus[0]) ||
  4244. (DSN_STATUS_CH_CLASS_TRANSIENT == szStatus[0]) ||
  4245. (DSN_STATUS_CH_CLASS_FAILED == szStatus[0]));
  4246. hr = S_OK;
  4247. Exit:
  4248. TraceFunctLeave();
  4249. return hr;
  4250. }
  4251. //---[ CDefaultDSNSink::HrGetStatusFromContext ]---------------------
  4252. //
  4253. //
  4254. // Description:
  4255. // Determine status based on supplied context information
  4256. // Parameters:
  4257. // hrRecipient HRESULT for this recipient
  4258. // dwRecipFlags Flags for this recipient
  4259. // dwDSNAction DSN Action for this recipient
  4260. // szStatus Buffer to return status in
  4261. // Returns:
  4262. // S_OK Was able to get a valid status code
  4263. // History:
  4264. // 7/7/98 - MikeSwa Created
  4265. //
  4266. //-----------------------------------------------------------------------------
  4267. HRESULT CDefaultDSNSink::HrGetStatusFromContext(
  4268. IN HRESULT hrRecipient,
  4269. IN DWORD dwRecipFlags,
  4270. IN DWORD dwDSNAction,
  4271. IN OUT CHAR szStatus[STATUS_STRING_SIZE])
  4272. {
  4273. HRESULT hr = S_OK;
  4274. BOOL fValidHRESULT = FALSE;
  4275. BOOL fRecipContext = FALSE;
  4276. int iStatus = 0;
  4277. int i = 0;
  4278. CHAR chStatusClass = DSN_STATUS_CH_INVALID;
  4279. CHAR rgchStatusSubject[3] = {DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID};
  4280. CHAR rgchStatusDetail[3] = {DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID};
  4281. //check to make sure that HRESULT is set according to the type of DSN happening
  4282. if (dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
  4283. {
  4284. if (FAILED(hrRecipient)) //must be a failure code
  4285. fValidHRESULT = TRUE;
  4286. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4287. }
  4288. else if (dwDSNAction & DSN_ACTION_DELAYED)
  4289. {
  4290. if (FAILED(hrRecipient)) //must be a failure code
  4291. fValidHRESULT = TRUE;
  4292. chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
  4293. }
  4294. else if ((dwDSNAction & DSN_ACTION_RELAYED) ||
  4295. (dwDSNAction & DSN_ACTION_DELIVERED) ||
  4296. (dwDSNAction & DSN_ACTION_EXPANDED))
  4297. {
  4298. if (SUCCEEDED(hrRecipient)) //must be a success code
  4299. fValidHRESULT = TRUE;
  4300. chStatusClass = DSN_STATUS_CH_CLASS_SUCCEEDED;
  4301. }
  4302. else
  4303. {
  4304. _ASSERT(0 && "No DSN Action specified");
  4305. }
  4306. //special case HRESULTS
  4307. if (fValidHRESULT)
  4308. {
  4309. switch (hrRecipient)
  4310. {
  4311. case CAT_E_GENERIC: // 5.1.0 - General Cat failure.
  4312. case CAT_E_BAD_RECIPIENT: //5.1.0 - general bad address error
  4313. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
  4314. rgchStatusDetail[0] = '0';
  4315. goto Exit;
  4316. case CAT_E_ILLEGAL_ADDRESS: //5.1.3 - bad address syntax
  4317. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
  4318. rgchStatusDetail[0] = '3';
  4319. goto Exit;
  4320. case CAT_W_SOME_UNDELIVERABLE_MSGS: //5.1.1 - recipient could not be resolved
  4321. case (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)):
  4322. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
  4323. rgchStatusDetail[0] = '1';
  4324. goto Exit;
  4325. case CAT_E_MULTIPLE_MATCHES: //5.1.4 - amiguous address
  4326. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
  4327. rgchStatusDetail[0] = '4';
  4328. goto Exit;
  4329. case PHATQ_E_UNKNOWN_MAILBOX_SERVER: // 5.1.6 -- no homeMDB/msExchHomeServerName
  4330. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
  4331. rgchStatusDetail[0] = '6';
  4332. goto Exit;
  4333. case CAT_E_NO_SMTP_ADDRESS: //5.1.7 - missing address
  4334. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
  4335. rgchStatusDetail[0] = '7';
  4336. goto Exit;
  4337. case AQUEUE_E_MAX_HOP_COUNT_EXCEEDED: //4.4.6
  4338. chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
  4339. case CAT_E_FORWARD_LOOP: //5.4.6
  4340. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
  4341. rgchStatusDetail[0] = '6';
  4342. goto Exit;
  4343. case PHATQ_E_BAD_LOCAL_DOMAIN: //5.4.8
  4344. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
  4345. rgchStatusDetail[0] = '8';
  4346. goto Exit;
  4347. case AQUEUE_E_LOOPBACK_DETECTED: //5.3.5
  4348. //server is configured to loop back on itself
  4349. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4350. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_SYSTEM;
  4351. rgchStatusDetail[0] = '5';
  4352. goto Exit;
  4353. case AQUEUE_E_MSG_EXPIRED: //4.4.7
  4354. chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
  4355. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
  4356. rgchStatusDetail[0] = '7';
  4357. goto Exit;
  4358. case AQUEUE_E_HOST_NOT_RESPONDING: //4.4.1
  4359. chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
  4360. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
  4361. rgchStatusDetail[0] = '1';
  4362. goto Exit;
  4363. case AQUEUE_E_CONNECTION_DROPPED: //4.4.2
  4364. chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
  4365. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
  4366. rgchStatusDetail[0] = '2';
  4367. goto Exit;
  4368. case AQUEUE_E_TOO_MANY_RECIPIENTS: //5.5.3
  4369. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4370. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_PROTOCOL;
  4371. rgchStatusDetail[0] = '3';
  4372. goto Exit;
  4373. case AQUEUE_E_LOCAL_MAIL_REFUSED: //5.2.1
  4374. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4375. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_MAILBOX;
  4376. rgchStatusDetail[0] = '1';
  4377. goto Exit;
  4378. case AQUEUE_E_MESSAGE_TOO_LARGE: //5.2.3
  4379. case AQUEUE_E_LOCAL_QUOTA_EXCEEDED: //5.2.3
  4380. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4381. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_MAILBOX;
  4382. rgchStatusDetail[0] = '3';
  4383. goto Exit;
  4384. case AQUEUE_E_ACCESS_DENIED: //5.7.1
  4385. case AQUEUE_E_SENDER_ACCESS_DENIED: //5.7.1
  4386. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4387. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_POLICY;
  4388. rgchStatusDetail[0] = '1';
  4389. goto Exit;
  4390. case AQUEUE_E_NO_ROUTE: //5.4.4
  4391. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4392. rgchStatusSubject[0] = '4';
  4393. rgchStatusDetail[0] = '4';
  4394. goto Exit;
  4395. case AQUEUE_E_QADMIN_NDR: //4.3.2
  4396. chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
  4397. rgchStatusSubject[0] = '3';
  4398. rgchStatusDetail[0] = '2';
  4399. goto Exit;
  4400. case AQUEUE_E_SMTP_GENERIC_ERROR: //5.4.0
  4401. chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
  4402. rgchStatusSubject[0] = '4';
  4403. rgchStatusDetail[0] = '0';
  4404. goto Exit;
  4405. }
  4406. }
  4407. if ((RP_ERROR_CONTEXT_STORE | RP_ERROR_CONTEXT_CAT | RP_ERROR_CONTEXT_MTA) &
  4408. dwRecipFlags)
  4409. fRecipContext = TRUE;
  4410. //Now look at the context on recipient flags
  4411. //$$TODO - Use HRESULT's for these case as well
  4412. if ((RP_ERROR_CONTEXT_STORE & dwRecipFlags) ||
  4413. (!fRecipContext && (DSN_ACTION_CONTEXT_STORE & dwDSNAction)))
  4414. {
  4415. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_MAILBOX;
  4416. rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
  4417. }
  4418. else if ((RP_ERROR_CONTEXT_CAT & dwRecipFlags) ||
  4419. (!fRecipContext && (DSN_ACTION_CONTEXT_CAT & dwDSNAction)))
  4420. {
  4421. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
  4422. rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
  4423. }
  4424. else if ((RP_ERROR_CONTEXT_MTA & dwRecipFlags) ||
  4425. (!fRecipContext && (DSN_ACTION_CONTEXT_MTA & dwDSNAction)))
  4426. {
  4427. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_PROTOCOL;
  4428. rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
  4429. }
  4430. else
  4431. {
  4432. rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_GENERAL;
  4433. rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
  4434. }
  4435. Exit:
  4436. if (SUCCEEDED(hr))
  4437. {
  4438. //compose szStatus
  4439. _ASSERT(DSN_STATUS_CH_INVALID != chStatusClass);
  4440. _ASSERT(DSN_STATUS_CH_INVALID != rgchStatusSubject[0]);
  4441. _ASSERT(DSN_STATUS_CH_INVALID != rgchStatusDetail[0]);
  4442. szStatus[iStatus] = chStatusClass;
  4443. iStatus++;
  4444. szStatus[iStatus] = DSN_STATUS_CH_DELIMITER;
  4445. iStatus++;
  4446. for (i = 0;
  4447. (i < 3) && (DSN_STATUS_CH_INVALID != rgchStatusSubject[i]);
  4448. i++)
  4449. {
  4450. szStatus[iStatus] = rgchStatusSubject[i];
  4451. iStatus++;
  4452. }
  4453. szStatus[iStatus] = DSN_STATUS_CH_DELIMITER;
  4454. iStatus++;
  4455. for (i = 0;
  4456. (i < 3) && (DSN_STATUS_CH_INVALID != rgchStatusDetail[i]);
  4457. i++)
  4458. {
  4459. szStatus[iStatus] = rgchStatusDetail[i];
  4460. iStatus++;
  4461. }
  4462. szStatus[iStatus] = '\0';
  4463. hr = S_OK;
  4464. }
  4465. return hr;
  4466. }
  4467. //---[ CDefaultDSNSink::HrGetStatusFromRFC821Status ]----------------
  4468. //
  4469. //
  4470. // Description:
  4471. // Attempts to generate a DSN status code from a integer version of a
  4472. // RFC821 response
  4473. // Parameters:
  4474. // IN dwRFC821Status Integer version of RFC821Status
  4475. // IN OUT szStatus Buffer to write status string to
  4476. // Returns:
  4477. // S_OK if valid status that could be converted to dsn status code
  4478. // S_FALSE if status code cannot be converted
  4479. // History:
  4480. // 7/9/98 - MikeSwa Created
  4481. //
  4482. // Note:
  4483. // Eventually, there may be a way to pass extended information in the
  4484. // DWORD to this event. We *could* also encode RFC1893 (x.xxx.xxx format)
  4485. // in a DWORD (in dwRFC821Status) as follows:
  4486. //
  4487. // 0xF 0 000 000
  4488. // | | \-/ \-/
  4489. // | | | +----- detail portion of status code
  4490. // | | +--------- subject portion of status code
  4491. // | +------------ class portion of status code
  4492. // +-------------- mask to distinguish from RFC821 status code
  4493. //
  4494. // For example "2.1.256" could be encoded as 0xF2001256
  4495. //
  4496. // If we do this, we will probably need to expose public functions to
  4497. // compress/uncompress.
  4498. //
  4499. // Yet another possiblity would be to expose an HRESULT facility "RFC1893"
  4500. // Use success, warning, and failed bits to denote the class, and then
  4501. // use the error code space to encode the status codes
  4502. //-----------------------------------------------------------------------------
  4503. HRESULT CDefaultDSNSink::HrGetStatusFromRFC821Status(
  4504. IN DWORD dwRFC821Status,
  4505. IN OUT CHAR szStatus[STATUS_STRING_SIZE])
  4506. {
  4507. HRESULT hr = S_OK;
  4508. //For now, there will be a very simple implementation just converts
  4509. //to 2.0.0, 4.0.0, or 5.0.0, but this function is designed to be
  4510. //the central place that converts RFC821 status codes to DSN (RFC1893)
  4511. //status codes
  4512. _ASSERT((!dwRFC821Status) ||
  4513. (((200 <= dwRFC821Status) && (299 >= dwRFC821Status)) ||
  4514. ((400 <= dwRFC821Status) && (599 >= dwRFC821Status))) &&
  4515. "Invalid Status Code");
  4516. //For now have simplistic mapping of RFC821 status codes
  4517. if ((200 <= dwRFC821Status) && (299 >= dwRFC821Status)) //200 level error
  4518. {
  4519. strcpy(szStatus, DSN_STATUS_SUCCEEDED);
  4520. }
  4521. else if ((400 <= dwRFC821Status) && (499 >= dwRFC821Status)) //400 level error
  4522. {
  4523. strcpy(szStatus, DSN_STATUS_DELAYED);
  4524. }
  4525. else if ((500 <= dwRFC821Status) && (599 >= dwRFC821Status)) //500 level error
  4526. {
  4527. strcpy(szStatus, DSN_STATUS_SMTP_PROTOCOL_ERROR);
  4528. }
  4529. else
  4530. {
  4531. hr = S_FALSE;
  4532. }
  4533. return hr;
  4534. }
  4535. //---[ CDefaultDSNSink::HrWriteHumanReadableListOfRecips ]-----------
  4536. //
  4537. //
  4538. // Description:
  4539. // Writes a list of recipients to the human readable portion
  4540. // Parameters:
  4541. // IN pIMailMsgRecipients Recipients interface
  4542. // IN prpfctxt Recipient filter context for this DSN
  4543. // IN dwDSNActionsNeeded Type of DSN that we are generating
  4544. // IN pdsnbuff DSN buffer to write DSN to
  4545. // Returns:
  4546. // S_OK on success
  4547. // History:
  4548. // 12/15/98 - MikeSwa Created
  4549. //
  4550. //-----------------------------------------------------------------------------
  4551. HRESULT CDefaultDSNSink::HrWriteHumanReadableListOfRecips(
  4552. IN IMailMsgRecipients *pIMailMsgRecipients,
  4553. IN IDSNRecipientIterator *pIRecipIter,
  4554. IN DWORD dwDSNActionsNeeded,
  4555. IN CDSNBuffer *pdsnbuff)
  4556. {
  4557. HRESULT hr = S_OK;
  4558. DWORD iCurrentRecip = 0;
  4559. DWORD dwCurrentRecipFlags = 0;
  4560. DWORD dwCurrentDSNAction = 0;
  4561. CHAR szBuffer[PROP_BUFFER_SIZE];
  4562. CHAR szAddressType[PROP_BUFFER_SIZE];
  4563. hr = pIRecipIter->HrReset();
  4564. if(FAILED(hr))
  4565. goto Exit;
  4566. hr = pIRecipIter->HrGetNextRecipient(
  4567. &iCurrentRecip,
  4568. &dwCurrentDSNAction);
  4569. while (SUCCEEDED(hr))
  4570. {
  4571. if(dwDSNActionsNeeded & dwCurrentDSNAction)
  4572. {
  4573. hr = HrGetRecipAddressAndType(
  4574. pIMailMsgRecipients, iCurrentRecip,
  4575. PROP_BUFFER_SIZE, szBuffer,
  4576. sizeof(szAddressType), szAddressType);
  4577. if (SUCCEEDED(hr))
  4578. {
  4579. //write address value
  4580. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_INDENT, sizeof(DSN_INDENT)-sizeof(CHAR));
  4581. if (FAILED(hr))
  4582. goto Exit;
  4583. hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
  4584. if (FAILED(hr))
  4585. goto Exit;
  4586. #ifdef NEVER
  4587. //Print the recipient flags as well
  4588. wsprintf(szBuffer, " (0x%08X)", dwCurrentRecipFlags);
  4589. pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
  4590. #endif //NEVER
  4591. hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-sizeof(CHAR));
  4592. if (FAILED(hr))
  4593. goto Exit;
  4594. }
  4595. else
  4596. {
  4597. //move along... these are not the error results you are interested in.
  4598. hr = S_OK;
  4599. }
  4600. }
  4601. hr = pIRecipIter->HrGetNextRecipient(
  4602. &iCurrentRecip,
  4603. &dwCurrentDSNAction);
  4604. }
  4605. Exit:
  4606. return hr;
  4607. }
  4608. //---[ CDefaultDSNSink::HrGetRecipAddressAndType ]-------------------
  4609. //
  4610. //
  4611. // Description:
  4612. // Gets the recipient address and returns a pointer to the appropriate
  4613. // string constant for the address type.
  4614. // Parameters:
  4615. // IN pIMailMsgRecipients Ptr To recipients interface
  4616. // IN iRecip Index of the recipient of interest
  4617. // IN cbAddressBuffer Size of buffer for address
  4618. // IN OUT pbAddressBuffer Address buffer to dump address in
  4619. // IN cbAddressType Size of buffer for address type
  4620. // IN OUT pszAddressType Buffer for address type.
  4621. // Returns:
  4622. // S_OK on success
  4623. // MAILMSG_E_PROPNOTFOUND if no address properties could be found
  4624. // History:
  4625. // 12/16/98 - MikeSwa Created
  4626. //
  4627. //-----------------------------------------------------------------------------
  4628. HRESULT CDefaultDSNSink::HrGetRecipAddressAndType(
  4629. IN IMailMsgRecipients *pIMailMsgRecipients,
  4630. IN DWORD iRecip,
  4631. IN DWORD cbAddressBuffer,
  4632. IN OUT LPSTR szAddressBuffer,
  4633. IN DWORD cbAddressType,
  4634. IN OUT LPSTR szAddressType)
  4635. {
  4636. TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrGetRecipAddressAndType");
  4637. HRESULT hr = S_OK;
  4638. BOOL fFoundAddress = FALSE;
  4639. DWORD i = 0;
  4640. LPSTR szDelimiterLocation = NULL;
  4641. CHAR szXDash[] = "x-";
  4642. CHAR chSave = '\0';
  4643. _ASSERT(szAddressType);
  4644. _ASSERT(cbAddressType);
  4645. _ASSERT(cbAddressBuffer);
  4646. _ASSERT(szAddressBuffer);
  4647. _ASSERT(pIMailMsgRecipients);
  4648. szAddressType[0] = '\0';
  4649. szAddressBuffer[0] = '\0';
  4650. for (i = 0; i < NUM_DSN_ADDRESS_PROPERTIES; i ++)
  4651. {
  4652. hr = pIMailMsgRecipients->GetStringA(iRecip, g_rgdwRecipPropIDs[i],
  4653. cbAddressBuffer, szAddressBuffer);
  4654. if (SUCCEEDED(hr))
  4655. {
  4656. fFoundAddress = TRUE;
  4657. strncpy(szAddressType, g_rgszAddressTypes[i], cbAddressType);
  4658. break;
  4659. }
  4660. }
  4661. if (!fFoundAddress)
  4662. {
  4663. hr = MAILMSG_E_PROPNOTFOUND;
  4664. ErrorTrace((LPARAM) this,
  4665. "Unable to find recip %d address for message", iRecip);
  4666. }
  4667. else if (IMMPID_RP_ADDRESS_OTHER == g_rgdwRecipPropIDs[i])
  4668. {
  4669. //Handle special case of IMMPID_RP_ADDRESS_OTHER... we should attempt to
  4670. //parse out address from "type:address" format of IMMPID_RP_ADDRESS_OTHER
  4671. //property
  4672. szDelimiterLocation = strchr(szAddressBuffer, ':');
  4673. if (szDelimiterLocation && cbAddressType > sizeof(szXDash))
  4674. {
  4675. chSave = *szDelimiterLocation;
  4676. *szDelimiterLocation = '\0';
  4677. DebugTrace((LPARAM) this,
  4678. "Found Address type of %s", szAddressBuffer);
  4679. strncpy(szAddressType, szXDash, cbAddressType);
  4680. strncat(szAddressType, szAddressBuffer,
  4681. cbAddressType - (sizeof(szXDash)-sizeof(CHAR)));
  4682. *szDelimiterLocation = chSave;
  4683. }
  4684. else
  4685. {
  4686. ErrorTrace((LPARAM) this,
  4687. "Unable to find address type for address %s", szAddressBuffer);
  4688. }
  4689. }
  4690. DebugTrace((LPARAM) this,
  4691. "Found recipient address %s:%s for recip %d (propery %i:%x)",
  4692. szAddressType, szAddressBuffer, iRecip, i, g_rgdwRecipPropIDs[i]);
  4693. TraceFunctLeave();
  4694. return hr;
  4695. }
  4696. //+------------------------------------------------------------
  4697. //
  4698. // Function: CPostDSNHandler::CPostDSNHandler
  4699. //
  4700. // Synopsis: Constructor. Initialize member variables
  4701. //
  4702. // Arguments:
  4703. // pIUnk: IUnknown to aggregate for refcounting (NOT refcounted internally)
  4704. // pDSNGenerator: CDSNGenerator object
  4705. // pIServerEvent: Interface for triggering events
  4706. // dwVSID: Virtual server instance number
  4707. // pISMTPServer: ISMTPServer interface
  4708. // pIMsgOrig: interface to original message
  4709. // pIAQDSNSubmission: pointer to internal interface for allocing /
  4710. // submitting DSNs
  4711. // pDefaultSink: Default sink pointer
  4712. //
  4713. // Returns: Nothing
  4714. //
  4715. // History:
  4716. // jstamerj 2001/05/14 13:17:34: Created.
  4717. //
  4718. //-------------------------------------------------------------
  4719. CPostDSNHandler::CPostDSNHandler(
  4720. IN IUnknown *pUnk,
  4721. IN CDSNGenerator *pDSNGenerator,
  4722. IN IAQServerEvent *pIServerEvent,
  4723. IN DWORD dwVSID,
  4724. IN ISMTPServer *pISMTPServer,
  4725. IN IMailMsgProperties *pIMsgOrig,
  4726. IN IDSNSubmission *pIAQDSNSubmission,
  4727. IN IDSNGenerationSink *pDefaultSink)
  4728. {
  4729. TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::CPostDSNHandler");
  4730. m_dwSig = SIGNATURE_CPOSTDSNHANDLER;
  4731. m_pUnk = pUnk;
  4732. m_pIDSNProps = NULL;
  4733. m_pDSNGenerator = pDSNGenerator;
  4734. m_pIServerEvent = pIServerEvent;
  4735. m_pIServerEvent->AddRef();
  4736. m_dwVSID = dwVSID;
  4737. m_pISMTPServer = pISMTPServer;
  4738. m_pISMTPServer->AddRef();
  4739. m_pIMsgOrig = pIMsgOrig;
  4740. m_pIMsgOrig->AddRef();
  4741. m_pIAQDSNSubmission = pIAQDSNSubmission;
  4742. m_pIAQDSNSubmission->AddRef();
  4743. m_pDefaultSink = pDefaultSink;
  4744. m_pDefaultSink->AddRef();
  4745. TraceFunctLeaveEx((LPARAM)this);
  4746. } // CPostDSNHandler::CPostDSNHandler
  4747. //+------------------------------------------------------------
  4748. //
  4749. // Function: CPostDSNHandler::~CPostDSNHandler
  4750. //
  4751. // Synopsis: Desctrutor -- cleanup
  4752. //
  4753. // Arguments: NONE
  4754. //
  4755. // Returns: NOTHING
  4756. //
  4757. // History:
  4758. // jstamerj 2001/05/14 13:21:28: Created.
  4759. //
  4760. //-------------------------------------------------------------
  4761. CPostDSNHandler::~CPostDSNHandler()
  4762. {
  4763. TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::~CPostDSNHandler");
  4764. _ASSERT(m_dwSig == SIGNATURE_CPOSTDSNHANDLER);
  4765. if(m_pIServerEvent)
  4766. m_pIServerEvent->Release();
  4767. if(m_pISMTPServer)
  4768. m_pISMTPServer->Release();
  4769. if(m_pIMsgOrig)
  4770. m_pIMsgOrig->Release();
  4771. if(m_pIDSNProps)
  4772. m_pIDSNProps->Release();
  4773. if(m_pIAQDSNSubmission)
  4774. m_pIAQDSNSubmission->Release();
  4775. if(m_pDefaultSink)
  4776. m_pDefaultSink->Release();
  4777. m_dwSig = SIGNATURE_CPOSTDSNHANDLER_INVALID;
  4778. TraceFunctLeaveEx((LPARAM)this);
  4779. } // CPostDSNHandler::~CPostDSNHandler
  4780. //+------------------------------------------------------------
  4781. //
  4782. // Function: CPostDSNHandler::QueryInterface
  4783. //
  4784. // Synopsis: Return an interface to this object
  4785. //
  4786. // Arguments:
  4787. // riid: interface IID requested
  4788. // ppvObj: out param for interface
  4789. //
  4790. // Returns:
  4791. // S_OK: Success
  4792. // E_NOINTERFACE: not supported
  4793. //
  4794. // History:
  4795. // jstamerj 2000/12/08 21:26:14: Created.
  4796. //
  4797. //-------------------------------------------------------------
  4798. HRESULT CPostDSNHandler::QueryInterface(
  4799. IN REFIID riid,
  4800. OUT LPVOID *ppvObj)
  4801. {
  4802. HRESULT hr = S_OK;
  4803. TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::QueryInterface");
  4804. *ppvObj = NULL;
  4805. if(riid == IID_IUnknown)
  4806. {
  4807. *ppvObj = (IUnknown *)this;
  4808. }
  4809. else if(riid == IID_IDSNSubmission)
  4810. {
  4811. *ppvObj = (IDSNSubmission *)this;
  4812. }
  4813. else
  4814. {
  4815. hr = E_NOINTERFACE;
  4816. }
  4817. if(SUCCEEDED(hr))
  4818. AddRef();
  4819. DebugTrace((LPARAM)this, "returning %08lx", hr);
  4820. TraceFunctLeaveEx((LPARAM)this);
  4821. return hr;
  4822. } // CPostDSNHandler::QueryInterface
  4823. //+------------------------------------------------------------
  4824. //
  4825. // Function: CPostDSNHandler::HrAllocBoundMessage
  4826. //
  4827. // Synopsis:
  4828. // Allocates a bound message.
  4829. //
  4830. // Arguments:
  4831. // ppMsg: Out param for Allocated mailmsg
  4832. // phContent: Out param for content handle. Handle is managed by mailmsg
  4833. //
  4834. // Returns:
  4835. // S_OK: Success
  4836. // error from SMTP
  4837. //
  4838. // History:
  4839. // jstamerj 2001/05/11 14:19:09: Created.
  4840. //
  4841. //-------------------------------------------------------------
  4842. HRESULT CPostDSNHandler::HrAllocBoundMessage(
  4843. OUT IMailMsgProperties **ppMsg,
  4844. OUT PFIO_CONTEXT *phContent)
  4845. {
  4846. HRESULT hr = S_OK;
  4847. TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::HrAllocBoundMessage");
  4848. if(m_pIAQDSNSubmission == NULL)
  4849. {
  4850. ErrorTrace((LPARAM)this, "PostDSNHandler called outside of event!");
  4851. hr = E_POINTER;
  4852. goto CLEANUP;
  4853. }
  4854. hr = m_pIAQDSNSubmission->HrAllocBoundMessage(
  4855. ppMsg,
  4856. phContent);
  4857. if(FAILED(hr))
  4858. goto CLEANUP;
  4859. CLEANUP:
  4860. DebugTrace((LPARAM)this, "returning %08lx", hr);
  4861. TraceFunctLeaveEx((LPARAM)this);
  4862. return hr;
  4863. } // CPostDSNHandler::HrAllocBoundMessage
  4864. //+------------------------------------------------------------
  4865. //
  4866. // Function: CPostDSNHandler::HrSubmitDSN
  4867. //
  4868. // Synopsis: Accept a sink generated DSN
  4869. //
  4870. // Arguments:
  4871. // dwDSNAction: Type of DSN
  4872. // cRecipsDSNd: # of recipients DSNd
  4873. // pDSNMsg: The DSN mailmsg
  4874. //
  4875. // Returns:
  4876. // S_OK: Success
  4877. //
  4878. // History:
  4879. // jstamerj 2000/12/08 21:27:56: Created.
  4880. //
  4881. //-------------------------------------------------------------
  4882. HRESULT CPostDSNHandler::HrSubmitDSN(
  4883. IN DWORD dwDSNAction,
  4884. IN DWORD cRecipsDSNd,
  4885. IN IMailMsgProperties *pDSNMsg)
  4886. {
  4887. HRESULT hr = S_OK;
  4888. TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::HrSubmitDSN");
  4889. if(pDSNMsg == NULL)
  4890. {
  4891. hr = E_POINTER;
  4892. goto CLEANUP;
  4893. }
  4894. if(m_pIAQDSNSubmission == NULL)
  4895. {
  4896. ErrorTrace((LPARAM)this, "PostDSNHandler called outside of event!");
  4897. hr = E_POINTER;
  4898. goto CLEANUP;
  4899. }
  4900. //
  4901. // Trigger event
  4902. //
  4903. _ASSERT(m_pDSNGenerator);
  4904. hr = m_pDSNGenerator->HrTriggerPostGenerateDSN(
  4905. m_pIServerEvent,
  4906. m_dwVSID,
  4907. m_pISMTPServer,
  4908. m_pIMsgOrig,
  4909. dwDSNAction,
  4910. cRecipsDSNd,
  4911. pDSNMsg,
  4912. m_pIDSNProps);
  4913. if(FAILED(hr))
  4914. goto CLEANUP;
  4915. _ASSERT(m_pIAQDSNSubmission);
  4916. hr = m_pIAQDSNSubmission->HrSubmitDSN(
  4917. dwDSNAction,
  4918. cRecipsDSNd,
  4919. pDSNMsg);
  4920. if(FAILED(hr))
  4921. goto CLEANUP;
  4922. CLEANUP:
  4923. DebugTrace((LPARAM)this, "returning %08lx", hr);
  4924. TraceFunctLeaveEx((LPARAM)this);
  4925. return hr;
  4926. } // CPostDSNHandler::HrSubmitDSN
  4927. //+------------------------------------------------------------
  4928. //
  4929. // Function: CDSNGenerator::HrStaticInit
  4930. //
  4931. // Synopsis: Initialize static member data
  4932. //
  4933. // Arguments: None
  4934. //
  4935. // Returns:
  4936. // S_OK: Success
  4937. // E_OUTOFMEMORY
  4938. //
  4939. // History:
  4940. // jstamerj 2001/05/11 10:59:26: Created.
  4941. //
  4942. //-------------------------------------------------------------
  4943. HRESULT CDSNGenerator::HrStaticInit()
  4944. {
  4945. HRESULT hr = S_OK;
  4946. TraceFunctEnterEx((LPARAM)0, "CDSNGenerator::HrStaticInit");
  4947. hr = CDSNPool::HrStaticInit();
  4948. if(FAILED(hr))
  4949. goto CLEANUP;
  4950. CLEANUP:
  4951. DebugTrace((LPARAM)0, "returning %08lx", hr);
  4952. TraceFunctLeaveEx((LPARAM)0);
  4953. return hr;
  4954. } // CDSNGenerator::HrStaticInit
  4955. //+------------------------------------------------------------
  4956. //
  4957. // Function: CDSNGenerator::StaticDeinit
  4958. //
  4959. // Synopsis: Deinitialize static data
  4960. //
  4961. // Arguments: None
  4962. //
  4963. // Returns: Nothing
  4964. //
  4965. // History:
  4966. // jstamerj 2001/05/11 11:00:31: Created.
  4967. //
  4968. //-------------------------------------------------------------
  4969. VOID CDSNGenerator::StaticDeinit()
  4970. {
  4971. TraceFunctEnterEx((LPARAM)0, "CDSNGenerator::HrStaticDeinit");
  4972. CDSNPool::StaticDeinit();
  4973. TraceFunctLeaveEx((LPARAM)0);
  4974. } // CDSNGenerator::HrStaticDeinit
  4975. //+------------------------------------------------------------
  4976. //
  4977. // Function: CDSNPool::HrStaticInit
  4978. //
  4979. // Synopsis: Initialize static member data
  4980. //
  4981. // Arguments: None
  4982. //
  4983. // Returns:
  4984. // S_OK: Success
  4985. // E_OUTOFMEMORY
  4986. //
  4987. // History:
  4988. // jstamerj 2001/05/11 11:02:18: Created.
  4989. //
  4990. //-------------------------------------------------------------
  4991. HRESULT CDSNPool::HrStaticInit()
  4992. {
  4993. HRESULT hr = S_OK;
  4994. TraceFunctEnterEx((LPARAM)0, "CDSNPool::HrStaticInit");
  4995. //
  4996. // There can't be more than one DSNGeneration per thread, so 1000
  4997. // objects should be more than enough
  4998. //
  4999. if(!sm_Pool.ReserveMemory(1000, sizeof(CDSNPool)))
  5000. {
  5001. hr = E_OUTOFMEMORY;
  5002. goto CLEANUP;
  5003. }
  5004. CLEANUP:
  5005. DebugTrace((LPARAM)0, "returning %08lx", hr);
  5006. TraceFunctLeaveEx((LPARAM)0);
  5007. return hr;
  5008. } // CDSNPool::HrStaticInit
  5009. //+------------------------------------------------------------
  5010. //
  5011. // Function: CDSNPool::StaticDeinit
  5012. //
  5013. // Synopsis: Deinitialize static member data
  5014. //
  5015. // Arguments: None
  5016. //
  5017. // Returns: Nothing
  5018. //
  5019. // History:
  5020. // jstamerj 2001/05/11 11:37:51: Created.
  5021. //
  5022. //-------------------------------------------------------------
  5023. VOID CDSNPool::StaticDeinit()
  5024. {
  5025. TraceFunctEnterEx((LPARAM)0, "CDSNPool::StaticDeinit");
  5026. sm_Pool.ReleaseMemory();
  5027. TraceFunctLeaveEx((LPARAM)0);
  5028. } // CDSNPool::StaticDeinit