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

📄 smtp.cpp

📁 这是一本学习 window编程的很好的参考教材
💻 CPP
📖 第 1 页 / 共 3 页
字号:
        strDest += _T("Content-Disposition: ");
        
        if (Attachments[n].Inline) strDest += _T("inline");
        else strDest += _T("attachment");
        
        strDest += _T(";\r\n\tfilename=\"");
        strDest += pszFile;

        // If the attachment has a content ID, write it out
        if (Attachments[n].ContentId.length())
        {
          wsprintf(szOut,_T("\r\nContent-ID: <%s>"),Attachments[n].ContentId.c_str());
          strDest += szOut;
        }
        strDest += _T("\r\n\r\n");

        // Write out the encoded attachment
        strDest += strValue;
        strTemp.erase();
        strValue.erase();
      }
      // Close the file and clear the temp buffer
      CloseHandle(hFile);
      free(pData);
    }
  }

  // If we are multipart, write out the trailing end sequence
  if (strBoundary.length())
  {
    wsprintf(szOut,_T("\r\n--%s--\r\n"),strBoundary.c_str());
    strDest += szOut;
  }
}

// Parses text into quoted-printable lines.
// See RFC 1521 for full details on how this works.
void CSmtpMessage::EncodeQuotedPrintable(String& strDest, String& strSrc)
{
  String strTemp;
  String strTemp2;
  LPTSTR pszTok1;
  LPTSTR pszTok2;
  TCHAR szSub[16];
  TCHAR ch;
  int n;

  strDest.erase();
  if (!strSrc.length()) return;

  // Change = signs and non-printable characters to =XX
  pszTok1 = (LPTSTR)strSrc.c_str();
  pszTok2 = pszTok1;
  do
  {
    if (*pszTok2 == '=' || *pszTok2 > 126 || 
      (*pszTok2 < 32 && (*pszTok2 != '\r' && *pszTok2 != '\n' && *pszTok2 != '\t')))
    {
      ch = *pszTok2;
      *pszTok2 = 0;
      strTemp += pszTok1;
      *pszTok2 = ch;
      wsprintf(szSub,_T("=%2.2X"),(BYTE)*pszTok2);
      strTemp += szSub;
      pszTok1 = pszTok2 + 1;
    }
    pszTok2 ++;
  } while (*pszTok2);

  // Append anything left after the search
  if (_tcslen(pszTok1)) strTemp += pszTok1;

  pszTok1 = (LPTSTR)strTemp.c_str();
  while (pszTok1)
  {
    pszTok2 = _tcschr(pszTok1,'\r');
    if (pszTok2) *pszTok2 = 0;
    while (1)
    {
      if (_tcslen(pszTok1) > 76)
      {
        n = 75; // Breaking at the 75th character
        if (pszTok1[n-1] == '=') n -= 1; // If the last character is an =, don't break the line there
        else if (pszTok1[n-2] == '=') n -= 2; // If we're breaking in the middle of a = sequence, back up!
        
        // Append the first section of the line to the total string
        ch = pszTok1[n];
        pszTok1[n] = 0;
        strDest += pszTok1;
        pszTok1[n] = ch;
        strDest += _T("=\r\n");
        pszTok1 += n;
      }
      else // Line is less than or equal to 76 characters
      {
        n = _tcslen(pszTok1); // If we have some trailing data, process it.
        if (n)
        {
          if (pszTok1[n-1] == ' ' || pszTok1[n-1] == '\t') // Last character is a space or tab
          {
            wsprintf(szSub,_T("=%2.2X"),(BYTE)pszTok1[n-1]);
            // Replace the last character with an =XX sequence
            pszTok1[n-1] = 0;
            strTemp2 = pszTok1;
            strTemp2 += szSub;
            // Since the string may now be larger than 76 characters, we have to reprocess the line
            pszTok1 = (LPTSTR)strTemp2.c_str();
          }
          else // Last character is not a space or tab
          {
            strDest += pszTok1;
            if (pszTok2) strDest += _T("\r\n");
            break; // Exit the loop which processes this line, and move to the next line
          }
        }
        else
        {
          if (pszTok2) strDest += _T("\r\n");
          break; // Move to the next line
        }
      }
    }
    if (pszTok2)
    {
      *pszTok2 = '\r';
      pszTok2 ++;
      if (*pszTok2 == '\n') pszTok2 ++;
    }
    pszTok1 = pszTok2;
  }
}

// Breaks a message's lines into a maximum of 76 characters
// Does some semi-intelligent wordwrapping to ensure the text is broken properly.
// If a line contains no break characters, it is forcibly truncated at the 76th char
void CSmtpMessage::BreakMessage(String& strDest, String& strSrc, int nLength)
{
  String strTemp = strSrc;
  String strLine;
  LPTSTR pszTok1;
  LPTSTR pszTok2;
  LPTSTR pszBreak;
  LPTSTR pszBreaks = _T(" -;.,?!");
  TCHAR ch;
  BOOL bNoBreaks;
  int nLen;

  strDest.erase();
  if (!strSrc.length()) return;

  nLen = strTemp.length();
  nLen += (nLen / 60) * 2;

  strDest.reserve(nLen);

  // Process each line one at a time
  pszTok1 = (LPTSTR)strTemp.c_str();
  while (pszTok1)
  {
    pszTok2 = _tcschr(pszTok1,'\r');
    if (pszTok2) *pszTok2 = 0;

    bNoBreaks = (!_tcspbrk(pszTok1,pszBreaks));
    nLen = _tcslen(pszTok1);
    while (nLen > nLength)
    {
      // Start at the 76th character, and move backwards until we hit a break character
      pszBreak = &pszTok1[nLength - 1];
      
      // If there are no break characters in the string, skip the backward search for them!
      if (!bNoBreaks)
      {
        while (!_tcschr(pszBreaks,*pszBreak) && pszBreak > pszTok1)
          pszBreak--;
      }
      pszBreak ++;
      ch = *pszBreak;
      *pszBreak = 0;
      strDest += pszTok1;
      
      strDest += _T("\r\n");
      *pszBreak = ch;
    
      nLen -= (pszBreak - pszTok1);
      // Advance the search to the next segment of text after the break
      pszTok1 = pszBreak;
    }
    strDest += pszTok1;
    if (pszTok2)
    {
      strDest += _T("\r\n");
      *pszTok2 = '\r';
      pszTok2 ++;
      if (*pszTok2 == '\n') pszTok2 ++;
    }
    pszTok1 = pszTok2;
  }
}

// Makes the message into a 7bit stream
void CSmtpMessage::Make7Bit(String& strDest, String& strSrc)
{
  LPTSTR pszTok;

  strDest = strSrc;

  pszTok = (LPTSTR)strDest.c_str();
  do
  {
    // Replace any characters above 126 with a ? character
    if (*pszTok > 126 || *pszTok < 0)
      *pszTok = '?';
    pszTok ++;
  } while (*pszTok);
}

// Encodes a message or binary stream into a properly-formatted message
// Takes care of breaking the message into 76-byte lines of text, encoding to
// Base64, quoted-printable and etc.
void CSmtpMessage::EncodeMessage(EncodingEnum code, String& strMsg, String& strMethod, LPBYTE pByte, DWORD dwSize)
{
  String strTemp;
  LPTSTR pszTok1;
  LPTSTR pszTok2;
  LPSTR pszBuffer = NULL;
  LPSTR pszTemp;
  DWORD dwStart = GetTickCount();

  if (!pByte)
  {
    pszBuffer = (LPSTR)malloc(strMsg.length() + 1);
    _T2A(pszBuffer,strMsg.c_str());
    pByte = (LPBYTE)pszBuffer;
    dwSize = strMsg.length();
  }

  // Guess the encoding scheme if we have to
  if (code == encodeGuess) code = GuessEncoding(pByte, dwSize);

  switch(code)
  {
  case encodeQuotedPrintable:
    strMethod = _T("quoted-printable");

    pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR));
    _A2T(pszTok1,(LPSTR)pByte);
    strMsg = pszTok1;
    free(pszTok1);
    
    EncodeQuotedPrintable(strTemp, strMsg);
    break;
  case encodeBase64:
    strMethod = _T("base64");
    {
      CBase64 cvt;      
      cvt.Encode(pByte, dwSize);
      pszTemp = (LPSTR)cvt.EncodedMessage();
      pszTok1 = (LPTSTR)malloc((lstrlenA(pszTemp)+1) * sizeof(TCHAR));
      _A2T(pszTok1,pszTemp);
    }
    strMsg = pszTok1;
    free(pszTok1);

    BreakMessage(strTemp, strMsg);
    break;
  case encode7Bit:
    strMethod = _T("7bit");

    pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR));
    _A2T(pszTok1,(LPSTR)pByte);
    strMsg = pszTok1;
    free(pszTok1);

    Make7Bit(strTemp, strMsg);
    strMsg = strTemp;
    BreakMessage(strTemp, strMsg);
    break;
  case encode8Bit:
    strMethod = _T("8bit");

    pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR));
    _A2T(pszTok1,(LPSTR)pByte);
    strMsg = pszTok1;
    free(pszTok1);

    BreakMessage(strTemp, strMsg);
    break;
  }

  if (pszBuffer) free(pszBuffer);

  strMsg.erase();

  // Parse the message text, replacing CRLF. sequences with CRLF.. sequences
  pszTok1 = (LPTSTR)strTemp.c_str();
  do
  {
    pszTok2 = _tcsstr(pszTok1,_T("\r\n."));
    if (pszTok2)
    {
      *pszTok2 = 0;
      strMsg += pszTok1;
      *pszTok2 = '\r';
      strMsg += _T("\r\n..");
      pszTok1 = pszTok2 + 3;
    }
  } while (pszTok2);  
  strMsg += pszTok1;

  TCHAR szOut[MAX_PATH];
  wsprintf(szOut,_T("Encoding took %dms\n"),GetTickCount() - dwStart);
  OutputDebugString(szOut);
}

// Makes a best-guess of the proper encoding to use for this stream of bytes
// It does this by counting the # of lines, the # of 8bit bytes and the number
// of 7bit bytes.  It also records the line and the count of lines over
// 76 characters.
// If the stream is 90% or higher 7bit, it uses a text encoding method.  If the stream
// is all at or under 76 characters, it uses 7bit or 8bit, depending on the content.
// If the lines are longer than 76 characters, use quoted printable.
// If the stream is under 90% 7bit characters, use base64 encoding.
EncodingEnum CSmtpMessage::GuessEncoding(LPBYTE pByte, DWORD dwLen)
{
  int n7Bit = 0;
  int n8Bit = 0;
  int nLineStart = 0;
  int nLinesOver76 = 0;
  int nLines = 0;
  DWORD n;

  // Count the content type, byte by byte
  for (n = 0;n < dwLen; n++)
  {
    if (pByte[n] > 126 || (pByte[n] < 32 && pByte[n] != '\t' && pByte[n] != '\r' && pByte[n] != '\n'))
      n8Bit ++;
    else n7Bit ++;

    // New line?  If so, record the line size
    if (pByte[n] == '\r')
    {
      nLines ++;
      nLineStart = (n - nLineStart) - 1;
      if (nLineStart > 76) nLinesOver76 ++;
      nLineStart = n + 1;      
    }
  }
  // Determine if it is mostly 7bit data
  if ((n7Bit * 100) / dwLen > 89)
  {
    // At least 90% text, so use a text-base encoding scheme
    if (!nLinesOver76)
    {
      if (!n8Bit) return encode7Bit;
      else return encode8Bit;
    }
    else return encodeQuotedPrintable;
  }
  return encodeBase64;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction for CSmtp
//////////////////////////////////////////////////////////////////////
CSmtp::CSmtp()
{
  LPSERVENT pEnt;
  
  m_bExtensions = TRUE;       // Use ESMTP if possible
  m_dwCmdTimeout = 30;        // Default to 30 second timeout
  m_hSocket = INVALID_SOCKET;

  // Try and get the SMTP service entry by name
  pEnt = getservbyname("SMTP","tcp");
  if (pEnt) m_wSmtpPort = pEnt->s_port;
  else m_wSmtpPort = htons(25);

}

CSmtp::~CSmtp()
{
  // Make sure any open connections are shut down
  Close();
}

// Connects to a SMTP server.  Returns TRUE if successfully connected, or FALSE otherwise.
BOOL CSmtp::Connect(LPTSTR pszServer)
{
  SOCKADDR_IN addr;
  int nRet;
  CHAR szHost[MAX_PATH];

  _T2A(szHost,pszServer);

  // Shut down any active connection
  Close();

  // Resolve the hostname
  addr.sin_family = AF_INET;
  addr.sin_port = m_wSmtpPort;
  addr.sin_addr.s_addr = inet_addr(szHost);
  if (addr.sin_addr.s_addr == INADDR_NONE)
  {
    LPHOSTENT pHost = gethostbyname(szHost);
    if (!pHost) return FALSE;
    addr.sin_addr.s_addr = *(LPDWORD)pHost->h_addr;
  }

  // Create a socket
  m_hSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if (m_hSocket == INVALID_SOCKET) return FALSE;

  // Connect to the host
  if (connect(m_hSocket,(LPSOCKADDR)&addr,sizeof(addr)) == SOCKET_ERROR)
  {
    Close();
    return FALSE;
  }

  // Get the initial response string
  nRet = SendCmd(NULL);
  if (nRet != 220)
  {
    RaiseError(nRet);
    Close();
    return FALSE;
  }

  // Send a HELLO message to the SMTP server
  if (SendHello())
  {
    Close();
    return FALSE;
  }

  return TRUE;
}

// Closes any active SMTP sessions and shuts down the socket.
void CSmtp::Close()
{
  if (m_hSocket != INVALID_SOCKET)
  {
    // If we're connected to a server, tell them we're quitting
    if (m_bConnected) SendQuitCmd();
    // Shutdown and close the socket
    shutdown(m_hSocket,2);
    closesocket(m_hSocket);
  }
  m_hSocket = INVALID_SOCKET;
}

// Send a command to the SMTP server and wait for a response
int CSmtp::SendCmd(LPTSTR pszCmd)
{
  USES_CONVERSION;
  FD_SET set;
  TIMEVAL tv;
  int nRet = 0;
  DWORD dwTick;
  CHAR szResult[CMD_RESPONSE_SIZE];
  LPSTR pszPos;
  LPSTR pszTok;
  DWORD dwPosition;
  DWORD dwLen;
  DWORD dwMax;
  BOOL bReportProgress = FALSE;
  LPSTR pszBuff;
  
  ZeroMemory(szResult,CMD_RESPONSE_SIZE);
  FD_ZERO(&set);

  // If we have a command to send, then send it.
  if (pszCmd)
  {
    pszBuff = (LPSTR)malloc(lstrlen(pszCmd)+1);
    _T2A(pszBuff,pszCmd);
    
    // Make sure the input buffer is clear before sending
    nRet = 1;
    while (nRet > 0)
    {
      FD_SET(m_hSocket,&set);
      tv.tv_sec = 0;
      tv.tv_usec = 0;
      nRet = select(1,&set,NULL,NULL,&tv);
      if (nRet == 1) nRet = recv(m_hSocket,szResult,CMD_RESPONSE_SIZE,0);
    }
    dwPosition = 0;
    dwLen = lstrlen(pszCmd);
    if (dwLen > CMD_BLOCK_SIZE) bReportProgress = TRUE;

⌨️ 快捷键说明

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