CSmtp.cpp
上传用户:young001
上传日期:2007-07-04
资源大小:33k
文件大小:27k
源码类别:

WEB邮件程序

开发平台:

Visual C++

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