/* * Copyright (C), 2000-2007 by the monit project group. * All Rights Reserved. * * 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 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #ifdef HAVE_STDIO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_SETJMP_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "monitor.h" #include "net.h" #include "socket.h" #include "base64.h" /** * Connect to a SMTP server and send mail. * * @author Jan-Henrik Haukeland, * * @version \$Id: sendmail.c,v 1.55 2007/11/06 20:32:30 martinp Exp $ * * @file */ /* ------------------------------------------------------------- Definitions */ typedef struct { Socket_T socket; sigjmp_buf error; const char *server; int port; const char *username; const char *password; Ssl_T ssl; char localhost[STRLEN]; } SendMail_T; /* -------------------------------------------------------------- Prototypes */ static void do_status(SendMail_T *S); static void open_server(SendMail_T *S); static void do_send(SendMail_T *S, const char *, ...); /* ------------------------------------------------------------------ Public */ /** * Send mail messages via SMTP * @param mail A Mail object * @return FALSE if failed, TRUE if passed */ int sendmail(Mail_T mail) { int rv; Mail_T m; SendMail_T S; char *b64 = NULL; char now[STRLEN]; ASSERT(mail); if((rv = sigsetjmp(S.error, TRUE))) { rv = FALSE; goto exit; } else { rv = TRUE; } open_server(&S); Util_getRFC822Date(NULL, now, STRLEN); if(gethostname(S.localhost, sizeof(S.localhost)) < 0) { snprintf(S.localhost, sizeof(S.localhost), "%s", LOCALHOST); } do_status(&S); /* Use EHLO if TLS or Authentication is requested */ if((S.ssl.use_ssl && S.ssl.version == SSL_VERSION_TLS) || S.username) { do_send(&S, "EHLO %s\r\n", S.localhost); } else { do_send(&S, "HELO %s\r\n", S.localhost); } do_status(&S); /* Switch to TLS now if configured */ if(S.ssl.use_ssl && S.ssl.version == SSL_VERSION_TLS) { do_send(&S, "STARTTLS\r\n"); do_status(&S); if(!socket_switch2ssl(S.socket, S.ssl)) { rv = FALSE; goto exit; } } /* Authenticate if possible */ if(S.username) { unsigned char buffer[STRLEN]; int len; len = snprintf((char *)buffer, STRLEN, "%c%s%c%s", '\0', S.username, '\0', S.password?S.password:""); b64 = encode_base64(len, buffer); do_send(&S, "AUTH PLAIN %s\r\n", b64); do_status(&S); } for(m= mail; m; m= m->next) { do_send(&S, "MAIL FROM: <%s>\r\n", m->from); do_status(&S); do_send(&S, "RCPT TO: <%s>\r\n", m->to); do_status(&S); do_send(&S, "DATA\r\n"); do_status(&S); do_send(&S, "From: %s\r\n", m->from); do_send(&S, "To: %s\r\n", m->to); do_send(&S, "Subject: %s\r\n", m->subject); do_send(&S, "Date: %s\r\n", now); do_send(&S, "X-Mailer: %s %s\r\n", prog, VERSION); do_send(&S, "Mime-Version: 1.0\r\n"); do_send(&S, "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"); do_send(&S, "Content-Transfer-Encoding: quoted-printable\r\n"); do_send(&S, "Message-id: <%ld@%s>\r\n", time(NULL), S.localhost); do_send(&S, "\r\n"); do_send(&S, "%s\r\n", m->message); do_send(&S, ".\r\n"); do_status(&S); } do_send(&S, "QUIT\r\n"); do_status(&S); exit: if(S.socket) socket_free(&S.socket); if(b64) FREE(b64); return rv; } /* ----------------------------------------------------------------- Private */ void do_send(SendMail_T *S, const char *s, ...) { long len; va_list ap; char *msg= NULL; va_start(ap,s); msg= Util_formatString(s, ap, &len); va_end(ap); if (socket_write(S->socket, msg, strlen(msg)) <= 0) { FREE(msg); LogError("Sendmail: error sending data to the server '%s' -- %s\n", S->server, STRERROR); siglongjmp(S->error, TRUE); } FREE(msg); } static void do_status(SendMail_T *S) { int status; char buf[STRLEN]; if(!socket_readln(S->socket, buf, sizeof(buf))) { LogError("Sendmail: error receiving data from the mailserver '%s' -- %s\n", S->server, STRERROR); siglongjmp(S->error, TRUE); } Util_chomp(buf); sscanf(buf, "%d", &status); if(status >= 400) { LogError("Sendmail error: %s\n", buf); siglongjmp(S->error, TRUE); } } static void open_server(SendMail_T *S) { MailServer_T mta= Run.mailservers; if(mta) { S->server = mta->host; S->port = mta->port; S->username = mta->username; S->password = mta->password; S->ssl = mta->ssl; } else { S->server = LOCALHOST; S->port = PORT_SMTP; S->username = NULL; S->password = NULL; S->ssl.use_ssl = FALSE; } do { /* wait with ssl-connect if SSL_VERSION_TLS is set (rfc2487) */ if(!S->ssl.use_ssl || S->ssl.version == SSL_VERSION_TLS) { S->socket= socket_new(S->server, S->port, SOCKET_TCP, FALSE, Run.mailserver_timeout); } else { S->socket= socket_create_t(S->server, S->port, SOCKET_TCP, S->ssl, Run.mailserver_timeout); } if(S->socket) break; LogError("Cannot open a connection to the mailserver '%s:%i' -- %s\n", S->server, S->port, STRERROR); if(mta && (mta= mta->next)) { S->server = mta->host; S->port = mta->port; S->username = mta->username; S->password = mta->password; S->ssl = mta->ssl; LogInfo("Trying the next mail server '%s:%i'\n", S->server, S->port); continue; } else { LogError("No mail servers are available\n"); siglongjmp(S->error, TRUE); } } while(TRUE); }