/* This file is part of Mailfromd. -*- c -*- Copyright (C) 2006-2019 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 . */ 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