smtp.cpp
来自「Shorthand是一个强大的脚本语言」· C++ 代码 · 共 409 行
CPP
409 行
/////////////////////////////////////////////////////////////////////////////
// $Header: /shorthand/src/smtp.cpp 2 8/28/02 6:27a Arm $
//---------------------------------------------------------------------------
// This file is part of "libAndrix" library - a collection of classes
// and functions developed by Andrei Remenchuk.
//---------------------------------------------------------------------------
// While you may own complete copyright on the project with which you have
// received this file, the author reserves the right to use code contained
// in this very file for any purposes, including publishing and usage in
// any free or commercial software.
//
// You may re-distribute this file or re-use it in your own free or
// commercial software provided that this text is included in the file.
// If you change this file you must include clear notice stating that
// you changed this file and the date of change.
//
// This statement doesn't apply to other files that are part of the same
// package unless otherwise noted.
//---------------------------------------------------------------------------
// (c) 1998-2002 Andrei Remenchuk <andrei@remenchuk.com>
//---------------------------------------------------------------------------
// smtp.cpp - SMTP client classes and functions
/////////////////////////////////////////////////////////////////////////////
#ifdef WIN32
#include <winsock.h>
#define SOCK_ERROR WSAGetLastError()
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#define SOCK_ERROR errno
#endif
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <limits.h>
#include "utils.h"
#include "smtp.h"
#include "except.h"
#pragma hdrstop
#if defined( DEBUG ) || defined( SMTP_TEST )
#define IFD(x) x
#else
#define IFD(x)
#endif
#define debugf TRACE
CSMTPSession::CSMTPSession(const char* host, int port)
{
disconnect();
memset(this, 0, sizeof(CSMTPSession));
this->host = strdup(host);
this->port = port;
socketTimeout = 30;
}
CSMTPSession::~CSMTPSession()
{
#ifdef WIN32
if (sock != 0) closesocket(sock);
if (needWSACleanup) WSACleanup();
#else
if (sock != 0) close(sock);
#endif
if (host != NULL) free(host);
if (errorText != NULL) free(errorText);
if (localHostName != NULL) free(localHostName);
}
void CSMTPSession::setContentType(const char* contentType)
{
this->contentType = contentType;
}
const char* CSMTPSession::getErrorText()
{
return errorText != NULL ? errorText : "";
}
int CSMTPSession::connect()
{
#ifdef WIN32
char buf[80];
gethostname(buf, sizeof(buf)-1);
if (WSAGetLastError() == WSANOTINITIALISED)
{
WSADATA wsa;
memset(&wsa, 0, sizeof(WSADATA));
if (WSAStartup(0x0101, &wsa) == SOCKET_ERROR)
return wsError("WinSock initialization failed (error %d)", WSAGetLastError());
needWSACleanup = true;
}
else
{
IFD(debugf("SMTP: WSA already initialized\n"));
}
#endif
struct hostent* hp;
unsigned int ip;
if (isalpha(host[0])) { /* server address is a name */
hp = gethostbyname(host);
}
else { /* Convert nnn.nnn address to a usable one */
ip = inet_addr(host);
hp = gethostbyaddr((char *)&ip,4,AF_INET);
}
if (hp == NULL)
return wsError("Cannot resolve address \"%s\" (error %d)", host, SOCK_ERROR);
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
memcpy(&(address.sin_addr), hp->h_addr, hp->h_length);
address.sin_family = hp->h_addrtype;
address.sin_port = htons(port);
sock = ::socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
return wsError("couldn't create socket (error %d)", SOCK_ERROR);
if (::connect(sock, (sockaddr*) &address, sizeof(address)) != 0)
return wsError("Connection to %s:%d failed (error %d)", host, port, SOCK_ERROR);
if (localHostName == NULL)
{
localHostName = (char*) malloc(80);
if (gethostname(localHostName, sizeof(localHostName)-1) != 0)
sprintf(localHostName, "localhost");
}
return status = 0;
}
int CSMTPSession::disconnect()
{
if (sock != 0)
{
#ifdef WIN32
closesocket(sock);
#else
close(sock);
#endif
sock = 0;
}
return 0;
}
/*
Wait for file descriptor FD to be readable, MAXTIME being the
timeout in seconds. If WRITEP is non-zero, checks for FD being
writable instead.
Returns 1 if FD is accessible, 0 for timeout and -1 for error in
select().
*/
int select_fd (int fd, int maxtime, int writep)
{
fd_set fds, exceptfds;
struct timeval timeout;
FD_ZERO (&fds);
FD_SET (fd, &fds);
FD_ZERO (&exceptfds);
FD_SET (fd, &exceptfds);
timeout.tv_sec = maxtime;
timeout.tv_usec = 0;
return ::select (fd + 1, writep ? NULL : &fds, writep ? &fds : NULL,
&exceptfds, &timeout);
}
void CSMTPSession::setError(const char* format, va_list args)
{
int width = vawidth(format, &args)+1;
if (width > errorCapacity) {
errorCapacity = width;
if (errorText == NULL) errorText = (char*) malloc(errorCapacity);
else errorText = (char*) realloc(errorText, errorCapacity);
}
vsprintf(errorText, format, args);
//dispatch_log_f(LOG_SMTP_CLIENT, 0, "SMTP: %s", errorText);
}
void CSMTPSession::setError(const char* format, ...)
{
va_list args;
va_start(args,format);
setError(format, args);
}
int CSMTPSession::wsError(const char* format, ...)
{
va_list args;
va_start(args, format);
setError(format, args);
status = SOCK_ERROR;
if (status == 0) status = -1;
return status;
}
int CSMTPSession::post(const char* format, ...)
{
va_list args;
va_start(args,format);
int width = vawidth(format, &args);
char* data = (char*) alloca(width+1);
vsprintf(data, format, args);
int rc = select_fd(sock, socketTimeout, 1);
if (rc == -1)
return wsError("select() failed (error %d)", SOCK_ERROR);
else if (rc == 0)
return wsError("connection timed out");
if (send(sock, data, strlen(data), 0) == -1)
return wsError("send() failed (error %d)", SOCK_ERROR);
flushInData();
//dispatch_log_f(LOG_SMTP_CLIENT, 3, "SMTP:OUT> %s", data);
return status = 0;
}
const char* CSMTPSession::getInData() {
return (inData != NULL) ? inData : "";
}
void CSMTPSession::ensureInCapacity(int n)
{
if (inCapacity < n)
{
inCapacity = n;
if (inData == NULL) inData = (char*) malloc(inCapacity);
else inData = (char*) realloc(inData, inCapacity);
}
}
/**
* Retrieves respose from SMTP server.
*
* return value:
* non-zero SMTP response code if response was successfully receieved.
* zero if error occured or response is invalid.
*/
int CSMTPSession::consume()
{
responseCode = 0;
flushInData();
int bufferSize = 1024;
while(true)
{
int rc = select_fd(sock, socketTimeout, 0);
if (rc == -1) {
setError("select() failed (error %d)", SOCK_ERROR);
return 0;
} else if (rc == 0) {
setError("connection timed out (error %d)", SOCK_ERROR);
return 0;
}
ensureInCapacity(inLength + bufferSize + 1);
int count = recv(sock, inData + inLength, bufferSize, 0);
if (count > 0)
{
inData[inLength+count] = '\0';
char* lf = strchr(inData, '\n');
inLength += count;
if (lf != NULL) break;
}
}
//dispatch_log_f(LOG_SMTP_CLIENT, 3, "SMTP:IN> %s", inData);
char* endp = NULL;
long code = strtol(inData, &endp, 10);
if (code == 0 || code == LONG_MAX || code == LONG_MIN)
{
status = -1401;
setError("Unrecognized response from SMTP server: %s\n", inData);
return 0;
}
return responseCode = code;
}
void CSMTPSession::flushInData()
{
inLength = 0;
if (inData != NULL) *inData = '\0';
}
int CSMTPSession::rejectResponse()
{
setError("Unrecognized response from SMTP server: %s", getInData());
return status = -1402;
}
int CSMTPSession::smtpError()
{
if (responseCode != 0)
{
const char* e = getInData();
//while(*e != '\0' && strchr("0123456789 ", *e) != NULL) e++;
setError("SMTP Error: %s", e);
return responseCode;
}
else
{
return status;
}
}
int CSMTPSession::sendMessage(const char* from, const char* to, const char* subject, const char* body)
{
int rc;
rc = connect(); if (rc) return rc;
IFD(debugf("smtp.connect(%s) succeeded\n", host));
consume();
if (responseCode != 220) return smtpError();
post("HELO %s\r\n", localHostName);
if (status) return status;
consume();
if (responseCode != 250) return smtpError();
post("MAIL FROM: %s\r\n", from);
if (status) return status;
consume();
if (responseCode != 250) return smtpError();
post("RCPT TO: %s\r\n", to);
if (status) return status;
consume();
if (responseCode != 250 && responseCode != 251) return smtpError();
post("DATA\r\n");
if (status) return status;
consume();
if (responseCode != 354) return smtpError();
string agent = "ShortHand";
//agent.printf("%s %d.%d.%d.%d", APP_TITLE, CFG().version(0), CFG().version(1), CFG().version(2), CFG().version(3));
post("From: %s\r\nTo: %s\r\nX-Mailer: %s\r\nSubject: %s\r\n", from, to, agent.cstr(), subject);
if (status) return status;
if (contentType && *contentType)
{
post("Content-Type: %s\r\n", contentType);
if (status) return status;
}
post("\r\n");
if (status) return status;
post("%s", body);
if (status) return status;
post("\r\n.\r\n");
if (status) return status;
consume();
if (responseCode != 250) return smtpError();
post("QUIT\r\n");
consume();
return 0;
}
#ifdef SMTP_TEST
int main(char** argv)
{
const char* host = "smtp-server.nyc.rr.com";
const char* from = "smtptest@remenchuk.com";
const char* to = "remena@agsltd.com";
int port = 25;
CSMTPSession smtp(host, port);
if (smtp.sendMessage(from, to, "Test", "This is a test\r\n\r\nBye") != 0)
{
printf("smtp failed:\n%s\n", smtp.getErrorText());
}
smtp.disconnect();
return 0;
}
#endif // SMTP_TEST
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?