1/* This file is part of Mailfromd. -*- c -*- 2 Copyright (C) 2006-2021 Sergey Poznyakoff 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 3, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17MF_BUILTIN_MODULE 18 19#include "msg.h" 20 21static int 22_send(eval_environ_t env, 23 const char *mailer_url, char *to, char *from, mu_message_t msg, 24 void (*destroy)(void*), void *ptr) 25{ 26 int status; 27 mu_mailer_t mailer; 28 mu_address_t to_addr = NULL; 29 mu_address_t from_addr = NULL; 30 31 if (!mailer_url) 32 mu_mailer_get_url_default(&mailer_url); 33 status = mu_mailer_create(&mailer, mailer_url); 34 if (status) { 35 destroy(ptr); 36 MF_THROW(mfe_failure, 37 _("cannot create mailer `%s': %s"), 38 mailer_url, mu_strerror(status)); 39 } 40 41 if (to) { 42 status = mu_address_create(&to_addr, to); 43 if (status) { 44 destroy(ptr); 45 mu_mailer_destroy(&mailer); 46 MF_THROW(mfe_failure, 47 _("bad recipient address `%s': %s"), 48 to, mu_strerror(status)); 49 } 50 } 51 52 if (!from || from[0] == 0) 53 from = "<>"; 54 status = mu_address_create(&from_addr, from); 55 if (status) { 56 mu_address_destroy(&to_addr); 57 mu_mailer_destroy(&mailer); 58 destroy(ptr); 59 MF_THROW(mfe_failure, 60 _("bad sender address `%s': %s"), 61 from, mu_strerror(status)); 62 } 63 64 /* FIXME: mailer flags? */ 65 status = mu_mailer_open(mailer, 0); 66 if (status) { 67 mu_address_destroy(&to_addr); 68 mu_address_destroy(&from_addr); 69 mu_mailer_destroy(&mailer); 70 destroy(ptr); 71 MF_THROW(mfe_failure, 72 _("opening mailer `%s' failed: %s"), 73 mailer_url, mu_strerror (status)); 74 } 75 76 status = mu_mailer_send_message(mailer, msg, from_addr, to_addr); 77 mu_address_destroy(&to_addr); 78 mu_address_destroy(&from_addr); 79 mu_mailer_destroy(&mailer); 80 destroy(ptr); 81 MF_ASSERT(status == 0, mfe_failure, 82 _("cannot send message: %s"), 83 mu_strerror(status)); 84 return 0; 85} 86 87static void 88_destroy_msg(void *ptr) 89{ 90 mu_message_t msg = ptr; 91 mu_message_destroy(&msg, mu_message_get_owner(msg)); 92} 93 94MF_DEFUN(send_mail, VOID, STRING text, 95 OPTIONAL, STRING to, STRING from, STRING mailer_url) 96{ 97 mu_message_t msg = NULL; 98 mu_stream_t stream = NULL; 99 100 mu_message_create(&msg, NULL); 101 mu_static_memory_stream_create(&stream, text, strlen(text)); 102 mu_message_set_stream(msg, stream, mu_message_get_owner(msg)); 103 104 _send(env, MF_OPTVAL(mailer_url, NULL), 105 MF_OPTVAL(to, NULL), 106 MF_OPTVAL(from, NULL), msg, _destroy_msg, msg); 107 mu_stream_unref(stream); 108} 109END 110 111MF_DEFUN(send_message, VOID, NUMBER nmsg, 112 OPTIONAL, STRING to, STRING from, STRING mailer_url) 113{ 114 mu_message_t msg = bi_message_from_descr(env, nmsg); 115 _send(env, MF_OPTVAL(mailer_url, NULL), 116 MF_OPTVAL(to, NULL), 117 MF_OPTVAL(from, NULL), msg, _destroy_msg, msg); 118} 119END 120 121static void 122add_headers(mu_message_t msg, char *headers) 123{ 124 mu_stream_t stream = NULL; 125 mu_header_t hdr = NULL; 126 size_t len; 127 128 if (!headers) 129 return; 130 131 len = strlen(headers); 132 mu_message_get_header(msg, &hdr); 133 134 mu_header_get_streamref(hdr, &stream); 135 mu_stream_seek(stream, 0, MU_SEEK_END, NULL); 136 mu_stream_write(stream, headers, len, NULL); 137 if (len < 2 || memcmp(headers + len - 2, "\n\n", 2)) { 138 if (len > 1 && headers[len-1] == '\n') 139 mu_stream_write(stream, "\n", 1, NULL); 140 else 141 mu_stream_write(stream, "\n\n", 2, NULL); 142 } 143 mu_stream_unref(stream); 144} 145 146MF_DEFUN(send_text, VOID, STRING text, STRING headers, 147 OPTIONAL, STRING to, STRING from, STRING mailer_url) 148{ 149 mu_message_t msg = NULL; 150 mu_body_t body = NULL; 151 mu_stream_t stream = NULL; 152 153 mu_message_create(&msg, NULL); 154 155 mu_message_get_body(msg, &body); 156 mu_body_get_streamref(body, &stream); 157 mu_stream_write(stream, text, strlen(text), NULL); 158 mu_stream_unref(stream); 159 160 add_headers(msg, headers); 161 _send(env, MF_OPTVAL(mailer_url, NULL), MF_OPTVAL(to, NULL), 162 MF_OPTVAL(from, NULL), msg, _destroy_msg, msg); 163} 164END 165 166 167static void 168mime_create_reason(mu_mime_t mime, const char *sender, const char *text) 169{ 170 mu_message_t newmsg; 171 mu_stream_t stream; 172 time_t t; 173 struct tm *tm; 174 mu_body_t body; 175 mu_header_t hdr; 176 char datestr[80]; 177 static char *content_header = 178 "Content-Type: text/plain;charset=US-ASCII\n" /* FIXME! */ 179 "Content-Transfer-Encoding: 8bit\n"; 180 181 mu_message_create(&newmsg, NULL); 182 mu_message_get_body(newmsg, &body); 183 mu_body_get_streamref(body, &stream); 184 185 time(&t); 186 tm = localtime(&t); 187 mu_strftime(datestr, sizeof datestr, "%a, %b %d %H:%M:%S %Y %Z", tm); 188 189 mu_stream_printf(stream, 190 "\n" 191 "The original message was received at %s from %s.\n", 192 datestr, sender); 193 194 mu_stream_write(stream, text, strlen(text), NULL); 195 mu_stream_flush(stream); 196 mu_stream_unref(stream); 197 mu_header_create(&hdr, content_header, strlen(content_header)); 198 mu_message_set_header(newmsg, hdr, NULL); 199 mu_mime_add_part(mime, newmsg); 200 mu_message_unref(newmsg); 201} 202 203static void 204mime_create_ds(mu_mime_t mime, char *recpt) 205{ 206 mu_message_t newmsg; 207 mu_stream_t stream; 208 mu_header_t hdr; 209 mu_body_t body; 210 char datestr[80]; 211 time_t t; 212 struct tm *tm; 213 214 time(&t); 215 tm = localtime(&t); 216 mu_strftime(datestr, sizeof datestr, "%a, %b %d %H:%M:%S %Y %Z", tm); 217 218 mu_message_create(&newmsg, NULL); 219 mu_message_get_header(newmsg, &hdr); 220 mu_header_set_value(hdr, "Content-Type", "message/delivery-status", 1); 221 mu_message_get_body(newmsg, &body); 222 mu_body_get_streamref(body, &stream); 223 mu_stream_printf(stream, "Reporting-UA: %s\n", PACKAGE_STRING); 224 mu_stream_printf(stream, "Arrival-Date: %s\n", datestr); 225 mu_stream_printf(stream, "Final-Recipient: RFC822; %s\n", 226 recpt ? recpt : "unknown"); 227 mu_stream_printf(stream, "%s", "Action: deleted\n"); 228 mu_stream_printf(stream, "%s", 229 "Disposition: automatic-action/" 230 "MDN-sent-automatically;deleted\n"); 231 mu_stream_printf(stream, "Last-Attempt-Date: %s\n", datestr); 232 mu_stream_unref(stream); 233 mu_mime_add_part(mime, newmsg); 234 mu_message_unref(newmsg); 235} 236 237/* Quote original message */ 238static int 239mime_create_quote(mu_mime_t mime, mu_stream_t istream) 240{ 241 mu_message_t newmsg; 242 mu_stream_t ostream; 243 mu_header_t hdr; 244 mu_body_t body; 245 char *buf = NULL; 246 size_t bufsize = 0; 247 248 mu_message_create(&newmsg, NULL); 249 mu_message_get_header(newmsg, &hdr); 250 mu_header_set_value(hdr, "Content-Type", "message/rfc822", 1); 251 mu_message_get_body (newmsg, &body); 252 mu_body_get_streamref(body, &ostream); 253 /* Skip envelope line */ 254 mu_stream_getline(istream, &buf, &bufsize, NULL); 255 free(buf); 256 mu_stream_copy(ostream, istream, 0, NULL); 257 mu_stream_unref(ostream); 258 mu_mime_add_part(mime, newmsg); 259 mu_message_unref(newmsg); 260 return 0; 261} 262 263static int 264build_mime(mu_mime_t *pmime, mu_stream_t msgstr, 265 char *sender, char *recpt, 266 char *text) 267{ 268 mu_mime_t mime = NULL; 269 int status; 270 271 mu_mime_create(&mime, NULL, 0); 272 mime_create_reason(mime, sender, text); 273 mime_create_ds(mime, recpt); 274 status = mime_create_quote(mime, msgstr); 275 if (status) { 276 mu_mime_destroy(&mime); 277 return status; 278 } 279 *pmime = mime; 280 return 0; 281} 282 283static void 284_destroy_mime(void *ptr) 285{ 286 mu_mime_t mime = ptr; 287 mu_mime_destroy(&mime); 288} 289 290MF_STATE(eom) 291MF_CAPTURE(mstr) 292MF_DEFUN(create_dsn, NUMBER, STRING sender, STRING recpt, STRING text, 293 OPTIONAL, STRING headers, STRING from) 294{ 295 int rc; 296 mu_mime_t mime; 297 mu_message_t msg; 298 299 rc = build_mime(&mime, mstr, sender, recpt, text); 300 MF_ASSERT(rc == 0, 301 mfe_failure, 302 _("cannot create DSN: %s"), 303 mu_strerror(rc)); 304 305 mu_mime_get_message(mime, &msg); 306 add_headers(msg, MF_OPTVAL(headers, NULL)); 307 rc = bi_message_register(env, NULL, msg, MF_MSG_STANDALONE); 308 if (rc < 0) { 309 mu_message_destroy(&msg, mu_message_get_owner(msg)); 310 MF_THROW(mfe_failure, 311 _("no more message slots available")); 312 } 313 MF_RETURN(rc); 314} 315END 316 317MF_STATE(eom) 318MF_CAPTURE(mstr) 319MF_DEFUN(send_dsn, VOID, STRING to, 320 STRING sender, STRING recpt, STRING text, 321 OPTIONAL, STRING headers, STRING from, STRING mailer_url) 322{ 323 int status; 324 mu_mime_t mime; 325 mu_message_t newmsg; 326 327 status = build_mime(&mime, mstr, sender, recpt, text); 328 MF_ASSERT(status == 0, 329 mfe_failure, 330 _("cannot create DSN: %s"), 331 mu_strerror(status)); 332 333 mu_mime_get_message(mime, &newmsg); 334 add_headers(newmsg, MF_OPTVAL(headers, NULL)); 335 _send(env, MF_OPTVAL(mailer_url, NULL), to, 336 MF_OPTVAL(from, NULL), newmsg, _destroy_mime, mime); 337} 338END 339 340