// nullmailer -- a simple relay-only MTA
// Copyright (C) 2006  Bruce Guenter <bruce@untroubled.org>
//
// 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 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
//
// You can contact me at <bruce@untroubled.org>.  There is also a mailing list
// available to discuss this package.  To subscribe, send an email to
// <nullmailer-subscribe@lists.untroubled.org>.

#include "config.h"
#include <stdlib.h>
#include <unistd.h>
#include "base64.h"
#include "connect.h"
#include "errcodes.h"
#include "fdbuf/fdbuf.h"
#include "itoa.h"
#include "mystring/mystring.h"
#include "protocol.h"

int port = 25;
const char* cli_program = "smtp";
const char* cli_help_prefix = "Send an email message via SMTP\n";

class smtp 
{
  fdibuf in;
  fdobuf out;
public:
  smtp(int fd);
  ~smtp();
  int get(mystring& str);
  int put(mystring cmd, mystring& result);
  void docmd(mystring cmd, int range, bool show_succ=false);
  void send_data(fdibuf* msg);
  void send_envelope(fdibuf* msg);
  void send(fdibuf* msg);
};

smtp::smtp(int fd)
  : in(fd), out(fd)
{
}

smtp::~smtp()
{
}

int smtp::get(mystring& str)
{
  mystring tmp;
  str = "";
  int code = -1;
  while(in.getline(tmp)) {
    if(tmp[tmp.length()-1] == '\r')
      tmp = tmp.left(tmp.length()-1);
    code = atoi(tmp.c_str());
    if(!!str)
      str += "/";
    str += tmp;
    if(tmp[3] != '-')
      break;
  }
  return code;
}

int smtp::put(mystring cmd, mystring& result)
{
  out << cmd << "\r\n";
  if(!out.flush())
    return -1;
  return get(result);
}

void smtp::docmd(mystring cmd, int range, bool show_succ)
{
  mystring msg;
  int code;
  if(!cmd)
    code = get(msg);
  else
    code = put(cmd, msg);
  if(code < range || code >= (range+100)) {
    int e;
    if(code >= 500)
      e = ERR_MSG_PERMFAIL;
    else if(code >= 400)
      e = ERR_MSG_TEMPFAIL;
    else
      e = ERR_PROTO;
    out << "QUIT\r\n";
    out.flush();
    protocol_fail(e, msg.c_str());
  }
  else if(show_succ)
    protocol_succ(msg.c_str());
}

void smtp::send_envelope(fdibuf* msg)
{
  mystring tmp;
  msg->getline(tmp);
  docmd("MAIL FROM:<" + tmp + ">", 200);
  while(msg->getline(tmp) && !!tmp)
    docmd("RCPT TO:<" + tmp + ">", 200);
}

void smtp::send_data(fdibuf* msg)
{
  docmd("DATA", 300);
  mystring tmp;
  while(msg->getline(tmp)) {
    if((tmp[0] == '.' && !(out << ".")) ||
       !(out << tmp << "\r\n"))
      protocol_fail(ERR_MSG_WRITE, "Error sending message to remote");
  }
  docmd(".", 200, true);
}

void smtp::send(fdibuf* msg)
{
  send_envelope(msg);
  send_data(msg);
}

void protocol_prep(fdibuf*)
{
}

void protocol_send(fdibuf* in, int fd)
{
  mystring hh = getenv("HELOHOST");
  if (!hh) protocol_fail(1, "$HELOHOST is not set");
  smtp conn(fd);
  conn.docmd("", 200);

  if (user != 0 && pass != 0) {
    conn.docmd("EHLO " + hh, 200);
    if (auth_method == AUTH_LOGIN) {
      mystring encoded;
      base64_encode(user, encoded);
      conn.docmd("AUTH LOGIN " + encoded, 300);
      encoded = "";
      base64_encode(pass, encoded);
      conn.docmd(encoded, 200);
    }
    else {
      mystring plain(user);
      plain += '\0';
      plain += user;
      plain += '\0';
      plain += pass;
      mystring encoded = "AUTH PLAIN ";
      base64_encode(plain, encoded);
      conn.docmd(encoded, 200);
    }
  }
  else
    conn.docmd("HELO " + hh, 200);

  conn.send(in);
}


syntax highlighted by Code2HTML, v. 0.9.1