304 lines
6.7 KiB
C++
304 lines
6.7 KiB
C++
|
//
|
||
|
// SMTP - Simple Mail Transfer Protocol Code
|
||
|
//
|
||
|
// Implements sends mail using SMTP RFC 821.
|
||
|
// Julian Jiggins, 12 January 1997
|
||
|
//
|
||
|
|
||
|
#include "private.h"
|
||
|
#include <winsock.h>
|
||
|
|
||
|
#define TF_THISMODULE TF_MAILAGENT
|
||
|
|
||
|
#define IS_DIGIT(ch) InRange(ch, TEXT('0'), TEXT('9'))
|
||
|
|
||
|
//
|
||
|
// Function prototypes for this module
|
||
|
//
|
||
|
SOCKET Connect(char *host, char *port);
|
||
|
|
||
|
#define READ_BUF_LEN 512
|
||
|
|
||
|
//
|
||
|
// Read all you can from a socket
|
||
|
//
|
||
|
void Read(SOCKET sock, char * readBuffer, int bufLen)
|
||
|
{
|
||
|
int numRead;
|
||
|
int totalRead = 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
numRead = recv(sock, readBuffer+totalRead, bufLen, 0);
|
||
|
totalRead += numRead;
|
||
|
}
|
||
|
while (0);
|
||
|
// while (numRead > 0);
|
||
|
|
||
|
//
|
||
|
// NULL terminate read string
|
||
|
//
|
||
|
readBuffer[totalRead] = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Send a string specifying an SMTP command and read in response, returning
|
||
|
// TRUE if it is the one expected.
|
||
|
// Note the SMTP protocol is designed such that only the 1 character of the
|
||
|
// response need be checked, but we check for the exact response (mostly cause
|
||
|
// I just read that bit in the RFC)
|
||
|
//
|
||
|
BOOL SendAndExpect(SOCKET sock, char * sendBuffer, char * szExpect)
|
||
|
{
|
||
|
char readBuffer[READ_BUF_LEN];
|
||
|
int len;
|
||
|
int numSent;
|
||
|
|
||
|
//
|
||
|
// Send string to socket
|
||
|
//
|
||
|
numSent = send(sock, sendBuffer, lstrlenA(sendBuffer), 0);
|
||
|
if (numSent == SOCKET_ERROR)
|
||
|
{
|
||
|
DBG_WARN("Error on send");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now read in response
|
||
|
//
|
||
|
Read(sock, readBuffer, READ_BUF_LEN);
|
||
|
|
||
|
DBG2("Sent: %s", sendBuffer);
|
||
|
DBG2("Read: %s", readBuffer);
|
||
|
|
||
|
//
|
||
|
// Expect beginning of response to contain szExpect string
|
||
|
//
|
||
|
len = lstrlenA(szExpect);
|
||
|
if (CompareStringA(LOCALE_SYSTEM_DEFAULT, 0,
|
||
|
readBuffer, len, szExpect, len) == 2)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#define SMTP_221 "221"
|
||
|
#define SMTP_250 "250"
|
||
|
#define SMTP_354 "354"
|
||
|
#define SMTP_EOM "\r\n.\r\n"
|
||
|
|
||
|
//
|
||
|
// Carry out SMTP negotiation
|
||
|
//
|
||
|
BOOL SMTPSendEmail(SOCKET sock, char * szToAddress, char * szFromAddress, char *szMessage)
|
||
|
{
|
||
|
char sendBuffer[256];
|
||
|
char readBuffer[READ_BUF_LEN];
|
||
|
BOOL b = TRUE;
|
||
|
int r, len;
|
||
|
|
||
|
//
|
||
|
// Read the opening response
|
||
|
//
|
||
|
Read(sock, readBuffer, sizeof(readBuffer));
|
||
|
DBG(readBuffer);
|
||
|
|
||
|
//
|
||
|
// say Hello and specify my domain
|
||
|
//
|
||
|
b = SendAndExpect(sock, "HELO ActiveDesktop\r\n", SMTP_250);
|
||
|
if (!b) goto done;
|
||
|
|
||
|
//
|
||
|
// First special sender in MAIL command
|
||
|
//
|
||
|
wnsprintfA(sendBuffer, ARRAYSIZE(sendBuffer), "MAIL FROM:<%s>\r\n",
|
||
|
szFromAddress);
|
||
|
b = SendAndExpect(sock, sendBuffer, SMTP_250);
|
||
|
if (!b) goto done;
|
||
|
|
||
|
//
|
||
|
// Now specify recipient(s)
|
||
|
//
|
||
|
wnsprintfA(sendBuffer, ARRAYSIZE(sendBuffer), "RCPT TO:<%s>\r\n",
|
||
|
szToAddress);
|
||
|
b = SendAndExpect(sock, sendBuffer, SMTP_250);
|
||
|
if (!b) goto done;
|
||
|
|
||
|
//
|
||
|
// Now send DATA command
|
||
|
//
|
||
|
b = SendAndExpect(sock, "DATA\r\n", SMTP_354);
|
||
|
if (!b) goto done;
|
||
|
|
||
|
//
|
||
|
// Now send mail message
|
||
|
//
|
||
|
len = lstrlenA(szMessage);
|
||
|
r = send(sock, szMessage, len, 0);
|
||
|
ASSERT(r != SOCKET_ERROR);
|
||
|
ASSERT(r == len);
|
||
|
|
||
|
//
|
||
|
// Specify end of message with single period.
|
||
|
//
|
||
|
b = SendAndExpect(sock, SMTP_EOM, SMTP_250);
|
||
|
if (!b) goto done;
|
||
|
|
||
|
//
|
||
|
// Say goodbye
|
||
|
//
|
||
|
b = SendAndExpect(sock, "QUIT\r\n", SMTP_221);
|
||
|
|
||
|
done:
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Main entry point -
|
||
|
// start winsock dll,
|
||
|
// connect to socket,
|
||
|
// and negotiate transfer
|
||
|
//
|
||
|
SMTPSendMessage(char * szServer, char * szToAddress, char * szFromAddress, char * szMessage)
|
||
|
{
|
||
|
int err;
|
||
|
SOCKET sock;
|
||
|
BOOL b = FALSE;
|
||
|
WSADATA wsaData;
|
||
|
|
||
|
//
|
||
|
// Init the winsock dll specifying which version we want.
|
||
|
//
|
||
|
err = WSAStartup((WORD)0x0101, &wsaData);
|
||
|
if (err)
|
||
|
{
|
||
|
DBG_WARN("WinSock startup error");
|
||
|
return FALSE;
|
||
|
}
|
||
|
DBG("WinSock successfully started");
|
||
|
|
||
|
//
|
||
|
// Actually form the socket connection to the host on port 25
|
||
|
//
|
||
|
sock = Connect(szServer, "25");
|
||
|
|
||
|
if (sock != 0)
|
||
|
{
|
||
|
DBG("Connected");
|
||
|
b = SMTPSendEmail(sock, szToAddress, szFromAddress, szMessage);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Done with winsock dll for now
|
||
|
//
|
||
|
WSACleanup();
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
#ifdef TEST
|
||
|
int
|
||
|
main(int argc, char * argv[])
|
||
|
{
|
||
|
char szMessage[1024];
|
||
|
BOOL b;
|
||
|
|
||
|
//
|
||
|
// Build message
|
||
|
//
|
||
|
lstrcpy(szMessage, "Subject: Subscription Updated: CNN Interactive\r\n");
|
||
|
lstrcat(szMessage, "Your subscription to CNN Interactive Subscription has been updated\r\n\r\n");
|
||
|
lstrcat(szMessage, "To view the subscription offline, just click here: ");
|
||
|
lstrcat(szMessage, "http://www.cnn.com\r\n");
|
||
|
lstrcat(szMessage, "\r\nThis message was sent by the IE4.0 Information Delivery Agent\r\n");
|
||
|
lstrcat(szMessage, "\r\n\r\n\r\nDoes this look okay guys? Julian.");
|
||
|
|
||
|
b = SMTPSendMessage("saranac", "joepe@microsoft.com", szMessage);
|
||
|
if (b)
|
||
|
DBG("Sent mail successfully");
|
||
|
else
|
||
|
DBG("Couldn't send email");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
SOCKET
|
||
|
Connect(char *host, char *port)
|
||
|
{
|
||
|
struct sockaddr_in sockaddress;
|
||
|
DWORD err;
|
||
|
SOCKET sock, connectresult;
|
||
|
|
||
|
//
|
||
|
// Get the socket address
|
||
|
//
|
||
|
if(IS_DIGIT(*host))
|
||
|
sockaddress.sin_addr.s_addr=inet_addr(host);
|
||
|
else
|
||
|
{
|
||
|
struct hostent *hp;
|
||
|
if((hp=gethostbyname(host))==NULL)
|
||
|
{
|
||
|
DBG_WARN2("Unknown host %s", host);
|
||
|
return 0;
|
||
|
}
|
||
|
memcpy(&sockaddress.sin_addr, hp->h_addr, sizeof(sockaddress.sin_addr));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the port address
|
||
|
//
|
||
|
if(IS_DIGIT(*port))
|
||
|
sockaddress.sin_port=htons((USHORT)StrToIntA(port));
|
||
|
else
|
||
|
{
|
||
|
DBG_WARN("The port should be a number");
|
||
|
return 0;
|
||
|
}
|
||
|
sockaddress.sin_family=AF_INET;
|
||
|
|
||
|
//
|
||
|
// Create a stream style socket
|
||
|
//
|
||
|
if((sock=socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
|
||
|
DBG_WARN("socket error");
|
||
|
|
||
|
|
||
|
DBG("Trying to connect");
|
||
|
|
||
|
connectresult=connect(sock,(struct sockaddr *) &sockaddress, sizeof(sockaddress));
|
||
|
|
||
|
if (connectresult == SOCKET_ERROR)
|
||
|
{
|
||
|
switch(err = WSAGetLastError())
|
||
|
{
|
||
|
case WSAECONNREFUSED:
|
||
|
DBG_WARN("ERROR - CONNECTION REFUSED.");
|
||
|
break;
|
||
|
case WSAENETUNREACH:
|
||
|
DBG_WARN("ERROR - THE NETWORK IS NOT REACHABLE FROM THIS HOST.");
|
||
|
break;
|
||
|
case WSAEINVAL:
|
||
|
DBG_WARN("ERROR - The socket is not already bound to an address.");
|
||
|
break;
|
||
|
case WSAETIMEDOUT:
|
||
|
DBG_WARN("ERROR - Connection timed out.");
|
||
|
break;
|
||
|
default:
|
||
|
DBG_WARN2("Couldn't connect %d", err);
|
||
|
break;
|
||
|
}
|
||
|
closesocket(sock);
|
||
|
return 0;
|
||
|
}
|
||
|
return sock;
|
||
|
}
|