/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sendmail.h"
#define LOGSENDINFO(buffer) if (get_verbose_mode()) { \
logmsg(LOG_INFO, "sendmail() : << %s", buffer); \
}
#define SENDINFO(s, buffer) if (!socket_write(s, 0, "%s\r\n", buffer)) { \
logmsg(LOG_ERR, "sendmail() : %s", strerror(errno)); \
FREE(buffer); \
socket_destroy(s); \
return 0; \
} \
else { \
FREE(buffer); \
}
static int g_sendmail_timeout = 2;
static int g_max_timeout_number = 2;
void set_sendmail_timeout(int second) {
g_sendmail_timeout = second;
}
void set_max_timeout_number(int max) {
g_max_timeout_number = max;
}
int get_helo(char **buffer) {
char ptr_tmp[MAXHOSTNAMELEN];
size_t size;
*buffer = strdup("EHLO ");
if (*buffer == NULL)
return 0;
if (gethostname(ptr_tmp, MAXHOSTNAMELEN) == -1)
return 0;
else {
size = strlen("EHLO ") + strlen(ptr_tmp) + 1;
if (!saferealloc((void **)buffer, *buffer, size * sizeof(char))) {
FREE(*buffer);
return 0;
}
strlcat(*buffer, ptr_tmp, size * sizeof(char));
}
return 1;
}
int get_authplain(char **buffer, const char *authuser, const char *authpass) {
char *auth_64 = NULL;
char *ptr_tmp = NULL;
size_t ptr_size, s;
int authuser_len = 0, authpass_len = 0;
*buffer = strdup("AUTH PLAIN ");
if (*buffer == NULL)
return 0;
ptr_size = strlen(authuser)*2 + 2 + strlen(authpass)+1;
ptr_tmp = (char *)malloc(ptr_size * sizeof(char));
if (ptr_tmp == NULL)
return 0;
/* We prepare the AUTH string in base64 style */
/* <user>\0<user>\0<pass> */
s = 0;
authuser_len = strlen(authuser);
authpass_len = strlen(authpass);
memset(ptr_tmp, 0, ptr_size);
memcpy(ptr_tmp, authuser, authuser_len * sizeof(char));
s += authuser_len;
memcpy(&ptr_tmp[s], "\0", sizeof(char));
s += sizeof(char);
memcpy(&ptr_tmp[s], authuser, authuser_len * sizeof(char));
s += authuser_len;
memcpy(&ptr_tmp[s], "\0", sizeof(char));
s += sizeof(char);
memcpy(&ptr_tmp[s], authpass, authpass_len * sizeof(char));
s += authpass_len;
memcpy(&ptr_tmp[s], "\0", sizeof(char));
if (!base64_encode(&auth_64, ptr_tmp, ptr_size-1)) {
FREE(ptr_tmp);
return 0;
}
FREE(ptr_tmp);
s = strlen("AUTH PLAIN ") + strlen(auth_64) + 1;
if (!saferealloc((void **)buffer, *buffer, s * sizeof(char))) {
FREE(*buffer);
FREE(auth_64);
return 0;
}
strlcat(*buffer, auth_64, s * sizeof(char));
FREE(auth_64);
return 1;
}
int get_mailfrom(char **buffer, const char *mailfrom) {
size_t size;
*buffer = strdup("MAIL FROM: <");
if (*buffer == NULL)
return 0;
size = strlen("MAIL FROM: <") + strlen(mailfrom) + 2;
if (!saferealloc((void **)buffer, *buffer, size * sizeof(char))) {
FREE(*buffer);
return 0;
}
strlcat(*buffer, mailfrom, size * sizeof(char));
strlcat(*buffer, ">", size * sizeof(char));
return 1;
}
int get_rcptto(char **buffer, const char *mailto) {
size_t size;
*buffer = strdup("RCPT TO: <");
if (*buffer == NULL)
return 0;
size = strlen("RCPT TO: <") + strlen(mailto) + 2;
if (!saferealloc((void **)buffer, *buffer, size * sizeof(char))) {
free(*buffer);
*buffer = NULL;
return 0;
}
strlcat(*buffer, mailto, size * sizeof(char));
strlcat(*buffer, ">", size * sizeof(char));
return 1;
}
char *SMTP_STATE_STRING(SMTP_STATE state) {
switch(state) {
case NOTHING:
return "NOTHING";
break;
case EHLO:
return "EHLO";
break;
case MAIL_FROM:
return "MAIL_FROM";
break;
case RCPT_TO:
return "RCPT_TO";
break;
case AUTH_PLAIN:
return "AUTH_PLAIN";
break;
case AUTH_LOGIN:
return "AUTH_LOGIN";
break;
case DATA:
return "DATA";
break;
case QUIT:
return "QUIT";
break;
}
return NULL;
}
int sendmail(const char *host, const char *port,
const char *mailfrom, const char *mailto,
const char *authuser, const char *authpass, SMTP_AUTH auth,
const char *additional_header, const char *msg) {
/*
S : we send
R : We receive
The following is a SMTP communication example
R: 220 server.com Simple Mail Transfer Service Ready
S: EHLO server.com
[R: 250 server.com] -> no AUTH mecanism
or
[R: 250-server.com
R: 250-PIPELINING
R: 250-SIZE 10240000
R: 250-VRFY
R: 250-ETRN
R: 250-AUTH DIGEST-MD5 CRAM-MD5 GSSAPI PLAIN LOGIN -> AUTH mecanism
R: 250-AUTH=DIGEST-MD5 CRAM-MD5 GSSAPI PLAIN LOGIN
R: 250-XVERP
R: 250 8BITMIME
S: AUTH PLAIN dGVzdAB0ZXN0AHRlc3RwYXNz -> AUTH PLAIN mecanism
R: 235 Authentication successful
]
S: MAIL FROM: <sender@example.com>
R: 250 OK
S: RCPT TO: <receiver@example2.com>
R: 250 OK
S: DATA
R: 354 Start mail input: end with <CRLF>.<CRLF>
S: Type everything you want
S: <CRLF>.<CRLF>
R: 250 OK
S: QUIT
R: 250 server.com closing transmission channel
*/
Socket *s = NULL;
fd_set fds;
int n, byte_read, nb_timeout = 0;
struct timeval tv;
char buffer_in[255];
char *buffer_out = NULL;
size_t size;
SMTP_STATE state = NOTHING;
s = socket_new(host, port, "tcp");
if (!socket_connect(s)) {
logmsg(LOG_ERR, "sendmail() : connect() error on %s !", host);
socket_destroy(s);
return 0;
}
for(;;) {
FD_ZERO(&fds);
FD_SET(s->fd, &fds);
tv.tv_sec = g_sendmail_timeout;
tv.tv_usec = 0;
if ((n = select(s->fd+1, &fds, NULL, NULL, &tv)) != -1) {
if (n == 0) {
/* select timeout */
nb_timeout++;
logmsg(LOG_ERR, "sendmail() : select() timeout (%d time(s))", nb_timeout);
if (g_max_timeout_number >= nb_timeout) {
logmsg(LOG_ERR, "sendmail() : max number of select() timeout reached (%d time(s) max)",
g_max_timeout_number);
break;
}
}
else {
if (FD_ISSET(s->fd, &fds)) {
nb_timeout = 0;
memset(buffer_in, 0, 255);
if ((byte_read = socket_readline(s, buffer_in, sizeof(buffer_in))) > 0) {
stripcrlf(buffer_in);
if (get_verbose_mode())
logmsg(LOG_INFO, "sendmail() : >> %s", buffer_in);
if (strncmp(buffer_in, "220 ", strlen("220 ")) == 0) {
/* 220 SMTP Server Name */
if (!get_helo(&buffer_out)) {
logmsg(LOG_ERR, "sendmail() : problem in get_helo()");
socket_destroy(s);
return 0;
}
LOGSENDINFO(buffer_out);
SENDINFO(s, buffer_out);
state = EHLO;
}
else if (strncmp(buffer_in, "250-AUTH ", strlen("250-AUTH ")) == 0) {
/* 250-AUTH PLAIN LOGIN */
if ((authuser == NULL) || (authpass == NULL) || (auth == NONE)) {
logmsg(LOG_ERR, "sendmail() : AUTH method required : set a username/password in your config file");
socket_destroy(s);
return 0;
}
/* We check the LOGIN method, first */
if ((strstr(buffer_in, " LOGIN") != NULL)
&& (auth == LOGIN)) {
state = AUTH_LOGIN;
}
else if ((strstr(buffer_in, " PLAIN") != NULL)
&& (auth == PLAIN)) {
state = AUTH_PLAIN;
}
else {
logmsg(LOG_ERR, "sendmail() : AUTH method other than LOGIN PLAIN, not supported yet");
socket_destroy(s);
return 0;
}
}
else if (strncmp(buffer_in, "250-", strlen("250-")) == 0) {
/* We skip other 250 sub messages */
}
else if (strncmp(buffer_in, "221 ", strlen("221 ")) == 0) {
/* 221 Bye */
socket_destroy(s);
return 1; /* We close the connection and it's OK */
}
else if (strncmp(buffer_in, "235 ", strlen("235 ")) == 0) {
/* 235 Authentication successful */
/* We send "MAIL FROM" command */
if (!get_mailfrom(&buffer_out, mailfrom)) {
logmsg(LOG_ERR, "sendmail() : problem in get_mailfrom()");
socket_destroy(s);
return 0;
}
LOGSENDINFO(buffer_out);
SENDINFO(s, buffer_out);
state = MAIL_FROM;
}
else if (strncmp(buffer_in, "250 ", strlen("250 ")) == 0) {
/* 250 OK */
switch(state) {
case EHLO:
/* We send "MAIL FROM" command */
if (!get_mailfrom(&buffer_out, mailfrom)) {
logmsg(LOG_ERR, "sendmail() : problem in get_mailfrom()");
socket_destroy(s);
return 0;
}
LOGSENDINFO(buffer_out);
SENDINFO(s, buffer_out);
state = MAIL_FROM;
break;
case MAIL_FROM:
/* We send "RCPT TO" command */
if (!get_rcptto(&buffer_out, mailto)) {
logmsg(LOG_ERR, "sendmail() : problem in get_rcptto()");
socket_destroy(s);
return 0;
}
LOGSENDINFO(buffer_out);
SENDINFO(s, buffer_out);
state = RCPT_TO;
break;
case RCPT_TO:
/* We send "DATA" */
buffer_out = strdup("DATA");
LOGSENDINFO(buffer_out);
SENDINFO(s, buffer_out);
state = DATA;
break;
case DATA:
/* We send "quit" */
buffer_out = strdup("QUIT");
LOGSENDINFO(buffer_out);
SENDINFO(s, buffer_out);
state = QUIT;
break;
case AUTH_PLAIN:
/* We do AUTH PLAIN */
if (!get_authplain(&buffer_out, authuser, authpass)) {
logmsg(LOG_ERR, "sendmail() : problem in get_authplain()");
socket_destroy(s);
return 0;
}
LOGSENDINFO(buffer_out);
SENDINFO(s, buffer_out);
break;
default:
logmsg(LOG_ERR, "sendmail() : 250 : current state %s", SMTP_STATE_STRING(state));
socket_destroy(s);
return 0;
break;
}
}
else if (strncmp(buffer_in, "354 ", strlen("354 ")) == 0) {
/* 354 Start mail input: end with <CRLF>.<CRLF> */
switch(state) {
case DATA:
/* Send a proper header */
size = strlen("From: ") + strlen(mailfrom) + 2 /* \r\n */ +
strlen("To: ") + strlen(mailto) + 2 /* \r\n */ +
(additional_header != NULL ? strlen(additional_header) : 0) + 2 /* \r\n */ + 1;
if (!saferealloc((void **)&buffer_out, buffer_out, size * sizeof(char))) {
logmsg(LOG_ERR, "sendmail() : %s", strerror(errno));
socket_destroy(s);
return 0;
}
*buffer_out = '\0';
strlcat(buffer_out, "From: ", size * sizeof(char));
strlcat(buffer_out, mailfrom, size * sizeof(char));
strlcat(buffer_out, "\r\n", size * sizeof(char));
strlcat(buffer_out, "To: ", size * sizeof(char));
strlcat(buffer_out, mailto, size * sizeof(char));
strlcat(buffer_out, "\r\n", size * sizeof(char));
if (additional_header != NULL) {
strlcat(buffer_out, additional_header, size * sizeof(char));
strlcat(buffer_out, "\r\n", size * sizeof(char));
}
SENDINFO(s, buffer_out);
/* We can now send the body */
buffer_out = strdup(msg);
size = strlen(msg) + strlen("\r\n.") + 1;
if (!saferealloc((void **)&buffer_out, buffer_out, size * sizeof(char))) {
logmsg(LOG_ERR, "sendmail() : %s", strerror(errno));
socket_destroy(s);
return 0;
}
strlcat(buffer_out, "\r\n.", size * sizeof(char));
SENDINFO(s, buffer_out);
break;
default:
logmsg(LOG_ERR, "sendmail() : 354 : current state %s", SMTP_STATE_STRING(state));
socket_destroy(s);
return 0;
break;
}
}
else if (strncmp(buffer_in, "535 ", strlen("535 ")) == 0) {
/* 535 Error: Authentication failed */
logmsg(LOG_ERR, "sendmail() : %s", buffer_in);
break;
}
else {
/* We have a SMTP error */
logmsg(LOG_ERR, "sendmail() : SMTP Error -> %s", buffer_in);
break;
}
}
else {
if (errno != 0)
logmsg(LOG_ERR, "sendmail() : readline error : %s (%d)", strerror(errno), errno);
else
logmsg(LOG_ERR, "sendmail() : readline error");
break;
}
}
}
}
else {
logmsg(LOG_ERR, "sendmail() : select() error");
break;
}
}
socket_destroy(s);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1