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