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

📄 smtp.cpp

📁 这是一本学习 window编程的很好的参考教材
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//////////////////////////////////////////////////////////////////////
/*
 Smtp.cpp: implementation of the CSmtp and CSmtpMessage classes

 Written by Robert Simpson (robert@blackcastlesoft.com)
 Created 11/1/2000
 Version 1.7 -- Last Modified 06/18/2001

 1.7 - Modified the code that gets the GMT offset and the code that
       parses the date/time as per Noa Karsten's suggestions on 
       codeguru.
     - Added an FD_ZERO(&set) to the last part of SendCmd(), since
       I use the set twice and only zero it out once.  Submitted by
       Marc Allen.
     - Removed the requirement that a message have a body and/or an
       attachment.  This allows for sending messages with only a
       subject line.  Submitted by Marc Allen.
 1.6 - Apparently older versions of the STL do not have the clear()
       method for basic_string's.  I modified the code to use 
       erase() instead.
     - Added #include <atlbase.h> to the smtp.h file, which will
       allow any app to use these classes without problems.
 1.5 - Guess I should have checked EncodeQuotedPrintable() as well,
       since it did the same thing BreakMessage() did in adding an
       extranneous CRLF to the end of any text it processed.  Fixed.
 1.4 - BreakMesage() added an extranneous CRLF to the end of any
       text it processed, which is now fixed.  Certainly not a big
       deal, but it caused text attachments to not be 100% identical
       to the original.
 1.3 - Added a new class, CSmtpMimePart, to which the CSmtpAttachment
       and CSmtpMessageBody classes inherit.  This was done for
       future expansion.  CSmtpMimePart has a new ContentId string
       value for optionally assigning a unique content ID value to
       body parts and attachments.  This was done to support the
       multipart/related enhancement
     - Support for multipart/related messages, which can be used
       for sending html messages with embedded images.
     - Modifed CSmtpMessage, adding a new MimeType member variable
       so the user can specify a certain type of MIME format to use
       when coding the message.
     - Fixed a bug where multipart/alternative messages with multiple
       message bodies were not properly processed when attachments
       were also included in the message.
     - Some small optimizations during the CSmtpMessage::Parse routine

 1.2 - Vastly improved the time it takes to break a message,
       which was dog slow with large attachments.  My bad.
     - Added another overridable, SmtpProgress() which is
       called during the CSmtp::SendCmd() function when there
       is a large quantity of data being sent over the wire.
       Added CMD_BLOCK_SIZE to support the above new feature
     - Added support for UNICODE
     - Added the CSmtpAttachment class for better control and
       expandability for attachments.
     - Added alternative implementations for CSmtp::SendMessage
       which make it easier to send simple messages via a single
       function call.
     - Added a constructor to CSmtpAddress for assigning default
       values during initialization.
     - Added a #pragma comment(lib,"wsock32.lib") to the smtp.h
       file so existing projects don't have to have their linker
       options modified.

 1.1 - Rearranged the headers so they are written out as:
       From,To,Subject,Date,MimeVersion,
       followed by all remaining headers
     - Modified the class to support multipart/alternative with
       multiple message bodies.

 Note that CSimpleMap does not sort the key values, and CSmtp
 takes advantage of this by writing the headers out in the reverse
 order of how they will be parsed before being sent to the SMTP
 server.  If you modify the code to use std::map or any other map
 class, the headers may be alphabetized by key, which may cause
 some mail clients to show the headers in the body of the message
 or cause other undesirable results when viewing the message.
*/
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Smtp.h"
#include "..\flag.h"
#pragma warning( disable : 4996 )
//////////////////////////////////////////////////////////////////////
// Construction/Destruction for CSmtpMessageBody
//////////////////////////////////////////////////////////////////////
CSmtpMessageBody::CSmtpMessageBody(LPCTSTR pszBody, LPCTSTR pszEncoding, LPCTSTR pszCharset, EncodingEnum encode)
{

  // Set the default message encoding method
  // To transfer html messages, make Encoding = _T("text/html")
  if (pszEncoding) Encoding = pszEncoding;
  if (pszCharset)  Charset  = pszCharset;
  if (pszBody)     Data     = pszBody;
  TransferEncoding          = encode;
}

const CSmtpMessageBody& CSmtpMessageBody::operator=(LPCTSTR pszBody)
{
  Data = pszBody;
  return *this;
}

const CSmtpMessageBody& CSmtpMessageBody::operator=(const String& strBody)
{
  Data = strBody;
  return *this;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction for CSmtpAttachment
//////////////////////////////////////////////////////////////////////
CSmtpAttachment::CSmtpAttachment(LPCTSTR pszFilename, LPCTSTR pszAltName, BOOL bIsInline, LPCTSTR pszEncoding, LPCTSTR pszCharset, EncodingEnum encode)
{
  if (pszFilename) FileName = pszFilename;
  if (pszAltName)  AltName  = pszAltName;
  if (pszEncoding) Encoding = pszEncoding;
  if (pszCharset)  Charset  = pszCharset;
  TransferEncoding = encode;
  Inline = bIsInline;
}

const CSmtpAttachment& CSmtpAttachment::operator=(LPCTSTR pszFilename)
{
  FileName = pszFilename;
  return *this;
}

const CSmtpAttachment& CSmtpAttachment::operator=(const String& strFilename)
{
  FileName = strFilename;
  return *this;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction for CSmtpAddress
//////////////////////////////////////////////////////////////////////
CSmtpAddress::CSmtpAddress(LPCTSTR pszAddress, LPCTSTR pszName)
{
  if (pszAddress) Address = pszAddress;
  if (pszName) Name = pszName;
}

const CSmtpAddress& CSmtpAddress::operator=(LPCTSTR pszAddress)
{
  Address = pszAddress;
  return *this;
}

const CSmtpAddress& CSmtpAddress::operator=(const String& strAddress)
{
  Address = strAddress;
  return *this;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction for CSmtpMessage
//////////////////////////////////////////////////////////////////////
CSmtpMessage::CSmtpMessage()
{
  TIME_ZONE_INFORMATION tzi;
  DWORD dwRet;
  long Offset; 

  // Get local time and timezone offset 
  GetLocalTime(&Timestamp); 
  GMTOffset = 0; 
  dwRet = GetTimeZoneInformation(&tzi); 
  Offset = tzi.Bias; 
  if (dwRet == TIME_ZONE_ID_STANDARD) Offset += tzi.StandardBias; 
  if (dwRet == TIME_ZONE_ID_DAYLIGHT) Offset += tzi.DaylightBias; 
  GMTOffset = -((Offset / 60) * 100 + (Offset % 60)); 

  MimeType = mimeGuess;
}

// Write all the headers to the e-mail message.
// This is done just before sending it, when we're sure the user wants it to go out.
void CSmtpMessage::CommitHeaders()
{
  TCHAR szTime[64];
  TCHAR szDate[64];
  TCHAR szOut[1024];
  String strHeader;
  String strValue;
  int n;

  // Assign a few standard headers to the message
  strHeader = _T("X-Priority");
  strValue  = _T("3 (Normal)");
  // Only add the key if it doesn't exist already in the headers map
  if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);
  
  strHeader = _T("X-MSMail-Priority");
  strValue  = _T("Normal");
  if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);
  
  strHeader = _T("X-Mailer");
  strValue  = _T("Internet Explorer Assistant");
  if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);
  
  strHeader = _T("Importance");
  strValue  = _T("Normal");
  if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);

  // Get the time/date stamp and GMT offset for the Date header.
  GetDateFormat(MAKELCID(LANG_ENGLISH, SORT_DEFAULT),0,&Timestamp,_T("ddd, d MMM yyyy"),szDate,64); 
  GetTimeFormat(MAKELCID(LANG_ENGLISH, SORT_DEFAULT),0,&Timestamp,_T("H:mm:ss"),szTime,64); 
  
  // Add the date/time stamp to the message headers 
  wsprintf(szOut,_T("%s %s %c%4.4d"),szDate,szTime,(GMTOffset>0)?'+':'-',GMTOffset); 
  
  strHeader = _T("Date");
  strValue = szOut;
  Headers.Remove(strHeader);
  Headers.Add(strHeader,strValue);

  // Write out the subject header
  strHeader = _T("Subject");
  strValue = Subject;
  Headers.Remove(strHeader);
  Headers.Add(strHeader,strValue);

  // Write out the TO header
  strValue.erase();
  strHeader = _T("To");
  if (Recipient.Name.length())
  {
    wsprintf(szOut,_T("\"%s\" "),Recipient.Name.c_str());
    strValue += szOut;
  }
  if (Recipient.Address.length())
  {
    wsprintf(szOut,_T("<%s>"),Recipient.Address.c_str());
    strValue += szOut;
  }
  // Write out all the CC'd names
  for (n = 0;n < CC.GetSize();n++)
  {
    if (strValue.length()) strValue += _T(",\r\n\t");
    if (CC[n].Name.length())
    {
      wsprintf(szOut,_T("\"%s\" "),CC[n].Name.c_str());
      strValue += szOut;
    }
    wsprintf(szOut,_T("<%s>"),CC[n].Address.c_str());
    strValue += szOut;
  }
  Headers.Remove(strHeader);
  Headers.Add(strHeader,strValue);

  // Write out the FROM header
  strValue.erase();
  strHeader = _T("From");
  if (Sender.Name.length())
  {
    wsprintf(szOut,_T("\"%s\" "),Sender.Name.c_str());
    strValue += szOut;
  }
  wsprintf(szOut,_T("<%s>"),Sender.Address.c_str());
  strValue += szOut;
  Headers.Remove(strHeader);
  Headers.Add(strHeader,strValue);
}

// Parse a message into a single string
void CSmtpMessage::Parse(String& strDest)
{
  String strHeader;
  String strValue;
  String strTemp;
  String strBoundary;  
  String strInnerBoundary;
  TCHAR szOut[1024];
  int n;

  strDest.erase();
  // Get a count of the sections to see if this will be a multipart message
  n  = Message.GetSize();
  n += Attachments.GetSize();

  // Remove this header in case the message is being reused
  strHeader = _T("Content-Type");
  Headers.Remove(strHeader);

  // If we have more than one section, then this is a multipart MIME message
  if (n > 1)
  {
    wsprintf(szOut,_T("CSmtpMsgPart123X456_000_%8.8X"),GetTickCount());
    strBoundary = szOut;

    lstrcpy(szOut,_T("multipart/"));

    if (MimeType == mimeGuess)
    {
      if (Attachments.GetSize() == 0) MimeType = mimeAlternative;
      else MimeType = mimeMixed;
    }
    switch(MimeType)
    {
    case mimeAlternative:
      lstrcat(szOut,_T("alternative"));
      break;
    case mimeMixed:
      lstrcat(szOut,_T("mixed"));
      break;
    case mimeRelated:
      lstrcat(szOut,_T("related"));
      break;
    }
    lstrcat(szOut,_T(";\r\n\tboundary=\""));
    lstrcat(szOut,strBoundary.c_str());
    lstrcat(szOut,_T("\""));

    strValue = szOut;
    Headers.Add(strHeader,strValue);
  }

  strHeader = _T("MIME-Version");
  strValue  = MIME_VERSION;
  Headers.Remove(strHeader);
  Headers.Add(strHeader,strValue);

  // Remove any message ID in the header and replace it with this message ID, if it exists
  strHeader = _T("Message-ID");
  Headers.Remove(strHeader);
  if (MessageId.length())
  {
    wsprintf(szOut,_T("<%s>"),MessageId.c_str());
    strValue = szOut;
    Headers.Add(strHeader,strValue);
  }

  // Finalize the message headers
  CommitHeaders();

  // Write out all the message headers -- done backwards on purpose!
  for (n = Headers.GetSize();n > 0;n--)
  {
    wsprintf(szOut,_T("%s: %s\r\n"),Headers.GetKeyAt(n-1).c_str(),Headers.GetValueAt(n-1).c_str());
    strDest += szOut;
  }
  if (strBoundary.length())
  {
    wsprintf(szOut,_T("\r\n%s\r\n"),MULTIPART_MESSAGE);
    strDest += szOut;
  }

  // If we have attachments and multiple message bodies, create a new multipart section
  // This is done so we can display our multipart/alternative section separate from the
  // main multipart/mixed environment, and support both attachments and alternative bodies.
  if (Attachments.GetSize() && Message.GetSize() > 1 && strBoundary.length())
  {
    wsprintf(szOut,_T("CSmtpMsgPart123X456_001_%8.8X"),GetTickCount());
    strInnerBoundary = szOut;
    
    wsprintf(szOut,_T("\r\n--%s\r\nContent-Type: multipart/alternative;\r\n\tboundary=\"%s\"\r\n"),strBoundary.c_str(),strInnerBoundary.c_str());
    strDest += szOut;
  }

  for (n = 0;n < Message.GetSize();n++)
  {
    // If we're multipart, then write the boundary line
    if (strBoundary.length() || strInnerBoundary.length())
    {
      strDest += _T("\r\n--");
      // If we have an inner boundary, write that one.  Otherwise write the outer one
      if (strInnerBoundary.length()) strDest += strInnerBoundary;
      else strDest += strBoundary;
      strDest += _T("\r\n");
    }
    strValue.erase();
    strDest += _T("Content-Type: ");
    strDest += Message[n].Encoding;
    // Include the character set if the message is text
    if (_tcsnicmp(Message[n].Encoding.c_str(),_T("text/"),5) == 0)
    {
      wsprintf(szOut,_T(";\r\n\tcharset=\"%s\""),Message[n].Charset.c_str());
      strDest += szOut;
    }
    strDest += _T("\r\n");

    // Encode the message
    strValue = Message[n].Data;
    EncodeMessage(Message[n].TransferEncoding,strValue,strTemp);

    // Write out the encoding method used and the encoded message
    strDest += _T("Content-Transfer-Encoding: ");    
    strDest += strTemp;
    
    // If the message body part has a content ID, write it out
    if (Message[n].ContentId.length())
    {
      wsprintf(szOut,_T("\r\nContent-ID: <%s>"),Message[n].ContentId.c_str());
      strDest += szOut;
    }
    strDest += _T("\r\n\r\n");
    strDest += strValue;
  }

  // If we have multiple message bodies, write out the trailing inner end sequence
  if (strInnerBoundary.length())
  {
    wsprintf(szOut,_T("\r\n--%s--\r\n"),strInnerBoundary.c_str());
    strDest += szOut;
  }

  // Process any attachments
  for (n = 0;n < Attachments.GetSize();n++)
  {
    HANDLE hFile;
    LPBYTE pData;
    LPTSTR pszFile;
    LPTSTR pszExt;
    DWORD dwSize;
    DWORD dwBytes;
    CRegKey cKey;
    TCHAR szType[MAX_PATH];
    TCHAR szFilename[MAX_PATH];

    // Get the filename of the attachment
    strValue = Attachments[n].FileName;

    // Open the file
    lstrcpy(szFilename,strValue.c_str());
    hFile = CreateFile(szFilename,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
    if (hFile != INVALID_HANDLE_VALUE)
    {
      // Get the size of the file, allocate the memory and read the contents.
      dwSize = GetFileSize(hFile,NULL);
      pData = (LPBYTE)malloc(dwSize + 1);
      ZeroMemory(pData,dwSize+1);

      if (ReadFile(hFile,pData,dwSize,&dwBytes,NULL))
      {
        // Write out our boundary marker
        if (strBoundary.length())
        {
          wsprintf(szOut,_T("\r\n--%s\r\n"),strBoundary.c_str());
          strDest += szOut;
        }

        // If no alternate name is supplied, strip the path to get the base filename
        if (!Attachments[n].AltName.length())
        {
          // Strip the path from the filename
          pszFile = _tcsrchr(szFilename,'\\');
          if (!pszFile) pszFile = szFilename;
          else pszFile ++;
        }
        else pszFile = (LPTSTR)Attachments[n].AltName.c_str();

        // Set the content type for the attachment.
        lstrcpy(szType,_T("application/octet-stream"));

        // Check the registry for a content type that overrides the above default
        pszExt = _tcschr(pszFile,'.');
        if (pszExt)
        {
          if (!cKey.Open(HKEY_CLASSES_ROOT,pszExt,KEY_READ))
          {
            DWORD dwSize = MAX_PATH;
            cKey.QueryValue(szType,_T("Content Type"),&dwSize);
            cKey.Close();
          }
        }

        // If the attachment has a specific encoding method, use it instead
        if (Attachments[n].Encoding.length())
          lstrcpy(szType,Attachments[n].Encoding.c_str());

        // Write out the content type and attachment types to the message
        wsprintf(szOut,_T("Content-Type: %s"),szType);
        strDest += szOut;
        // If the content type is text, write the charset
        if (_tcsnicmp(szType,_T("text/"),5) == 0)
        {
          wsprintf(szOut,_T(";\r\n\tcharset=\"%s\""),Attachments[n].Charset.c_str());
          strDest += szOut;
        }
        wsprintf(szOut,_T(";\r\n\tname=\"%s\"\r\n"),pszFile);
        strDest += szOut;

        // Encode the attachment
        EncodeMessage(Attachments[n].TransferEncoding,strValue,strTemp,pData,dwSize);

        // Write out the transfer encoding method
        wsprintf(szOut,_T("Content-Transfer-Encoding: %s\r\n"),strTemp.c_str());
        strDest += szOut;

        // Write out the attachment's disposition

⌨️ 快捷键说明

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