/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2006-2020 Sergey Poznyakoff

   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, 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/>. */

MF_BUILTIN_MODULE

#include "msg.h"

static int
_send(eval_environ_t env,
      const char *mailer_url, char *to, char *from, mu_message_t msg,
      void (*destroy)(void*), void *ptr)
{
	int status;
	mu_mailer_t mailer;
	mu_address_t to_addr = NULL;
	mu_address_t from_addr = NULL;
	
	if (!mailer_url)
		mu_mailer_get_url_default(&mailer_url);
	status = mu_mailer_create(&mailer, mailer_url);
	if (status) {
		destroy(ptr);
		MF_THROW(mfe_failure,
			 _("cannot create mailer `%s': %s"),
			 mailer_url, mu_strerror(status));
	}

	if (to) {
		status = mu_address_create(&to_addr, to);
		if (status) {
			destroy(ptr);
			mu_mailer_destroy(&mailer);
			MF_THROW(mfe_failure,
				 _("bad recipient address `%s': %s"),
				 to, mu_strerror(status));
		}
	}
	
	if (!from || from[0] == 0)
		from = "<>";
	status = mu_address_create(&from_addr, from);
	if (status) {
		mu_address_destroy(&to_addr);
		mu_mailer_destroy(&mailer);
		destroy(ptr);
		MF_THROW(mfe_failure,
			 _("bad sender address `%s': %s"),
			 from, mu_strerror(status));
	}
	
	/* FIXME: mailer flags? */
	status = mu_mailer_open(mailer, 0);
	if (status) {
		mu_address_destroy(&to_addr);
		mu_address_destroy(&from_addr);
		mu_mailer_destroy(&mailer);
		destroy(ptr);
		MF_THROW(mfe_failure,
			 _("opening mailer `%s' failed: %s"),
			 mailer_url, mu_strerror (status));
	}
	
	status = mu_mailer_send_message(mailer, msg, from_addr, to_addr);
	mu_address_destroy(&to_addr);
	mu_address_destroy(&from_addr);
	mu_mailer_destroy(&mailer);
	destroy(ptr);
	MF_ASSERT(status == 0, mfe_failure,
		  _("cannot send message: %s"),
		  mu_strerror(status));
	return 0;
}

static void
_destroy_msg(void *ptr)
{
	mu_message_t msg = ptr;
	mu_message_destroy(&msg, mu_message_get_owner(msg));
}

MF_DEFUN(send_mail, VOID, STRING text, 
	 OPTIONAL, STRING to, STRING from, STRING mailer_url)
{
	mu_message_t msg = NULL;
	mu_stream_t stream = NULL;
	
	mu_message_create(&msg, NULL);
	mu_static_memory_stream_create(&stream, text, strlen(text));
	mu_message_set_stream(msg, stream, mu_message_get_owner(msg));
	
	_send(env, MF_OPTVAL(mailer_url, NULL),
	      MF_OPTVAL(to, NULL),
              MF_OPTVAL(from, NULL), msg, _destroy_msg, msg);
	mu_stream_unref(stream);
}
END

MF_DEFUN(send_message, VOID, NUMBER nmsg,
	 OPTIONAL, STRING to, STRING from, STRING mailer_url)
{
	mu_message_t msg = bi_message_from_descr(env, nmsg);
	_send(env, MF_OPTVAL(mailer_url, NULL),
	      MF_OPTVAL(to, NULL),
              MF_OPTVAL(from, NULL), msg, _destroy_msg, msg);
}
END

static void
add_headers(mu_message_t msg, char *headers)
{
	mu_stream_t stream = NULL;
	mu_header_t hdr = NULL;
	size_t len;

	if (!headers)
		return;
	
	len = strlen(headers);
	mu_message_get_header(msg, &hdr);
	
	mu_header_get_streamref(hdr, &stream);
	mu_stream_seek(stream, 0, MU_SEEK_END, NULL);
	mu_stream_write(stream, headers, len, NULL);
	if (len < 2 || memcmp(headers + len - 2, "\n\n", 2)) {
		if (len > 1 && headers[len-1] == '\n')
			mu_stream_write(stream, "\n", 1, NULL);
		else
			mu_stream_write(stream, "\n\n", 2, NULL);
	}
	mu_stream_unref(stream);
}

MF_DEFUN(send_text, VOID, STRING text, STRING headers, 
	 OPTIONAL, STRING to, STRING from, STRING mailer_url)
{
	mu_message_t msg = NULL;
	mu_body_t body = NULL;
	mu_stream_t stream = NULL;
	
	mu_message_create(&msg, NULL);

	mu_message_get_body(msg, &body);
	mu_body_get_streamref(body, &stream);
	mu_stream_write(stream, text, strlen(text), NULL);
	mu_stream_unref(stream);
	
	add_headers(msg, headers);
	_send(env, MF_OPTVAL(mailer_url, NULL), MF_OPTVAL(to, NULL), 
              MF_OPTVAL(from, NULL), msg, _destroy_msg, msg);
}
END


static void
mime_create_reason(mu_mime_t mime, const char *sender, const char *text)
{
	mu_message_t newmsg;
	mu_stream_t stream;
	time_t t;
	struct tm *tm;
	mu_body_t body;
	mu_header_t hdr;
	char datestr[80];
	static char *content_header =
		"Content-Type: text/plain;charset=US-ASCII\n" /* FIXME! */
		"Content-Transfer-Encoding: 8bit\n";
 
	mu_message_create(&newmsg, NULL);
	mu_message_get_body(newmsg, &body);
	mu_body_get_streamref(body, &stream);

	time(&t);
	tm = localtime(&t);
	mu_strftime(datestr, sizeof datestr, "%a, %b %d %H:%M:%S %Y %Z", tm);

	mu_stream_printf(stream,
			 "\n"
			 "The original message was received at %s from %s.\n",
			 datestr, sender);

	mu_stream_write(stream, text, strlen(text), NULL);
	mu_stream_flush(stream);
	mu_stream_unref(stream);
	mu_header_create(&hdr, content_header, strlen(content_header));
	mu_message_set_header(newmsg, hdr, NULL);
	mu_mime_add_part(mime, newmsg);
	mu_message_unref(newmsg);
}

static void
mime_create_ds(mu_mime_t mime, char *recpt)
{
	mu_message_t newmsg;
	mu_stream_t stream;
	mu_header_t hdr;
	mu_body_t body;
	char datestr[80];
	time_t t;
	struct tm *tm;
	
	time(&t);
	tm = localtime(&t);
	mu_strftime(datestr, sizeof datestr, "%a, %b %d %H:%M:%S %Y %Z", tm);

	mu_message_create(&newmsg, NULL);
	mu_message_get_header(newmsg, &hdr); 
	mu_header_set_value(hdr, "Content-Type", "message/delivery-status", 1);
	mu_message_get_body(newmsg, &body);
	mu_body_get_streamref(body, &stream);
	mu_stream_printf(stream, "Reporting-UA: %s\n", PACKAGE_STRING);
	mu_stream_printf(stream, "Arrival-Date: %s\n", datestr);
	mu_stream_printf(stream, "Final-Recipient: RFC822; %s\n",
			 recpt ? recpt : "unknown");
	mu_stream_printf(stream, "%s", "Action: deleted\n");
	mu_stream_printf(stream, "%s",
			 "Disposition: automatic-action/"
			 "MDN-sent-automatically;deleted\n");
	mu_stream_printf(stream, "Last-Attempt-Date: %s\n", datestr);
	mu_stream_unref(stream);
	mu_mime_add_part(mime, newmsg);
	mu_message_unref(newmsg);
}

/* Quote original message */
static int
mime_create_quote(mu_mime_t mime, mu_stream_t istream)
{
	mu_message_t newmsg;
	mu_stream_t ostream;
	mu_header_t hdr;
	mu_body_t body;
	char *buf = NULL;
	size_t bufsize = 0;
	
	mu_message_create(&newmsg, NULL);
	mu_message_get_header(newmsg, &hdr); 
	mu_header_set_value(hdr, "Content-Type", "message/rfc822", 1);
	mu_message_get_body (newmsg, &body);
	mu_body_get_streamref(body, &ostream);
	/* Skip envelope line */
	mu_stream_getline(istream, &buf, &bufsize, NULL);
	free(buf);
	mu_stream_copy(ostream, istream, 0, NULL);
	mu_stream_unref(ostream);
	mu_mime_add_part(mime, newmsg);
	mu_message_unref(newmsg);
	return 0;
}
  
static int
build_mime(mu_mime_t *pmime, mu_stream_t msgstr,
	   char *sender, char *recpt,
	   char *text)
{
	mu_mime_t mime = NULL;
	int status;
  
	mu_mime_create(&mime, NULL, 0);
	mime_create_reason(mime, sender, text);
	mime_create_ds(mime, recpt);
        status = mime_create_quote(mime, msgstr);
	if (status) {
		mu_mime_destroy(&mime);
		return status;
	}
	*pmime = mime;
	return 0;
}

static void
_destroy_mime(void *ptr)
{
	mu_mime_t mime = ptr;
	mu_mime_destroy(&mime);
}

MF_STATE(eom)
MF_CAPTURE(mstr)
MF_DEFUN(create_dsn, NUMBER, STRING sender, STRING recpt, STRING text,
	 OPTIONAL, STRING headers, STRING from)
{
	int rc;
	mu_mime_t mime;
	mu_message_t msg;
	
	rc = build_mime(&mime, mstr, sender, recpt, text);
	MF_ASSERT(rc == 0,
		  mfe_failure,
		  _("cannot create DSN: %s"),
		  mu_strerror(rc));

	mu_mime_get_message(mime, &msg);
	add_headers(msg, MF_OPTVAL(headers, NULL));
	rc = bi_message_register(env, NULL, msg, MF_MSG_STANDALONE);
	if (rc < 0) {
		mu_message_destroy(&msg, mu_message_get_owner(msg));
		MF_THROW(mfe_failure,
			 _("no more message slots available"));
	}
	MF_RETURN(rc);
}
END

MF_STATE(eom)
MF_CAPTURE(mstr)
MF_DEFUN(send_dsn, VOID, STRING to, 
	 STRING sender, STRING recpt, STRING text,
	 OPTIONAL, STRING headers, STRING from, STRING mailer_url)
{
	int status;
	mu_mime_t mime;
	mu_message_t newmsg;
	
	status = build_mime(&mime, mstr, sender, recpt, text);
	MF_ASSERT(status == 0,
		  mfe_failure,
		  _("cannot create DSN: %s"),
		  mu_strerror(status));

	mu_mime_get_message(mime, &newmsg);
	add_headers(newmsg, MF_OPTVAL(headers, NULL));
	_send(env, MF_OPTVAL(mailer_url, NULL), to, 
              MF_OPTVAL(from, NULL), newmsg, _destroy_mime, mime);
}
END