⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smtp.cpp

📁 这是一本学习 window编程的很好的参考教材
💻 CPP
📖 第 1 页 / 共 3 页
字号:
	 

    while (dwLen > dwPosition)
    {
      dwMax = min(CMD_BLOCK_SIZE,dwLen - dwPosition);
      nRet = send(m_hSocket,&pszBuff[dwPosition],dwMax,0);
      if (nRet == SOCKET_ERROR)
      {
        free(pszBuff);
        return nRet;
      }
      dwPosition += dwMax;
      if(dwLen>1024)SetText ("Remain%d Bytes,Total:%d Have Sent %d%。",dwPosition,dwLen,(int)dwPosition*100/dwLen);
      if (bReportProgress)
      {
        if (!SmtpProgress(pszBuff,dwPosition,dwLen))
        {
          free(pszBuff);
          return -1;
        }
      }
    }
	
    // Wait for the CMD to finish being sent
    FD_ZERO(&set);
    FD_SET(m_hSocket,&set);
    nRet = select(1,NULL,&set,NULL,NULL);
    free(pszBuff);
  }

  // Prepare to receive a response
  ZeroMemory(szResult,CMD_RESPONSE_SIZE);
  pszPos = szResult;
  // Wait for the specified timeout for a full response string
  dwTick = GetTickCount();
  while (GetTickCount() - dwTick < (m_dwCmdTimeout * 1000))
  {
    FD_SET(m_hSocket,&set);
    
    tv.tv_sec = m_dwCmdTimeout - ((GetTickCount() - dwTick) / 1000);
    tv.tv_usec = 0;

    // Check the socket for readability
    nRet = select(1,&set,NULL,NULL,&tv);
    if (nRet == SOCKET_ERROR) break;

    // If the socket has data, read it.
    if (nRet == 1)
    {
      nRet = recv(m_hSocket,pszPos,CMD_RESPONSE_SIZE - (pszPos - szResult),0);
      // Treats a graceful shutdown as an error
      if (nRet == 0) nRet = SOCKET_ERROR;
      if (nRet == SOCKET_ERROR) break;
      
      // Add the data to the total response string & check for a LF
      pszPos += nRet;
      pszTok = strrchr(szResult,'\n');
      if (pszTok)
      {
        // Truncate CRLF combination and exit our wait loop
        pszTok --;
        pszTok[0] = 0;
        break;
      }
    }
  }
  // Assign the response string
  m_strResult = A2CT(szResult);

  // Evaluate the numeric response code
  if (nRet && nRet != SOCKET_ERROR)
  {
    szResult[3] = 0;
    nRet = atoi(szResult);
    SmtpCommandResponse(pszCmd, nRet, (LPTSTR)m_strResult.c_str());
  }
  else nRet = -1;

  return nRet;
}

// Placeholder function -- overridable
// This function is called when the SMTP server gives us an unexpected error
// The <nError> value is the SMTP server's numeric error response, and the <pszErr>
// is the descriptive error text
//
// <pszErr> may be NULL if the server failed to respond before the timeout!
// <nError> will be -1 if a catastrophic failure occurred.
//
// Return 0, or nError.  The return value is currently ignored.
int CSmtp::SmtpError(int /*nError*/, LPTSTR pszErr)
{
#ifdef _DEBUG
  if (pszErr)
  {
    OutputDebugString(_T("SmtpError: "));
    OutputDebugString(pszErr);
    OutputDebugString(_T("\n"));
  }
#endif
  return 0;
}

// Placeholder function -- overridable
// Currently the only warning condition that this class is designed for is
// an authentication failure.  In that case, <nWarning> will be 535,
// which is the RFC error for authentication failure.  If authentication
// fails, you can override this function to prompt the user for a new
// username and password.  Change the <m_strUser> and <m_strPass> member
// variables and return TRUE to retry authentication.
//
// <pszWarning> may be NULL if the server did not respond in time!
//
// Return FALSE to abort authentication, or TRUE to retry.
int CSmtp::SmtpWarning(int /*nWarning*/, LPTSTR pszWarning)
{
#ifdef _DEBUG
  if (pszWarning)
  {
    OutputDebugString(_T("SmtpWarning: "));
    OutputDebugString(pszWarning);
    OutputDebugString(_T("\n"));
  }
#endif
  return 0;
}

// Placeholder function -- overridable
// This is an informational callback only, and provides a means to inform
// the caller as the SMTP session progresses.
// ALWAYS check for NULL values on <pszCmd> and <pszResponse> before performing
// any actions!
// <nResponse> will be -1 if a catastrophic failure occurred, but that will
// be raised in the SmtpError() event later on during processing.
void CSmtp::SmtpCommandResponse(LPTSTR pszCmd, int /*nResponse*/, LPTSTR pszResponse)
{
#ifdef _DEBUG
/*  if (pszCmd)
  {
    TCHAR szOut[MAX_PATH+1];
    OutputDebugString(_T("SmtpCommand : "));
    while (lstrlen(pszCmd) > MAX_PATH)
    {
      lstrcpyn(szOut,pszCmd,MAX_PATH+1);
      OutputDebugString(szOut);
      Sleep(100);
      pszCmd += MAX_PATH;
    }
    OutputDebugString(pszCmd);
  }
  OutputDebugString(_T("SmtpResponse: "));
  OutputDebugString(pszResponse);
  OutputDebugString(_T("\n"));*/
#endif
}

// Placeholder function -- overridable
// This is a progress callback to indicate that data is being sent over the wire
// and that the operation may take some time.
// Return TRUE to continue sending, or FALSE to abort the transfer
BOOL CSmtp::SmtpProgress(LPSTR /*pszBuffer*/, DWORD /*dwBytesSent*/, DWORD /*dwBytesTotal*/)
{
  return TRUE; // Continue sending the data
}

// Raises a SmtpError() condition
int CSmtp::RaiseError(int nError)
{
  // If the error code is -1, something catastrophic happened
  // so we're effectively not connected to any SMTP server.
  if (nError == -1) m_bConnected = FALSE;
  return SmtpError(nError, (LPTSTR)m_strResult.c_str());
}

// Warnings are recoverable errors that we may be able to continue working with
int CSmtp::RaiseWarning(int nWarning)
{
  return SmtpWarning(nWarning, (LPTSTR)m_strResult.c_str());
}

// E-Mail's a message
// Returns 0 if successful, -1 if an internal error occurred, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendMessage(CSmtpMessage &msg)
{
  int nRet;
  int n;
//  int nRecipients = 0;
  int nRecipientCount = 0;

  // Check if we have a sender
  if (!msg.Sender.Address.length()) return -1;
  
  // Check if we have recipients
  if (!msg.Recipient.Address.length() && !msg.CC.GetSize()) return -1;

  // Check if we have a message body or attachments
  // *** Commented out to remove the requirement that a message have a body or attachments
  //  if (!msg.Message.GetSize() && !msg.Attachments.GetSize()) return -1;

  // Send the sender's address
  nRet = SendFrom((LPTSTR)msg.Sender.Address.c_str());
  if (nRet) return nRet;
  
  // If we have a recipient, send it
  nRecipientCount = 0; // Count of recipients
  if (msg.Recipient.Address.length())
  {
    nRet = SendTo((LPTSTR)msg.Recipient.Address.c_str());
    if (!nRet) nRecipientCount ++;
  }

  // If we have any CC's, send those.
  for (n = 0;n < msg.CC.GetSize();n++)
  {
    nRet = SendTo((LPTSTR)msg.CC[n].Address.c_str());
    if (!nRet) nRecipientCount ++;
  }

  // If we have any bcc's, send those.
  for (n = 0;n < msg.BCC.GetSize();n++)
  {
    nRet = SendTo((LPTSTR)msg.BCC[n].Address.c_str());
    if (!nRet) nRecipientCount ++;
  }
  // If we failed on all recipients, we must abort.
  if (!nRecipientCount)
    RaiseError(nRet);
  else
    nRet = SendData(msg);

  return nRet;
}

// Simplified way to send a message.
// <pvAttachments> can be either an LPTSTR containing NULL terminated strings, in which
// case <dwAttachmentCount> should be zero, or <pvAttachments> can be an LPTSTR * 
// containing an array of LPTSTR's, in which case <dwAttachmentCount> should equal the
// number of strings in the array.
int CSmtp::SendMessage(CSmtpAddress &addrFrom, CSmtpAddress &addrTo, LPCTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount)
{
  CSmtpMessage message;
  CSmtpMessageBody body;
  CSmtpAttachment attach;

  body = pszMessage;

  message.Sender = addrFrom;
  message.Recipient = addrTo;
  message.Message.Add(body);
  message.Subject = pszSubject;

  // If the attachment count is zero, but the pvAttachments variable is not NULL,
  // assume that the ppvAttachments variable is a string value containing NULL terminated
  // strings.  A double NULL ends the list.
  // Example: LPTSTR pszAttachments = "foo.exe\0bar.zip\0autoexec.bat\0\0";
  if (!dwAttachmentCount && pvAttachments)
  {
    LPTSTR pszAttachments = (LPTSTR)pvAttachments;
    while (lstrlen(pszAttachments))
    {
      attach.FileName = pszAttachments;
      message.Attachments.Add(attach);
      pszAttachments = &pszAttachments[lstrlen(pszAttachments)];
    }
  }

  // dwAttachmentCount is not zero, so assume pvAttachments is an array of LPTSTR's
  // Example: LPTSTR *ppszAttachments = {"foo.exe","bar.exe","autoexec.bat"};
  if (pvAttachments && dwAttachmentCount)
  {
    LPTSTR *ppszAttachments = (LPTSTR *)pvAttachments;    
    while (dwAttachmentCount-- && ppszAttachments)
    {
      attach.FileName = ppszAttachments[dwAttachmentCount];
      message.Attachments.Add(attach);
    }
  }
  return SendMessage(message);
}

// Yet an even simpler method for sending a message
// <pszAddrFrom> and <pszAddrTo> should be e-mail addresses with no decorations
// Example:  "foo@bar.com"
// <pvAttachments> and <dwAttachmentCount> are described above in the alternative
// version of this function
int CSmtp::SendMessage(LPTSTR pszAddrFrom, LPTSTR pszAddrTo, LPTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount)
{
  CSmtpAddress addrFrom(pszAddrFrom);
  CSmtpAddress addrTo(pszAddrTo);

  return SendMessage(addrFrom,addrTo,pszSubject,pszMessage,pvAttachments,dwAttachmentCount);
}

// Tell the SMTP server we're quitting
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendQuitCmd()
{
  int nRet;

  if (!m_bConnected) return 0;

  nRet = SendCmd(_T("QUIT\r\n"));
  if (nRet != 221) RaiseError(nRet);

  m_bConnected = FALSE;

  return (nRet == 221) ? 0:nRet;
}

// Initiate a conversation with the SMTP server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendHello()
{
  int nRet = 0;
  TCHAR szName[64];
  TCHAR szMsg[MAX_PATH];
  DWORD dwSize = 64;

  GetComputerName(szName,&dwSize);

  // First try a EHLO if we're using ESMTP
  wsprintf(szMsg,_T("EHLO %s\r\n"),szName);
  if (m_bExtensions) nRet = SendCmd(szMsg);

  // If we got a 250 response, we're using ESMTP, otherwise revert to regular SMTP
  if (nRet != 250)
  {
    m_bUsingExtensions = FALSE;
    szMsg[0] = 'H';
    szMsg[1] = 'E';
    nRet = SendCmd(szMsg);
  }
  else m_bUsingExtensions = TRUE;

  // Raise any unexpected responses
  if (nRet != 250)
  {
    RaiseError(nRet);
    return nRet;
  }

  // We're connected!
  m_bConnected = TRUE;

  // Send authentication if we have any.
  // We don't fail just because authentication failed, however.
  if (m_bUsingExtensions) SendAuthentication();

  return 0;
}

// Requests authentication for the session if the server supports it,
// and attempts to submit the user's credentials.
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendAuthentication()
{
  USES_CONVERSION;
  int nRet = 0;
  CBase64 cvt;
  LPCSTR pszTemp;
  TCHAR szMsg[MAX_PATH];
  CHAR szAuthType[MAX_PATH];

  // This is an authentication loop, we can authenticate multiple times in case of failure.
  while(1)
  {
    // If we don't have a username, skip authentication
    if (!m_strUser.length()) return 0;
    
    // Make the authentication request
    nRet = SendCmd(_T("AUTH LOGIN\r\n"));
    // If it was rejected, we have to abort.
    if (nRet != 334)
    {
      RaiseWarning(nRet);
      return nRet;
    }
    
    // Authentication has 2 stages for username and password.
    // It is possible if the authentication fails here that we can
    // resubmit proper credentials.
    while (1)
    {
      // Decode the authentication string being requested
      _T2A(szAuthType,&(m_strResult.c_str())[4]);

      cvt.Decode(szAuthType);
      pszTemp = cvt.DecodedMessage();
      
      if (!lstrcmpiA(pszTemp,"Username:"))
        cvt.Encode(T2CA(m_strUser.c_str()));
      else if (!lstrcmpiA(pszTemp,"Password:"))
        cvt.Encode(T2CA(m_strPass.c_str()));
      else break;
      
      wsprintf(szMsg,_T("%s\r\n"),A2CT(cvt.EncodedMessage()));
      nRet = SendCmd(szMsg);
      
      // If we got a failed authentication request, raise a warning.
      // this gives the owner a chance to change the username and password.
      if (nRet == 535)
      {
        // Return FALSE to fail, or TRUE to retry
        nRet = RaiseWarning(nRet);
        if (!nRet)
        {
          // Reset the error back to 535.  It's now an error rather than a warning
          nRet = 535;
          break;
        }
      }
      // Break on any response other than 334, which indicates a request for more information
      if (nRet != 334) break;
    }
    // Break if we're not retrying a failed authentication
    if (nRet != TRUE) break;
  }
  // Raise an error if we failed to authenticate
  if (nRet != 235) RaiseError(nRet);

  return (nRet == 235) ? 0:nRet;
}

// Send a MAIL FROM command to the server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendFrom(LPTSTR pszFrom)
{
  int nRet = 0;
  TCHAR szMsg[MAX_PATH];

  wsprintf(szMsg,_T("MAIL FROM: <%s>\r\n"),pszFrom);
  
  while (1)
  {
    nRet = SendCmd(szMsg);
    // Send authentication if required, and retry the command
    if (nRet == 530||nRet == 505) nRet = SendAuthentication();
    else break;
  }
  // Raise an error if we failed
  if (nRet != 250) RaiseError(nRet);
  return (nRet == 250) ? 0:nRet;
}

// Send a RCPT TO command to the server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendTo(LPTSTR pszTo)
{
  int nRet;
  TCHAR szMsg[MAX_PATH];

  wsprintf(szMsg,_T("RCPT TO: <%s>\r\n"),pszTo);
  
  nRet = SendCmd(szMsg);
  if (nRet != 250 && nRet != 251) RaiseWarning(nRet);
  return (nRet == 250 || nRet == 251) ? 0:nRet;
}

// Send the body of an e-mail message to the server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendData(CSmtpMessage &msg)
{
  int nRet;
  String strMsg;

  // Send the DATA command.  We need a 354 to proceed
  nRet = SendCmd(_T("DATA\r\n"));
  if (nRet != 354)
  {
    RaiseError(nRet);
    return nRet;
  }

  // Parse the body of the email message
  msg.Parse(strMsg);
  strMsg += _T("\r\n.\r\n");

  // Send the body and expect a 250 OK reply.
  nRet = SendCmd((LPTSTR)strMsg.c_str());
  if (nRet != 250) RaiseError(nRet);
  
  return (nRet == 250) ? 0:nRet;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -