SMTP.cpp
上传用户:hsaozhenyu
上传日期:2007-01-03
资源大小:48k
文件大小:8k
源码类别:

Email客户端

开发平台:

Visual C++

  1. // SMTP.cpp: implementation of the CSMTP class.
  2. // Copyright (c) 1998, Wes Clyburn
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "ZapMail.h"
  6. #include "SMTP.h"
  7. #ifdef _DEBUG
  8. #undef THIS_FILE
  9. static char THIS_FILE[]=__FILE__;
  10. #define new DEBUG_NEW
  11. #endif
  12. // Static member initializers
  13. //
  14. // Note: the order of the entries is important.
  15. //       They must be synchronized with eResponse entries. 
  16. CSMTP::response_code CSMTP::response_table[] =
  17. {
  18. { 250, "SMTP server error" }, // GENERIC_SUCCESS
  19. { 220, "SMTP server not available" }, // CONNECT_SUCCESS
  20. { 354, "SMTP server not ready for data" }, // DATA_SUCCESS
  21. { 221, "SMTP server didn't terminate session" }   // QUIT_SUCCESS
  22. };
  23. //////////////////////////////////////////////////////////////////////
  24. // Construction/Destruction
  25. //////////////////////////////////////////////////////////////////////
  26. CSMTP::CSMTP( LPCTSTR szSMTPServerName, UINT nPort )
  27. {
  28. ASSERT( szSMTPServerName != NULL );
  29. AfxSocketInit();
  30. m_sMailerName = _T( "WC Mail" );
  31. m_sSMTPServerHostName = szSMTPServerName;
  32. m_nPort = nPort;
  33. m_bConnected = FALSE;
  34. m_sError = _T( "OK" );
  35. }
  36. CSMTP::~CSMTP()
  37. {
  38. if( m_bConnected )
  39. Disconnect();
  40. }
  41. CString CSMTP::GetServerHostName()
  42. {
  43. return m_sSMTPServerHostName;
  44. }
  45. BOOL CSMTP::Connect()
  46. {
  47. CString sHello;
  48. TCHAR local_host[ 80 ]; // Warning: arbitrary size
  49. if( m_bConnected )
  50. return TRUE;
  51. if( !m_wsSMTPServer.Create() )
  52. {
  53. m_sError = _T( "Unable to create the socket." );
  54. return FALSE;
  55. }
  56. if( !m_wsSMTPServer.Connect( GetServerHostName(), GetPort() ) )
  57. {
  58. m_sError = _T( "Unable to connect to server" );
  59. m_wsSMTPServer.Close();
  60. return FALSE;
  61. }
  62. if( !get_response( CONNECT_SUCCESS ) )
  63. {
  64. m_sError = _T( "Server didn't respond." );
  65. m_wsSMTPServer.Close();
  66. return FALSE;
  67. }
  68. gethostname( local_host, 80 );
  69. sHello.Format( "HELO %srn", local_host );
  70. m_wsSMTPServer.Send( (LPCTSTR)sHello, sHello.GetLength() );
  71. if( !get_response( GENERIC_SUCCESS ) )
  72. {
  73. m_wsSMTPServer.Close();
  74. return FALSE;
  75. }
  76. m_bConnected = TRUE;
  77. return TRUE;
  78. }
  79. BOOL CSMTP::Disconnect()
  80. {
  81. BOOL ret;
  82. if( !m_bConnected )
  83. return TRUE;
  84. // Disconnect gracefully from the server and close the socket
  85. CString sQuit = _T( "QUITrn" );
  86. m_wsSMTPServer.Send( (LPCTSTR)sQuit, sQuit.GetLength() );
  87. // No need to check return value here.
  88. // If it fails, the message is available with GetLastError
  89. ret = get_response( QUIT_SUCCESS );
  90. m_wsSMTPServer.Close();
  91. m_bConnected = FALSE;
  92. return ret;
  93. }
  94. UINT CSMTP::GetPort()
  95. {
  96. return m_nPort;
  97. }
  98. CString CSMTP::GetMailerName()
  99. {
  100. return m_sMailerName;
  101. }
  102. CString CSMTP::GetLastError()
  103. {
  104. return m_sError;
  105. }
  106. BOOL CSMTP::SendMessage(CMailMessage * msg)
  107. {
  108. ASSERT( msg != NULL );
  109. if( !m_bConnected )
  110. {
  111. m_sError = _T( "Must be connected" );
  112. return FALSE;
  113. }
  114. if( FormatMailMessage( msg ) == FALSE )
  115. {
  116. return FALSE;
  117. }
  118. if( transmit_message( msg ) == FALSE )
  119. {
  120. return FALSE;
  121. }
  122. return TRUE;
  123. }
  124. BOOL CSMTP::FormatMailMessage( CMailMessage* msg )
  125. {
  126. ASSERT( msg != NULL );
  127. if( prepare_header( msg ) == FALSE )
  128. {
  129. return FALSE;
  130. }
  131. // Append a CR/LF to body if necessary.
  132. if( msg->m_sBody.Right( 2 ) != "rn" )
  133. msg->m_sBody += "rn";
  134. return TRUE;
  135. }
  136. void CSMTP::SetServerProperties( LPCTSTR sServerHostName, UINT nPort)
  137. {
  138. ASSERT( sServerHostName != NULL );
  139. // Needs to be safe in non-debug too
  140. if( sServerHostName == NULL )
  141. return;
  142. m_sSMTPServerHostName = sServerHostName;
  143. m_nPort = nPort;
  144. }
  145. // Create the SMTP message header as per RFC822
  146. BOOL CSMTP::prepare_header(CMailMessage * msg)
  147. {
  148. ASSERT( msg != NULL );
  149. CString sTo;
  150. CString sDate;
  151. CString& sHeader = msg->m_sHeader; // Shorthand
  152. if( msg->GetNumRecipients() <= 0 )
  153. {
  154. m_sError = "No Recipients";
  155. return FALSE;
  156. }
  157. sHeader = ""; // Clear it
  158. // Get the recipients into a single string
  159. sTo = "";
  160. CString sEmail = "";
  161. CString sFriendly = "";
  162. for( int i = 0; i < msg->GetNumRecipients(); i++ )
  163. {
  164. msg->GetRecipient( sEmail, sFriendly, i );
  165. sTo += ( i > 0 ? "," : "" );
  166. sTo += sFriendly;
  167. sTo += "<";
  168. sTo += sEmail;
  169. sTo += ">";
  170. }
  171. msg->m_tDateTime = msg->m_tDateTime.GetCurrentTime();
  172. // Format: Mon, 01 Jun 98 01:10:30 GMT
  173. sDate = msg->m_tDateTime.Format( "%a, %d %b %y %H:%M:%S %Z" );
  174. sHeader.Format( "Date: %srn"
  175. "From: %srn"
  176. "To: %srn"
  177. "Subject: %srn"
  178. "X-Mailer: <%s>rnrn",
  179. // Include other extension lines if desired
  180. (LPCTSTR)sDate,
  181. (LPCTSTR)msg->m_sFrom,
  182. (LPCTSTR)sTo,
  183. (LPCTSTR)msg->m_sSubject,
  184. (LPCTSTR)m_sMailerName );
  185. return TRUE;
  186. }
  187. CString CSMTP::prepare_body(CMailMessage * msg)
  188. {
  189. ASSERT( msg != NULL );
  190. CString sTemp;
  191. CString sCooked = "";
  192. LPTSTR szBad = "rn.rn";
  193. LPTSTR szGood = "rn..rn";
  194. int nPos;
  195. int nStart = 0;
  196. int nBadLength = strlen( szBad );
  197. sTemp = msg->m_sBody;
  198. if( sTemp.Left( 3 ) == ".rn" )
  199. sTemp = "." + sTemp;
  200. //
  201. // This is a little inefficient because it beings a search
  202. // at the beginning of the string each time. This was
  203. // the only thing I could think of that handled ALL variations.
  204. // In particular, the sequence "rn.rn.rn" is troublesome. 
  205. // (Even CStringEx's FindReplace wouldn't handle that situation
  206. // with the global flag set.)
  207. //
  208. while( (nPos = sTemp.Find( szBad )) > -1 )
  209. {
  210. sCooked = sTemp.Mid( nStart, nPos );
  211. sCooked += szGood;
  212. sTemp = sCooked + sTemp.Right( sTemp.GetLength() - (nPos + nBadLength) );
  213. }
  214. return sTemp;
  215. }
  216. BOOL CSMTP::transmit_message(CMailMessage * msg)
  217. {
  218. CString sFrom;
  219. CString sTo;
  220. CString sTemp;
  221. CString sEmail;
  222. ASSERT( msg != NULL );
  223. if( !m_bConnected )
  224. {
  225. m_sError = _T( "Must be connected" );
  226. return FALSE;
  227. }
  228. // Send the MAIL command
  229. //
  230. sFrom.Format( "MAIL From: <%s>rn", (LPCTSTR)msg->m_sFrom );
  231. m_wsSMTPServer.Send( (LPCTSTR)sFrom, sFrom.GetLength() );
  232. if( !get_response( GENERIC_SUCCESS ) )
  233. return FALSE;
  234. // Send RCPT commands (one for each recipient)
  235. //
  236. for( int i = 0; i < msg->GetNumRecipients(); i++ )
  237. {
  238. msg->GetRecipient( sEmail, sTemp, i );
  239. sTo.Format( "RCPT TO: <%s>rn", (LPCTSTR)sEmail );
  240. m_wsSMTPServer.Send( (LPCTSTR)sTo, sTo.GetLength() );
  241. get_response( GENERIC_SUCCESS );
  242. }
  243. // Send the DATA command
  244. sTemp = "DATArn";
  245. m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
  246. if( !get_response( DATA_SUCCESS ) )
  247. {
  248. return FALSE;
  249. }
  250. // Send the header
  251. //
  252. m_wsSMTPServer.Send( (LPCTSTR)msg->m_sHeader, msg->m_sHeader.GetLength() );
  253. // Send the body
  254. //
  255. sTemp = prepare_body( msg );
  256. m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
  257. // Signal end of data
  258. //
  259. sTemp = "rn.rn";
  260. m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
  261. if( !get_response( GENERIC_SUCCESS ) )
  262. {
  263. return FALSE;
  264. }
  265. return TRUE;
  266. }
  267. BOOL CSMTP::get_response( UINT response_expected )
  268. {
  269. ASSERT( response_expected >= GENERIC_SUCCESS );
  270. ASSERT( response_expected < LAST_RESPONSE );
  271. CString sResponse;
  272. UINT response;
  273. response_code* pResp; // Shorthand
  274. if( m_wsSMTPServer.Receive( response_buf, RESPONSE_BUFFER_SIZE ) == SOCKET_ERROR )
  275. {
  276. m_sError = _T( "Socket Error" );
  277. return FALSE;
  278. }
  279. sResponse = response_buf;
  280. sscanf( (LPCTSTR)sResponse.Left( 3 ), "%d", &response );
  281. pResp = &response_table[ response_expected ];
  282. if( response != pResp->nResponse )
  283. {
  284. m_sError.Format( "%d:%s", response, (LPCTSTR)pResp->sMessage );
  285. return FALSE;
  286. }
  287. return TRUE;
  288. }