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

Ftp客户端

开发平台:

Visual C++

  1. /*
  2. Module : SMTP.CPP
  3. Purpose: Implementation for a MFC class encapsulation of the SMTP protocol
  4. Created: PJN / 22-05-1998
  5. History: PJN / 15-06-1998 1) Fixed the case where a single dot occurs on its own
  6.                           in the body of a message
  7. 2) Class now supports Reply-To Header Field
  8.                           3) Class now supports file attachments
  9.  PJN / 18-06-1998 1) Fixed a memory overwrite problem which was occurring 
  10.                   with the buffer used for encoding base64 attachments
  11.          PJN / 27-06-1998 1) The case where a line begins with a "." but contains
  12.                           other text is now also catered for. See RFC821, Section 4.5.2
  13.                           for further details.
  14.                           2) m_sBody in CSMTPMessage has now been made protected.
  15.                           Client applications now should call AddBody instead. This
  16.                           ensures that FixSingleDot is only called once even if the 
  17.                           same message is sent a number of times.
  18.                           3) Fixed a number of problems with how the MIME boundaries
  19.                           were defined and sent.
  20.                           4) Got rid of an unreferenced formal parameter 
  21.                           compiler warning when doing a release build
  22. Copyright (c) 1998 by PJ Naughter.  
  23. All rights reserved.
  24. */
  25. //////////////// Includes ////////////////////////////////////////////
  26. #include "stdafx.h"
  27. #include "smtp.h"
  28. //////////////// Macros / Locals /////////////////////////////////////
  29. #ifdef _DEBUG
  30. #define new DEBUG_NEW
  31. #undef THIS_FILE
  32. static char THIS_FILE[] = __FILE__;
  33. #endif
  34. char CSMTPAttachment::m_base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  35.                                       "abcdefghijklmnopqrstuvwxyz0123456789+/";
  36. #define BASE64_MAXLINE  76
  37. #define EOL  "rn"
  38. //////////////// Implementation //////////////////////////////////////
  39. CSMTPSocket::CSMTPSocket()
  40. {
  41.   m_hSocket = INVALID_SOCKET; //default to an invalid scoket descriptor
  42. }
  43. CSMTPSocket::~CSMTPSocket()
  44. {
  45.   Close();
  46. }
  47. BOOL CSMTPSocket::Create()
  48. {
  49.   m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
  50.   return (m_hSocket != INVALID_SOCKET);
  51. }
  52. BOOL CSMTPSocket::Connect(LPCTSTR pszHostAddress, int nPort)
  53. {
  54. //For correct operation of the T2A macro, see MFC Tech Note 59
  55. USES_CONVERSION;
  56.   //must have been created first
  57.   ASSERT(m_hSocket != INVALID_SOCKET);
  58.   
  59. LPSTR lpszAscii = T2A((LPTSTR)pszHostAddress);
  60. //Determine if the address is in dotted notation
  61. SOCKADDR_IN sockAddr;
  62. ZeroMemory(&sockAddr, sizeof(sockAddr));
  63. sockAddr.sin_family = AF_INET;
  64. sockAddr.sin_port = htons((u_short)nPort);
  65. sockAddr.sin_addr.s_addr = inet_addr(lpszAscii);
  66. //If the address is not dotted notation, then do a DNS 
  67. //lookup of it.
  68. if (sockAddr.sin_addr.s_addr == INADDR_NONE)
  69. {
  70. LPHOSTENT lphost;
  71. lphost = gethostbyname(lpszAscii);
  72. if (lphost != NULL)
  73. sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;
  74. else
  75. {
  76. WSASetLastError(WSAEINVAL); 
  77. return FALSE;
  78. }
  79. }
  80. //Call the protected version which takes an address 
  81. //in the form of a standard C style struct.
  82. return Connect((SOCKADDR*)&sockAddr, sizeof(sockAddr));
  83. }
  84. BOOL CSMTPSocket::Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen)
  85. {
  86. return (connect(m_hSocket, lpSockAddr, nSockAddrLen) != SOCKET_ERROR);
  87. }
  88. BOOL CSMTPSocket::Send(LPCSTR pszBuf, int nBuf)
  89. {
  90.   //must have been created first
  91.   ASSERT(m_hSocket != INVALID_SOCKET);
  92.   return (send(m_hSocket, pszBuf, nBuf, 0) != SOCKET_ERROR);
  93. }
  94. int CSMTPSocket::Receive(LPSTR pszBuf, int nBuf)
  95. {
  96.   //must have been created first
  97.   ASSERT(m_hSocket != INVALID_SOCKET);
  98.   return recv(m_hSocket, pszBuf, nBuf, 0); 
  99. }
  100. void CSMTPSocket::Close()
  101. {
  102. if (m_hSocket != INVALID_SOCKET)
  103. {
  104. VERIFY(SOCKET_ERROR != closesocket(m_hSocket));
  105. m_hSocket = INVALID_SOCKET;
  106. }
  107. }
  108. BOOL CSMTPSocket::IsReadible(BOOL& bReadible)
  109. {
  110.   timeval timeout = {0, 0};
  111.   fd_set fds;
  112.   FD_ZERO(&fds);
  113.   FD_SET(m_hSocket, &fds);
  114.   int nStatus = select(0, &fds, NULL, NULL, &timeout);
  115.   if (nStatus == SOCKET_ERROR)
  116.   {
  117.     return FALSE;
  118.   }
  119.   else
  120.   {
  121.     bReadible = !(nStatus == 0);
  122.     return TRUE;
  123.   }
  124. }
  125. CSMTPAddress::CSMTPAddress(const CString& sAddress) : m_sEmailAddress(sAddress) 
  126. {
  127. }
  128. CSMTPAddress::CSMTPAddress(const CString& sFriendly, const CString& sAddress) : 
  129.               m_sFriendlyName(sFriendly), m_sEmailAddress(sAddress) 
  130. {
  131. }
  132. CSMTPAddress& CSMTPAddress::operator=(const CSMTPAddress& r) 
  133.   m_sFriendlyName = r.m_sFriendlyName; 
  134. m_sEmailAddress = r.m_sEmailAddress; 
  135. return *this;
  136. }
  137. CString CSMTPAddress::GetRegularFormat() const
  138. {
  139.   ASSERT(!m_sEmailAddress.IsEmpty()); //Email Address must be valid
  140.   CString sAddress;
  141.   if (m_sFriendlyName.IsEmpty())
  142.     sAddress = m_sEmailAddress;  //Just transfer the address across directly
  143.   else
  144.     sAddress.Format(_T("%s <%s>"), m_sFriendlyName, m_sEmailAddress);
  145.   return sAddress;
  146. }
  147. CSMTPAttachment::CSMTPAttachment()
  148. {
  149.   m_pszEncoded = NULL;
  150.   m_nEncodedSize = 0;
  151. }
  152. CSMTPAttachment::~CSMTPAttachment()
  153. {
  154.   //free up any memory we allocated
  155.   if (m_pszEncoded)
  156. {
  157.     delete [] m_pszEncoded;
  158. m_pszEncoded = NULL;
  159. }
  160. }
  161. BOOL CSMTPAttachment::Attach(const CString& sFilename)
  162. {
  163.   ASSERT(sFilename.GetLength());  //Empty Filename !
  164.   //free up any memory we previously allocated
  165.   if (m_pszEncoded)
  166.   {
  167.     delete [] m_pszEncoded;
  168.     m_pszEncoded = NULL;
  169.   }
  170.   //determine the file size
  171.   CFileStatus fs;
  172.   if (!CFile::GetStatus(sFilename, fs))
  173.     return FALSE;
  174.   //open up the file for reading in
  175.   CFile infile;
  176.   if (!infile.Open(sFilename, CFile::modeRead | CFile::shareDenyWrite))
  177.     return FALSE;
  178.   //read in the contents of the input file
  179.   char* pszIn = new char[fs.m_size];
  180.   infile.Read(pszIn, fs.m_size);
  181.   //allocate the encoded buffer
  182.   int nOutSize = Base64BufferSize(fs.m_size);
  183.   m_pszEncoded = new char[nOutSize];
  184.   //Do the encoding
  185.   EncodeBase64(pszIn, fs.m_size, m_pszEncoded, nOutSize, &m_nEncodedSize);
  186.   //delete the input buffer
  187.   delete [] pszIn;
  188.   //Close the input file
  189.   infile.Close();
  190. //Hive away the filename
  191.   TCHAR sPath[_MAX_PATH];
  192.   TCHAR sFname[_MAX_FNAME];
  193.   TCHAR sExt[_MAX_EXT];
  194.   _tsplitpath(sFilename, NULL, NULL, sFname, sExt);
  195.   _tmakepath(sPath, NULL, NULL, sFname, sExt);
  196. m_sFilename = sPath;
  197.   return TRUE;
  198. }
  199. int CSMTPAttachment::Base64BufferSize(int nInputSize)
  200. {
  201.   int nOutSize = (nInputSize+2)/3*4;                    // 3:4 conversion ratio
  202.   nOutSize += strlen(EOL)*nOutSize/BASE64_MAXLINE + 3;  // Space for newlines and NUL
  203.   return nOutSize;
  204. }
  205. BOOL CSMTPAttachment::EncodeBase64(const char* pszIn, int nInLen, char* pszOut, int nOutSize, int* nOutLen)
  206. {
  207.   //Input Parameter validation
  208.   ASSERT(pszIn);
  209.   ASSERT(pszOut);
  210.   ASSERT(nOutSize);
  211.   ASSERT(nOutSize >= Base64BufferSize(nInLen));
  212. #ifndef _DEBUG
  213.   //justs get rid of "unreferenced formal parameter"
  214.   //compiler warning when doing a release build
  215.   nOutSize;
  216. #endif
  217.   //Set up the parameters prior to the main encoding loop
  218.   int nInPos  = 0;
  219.   int nOutPos = 0;
  220.   int nLineLen = 0;
  221.   // Get three characters at a time from the input buffer and encode them
  222.   for (int i=0; i<nInLen/3; ++i) 
  223.   {
  224.     //Get the next 2 characters
  225.     int c1 = pszIn[nInPos++] & 0xFF;
  226.     int c2 = pszIn[nInPos++] & 0xFF;
  227.     int c3 = pszIn[nInPos++] & 0xFF;
  228.     //Encode into the 4 6 bit characters
  229.     pszOut[nOutPos++] = m_base64tab[(c1 & 0xFC) >> 2];
  230.     pszOut[nOutPos++] = m_base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
  231.     pszOut[nOutPos++] = m_base64tab[((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6)];
  232.     pszOut[nOutPos++] = m_base64tab[c3 & 0x3F];
  233.     nLineLen += 4;
  234.     //Handle the case where we have gone over the max line boundary
  235.     if (nLineLen >= BASE64_MAXLINE-3) 
  236.     {
  237.       char* cp = EOL;
  238.       pszOut[nOutPos++] = *cp++;
  239.       if (*cp) 
  240.         pszOut[nOutPos++] = *cp;
  241.       nLineLen = 0;
  242.     }
  243.   }
  244.   // Encode the remaining one or two characters in the input buffer
  245.   char* cp;
  246.   switch (nInLen % 3) 
  247.   {
  248.     case 0:
  249.     {
  250.       cp = EOL;
  251.       pszOut[nOutPos++] = *cp++;
  252.       if (*cp) 
  253.         pszOut[nOutPos++] = *cp;
  254.       break;
  255.     }
  256.     case 1:
  257.     {
  258.       int c1 = pszIn[nInPos] & 0xFF;
  259.       pszOut[nOutPos++] = m_base64tab[(c1 & 0xFC) >> 2];
  260.       pszOut[nOutPos++] = m_base64tab[((c1 & 0x03) << 4)];
  261.       pszOut[nOutPos++] = '=';
  262.       pszOut[nOutPos++] = '=';
  263.       cp = EOL;
  264.       pszOut[nOutPos++] = *cp++;
  265.       if (*cp) 
  266.         pszOut[nOutPos++] = *cp;
  267.       break;
  268.     }
  269.     case 2:
  270.     {
  271.       int c1 = pszIn[nInPos++] & 0xFF;
  272.       int c2 = pszIn[nInPos] & 0xFF;
  273.       pszOut[nOutPos++] = m_base64tab[(c1 & 0xFC) >> 2];
  274.       pszOut[nOutPos++] = m_base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
  275.       pszOut[nOutPos++] = m_base64tab[((c2 & 0x0F) << 2)];
  276.       pszOut[nOutPos++] = '=';
  277.       cp = EOL;
  278.       pszOut[nOutPos++] = *cp++;
  279.       if (*cp) 
  280.         pszOut[nOutPos++] = *cp;
  281.       break;
  282.     }
  283.     default: 
  284.     {
  285.       ASSERT(FALSE); 
  286.       break;
  287.     }
  288.   }
  289.   pszOut[nOutPos] = 0;
  290.   *nOutLen = nOutPos;
  291.   return TRUE;
  292. }
  293. CSMTPMessage::CSMTPMessage(LPCSTR sXMailer, BOOL bWithDate, BOOL bWithXMailer)
  294. {
  295.   m_sXMailer = sXMailer;
  296.   m_bWithDate = bWithDate;
  297.   m_bWithXMailer = bWithXMailer;
  298. }
  299. CSMTPMessage::~CSMTPMessage()
  300. {
  301. }
  302. int CSMTPMessage::AddRecipient(CSMTPAddress& recipient)
  303. {
  304.   return m_Recipients.Add(recipient);
  305. }
  306. void CSMTPMessage::RemoveRecipient(int nIndex)
  307. {
  308. m_Recipients.RemoveAt(nIndex);
  309. }
  310. CSMTPAddress CSMTPMessage::GetRecipient(int nIndex) const
  311. {
  312.   return m_Recipients.GetAt(nIndex);
  313. }
  314. int CSMTPMessage::GetNumberOfRecipients() const
  315. {
  316. return m_Recipients.GetSize();
  317. }
  318. int CSMTPMessage::AddAttachment(CSMTPAttachment* pAttachment)
  319. {
  320.   ASSERT(pAttachment->GetFilename().GetLength()); //an empty filename !
  321. return m_Attachments.Add(pAttachment);
  322. }
  323. void CSMTPMessage::RemoveAttachment(int nIndex)
  324. {
  325. m_Attachments.RemoveAt(nIndex);
  326. }
  327. CSMTPAttachment* CSMTPMessage::GetAttachment(int nIndex) const
  328. {
  329. return m_Attachments.GetAt(nIndex);
  330. }
  331. int CSMTPMessage::GetNumberOfAttachments() const
  332. {
  333. return m_Attachments.GetSize();
  334. }
  335. CString CSMTPMessage::GetHeader() const
  336. {
  337.   //Create the "Date:" part of the header
  338.   CTime now(CTime::GetCurrentTime());
  339.   CString sDate(now.Format(_T("%a, %d %b %Y %H:%M:%S %Z")));
  340.   //Create the "To:" part of the header
  341.   CString sTo;
  342.   for (int i=0; i<GetNumberOfRecipients(); i++)
  343.   {
  344.     CSMTPAddress recipient = GetRecipient(i);
  345.     if (i)
  346.     sTo += _T(",");
  347.     sTo += recipient.GetRegularFormat();
  348.   }
  349. /*
  350. CString sBuf;
  351. sBuf.Format(_T("From: %srn")
  352.               _T("To: %srn")
  353. _T("Subject: %srn")
  354. _T("Date: %srn")
  355. _T("X-Mailer: %srn"), 
  356. m_From.GetRegularFormat(),
  357.               sTo, 
  358. m_sSubject,
  359. sDate,
  360. m_sXMailer);
  361. */
  362. //Stick everything together
  363. CString sBuf;
  364. sBuf.Format(_T("From: %srnTo: %srnSubject: %srn"),
  365. m_From.GetRegularFormat(), sTo, m_sSubject);
  366.   if(m_bWithDate)
  367.     sBuf += _T("Date: ") + sDate + _T("rn");            // add current date
  368.   if(m_bWithXMailer)
  369.     sBuf += _T("X-Mailer: ") + m_sXMailer + _T("rn");   // add email program
  370. //Add the optional Reply-To Field
  371. if (m_ReplyTo.m_sEmailAddress.GetLength())
  372. {
  373. CString sReply;
  374. sReply.Format(_T("Reply-To: %srn"), m_ReplyTo.GetRegularFormat());
  375. sBuf += sReply;
  376. }
  377. //Add the optional fields if attachments are included
  378. if (m_Attachments.GetSize())
  379. sBuf += _T("MIME-Version: 1.0rnContent-type: multipart/mixed; boundary="#BOUNDARY#"rn");
  380. sBuf += _T("rn");
  381. //Return the result
  382. return sBuf;
  383. }
  384. void CSMTPMessage::FixSingleDot(CString& sBody)
  385. {
  386.   int nFind = sBody.Find(_T("n."));
  387.   if (nFind != -1)
  388.   {
  389.   CString sLeft(sBody.Left(nFind+1));
  390.   CString sRight(sBody.Right(sBody.GetLength()-(nFind+1)));
  391.   FixSingleDot(sRight);
  392.   sBody = sLeft + _T(".") + sRight;
  393.   }
  394. }
  395. void CSMTPMessage::AddBody(const CString& sBody)
  396. {
  397.   m_sBody = sBody;
  398.   //Fix the case of a single dot on a line in the message body
  399.   FixSingleDot(m_sBody);
  400. }
  401. CSMTPConnection::CSMTPConnection()
  402. {
  403.   m_bConnected = FALSE;
  404. #ifdef _DEBUG
  405.   m_dwTimeout = 20000; //default timeout of 20 seconds when debugging
  406. #else
  407.   m_dwTimeout = 2000;  //default timeout of 2 seconds for normal release code
  408. #endif
  409. }
  410. CSMTPConnection::~CSMTPConnection()
  411. {
  412.   if (m_bConnected)
  413.     Disconnect();
  414. }
  415. BOOL CSMTPConnection::Connect(LPCTSTR pszHostName, int nPort)
  416. {
  417. //For correct operation of the T2A macro, see MFC Tech Note 59
  418. USES_CONVERSION;
  419. //paramater validity checking
  420.   ASSERT(pszHostName);
  421.   //Create the socket
  422.   if (!m_SMTP.Create())
  423.   {
  424.     TRACE(_T("Failed to create client socketn"));
  425.     return FALSE;
  426.   }
  427.   //Connect to the SMTP Host
  428.   if (!m_SMTP.Connect(pszHostName, nPort))
  429.   {
  430.     TRACE(_T("Could not connect to the SMTP server %s on port %dn"), pszHostName, nPort);
  431.     return FALSE;
  432.   }
  433.   else
  434.   {
  435.     //We're now connected !!
  436.     m_bConnected = TRUE;
  437.     //check the response to the login
  438.     if (!ReadCommandResponse(220))
  439.     {
  440.       Disconnect();
  441.       return FALSE;
  442.     }
  443. //retreive the localhost name
  444.     char sHostName[100];
  445. gethostname(sHostName, sizeof(sHostName));
  446.     TCHAR* pszHostName = A2T(sHostName);
  447.     //Send the HELO command
  448. CString sBuf;
  449. sBuf.Format(_T("HELO %srn"), pszHostName);
  450.     LPCSTR pszData = T2A((LPTSTR) (LPCTSTR) sBuf);
  451.     int nCmdLength = strlen(pszData);
  452.     if (!m_SMTP.Send(pszData, nCmdLength))
  453.     {
  454.       Disconnect();
  455.       return FALSE;
  456.     }
  457. //check the response to the HELO command
  458.     if (!ReadCommandResponse(250))
  459.     {
  460.       Disconnect();
  461.       return FALSE;
  462.     } 
  463.     return TRUE;
  464.   }
  465. }
  466. BOOL CSMTPConnection::Disconnect()
  467. {
  468.   BOOL bSuccess = FALSE;      
  469.   //disconnect from the SMTP server if connected 
  470.   if (m_bConnected)
  471.   {
  472.     char sBuf[10];
  473.     strcpy(sBuf, "QUITrn");
  474.     int nCmdLength = strlen(sBuf);
  475.     if (!m_SMTP.Send(sBuf, nCmdLength))
  476.       TRACE(_T("Failed in call to send QUIT commandn"));
  477.     //Check the reponse
  478.     bSuccess = ReadCommandResponse(221);
  479.     if (!bSuccess)
  480.       SetLastError(ERROR_BAD_COMMAND);
  481.     //Reset all the state variables
  482.     m_bConnected = FALSE;
  483.   }
  484.   else
  485.     TRACE(_T("Already disconnectedn"));
  486.  
  487.   //free up our socket
  488.   m_SMTP.Close();
  489.  
  490.   return bSuccess;
  491. }
  492. BOOL CSMTPConnection::SendMessage(CSMTPMessage& Message)
  493. {
  494. //For correct operation of the T2A macro, see MFC Tech Note 59
  495. USES_CONVERSION;
  496. //paramater validity checking
  497.   ASSERT(m_bConnected); //Must be connected to send a message
  498.   //Send the MAIL command
  499. ASSERT(Message.m_From.m_sEmailAddress.GetLength());
  500.   CString sBuf;
  501.   sBuf.Format(_T("MAIL FROM:<%s>rn"), Message.m_From.m_sEmailAddress);
  502.   LPCSTR pszMailFrom = T2A((LPTSTR) (LPCTSTR) sBuf);
  503.   int nCmdLength = strlen(pszMailFrom);
  504.   if (!m_SMTP.Send(pszMailFrom, nCmdLength))
  505.   {
  506.     TRACE(_T("Failed in call to send MAIL commandn"));
  507.     return FALSE;
  508.   }
  509.   //check the response to the MAIL command
  510.   if (!ReadCommandResponse(250))
  511.   {
  512.     SetLastError(ERROR_BAD_COMMAND);
  513.     return FALSE;
  514.   } 
  515.   //Send the RCPT command, one for each recipient
  516.   ASSERT(Message.GetNumberOfRecipients()); //Must have at least one recipient for the message
  517.   for (int i=0; i<Message.GetNumberOfRecipients(); i++)
  518.   {
  519.     CSMTPAddress recipient = Message.GetRecipient(i);
  520. ASSERT(recipient.m_sEmailAddress.GetLength()); //must have an email address for this recipient
  521.     sBuf.Format(_T("RCPT TO:<%s>rn"), recipient.m_sEmailAddress);
  522.     LPTSTR pszRCPT = T2A((LPTSTR) (LPCTSTR) sBuf);
  523.     int nCmdLength = strlen(pszRCPT);
  524.     if (!m_SMTP.Send(pszRCPT, nCmdLength))
  525.     {
  526.       TRACE(_T("Failed in call to send MAIL commandn"));
  527.       return FALSE;
  528.     }
  529.     //check the response to each RCPT command
  530.     if (!ReadCommandResponse(250))
  531.     {
  532.       SetLastError(ERROR_BAD_COMMAND);
  533.       return FALSE;
  534.     } 
  535.   }
  536.   //Send the DATA command
  537.   char* pszDataCommand = "DATArn";
  538.   nCmdLength = strlen(pszDataCommand);
  539.   if (!m_SMTP.Send(pszDataCommand, nCmdLength))
  540.   {
  541.     TRACE(_T("Failed in call to send MAIL commandn"));
  542.     return FALSE;
  543.   }
  544.   //check the response to the DATA command
  545.   if (!ReadCommandResponse(354))
  546.   {
  547.     SetLastError(ERROR_BAD_COMMAND);
  548.     return FALSE;
  549.   } 
  550.   //Send the Header
  551.   CString sHeader = Message.GetHeader();
  552.   char* pszHeader = T2A((LPTSTR) (LPCTSTR) sHeader);
  553.   nCmdLength = strlen(pszHeader);
  554.   if (!m_SMTP.Send(pszHeader, nCmdLength))
  555.   {
  556.     TRACE(_T("Failed in call to send the headern"));
  557.     return FALSE;
  558.   }
  559. //Send the Mime Header for the body
  560.   if (Message.m_Attachments.GetSize())
  561.   {
  562.   char* psBodyHeader = (_T("rn--#BOUNDARY#rn")
  563.                       _T("Content-Type: text/plain; charset=us-asciirn")
  564.         _T("Content-Transfer-Encoding: quoted-printablernrn"));
  565.     nCmdLength = strlen(psBodyHeader);
  566.     if (!m_SMTP.Send(psBodyHeader, nCmdLength))
  567.     {
  568.       TRACE(_T("Failed in call to send the body headern"));
  569.       return FALSE;
  570.     }
  571.   }
  572.   //Send the body
  573.   char* pszBody = T2A((LPTSTR) (LPCTSTR) Message.m_sBody);
  574.   nCmdLength = strlen(pszBody);
  575.   if (!m_SMTP.Send(pszBody, nCmdLength))
  576.   {
  577.     TRACE(_T("Failed in call to send the headern"));
  578.     return FALSE;
  579.   }
  580.   //Send all the attachments
  581.   for (i=0; i<Message.m_Attachments.GetSize(); i++)
  582.   {
  583.     CSMTPAttachment* pAttachment = Message.m_Attachments.GetAt(i);
  584. //First send the Mime header for each attachment
  585. CString sContent;
  586. sContent.Format(_T("rnrn--#BOUNDARY#rn")
  587.                 _T("Content-Type: application/octet-stream; name=%srn")
  588. _T("Content-Transfer-Encoding: base64rn")
  589. _T("Content-Disposition: attachment; filename=%srnrn"), pAttachment->GetFilename(), pAttachment->GetFilename());
  590. char* pszContent = T2A((LPTSTR) (LPCTSTR) sContent);
  591. nCmdLength = strlen(pszContent);
  592. if (!m_SMTP.Send(pszContent, nCmdLength))
  593. {
  594. TRACE(_T("Failed in call to send Mime attachment headern"));
  595. return FALSE;
  596. }
  597.     //Then send the encoded attachment
  598.     if (!m_SMTP.Send(pAttachment->GetEncodedBuffer(), pAttachment->GetEncodedSize()))
  599.     {
  600.       TRACE(_T("Failed in call to send the attachmentn"));
  601.       return FALSE;
  602.     }
  603.   }
  604. //Send the final mime boundary
  605.   if (Message.m_Attachments.GetSize())
  606.   {
  607.   char* pszFinalBoundary = "rn--#BOUNDARY#--";
  608.   nCmdLength = strlen(pszFinalBoundary);
  609.   if (!m_SMTP.Send(pszFinalBoundary, nCmdLength))
  610.   {
  611.   TRACE(_T("Failed in call to send Mime attachment headern"));
  612.   return FALSE;
  613.   }
  614.   }
  615.   //Send the end of message indicator
  616.   char* pszEOM = "rn.rn";
  617. nCmdLength = strlen(pszEOM);
  618.   if (!m_SMTP.Send(pszEOM, nCmdLength))
  619.   {
  620.     TRACE(_T("Failed in call to send end of message indicatorn"));
  621.     return FALSE;
  622.   }
  623.   //check the response to the End of Message command
  624.   if (!ReadCommandResponse(250))
  625.   {
  626.     SetLastError(ERROR_BAD_COMMAND);
  627.     return FALSE;
  628.   } 
  629. return TRUE;
  630. }
  631. BOOL CSMTPConnection::ReadCommandResponse(int nExpectedCode)
  632. {
  633. char sBuf[1000];
  634. return ReadResponse(sBuf, sizeof(sBuf), "rn", nExpectedCode);
  635. }
  636. BOOL CSMTPConnection::ReadResponse(LPSTR pszBuffer, int nBuf, LPSTR pszTerminator, int nExpectedCode)
  637. {
  638. //paramater validity checking
  639. ASSERT(pszBuffer);
  640. ASSERT(nBuf);
  641.   //must have been created first
  642.   ASSERT(m_bConnected);
  643.   //retrieve the reponse using until we
  644. //get the terminator or a timeout occurs
  645. BOOL bFoundTerminator = FALSE;
  646. int nReceived = 0;
  647. DWORD dwStartTicks = ::GetTickCount();
  648. while (!bFoundTerminator)
  649. {
  650. //timeout has occured
  651. if ((::GetTickCount() - dwStartTicks) > m_dwTimeout)
  652. {
  653.   pszBuffer[nReceived] = '';
  654. SetLastError(WSAETIMEDOUT);
  655.       m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
  656. return FALSE;
  657. }
  658.     //check the socket for readability
  659.     BOOL bReadible;
  660.     if (!m_SMTP.IsReadible(bReadible))
  661.     {
  662.     pszBuffer[nReceived] = '';
  663. m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
  664. return FALSE;
  665.     }
  666.     else if (!bReadible) //no data to receive, just loop around
  667.     {
  668.       Sleep(100); //Wait for 100 ms prior to looping around, 
  669.                   //helps to improve performance of system
  670.       continue;
  671.     }
  672. //receive the data from the socket
  673.   int nData = m_SMTP.Receive(pszBuffer+nReceived, nBuf-nReceived);
  674. if (nData == SOCKET_ERROR)
  675. {
  676.   pszBuffer[nReceived] = '';
  677.       m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
  678.   return FALSE; 
  679. }
  680. else
  681. {
  682.       if (nData)
  683.   dwStartTicks = ::GetTickCount(); //Reset the idle timeout
  684.   nReceived += nData;    //Increment the count of data received
  685. }
  686. pszBuffer[nReceived] = ''; //temporarily NULL terminate the string
  687. //so that strstr works
  688. bFoundTerminator = (strstr(pszBuffer, pszTerminator) != NULL);
  689. }
  690. //Remove the terminator from the response data
  691.   pszBuffer[nReceived - strlen(pszTerminator)] = '';
  692.   //determine if the response is an error
  693. char sCode[4];
  694. strncpy(sCode, pszBuffer, 3);
  695. sCode[3] = '';
  696. sscanf(sCode, "%d", &m_nLastCommandResponseCode);
  697. BOOL bSuccess = (m_nLastCommandResponseCode == nExpectedCode);
  698.   if (!bSuccess)
  699.   {
  700.     SetLastError(WSAEPROTONOSUPPORT);
  701.     m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
  702.   }
  703.   return bSuccess;
  704. }