Smtp.cpp
上传用户:dengkfang
上传日期:2008-12-30
资源大小:5233k
文件大小:27k
源码类别:

CA认证

开发平台:

Visual C++

  1. // CSmtp.cpp: implementation of the CSmtp class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "smtp.h"
  6. #ifdef _DEBUG
  7. #undef THIS_FILE
  8. static char THIS_FILE[]=__FILE__;
  9. #define new DEBUG_NEW
  10. #endif
  11. //////////////////////////////////////////////////////////////////////
  12. // CMailMessage
  13. // Formats a message compliant with RFC 822.
  14. //////////////////////////////////////////////////////////////////////
  15. // Construction/Destruction
  16. //////////////////////////////////////////////////////////////////////
  17. CMailMessage::CMailMessage()
  18. {
  19. m_sMailerName = "MiniCA Reg";
  20. SetCharsPerLine(76);
  21. }
  22. CMailMessage::~CMailMessage()
  23. {
  24. }
  25. BOOL CMailMessage::AddRecipient(LPCTSTR szEmailAddress, LPCTSTR szFriendlyName)
  26. {
  27. ASSERT(szEmailAddress != NULL);
  28. ASSERT(szFriendlyName != NULL);
  29. CRecipient to;
  30. to.m_sEmailAddress = szEmailAddress;
  31. to.m_sFriendlyName = szFriendlyName;
  32. m_Recipients.Add(to);
  33. return TRUE;
  34. }
  35. // sEmailAddress and sFriendlyName are OUTPUT parameters.
  36. // If the function fails, it will return FALSE, and the OUTPUT
  37. // parameters will not be touched.
  38. BOOL CMailMessage::GetRecipient(CString & sEmailAddress, CString & sFriendlyName, int nIndex)
  39. {
  40. CRecipient to;
  41. if(nIndex < 0 || nIndex > m_Recipients.GetUpperBound())
  42. return FALSE;
  43. to = m_Recipients[nIndex];
  44. sEmailAddress = to.m_sEmailAddress;
  45. sFriendlyName = to.m_sFriendlyName;
  46. return TRUE;
  47. }
  48. int CMailMessage::GetNumRecipients()
  49. { return m_Recipients.GetSize(); }
  50. BOOL CMailMessage::AddMultipleRecipients(LPCTSTR szRecipients)
  51. {
  52. TCHAR* buf;
  53. UINT pos;
  54. UINT start;
  55. CString sTemp;
  56. CString sEmail;
  57. CString sFriendly;
  58. UINT length;
  59. int nMark;
  60. int nMark2;
  61. ASSERT(szRecipients != NULL);
  62. // Add Recipients
  63. length = strlen(szRecipients);
  64. buf = new TCHAR[length + 1]; // Allocate a work area (don't touch parameter itself)
  65. strcpy(buf, szRecipients);
  66. for(pos = 0, start = 0; pos <= length; pos++) {
  67. if(buf[pos] == ';' || buf[pos] == 0) {
  68. // First, pick apart the sub-strings (separated by ';')
  69. //  Store it in sTemp.
  70. buf[pos] = 0; // Redundant when at the end of string, but who cares.
  71. sTemp = &buf[start];
  72. // Now divide the substring into friendly names and e-mail addresses.
  73. nMark = sTemp.Find('<');
  74. if(nMark >= 0) {
  75. sFriendly = sTemp.Left(nMark);
  76. nMark2 = sTemp.Find('>');
  77. if(nMark2 < nMark) {
  78. delete[] buf;
  79. return FALSE;
  80. }
  81. // End of mark at closing bracket or end of string
  82. nMark2 > -1 ? nMark2 = nMark2 : nMark2 = sTemp.GetLength() - 1;
  83. sEmail = sTemp.Mid(nMark + 1, nMark2 - (nMark + 1));
  84. } else {
  85. sEmail = sTemp;
  86. sFriendly = _T("");
  87. }
  88. AddRecipient(sEmail, sFriendly);
  89. start = pos + 1;
  90. }
  91. }
  92. delete[] buf;
  93. return TRUE;
  94. }
  95. void CMailMessage::FormatMessage()
  96. {
  97. start_header();
  98. prepare_header();
  99. end_header();
  100. prepare_body();
  101. }
  102. void CMailMessage::SetCharsPerLine(UINT nCharsPerLine)
  103. { m_nCharsPerLine = nCharsPerLine; }
  104. UINT CMailMessage::GetCharsPerLine()
  105. {
  106. return m_nCharsPerLine;
  107. }
  108. // Create header as per RFC 822
  109. //
  110. void CMailMessage::prepare_header()
  111. {
  112. CString sTemp;
  113. sTemp = _T("");
  114. // From:
  115. sTemp = _T("From: ") + m_sFrom;
  116. add_header_line((LPCTSTR) sTemp);
  117. // To:
  118. sTemp = _T("To: ");
  119. CString sEmail = _T("");
  120. CString sFriendly = _T("");
  121. for(int i = 0; i < GetNumRecipients(); i++) {
  122. GetRecipient(sEmail, sFriendly, i);
  123. sTemp += (i > 0 ? _T(",") : _T(""));
  124. sTemp += sFriendly;
  125. sTemp += _T("<");
  126. sTemp += sEmail;
  127. sTemp += _T(">");
  128. }
  129. add_header_line((LPCTSTR) sTemp);
  130. // Date:
  131. m_tDateTime = m_tDateTime.GetCurrentTime();
  132. // Format: Mon, 01 Jun 98 01:10:30 GMT
  133. sTemp = _T("Date: ");
  134. sTemp += m_tDateTime.Format("%a, %d %b %y %H:%M:%S %Z");
  135. add_header_line((LPCTSTR) sTemp);
  136. // Subject:
  137. sTemp = _T("Subject: ") + m_sSubject;
  138. add_header_line((LPCTSTR) sTemp);
  139. // X-Mailer
  140. sTemp = _T("X-Mailer: ") + m_sMailerName;
  141. add_header_line((LPCTSTR) sTemp);
  142. }
  143. void CMailMessage::prepare_body()
  144. {
  145. // Append a CR/LF to body if necessary.
  146. if(m_sBody.Right(2) != _T("rn"))
  147. m_sBody += _T("rn");
  148. }
  149. void CMailMessage::start_header()
  150. { m_sHeader = _T(""); }
  151. void CMailMessage::end_header()
  152. { m_sHeader += _T("rn"); }
  153. void CMailMessage::add_header_line(LPCTSTR szHeaderLine)
  154. {
  155. CString sTemp;
  156. sTemp.Format(_T("%srn"), szHeaderLine);
  157. m_sHeader += sTemp;
  158. }
  159. //////////////////////////////////////////////////////////////////////
  160. // CMIMEContentAgent
  161. //////////////////////////////////////////////////////////////////////
  162. CMIMEContentAgent::CMIMEContentAgent(int nMIMEType)
  163. { m_nMIMETypeIHandle = nMIMEType; }
  164. CMIMEContentAgent::~CMIMEContentAgent()
  165. {}
  166. BOOL CMIMEContentAgent::QueryType(int nContentType)
  167. { return nContentType == m_nMIMETypeIHandle ? TRUE : FALSE; }
  168. //////////////////////////////////////////////////////////////////////
  169. // CMIMECode
  170. //////////////////////////////////////////////////////////////////////
  171. CMIMECode::CMIMECode()
  172. {
  173. }
  174. CMIMECode::~CMIMECode()
  175. {
  176. }
  177. //////////////////////////////////////////////////////////////////////
  178. // CBase64
  179. //////////////////////////////////////////////////////////////////////
  180. // Static Member Initializers
  181. //
  182. // The 7-bit alphabet used to encode binary information
  183. CString CBase64::m_sBase64Alphabet = 
  184. _T( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" );
  185. int CBase64::m_nMask[] = { 0, 1, 3, 7, 15, 31, 63, 127, 255 };
  186. CBase64::CBase64()
  187. {
  188. }
  189. CBase64::~CBase64()
  190. {
  191. }
  192. CString CBase64::Encode(LPCTSTR szEncoding, int nSize)
  193. {
  194. CString sOutput = _T( "" );
  195. int nNumBits = 6;
  196. UINT nDigit;
  197. int lp = 0;
  198. ASSERT( szEncoding != NULL );
  199. if( szEncoding == NULL )
  200. return sOutput;
  201. m_szInput = szEncoding;
  202. m_nInputSize = nSize;
  203. m_nBitsRemaining = 0;
  204. nDigit = read_bits( nNumBits, &nNumBits, lp );
  205. while( nNumBits > 0 )
  206. {
  207. sOutput += m_sBase64Alphabet[ (int)nDigit ];
  208. nDigit = read_bits( nNumBits, &nNumBits, lp );
  209. }
  210. // Pad with '=' as per RFC 1521
  211. while( sOutput.GetLength() % 4 != 0 )
  212. {
  213. sOutput += '=';
  214. }
  215. return sOutput;
  216. }
  217. // The size of the output buffer must not be less than
  218. // 3/4 the size of the input buffer. For simplicity,
  219. // make them the same size.
  220. int CBase64::Decode(LPCTSTR szDecoding, LPTSTR szOutput)
  221. {
  222. CString sInput;
  223.     int c, lp =0;
  224. int nDigit;
  225.     int nDecode[ 256 ];
  226. ASSERT( szDecoding != NULL );
  227. ASSERT( szOutput != NULL );
  228. if( szOutput == NULL )
  229. return 0;
  230. if( szDecoding == NULL )
  231. return 0;
  232. sInput = szDecoding;
  233. if( sInput.GetLength() == 0 )
  234. return 0;
  235. // Build Decode Table
  236. //
  237. for( int i = 0; i < 256; i++ ) 
  238. nDecode[i] = -2; // Illegal digit
  239. for( i=0; i < 64; i++ )
  240. {
  241. nDecode[ m_sBase64Alphabet[ i ] ] = i;
  242. nDecode[ m_sBase64Alphabet[ i ] | 0x80 ] = i; // Ignore 8th bit
  243. nDecode[ '=' ] = -1; 
  244. nDecode[ '=' | 0x80 ] = -1; // Ignore MIME padding char
  245.     }
  246. // Clear the output buffer
  247. memset( szOutput, 0, sInput.GetLength() + 1 );
  248. // Decode the Input
  249. //
  250. for( lp = 0, i = 0; lp < sInput.GetLength(); lp++ )
  251. {
  252. c = sInput[ lp ];
  253. nDigit = nDecode[ c & 0x7F ];
  254. if( nDigit < -1 ) 
  255. {
  256. return 0;
  257. else if( nDigit >= 0 ) 
  258. // i (index into output) is incremented by write_bits()
  259. write_bits( nDigit & 0x3F, 6, szOutput, i );
  260.     }
  261. return i;
  262. }
  263. UINT CBase64::read_bits(int nNumBits, int * pBitsRead, int& lp)
  264. {
  265.     ULONG lScratch;
  266.     while( ( m_nBitsRemaining < nNumBits ) && 
  267.    ( lp < m_nInputSize ) ) 
  268. {
  269. int c = m_szInput[ lp++ ];
  270.         m_lBitStorage <<= 8;
  271.         m_lBitStorage |= (c & 0xff);
  272. m_nBitsRemaining += 8;
  273.     }
  274.     if( m_nBitsRemaining < nNumBits ) 
  275. {
  276. lScratch = m_lBitStorage << ( nNumBits - m_nBitsRemaining );
  277. *pBitsRead = m_nBitsRemaining;
  278. m_nBitsRemaining = 0;
  279.     } 
  280. else 
  281. {
  282. lScratch = m_lBitStorage >> ( m_nBitsRemaining - nNumBits );
  283. *pBitsRead = nNumBits;
  284. m_nBitsRemaining -= nNumBits;
  285.     }
  286.     return (UINT)lScratch & m_nMask[nNumBits];
  287. }
  288. void CBase64::write_bits(UINT nBits,
  289.  int nNumBits,
  290.  LPTSTR szOutput,
  291.  int& i)
  292. {
  293. UINT nScratch;
  294. m_lBitStorage = (m_lBitStorage << nNumBits) | nBits;
  295. m_nBitsRemaining += nNumBits;
  296. while( m_nBitsRemaining > 7 ) 
  297. {
  298. nScratch = m_lBitStorage >> (m_nBitsRemaining - 8);
  299. szOutput[ i++ ] = nScratch & 0xFF;
  300. m_nBitsRemaining -= 8;
  301. }
  302. }
  303. //////////////////////////////////////////////////////////////////////
  304. // CAppOctetStream
  305. //////////////////////////////////////////////////////////////////////
  306. // IMPORTANT: The number of bytes we read must be
  307. //  a multiple of 3 because CBase64's Encode()
  308. //  method will append padding characters ('=')
  309. //  to make the output's size a multiple of 4.
  310. //  (Base64 treats 3 8-bit bytes as 4 6-bit 'bytes').
  311. //  MIME decoders are free to treat '=' as a signal
  312. //  that there's no more data, so we don't want to pad
  313. //  until we're supposed to.
  314. // When at the end of the file, the # of bytes read
  315. //  may not be a multiple of 3, but that's okay
  316. //  because we DO want the padding chars then.
  317. #define BYTES_TO_READ 54 // This number guarantess output won't
  318.  // won't exceed line-length limit
  319. CAppOctetStream::CAppOctetStream(int nContentType)
  320. :CMIMEContentAgent(nContentType)
  321. {
  322. }
  323. CAppOctetStream::~CAppOctetStream()
  324. {
  325. }
  326. BOOL CAppOctetStream::AppendPart(LPCTSTR szContent, 
  327.  LPCTSTR szParameters, 
  328.  int nEncoding, 
  329.  BOOL bPath, 
  330.  CString & sDestination)
  331. {
  332. CStdioFile fAttachment;
  333. ASSERT(szContent != NULL);
  334. // This class handles only file attachments, so
  335. // it ignores the bPath parameter.
  336. if(szContent == NULL)
  337. return FALSE;
  338. if(!fAttachment.Open(szContent, (CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary)))
  339. return FALSE;
  340. sDestination += build_sub_header(szContent,
  341.       szParameters,
  342.   nEncoding,
  343.   TRUE);
  344. attach_file(&fAttachment, CMIMEMessage::BASE64, sDestination );
  345. fAttachment.Close();
  346. return TRUE;
  347. }
  348. CString CAppOctetStream::build_sub_header(LPCTSTR szContent, 
  349.   LPCTSTR szParameters, 
  350.   int nEncoding, 
  351.   BOOL bPath)
  352. {
  353. CString sSubHeader;
  354. CString sTemp;
  355. TCHAR szFName[ _MAX_FNAME ];
  356. TCHAR szExt[ _MAX_EXT ];
  357. _tsplitpath( szContent, NULL, NULL, szFName, szExt );
  358. // This class ignores szParameters and nEncoding.
  359. // It controls its own parameters and only handles
  360. // Base64 encoding.
  361. if( bPath )
  362. sTemp.Format( "; file=%s%s", szFName, szExt );
  363. else
  364. sTemp = _T( "" );
  365. sSubHeader.Format( _T( "Content-Type: %s%srn" ),
  366. (LPCTSTR)GetContentTypeString(),
  367. (LPCTSTR)sTemp );
  368. sSubHeader += _T( "Content-Transfer-Encoding: base64rn" );
  369. sTemp.Format( _T( "Content-Disposition: attachment; filename=%s%srn" ),
  370.   szFName, szExt );
  371. sSubHeader += sTemp;
  372. // Signal end of sub-header.
  373. sSubHeader += _T( "rn" ); // Warning: numerous concatenations
  374. // are inefficient.
  375. return sSubHeader;
  376. }
  377. CString CAppOctetStream::GetContentTypeString()
  378. {
  379. CString s;
  380. s = _T( "application/octet-stream" );
  381. return s;
  382. }
  383. // Caller is responsible for opening and closing the file
  384. void CAppOctetStream::attach_file(CStdioFile* pFileAtt, 
  385.   int nEncoding, 
  386.   CString & sDestination)
  387. {
  388. CMIMECode* pEncoder;
  389. int nBytesRead;
  390. TCHAR szBuffer[ BYTES_TO_READ + 1 ];
  391. ASSERT( pFileAtt != NULL );
  392. if( pFileAtt == NULL )
  393. return;
  394. switch( nEncoding )
  395. {
  396. // This class handles only Base64 encoding, but others
  397. //  may be added here.
  398. default:
  399. // Fall through to...
  400. case CMIMEMessage::BASE64:
  401. try 
  402. {
  403. pEncoder = new CBase64;
  404. }
  405. catch( CMemoryException* e )
  406. {
  407. delete e;
  408. return;
  409. }
  410. }
  411. if( pEncoder == NULL ) // Old habits are hard to break
  412. return;
  413. do
  414. {
  415. try
  416. {
  417. nBytesRead = pFileAtt->Read( szBuffer, BYTES_TO_READ );
  418. }
  419. catch( CFileException* e )
  420. {
  421. delete e;
  422. break;
  423. }
  424. szBuffer[ nBytesRead ] = 0; // Terminate the string
  425. sDestination += pEncoder->Encode( szBuffer, nBytesRead );
  426. sDestination += _T( "rn" );
  427. } while( nBytesRead == BYTES_TO_READ );
  428. sDestination += _T( "rn" );
  429. delete pEncoder;
  430. }
  431. //////////////////////////////////////////////////////////////////////
  432. // CTextPlain
  433. //////////////////////////////////////////////////////////////////////
  434. CTextPlain::CTextPlain( int nContentType, UINT nWrapPos )
  435. :CMIMEContentAgent( nContentType )
  436. {
  437. m_nWrapPos = nWrapPos;
  438. }
  439. CTextPlain::~CTextPlain()
  440. {
  441. }
  442. CString CTextPlain::GetContentTypeString()
  443. {
  444. CString s;
  445. s = _T( "text/plain" );
  446. return s;
  447. }
  448. BOOL CTextPlain::AppendPart(LPCTSTR szContent, 
  449. LPCTSTR szParameters, 
  450. int nEncoding, 
  451. BOOL bPath, 
  452. CString & sDestination)
  453. {
  454. CString sSubHeader;
  455. CString sWrapped;
  456. sSubHeader = build_sub_header( szContent,
  457.    szParameters,
  458.    nEncoding,
  459.    bPath );
  460. sWrapped = wrap_text( szContent );
  461. sDestination += (sSubHeader + sWrapped);
  462. return TRUE;
  463. }
  464. CString CTextPlain::build_sub_header(LPCTSTR szContent, 
  465.  LPCTSTR szParameters, 
  466.  int nEncoding, 
  467.  BOOL bPath)
  468. {
  469. CString sSubHeader;
  470. sSubHeader.Format( _T( "Content-Type: %s%srn" ),
  471.    (LPCTSTR)GetContentTypeString(),
  472.    szParameters );
  473. sSubHeader += _T( "Content-Transfer-Encoding: " );
  474. switch( nEncoding )
  475. {
  476. // This class handles only 7bit encoding, but others
  477. //  may be added here.
  478. default:
  479. //Fall through to...
  480. case CMIMEMessage::_7BIT:
  481. sSubHeader += _T( "7Bit" );
  482. }
  483. sSubHeader += _T( "rnrn" );
  484. return sSubHeader;
  485. }
  486. CString CTextPlain::wrap_text(LPCTSTR szText)
  487. {
  488. CString sTemp;
  489. CString sLeft;
  490. CString sRight;
  491. int lp = 0;
  492. UINT nCount = 0;
  493. int nSpacePos = 0;
  494. ASSERT( szText != NULL );
  495. if( szText == NULL )
  496. return sTemp;
  497. sTemp = szText;
  498. while( lp < sTemp.GetLength() )
  499. {
  500. if( sTemp[ lp ] == ' ' )
  501. nSpacePos = lp;
  502. // Reset counter on newline
  503. if( sTemp.Mid( lp, 2 ) == _T( "rn" ) )
  504. nCount = 0;
  505. // Wrap text at last found space
  506. if( nCount > m_nWrapPos )
  507. {
  508. sLeft = sTemp.Left( nSpacePos );
  509. sRight = sTemp.Right( sTemp.GetLength() - nSpacePos );
  510. sLeft.TrimRight();
  511. sRight.TrimLeft();
  512. sLeft += _T( "rn" );
  513. sTemp = sLeft + sRight;
  514. nCount = 0;
  515. }
  516. else
  517. nCount++;
  518. lp++;
  519. }
  520. return sTemp;
  521. }
  522. //////////////////////////////////////////////////////////////////////
  523. // CMIMEMessage
  524. //////////////////////////////////////////////////////////////////////
  525. // Static Member Initializers
  526. CMIMEMessage::CMIMETypeManager CMIMEMessage::m_MIMETypeManager;
  527. CMIMEMessage::CMIMEMessage()
  528. {
  529. m_sMIMEContentType = _T( "multipart/mixed");
  530. m_sPartBoundary = _T( "WC_MAIL_PaRt_BoUnDaRy_05151998" );
  531. m_sNoMIMEText = _T( "This is a multi-part message in MIME format." );
  532. // Register the MIME types handled by this class
  533. //
  534. CMIMEContentAgent* pType;
  535. // These objects are deleted by CMIMTypeManager's destructor
  536. pType = new CTextPlain( TEXT_PLAIN, GetCharsPerLine() );
  537. register_mime_type( pType );
  538. pType = new CAppOctetStream( APPLICATION_OCTETSTREAM );
  539. register_mime_type( pType );
  540. }
  541. CMIMEMessage::~CMIMEMessage()
  542. {
  543. }
  544. // This implementation adds the part to the part-list used
  545. //  to build the body.
  546. BOOL CMIMEMessage::AddMIMEPart(LPCTSTR szContent,
  547.    int nContentType, 
  548.    LPCTSTR szParameters, 
  549.    int nEncoding, 
  550.    BOOL bPath )
  551. {
  552. CMIMEPart part;
  553. part.m_nContentType = nContentType;
  554. part.m_sParameters = szParameters;
  555. part.m_nEncoding = nEncoding;
  556. part.m_bPath = bPath;
  557. part.m_sContent = szContent;
  558. part.m_sContent.TrimLeft();
  559. part.m_sContent.TrimRight();
  560. if( nContentType == TEXT_PLAIN )
  561. m_MIMEPartList.AddHead( part );
  562. else
  563. m_MIMEPartList.AddTail( part );
  564. return TRUE;
  565. }
  566. void CMIMEMessage::prepare_header()
  567. {
  568. CString sTemp;
  569. // Let the base class add its headers
  570. CMailMessage::prepare_header();
  571. add_header_line( _T( "MIME-Version: 1.0" ) );
  572. sTemp.Format( _T( "Content-Type: %s; boundary=%s" ),
  573.   (LPCTSTR)m_sMIMEContentType,
  574.   (LPCTSTR)m_sPartBoundary );
  575. add_header_line( (LPCTSTR)sTemp );
  576. }
  577. void CMIMEMessage::prepare_body()
  578. {
  579. // Class user may have assigned body text directly.
  580. // Convert it to just another MIME part to be processed.
  581. // If this default Content-Type isn't good enough for the
  582. // class user, he or she should have used AddMIMEPart() instead.
  583. if( m_sBody != _T( "" ) )
  584. AddMIMEPart( (LPCTSTR)m_sBody, TEXT_PLAIN, "", _7BIT, FALSE );
  585. // Initialize the body (replace current contents).
  586. m_sBody = m_sNoMIMEText;
  587. m_sBody += _T( "rnrn" );
  588. append_mime_parts();
  589. insert_message_end( m_sBody );
  590. // Let the base class take me to Funky Town
  591. CMailMessage::prepare_body();
  592. }
  593. void CMIMEMessage::insert_boundary( CString& sText )
  594. {
  595. CString sTemp;
  596. if( sText.Right( 2 ) != _T( "rn" ) )
  597. sText += _T( "rn" );
  598. sTemp.Format( _T( "--%srn" ), (LPCTSTR)m_sPartBoundary );
  599. sText += sTemp;
  600. }
  601. void CMIMEMessage::insert_message_end( CString& sText )
  602. {
  603. CString sTemp;
  604. if( sText.Right( 2 ) != _T( "rn" ) )
  605. sText += _T( "rn" );
  606. sTemp.Format( _T( "--%s--rn" ), (LPCTSTR)m_sPartBoundary );
  607. sText += sTemp;
  608. }
  609. void CMIMEMessage::register_mime_type(CMIMEContentAgent* pMIMEType)
  610. {
  611. ASSERT( pMIMEType != NULL );
  612. if( pMIMEType == NULL )
  613. return;
  614. m_MIMETypeManager.RegisterMIMEType( pMIMEType );
  615. }
  616. void CMIMEMessage::append_mime_parts()
  617. {
  618. POSITION part_position;
  619. CMIMEPart* pMIMEPart = NULL;
  620. CMIMEContentAgent* pMIMEType = NULL;
  621. part_position = m_MIMEPartList.GetHeadPosition();
  622. // Get each part from the list, retrieve a handler for it,
  623. //  and let the handler do its thing.
  624. while( part_position != NULL )
  625. {
  626. pMIMEPart = & m_MIMEPartList.GetNext( part_position );
  627. pMIMEType = m_MIMETypeManager.GetHandler( pMIMEPart->m_nContentType );
  628. if( pMIMEType != NULL )
  629. {
  630. insert_boundary( m_sBody );
  631. pMIMEType->AppendPart( pMIMEPart->m_sContent,
  632.    pMIMEPart->m_sParameters,
  633.    pMIMEPart->m_nEncoding,
  634.    pMIMEPart->m_bPath,
  635.    m_sBody );
  636. }
  637. }
  638. }
  639. //////////////////////////////////////////////////////////////////////
  640. // CMIMETypeManager Implementation
  641. //////////////////////////////////////////////////////////////////////
  642. CMIMEMessage::CMIMETypeManager::CMIMETypeManager()
  643. {
  644. }
  645. CMIMEMessage::CMIMETypeManager::~CMIMETypeManager()
  646. {
  647. POSITION pos;
  648. CMIMEContentAgent* p;
  649. m_csAccess.Lock();
  650. pos = m_MIMETypeList.GetHeadPosition();
  651. while( pos != NULL )
  652. {
  653. p = m_MIMETypeList.GetNext( pos );
  654. delete p;
  655. }
  656. }
  657. void CMIMEMessage::CMIMETypeManager::RegisterMIMEType(CMIMEContentAgent *pMIMEType)
  658. {
  659. ASSERT( pMIMEType != NULL );
  660. if( pMIMEType == NULL )
  661. return;
  662. m_csAccess.Lock();
  663. m_MIMETypeList.AddTail( pMIMEType );
  664. }
  665. CMIMEContentAgent* CMIMEMessage::CMIMETypeManager::GetHandler(int nContentType)
  666. {
  667. POSITION pos;
  668. CMIMEContentAgent* pType = NULL;
  669. m_csAccess.Lock();
  670. pos = m_MIMETypeList.GetHeadPosition();
  671. while( pos != NULL )
  672. {
  673. pType = m_MIMETypeList.GetNext( pos );
  674. if( pType->QueryType( nContentType ) == TRUE )
  675. break;
  676. }
  677. return pType;
  678. }
  679. //////////////////////////////////////////////////////////////////////
  680. // Construction/Destruction
  681. //////////////////////////////////////////////////////////////////////
  682. // Static member initializers
  683. //
  684. // Note: the order of the entries is important.
  685. //       They must be synchronized with eResponse entries. 
  686. CSmtp::response_code CSmtp::response_table[] = {
  687. // GENERIC_SUCCESS
  688. {250, _T("SMTP server error")},
  689. // CONNECT_SUCCESS
  690. {220, _T("SMTP server not available")},
  691. // AUTHQUE_SUCCESS
  692. {334, _T("SMTP server authentication error")},
  693. // AUTH_SUCCESS
  694. {235, _T("Error username or password")},
  695. // DATA_SUCCESS
  696. {354, _T("SMTP server not ready for data")},
  697. // QUIT_SUCCESS
  698. {221, _T("SMTP server didn't terminate session")}
  699. };
  700. //////////////////////////////////////////////////////////////////////
  701. // Construction/Destruction
  702. //////////////////////////////////////////////////////////////////////
  703. CSmtp::CSmtp(LPCTSTR szSMTPServerName, UINT nPort)
  704. {
  705. ASSERT(szSMTPServerName != NULL);
  706. AfxSocketInit();
  707. m_sSMTPServerHostName = szSMTPServerName;
  708. m_nPort = nPort;
  709. m_bConnected = FALSE;
  710. m_bAuthed = FALSE;
  711. m_sError = _T("OK");
  712. response_buf = NULL;
  713. }
  714. CSmtp::~CSmtp()
  715. { Disconnect(); }
  716. CString CSmtp::GetServerHostName()
  717. {
  718. return m_sSMTPServerHostName;
  719. }
  720. BOOL CSmtp::Connect()
  721. {
  722. CString sHello;
  723. TCHAR local_host[80]; // Warning: arbitrary size
  724. if(m_bConnected) return TRUE;
  725. try {
  726. // This will be deleted in Disconnect();
  727. response_buf = new TCHAR[RESPONSE_BUFFER_SIZE];
  728. // I can't count on all class users' applications
  729. // to have exception-throwing operator-new implementations,
  730. // so I'll soul-kiss the ones that don't.
  731. if(response_buf == NULL) {
  732. m_sError = _T("Not enough memory");
  733. return FALSE;
  734. }
  735. } catch(CException* e) {
  736. response_buf = NULL;
  737. m_sError = _T("Not enough memory");
  738. delete e;
  739. return FALSE;
  740. }
  741. if(!m_wsSMTPServer.Create()) {
  742. m_sError = _T("Unable to create the socket");
  743. delete response_buf;
  744. response_buf = NULL;
  745. return FALSE;
  746. }
  747. if( !m_wsSMTPServer.Connect(GetServerHostName(), GetPort())) {
  748. m_sError = _T("Unable to connect to server");
  749. m_wsSMTPServer.Close();
  750. delete response_buf;
  751. response_buf = NULL;
  752. return FALSE;
  753. }
  754. if(!get_response(CONNECT_SUCCESS)) {
  755. m_sError = _T( "Server didn't respond" );
  756. m_wsSMTPServer.Close();
  757. delete response_buf;
  758. response_buf = NULL;
  759. return FALSE;
  760. }
  761. gethostname(local_host, 80);
  762. sHello.Format(_T( "HELO %srn" ), local_host);
  763. m_wsSMTPServer.Send((LPCTSTR)sHello, sHello.GetLength());
  764. if(!get_response(GENERIC_SUCCESS)) {
  765. m_wsSMTPServer.Close();
  766. delete response_buf;
  767. response_buf = NULL;
  768. return FALSE;
  769. }
  770. m_bConnected = TRUE;
  771. return TRUE;
  772. }
  773. BOOL CSmtp::Disconnect()
  774. {
  775. BOOL ret;
  776. if(!m_bConnected) return TRUE;
  777. // Disconnect gracefully from the server and close the socket
  778. CString sQuit = _T("QUITrn");
  779. m_wsSMTPServer.Send((LPCTSTR)sQuit, sQuit.GetLength());
  780. // No need to check return value here.
  781. // If it fails, the message is available with GetLastError
  782. ret = get_response(QUIT_SUCCESS);
  783. m_wsSMTPServer.Close();
  784. if(response_buf != NULL) {
  785. delete[] response_buf;
  786. response_buf = NULL;
  787. }
  788. m_bConnected = FALSE;
  789. return ret;
  790. }
  791. UINT CSmtp::GetPort()
  792. { return m_nPort; }
  793. CString CSmtp::GetLastError()
  794. { return m_sError; }
  795. BOOL CSmtp::SendMessage(CMailMessage* msg)
  796. {
  797. ASSERT(msg != NULL);
  798. if(!m_bConnected) {
  799. m_sError = _T("Must be connected");
  800. return FALSE;
  801. }
  802. if(!m_bAuthed){
  803. m_sError = _T("Must be Authed");
  804. return FALSE;
  805. }
  806. if(FormatMailMessage(msg) == FALSE) {
  807. return FALSE;
  808. }
  809. if(transmit_message(msg) == FALSE) {
  810. return FALSE;
  811. }
  812. return TRUE;
  813. }
  814. BOOL CSmtp::FormatMailMessage(CMailMessage* msg)
  815. {
  816. ASSERT(msg != NULL);
  817. if(msg -> GetNumRecipients() == 0) {
  818. m_sError = _T("No Recipients");
  819. return FALSE;
  820. }
  821. msg -> FormatMessage();
  822. return TRUE;
  823. }
  824. void CSmtp::SetServerProperties(LPCTSTR szSMTPServerName, UINT nPort)
  825. {
  826. ASSERT(szSMTPServerName != NULL);
  827. // Needs to be safe in non-debug too
  828. if(szSMTPServerName == NULL) return;
  829. m_sSMTPServerHostName = szSMTPServerName;
  830. m_nPort = nPort;
  831. }
  832. CString CSmtp::cook_body(CMailMessage* msg)
  833. {
  834. ASSERT(msg != NULL);
  835. CString sTemp;
  836. CString sCooked = _T("");
  837. LPTSTR szBad = _T("rn.rn");
  838. LPTSTR szGood = _T("rn..rn");
  839. int nPos;
  840. int nStart = 0;
  841. int nBadLength = strlen(szBad);
  842. sTemp = msg -> m_sBody;
  843. if(sTemp.Left(3) == _T(".rn"))
  844. sTemp = _T(".") + sTemp;
  845. //
  846. // This is a little inefficient because it beings a search
  847. // at the beginning of the string each time. This was
  848. // the only thing I could think of that handled ALL variations.
  849. // In particular, the sequence "rn.rn.rn" is troublesome. 
  850. // (Even CStringEx's FindReplace wouldn't handle that situation
  851. // with the global flag set.)
  852. //
  853. while((nPos = sTemp.Find(szBad)) > -1) {
  854. sCooked = sTemp.Mid(nStart, nPos);
  855. sCooked += szGood;
  856. sTemp = sCooked + sTemp.Right(sTemp.GetLength() - (nPos + nBadLength));
  857. }
  858. return sTemp;
  859. }
  860. BOOL CSmtp::transmit_message(CMailMessage* msg)
  861. {
  862. CString sFrom;
  863. CString sTo;
  864. CString sTemp;
  865. CString sEmail;
  866. ASSERT(msg != NULL);
  867. if(!m_bConnected) {
  868. m_sError = _T("Must be connected");
  869. return FALSE;
  870. }
  871. if(!m_bAuthed){
  872. m_sError = _T("Must be Authed");
  873. return FALSE;
  874. }
  875. // Send the MAIL command
  876. sFrom.Format(_T( "MAIL From: <%s>rn" ), (LPCTSTR)msg->m_sFrom);
  877. m_wsSMTPServer.Send((LPCTSTR)sFrom, sFrom.GetLength());
  878. if(!get_response(GENERIC_SUCCESS)){
  879. m_sError = _T("Error sender address");
  880. return FALSE;
  881. }
  882. // Send RCPT commands (one for each recipient)
  883. for(int i = 0; i < msg->GetNumRecipients(); i++) {
  884. msg->GetRecipient(sEmail, sTemp, i);
  885. sTo.Format(_T("RCPT TO: <%s>rn"), (LPCTSTR)sEmail);
  886. m_wsSMTPServer.Send((LPCTSTR)sTo, sTo.GetLength());
  887. get_response(GENERIC_SUCCESS);
  888. }
  889. // Send the DATA command
  890. sTemp = _T("DATArn");
  891. m_wsSMTPServer.Send((LPCTSTR)sTemp, sTemp.GetLength());
  892. if( !get_response(DATA_SUCCESS)) {
  893. return FALSE;
  894. }
  895. // Send the header
  896. m_wsSMTPServer.Send((LPCTSTR)msg -> m_sHeader, msg -> m_sHeader.GetLength());
  897. // Send the body
  898. sTemp = cook_body(msg);
  899. m_wsSMTPServer.Send((LPCTSTR)sTemp, sTemp.GetLength());
  900. // Signal end of data
  901. sTemp = _T("rn.rn");
  902. m_wsSMTPServer.Send((LPCTSTR)sTemp, sTemp.GetLength());
  903. if( !get_response(GENERIC_SUCCESS)) {
  904. return FALSE;
  905. }
  906. return TRUE;
  907. }
  908. BOOL CSmtp::get_response(UINT response_expected)
  909. {
  910. ASSERT(response_expected >= GENERIC_SUCCESS);
  911. ASSERT(response_expected < LAST_RESPONSE);
  912. CString sResponse;
  913. UINT response;
  914. response_code* pResp; // Shorthand
  915. if(m_wsSMTPServer.Receive(response_buf, RESPONSE_BUFFER_SIZE) == SOCKET_ERROR) {
  916. m_sError = _T("Socket Error");
  917. return FALSE;
  918. }
  919. sResponse = response_buf;
  920. sscanf((LPCTSTR)sResponse.Left(3), _T("%d"), &response);
  921. pResp = &response_table[response_expected];
  922. if(response != pResp -> nResponse) {
  923. m_sError.Format( _T("%d:%s"), response, (LPCTSTR)pResp -> sMessage);
  924. return FALSE;
  925. }
  926. return TRUE;
  927. }
  928. BOOL CSmtp::Auth()
  929. {
  930. CString sAuth;
  931. if(!m_bConnected) return FALSE;
  932. sAuth.Format(_T( "auth loginrn" )); //construct auth quest
  933. m_wsSMTPServer.Send((LPCTSTR)sAuth, sAuth.GetLength());
  934. if(!get_response(AUTHQUE_SUCCESS)) {
  935. m_sError="SMTP server with no auth";
  936. m_bAuthed=TRUE;
  937. return TRUE;
  938. }
  939. sAuth.Empty();
  940. sAuth.Format(_T( "%srn" ), m_User); //m_User is an string encoded with CBASE64
  941. m_wsSMTPServer.Send((LPCTSTR)sAuth, sAuth.GetLength());
  942. if(!get_response(AUTHQUE_SUCCESS)) {
  943. m_wsSMTPServer.Close();
  944. delete response_buf;
  945. response_buf = NULL;
  946. return FALSE;
  947. }
  948. sAuth.Empty();
  949. sAuth.Format(_T( "%srn" ), m_Pass); //m_sSMTPPass is an string encoded with CBASE64
  950. m_wsSMTPServer.Send((LPCTSTR)sAuth, sAuth.GetLength());
  951. if(!get_response(AUTH_SUCCESS)) {
  952. m_wsSMTPServer.Close();
  953. delete response_buf;
  954. response_buf = NULL;
  955. return FALSE;
  956. }
  957. m_bAuthed = TRUE;
  958. return TRUE;
  959. }