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

📄 smtp.cpp

📁 一个简单的群发软件源代码
💻 CPP
字号:
#include "stdafx.h"
#include "smtp.h"
#include "Base64.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define DEBUG

CSMTP::response_code CSMTP::response_table[] = 
{
	{ 250, _T( "SMTP server error" ) },
	{ 220, _T( "SMTP server not available" ) },
	{ 354, _T( "SMTP server not ready for data" ) },
	{ 221, _T( "SMTP server didn't terminate session" ) }
};

CSMTP::CSMTP(LPCSTR szSMTPServerName, UINT nPort)
{
	ASSERT( szSMTPServerName != NULL );
	AfxSocketInit();
	m_sSMTPServerHostName = szSMTPServerName;
	m_nPort = nPort;
	m_bConnected = FALSE;
	m_sError = _T("OK");
	response_buf = NULL;
}

CSMTP::~CSMTP()
{
	Disconnect();
}

CString CSMTP::GetServerHostName()
{
	return m_sSMTPServerHostName;
}

#define HOSTNAMELENGTH 88
BOOL CSMTP::Connect()
{
	CString sHello;
	TCHAR local_host[HOSTNAMELENGTH];
	
	// 如果建立了连接则返回
	if( m_bConnected ) 
		return TRUE;

	response_buf = new TCHAR[RESPONSE_BUFFER_SIZE];
	
	if(!response_buf)
	{
		m_sError = _T("内存不足!");
		return FALSE;
	}
	
	if( !m_wsSMTPServer.Create() )
	{
		m_sError = _T("无法创建套接字");
		delete[] response_buf;
		response_buf = NULL;
		return FALSE;
	}

	if(  !m_wsSMTPServer.Connect(GetServerHostName(), GetPort() )  )
	{
		m_sError = _T("无法与服务器建立连接!");
		m_wsSMTPServer.Close();
		delete[] response_buf;
		response_buf = NULL;
		return FALSE;
	}

	if( !get_response(CONNECT_SUCCESS) )
	{
		// 接收应答失败
		m_sError = _T("服务器没有回答");
		m_wsSMTPServer.Close();
		delete[] response_buf;
		response_buf = NULL;
		return FALSE;
	}
	gethostname(local_host, HOSTNAMELENGTH);

	// 以HELO命令开始发信
	sHello.Format( _T("EHLO %s\r\n"), local_host);
	m_wsSMTPServer.Send((LPCSTR) sHello, sHello.GetLength());
	
	if( !get_response(GENERIC_SUCCESS))
	{
		m_wsSMTPServer.Close();
		delete[] response_buf;
		response_buf = NULL;
		return FALSE;
	}

	m_bConnected = TRUE;

	return SendAuthentication();
}

int CSMTP::SendCmd(char *Command)
{
	int len = strlen(Command);
	if(m_wsSMTPServer.Send(Command, len))
	{
		if(m_wsSMTPServer.Receive(response_buf, RESPONSE_BUFFER_SIZE) != SOCKET_ERROR )
		{
			response_buf[3] = 0;
			return atoi(response_buf);
		}
	}
	return -1;
}

BOOL CSMTP::SendAuthentication()
{
  int nRet = 0;
  CBase64 cvt;
  CHAR *pszAuthType = NULL;
  CString TmpS;

  // This is an authentication loop, we can authenticate multiple times in case of failure.
  while(1)
  {
	  TmpS = _T("AUTH LOGIN\r\n");
		nRet = SendCmd((char *)LPCTSTR(TmpS));
    // If it was rejected, we have to abort.
    if (nRet != 334)
    {
      return FALSE;
    }
    // Authentication has 2 stages for username and password.
    // It is possible if the authentication fails here that we can
    // resubmit proper credentials.
    while (1)
    {
		pszAuthType = new CHAR[strlen(response_buf+4)+100];
		if(pszAuthType)
		{
			strcpy(pszAuthType, response_buf+4);
			cvt.Decode(pszAuthType);
			TmpS = cvt.DecodedMessage();
			TmpS = TmpS.Left(9);
			TmpS.MakeLower();

			if (TmpS == "username:")
				cvt.Encode("alex");
			else if (TmpS == "password:")
				cvt.Encode("m85ti8e9");
			else
			{
				delete pszAuthType;
				pszAuthType = NULL;
				break;
			}

			TmpS.Format("%s\r\n",cvt.EncodedMessage());
			nRet = SendCmd((char *)LPCTSTR(TmpS));

			// 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)
			{
				delete pszAuthType;
				pszAuthType = NULL;
				break;
			}
			// Break on any response other than 334, which indicates a request for more information
			if (nRet != 334)
			{
				delete pszAuthType;
				pszAuthType = NULL;
				break;
			}
			else
			{
				delete pszAuthType;
				pszAuthType = NULL;
			}
		}
		else
			break;
    }

	if(pszAuthType)
		delete pszAuthType;
	pszAuthType = NULL;

    // Break if we're not retrying a failed authentication
    if (nRet != TRUE) break;
  }

  if(nRet == 535)
	  return TRUE;

  return (nRet == 235) ? TRUE : FALSE;
}

BOOL CSMTP::Disconnect()
{
	BOOL bRet;
	if( !m_bConnected )
		return TRUE;

	// 发送QUIT命令断开连接
	CString strQuit = _T("QUIT\r\n");
	m_wsSMTPServer.Send((LPCSTR)strQuit, strQuit.GetLength());

	bRet = get_response(QUIT_SUCCESS);
	
	m_wsSMTPServer.Close();

	if( response_buf != NULL )
	{
		delete[] response_buf;
		response_buf = NULL;
	}
	m_bConnected = FALSE;
	
	return bRet;
}

UINT CSMTP::GetPort()
{
	return m_nPort;
}

CString CSMTP::GetLastError()
{
	return m_sError;
}

BOOL CSMTP::SendMessage(CMailMessage* msg)
{
	if( msg == NULL )
		return FALSE;
	if( !m_bConnected )
	{
		m_sError = _T("哟,忘了先连服务器了");
		return FALSE;
	}
	
	if( !FormatMailMessage(msg) )
		return FALSE;

	if( !transmit_message(msg) )
		return FALSE;
	return TRUE;
}

BOOL CSMTP::FormatMailMessage(CMailMessage * msg)
{
	if( !msg ) 
		return FALSE;

	msg->FormatMessage();
	return TRUE;
}

void CSMTP::SetServerProperties(LPCSTR szSMTPServerName, UINT nPort)
{
	if( szSMTPServerName == NULL )
		return;
	m_sSMTPServerHostName = szSMTPServerName;
	m_nPort = nPort;
}

CString CSMTP::cook_body(CMailMessage* msg)
{
	ASSERT(msg);

	CString strTemp = msg->m_strBody;
	CString strCook = _T("");
	CString strBad = _T("\r\n.\r\n");
	CString strGood = _T("\r\n..\r\n");
	int nPos, nStart = 0, nBadLength = strBad.GetLength();

	if( strTemp.Left(3) == _T(".\r\n") )
		strTemp = _T(".") + strTemp;
	while( (nPos = strTemp.Find(strBad)) > -1 )
	{
		strCook = strTemp.Mid(nStart, nPos);
		strCook += strGood;
		strTemp = strCook + strTemp.Right(strTemp.GetLength() - (nPos + nBadLength));
	}
	return strTemp;
}

BOOL CSMTP::transmit_message(CMailMessage *msg)
{
	CString strFrom, strTo, strTemp, strEmail;
	if( msg == NULL )
		return FALSE;
	if( !m_bConnected )
	{
		m_sError = _T("请先建立连接!");
		return FALSE;
	}
	
	// 发送MAIL命令
	strFrom.Format( _T("MAIL From: <%s>\r\n"), (LPCSTR)msg->m_strFrom);
	m_wsSMTPServer.Send((LPCSTR)strFrom, strFrom.GetLength());
	if( !get_response(GENERIC_SUCCESS) )
		return FALSE;
	
	// 发送RCPT命令
//#ifdef DEBUG 
//	strTo.Format(_T("RCPT TO: <%s>\r\n"), (LPCSTR)"beanstud-007@smtp.dzbjet.com");
//#else	
	strTo.Format(_T("RCPT TO: <%s>\r\n"), (LPCSTR)msg->m_strTo);
//#endif
	m_wsSMTPServer.Send((LPCSTR)strTo, strTo.GetLength());
	if( !get_response(GENERIC_SUCCESS) ) return FALSE;

	// 发送DATA命令
	strTemp = _T("DATA\r\n");
	m_wsSMTPServer.Send((LPCSTR)strTemp, strTemp.GetLength());
	if( !get_response(DATA_SUCCESS) )
	{
		// 发送DATA命令失败
		return FALSE;
	}

	// 发送邮件头
	m_wsSMTPServer.Send((LPCSTR)msg->m_strHeader, msg->m_strHeader.GetLength());
	
	// 发送邮件正文
	strTemp = cook_body(msg);
	m_wsSMTPServer.Send((LPCSTR)strTemp, strTemp.GetLength());

	// 给邮件正文加上结束标志
	strTemp = _T("\r\n.\r\n");
	m_wsSMTPServer.Send((LPCSTR)strTemp, strTemp.GetLength());
	if(!get_response(GENERIC_SUCCESS))
		return FALSE;
	return TRUE;
}

BOOL CSMTP::get_response(UINT response_expected)
{
	ASSERT( response_expected >= GENERIC_SUCCESS );
	ASSERT( response_expected < LAST_RESPONSE );

	CString sResponse;
	UINT response;
	response_code* pResp;

	if( m_wsSMTPServer.Receive(response_buf, RESPONSE_BUFFER_SIZE) == SOCKET_ERROR )
	{
		m_sError = _T("套接字错误");
		return FALSE;
	}
	sResponse = response_buf;
	sscanf( (LPCSTR)sResponse.Left(3), _T("%d"), &response );
	pResp = &response_table[response_expected];
	if( response != pResp->nResponse )
	{
		m_sError.Format(_T("%d : %s"), response, (LPCSTR)pResp->sMessage );
		return FALSE;
	}
	return TRUE;
}

void CSMTP::Cancel()
{
	// 取消阻塞的连接
	m_wsSMTPServer.CancelBlockingCall();
	
	// 关闭套接字
	m_wsSMTPServer.Close();
}

⌨️ 快捷键说明

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