/*
 * 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 <http://www.gnu.org/licenses/>.
 */


#include <config.h>

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif 

#ifdef HAVE_SETJMP_H
#include <setjmp.h>
#endif 

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#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, <hauk@tildeslash.com>
 *
 *  @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);
  
}

