1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-sendmail-transport.c: Sendmail-based transport class
3 *
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 *
6 * This library is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Dan Winship <danw@ximian.com>
19 */
20
21 #include "evolution-data-server-config.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/wait.h>
29
30 #include <glib/gi18n-lib.h>
31
32 #include "camel-sendmail-settings.h"
33 #include "camel-sendmail-transport.h"
34
G_DEFINE_TYPE(CamelSendmailTransport,camel_sendmail_transport,CAMEL_TYPE_TRANSPORT)35 G_DEFINE_TYPE (
36 CamelSendmailTransport,
37 camel_sendmail_transport, CAMEL_TYPE_TRANSPORT)
38
39 static gchar *
40 sendmail_get_name (CamelService *service,
41 gboolean brief)
42 {
43 if (brief)
44 return g_strdup (_("sendmail"));
45 else
46 return g_strdup (_("Mail delivery via the sendmail program"));
47 }
48
49 static GPtrArray *
parse_sendmail_args(const gchar * binary,const gchar * args,const gchar * from_addr,CamelAddress * recipients)50 parse_sendmail_args (const gchar *binary,
51 const gchar *args,
52 const gchar *from_addr,
53 CamelAddress *recipients)
54 {
55 GPtrArray *args_arr;
56 gint ii, len, argc = 0;
57 gchar **argv = NULL;
58
59 g_return_val_if_fail (binary != NULL, NULL);
60 g_return_val_if_fail (args != NULL, NULL);
61 g_return_val_if_fail (from_addr != NULL, NULL);
62
63 len = camel_address_length (recipients);
64
65 args_arr = g_ptr_array_new_full (5, g_free);
66 g_ptr_array_add (args_arr, g_strdup (binary));
67
68 if (args && g_shell_parse_argv (args, &argc, &argv, NULL) && argc > 0 && argv) {
69 for (ii = 0; ii < argc; ii++) {
70 const gchar *arg = argv[ii];
71
72 if (g_strcmp0 (arg, "%F") == 0) {
73 g_ptr_array_add (args_arr, g_strdup (from_addr));
74 } else if (g_strcmp0 (arg, "%R") == 0) {
75 gint jj;
76
77 for (jj = 0; jj < len; jj++) {
78 const gchar *addr = NULL;
79
80 if (!camel_internet_address_get (
81 CAMEL_INTERNET_ADDRESS (recipients), jj, NULL, &addr)) {
82
83 /* should not happen, as the array is checked beforehand */
84
85 g_ptr_array_free (args_arr, TRUE);
86 g_strfreev (argv);
87
88 return NULL;
89 }
90
91 g_ptr_array_add (args_arr, g_strdup (addr));
92 }
93 } else {
94 g_ptr_array_add (args_arr, g_strdup (arg));
95 }
96 }
97
98 g_strfreev (argv);
99 }
100
101 g_ptr_array_add (args_arr, NULL);
102
103 return args_arr;
104 }
105
106 static gboolean
sendmail_send_to_sync(CamelTransport * transport,CamelMimeMessage * message,CamelAddress * from,CamelAddress * recipients,gboolean * out_sent_message_saved,GCancellable * cancellable,GError ** error)107 sendmail_send_to_sync (CamelTransport *transport,
108 CamelMimeMessage *message,
109 CamelAddress *from,
110 CamelAddress *recipients,
111 gboolean *out_sent_message_saved,
112 GCancellable *cancellable,
113 GError **error)
114 {
115 CamelNameValueArray *previous_headers = NULL;
116 const gchar *header_name = NULL, *header_value = NULL;
117 const gchar *from_addr, *addr;
118 GPtrArray *argv_arr;
119 gint i, len, fd[2], nullfd, wstat;
120 CamelStream *filter;
121 CamelMimeFilter *crlf;
122 sigset_t mask, omask;
123 CamelStream *out;
124 CamelSendmailSettings *settings;
125 const gchar *binary = SENDMAIL_PATH;
126 gchar *custom_binary = NULL, *custom_args = NULL;
127 gboolean success;
128 pid_t pid;
129 guint ii;
130
131 success = camel_internet_address_get (
132 CAMEL_INTERNET_ADDRESS (from), 0, NULL, &from_addr);
133
134 if (!success) {
135 g_set_error (
136 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
137 _("Failed to read From address"));
138 return FALSE;
139 }
140
141 settings = CAMEL_SENDMAIL_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (transport)));
142
143 if (!camel_sendmail_settings_get_send_in_offline (settings)) {
144 CamelSession *session;
145 gboolean is_online;
146
147 session = camel_service_ref_session (CAMEL_SERVICE (transport));
148 is_online = session && camel_session_get_online (session);
149 g_clear_object (&session);
150
151 if (!is_online) {
152 g_set_error (
153 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
154 _("Message send in offline mode is disabled"));
155 return FALSE;
156 }
157 }
158
159 if (camel_sendmail_settings_get_use_custom_binary (settings)) {
160 custom_binary = camel_sendmail_settings_dup_custom_binary (settings);
161 if (custom_binary && *custom_binary)
162 binary = custom_binary;
163 }
164
165 if (camel_sendmail_settings_get_use_custom_args (settings)) {
166 custom_args = camel_sendmail_settings_dup_custom_args (settings);
167 /* means no arguments used */
168 if (!custom_args)
169 custom_args = g_strdup ("");
170 }
171
172 g_object_unref (settings);
173
174 len = camel_address_length (recipients);
175 for (i = 0; i < len; i++) {
176 success = camel_internet_address_get (
177 CAMEL_INTERNET_ADDRESS (recipients), i, NULL, &addr);
178
179 if (!success) {
180 g_set_error (
181 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
182 _("Could not parse recipient list"));
183 g_free (custom_binary);
184 g_free (custom_args);
185
186 return FALSE;
187 }
188 }
189
190 argv_arr = parse_sendmail_args (
191 binary,
192 custom_args ? custom_args : "-i -f %F -- %R",
193 from_addr,
194 recipients);
195
196 if (!argv_arr) {
197 g_set_error (
198 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
199 _("Could not parse arguments"));
200
201 g_free (custom_binary);
202 g_free (custom_args);
203
204 return FALSE;
205 }
206
207 /* unlink the bcc headers */
208 previous_headers = camel_medium_dup_headers (CAMEL_MEDIUM (message));
209 camel_medium_remove_header (CAMEL_MEDIUM (message), "Bcc");
210
211 if (pipe (fd) == -1) {
212 g_set_error (
213 error, G_IO_ERROR,
214 g_io_error_from_errno (errno),
215 _("Could not create pipe to “%s”: %s: "
216 "mail not sent"), binary, g_strerror (errno));
217
218 /* restore the bcc headers */
219 for (ii = 0; camel_name_value_array_get (previous_headers, ii, &header_name, &header_value); ii++) {
220 if (!g_ascii_strcasecmp (header_name, "Bcc")) {
221 camel_medium_add_header (CAMEL_MEDIUM (message), header_name, header_value);
222 }
223 }
224
225 camel_name_value_array_free (previous_headers);
226 g_free (custom_binary);
227 g_free (custom_args);
228 g_ptr_array_free (argv_arr, TRUE);
229
230 return FALSE;
231 }
232
233 /* Block SIGCHLD so the calling application doesn't notice
234 * sendmail exiting before we do.
235 */
236 sigemptyset (&mask);
237 sigaddset (&mask, SIGCHLD);
238 sigprocmask (SIG_BLOCK, &mask, &omask);
239
240 pid = fork ();
241 switch (pid) {
242 case -1:
243 g_set_error (
244 error, G_IO_ERROR,
245 g_io_error_from_errno (errno),
246 _("Could not fork “%s”: %s: "
247 "mail not sent"), binary, g_strerror (errno));
248 close (fd[0]);
249 close (fd[1]);
250 sigprocmask (SIG_SETMASK, &omask, NULL);
251
252 /* restore the bcc headers */
253 for (ii = 0; camel_name_value_array_get (previous_headers, ii, &header_name, &header_value); ii++) {
254 if (!g_ascii_strcasecmp (header_name, "Bcc")) {
255 camel_medium_add_header (CAMEL_MEDIUM (message), header_name, header_value);
256 }
257 }
258
259 camel_name_value_array_free (previous_headers);
260 g_free (custom_binary);
261 g_free (custom_args);
262 g_ptr_array_free (argv_arr, TRUE);
263
264 return FALSE;
265 case 0:
266 /* Child process */
267 nullfd = open ("/dev/null", O_RDWR);
268 dup2 (fd[0], STDIN_FILENO);
269 if (nullfd != -1) {
270 /*dup2 (nullfd, STDOUT_FILENO);
271 dup2 (nullfd, STDERR_FILENO);*/
272 close (nullfd);
273 }
274 close (fd[1]);
275
276 execv (binary, (gchar **) argv_arr->pdata);
277 _exit (255);
278 }
279
280 g_ptr_array_free (argv_arr, TRUE);
281
282 /* Parent process. Write the message out. */
283 close (fd[0]);
284 out = camel_stream_fs_new_with_fd (fd[1]);
285
286 /* XXX Workaround for lame sendmail implementations
287 * that can't handle CRLF eoln sequences. */
288 filter = camel_stream_filter_new (out);
289 crlf = camel_mime_filter_crlf_new (
290 CAMEL_MIME_FILTER_CRLF_DECODE,
291 CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
292 camel_stream_filter_add (CAMEL_STREAM_FILTER (filter), crlf);
293 g_object_unref (crlf);
294 g_object_unref (out);
295
296 out = (CamelStream *) filter;
297 if (camel_data_wrapper_write_to_stream_sync (
298 CAMEL_DATA_WRAPPER (message), out, cancellable, error) == -1
299 || camel_stream_close (out, cancellable, error) == -1) {
300 g_object_unref (out);
301 g_prefix_error (error, _("Could not send message: "));
302
303 /* Wait for sendmail to exit. */
304 while (waitpid (pid, &wstat, 0) == -1 && errno == EINTR)
305 ;
306
307 sigprocmask (SIG_SETMASK, &omask, NULL);
308
309 /* restore the bcc headers */
310 for (ii = 0; camel_name_value_array_get (previous_headers, ii, &header_name, &header_value); ii++) {
311 if (!g_ascii_strcasecmp (header_name, "Bcc")) {
312 camel_medium_add_header (CAMEL_MEDIUM (message), header_name, header_value);
313 }
314 }
315
316 camel_name_value_array_free (previous_headers);
317 g_free (custom_binary);
318 g_free (custom_args);
319
320 return FALSE;
321 }
322
323 g_object_unref (out);
324
325 /* Wait for sendmail to exit. */
326 while (waitpid (pid, &wstat, 0) == -1 && errno == EINTR)
327 ;
328
329 sigprocmask (SIG_SETMASK, &omask, NULL);
330
331 /* restore the bcc headers */
332 for (ii = 0; camel_name_value_array_get (previous_headers, ii, &header_name, &header_value); ii++) {
333 if (!g_ascii_strcasecmp (header_name, "Bcc")) {
334 camel_medium_add_header (CAMEL_MEDIUM (message), header_name, header_value);
335 }
336 }
337
338 camel_name_value_array_free (previous_headers);
339
340 if (!WIFEXITED (wstat)) {
341 g_set_error (
342 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
343 _("“%s” exited with signal %s: mail not sent."),
344 binary, g_strsignal (WTERMSIG (wstat)));
345 g_free (custom_binary);
346 g_free (custom_args);
347
348 return FALSE;
349 } else if (WEXITSTATUS (wstat) != 0) {
350 if (WEXITSTATUS (wstat) == 255) {
351 g_set_error (
352 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
353 _("Could not execute “%s”: mail not sent."),
354 binary);
355 } else {
356 g_set_error (
357 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
358 _("“%s” exited with status %d: "
359 "mail not sent."),
360 binary, WEXITSTATUS (wstat));
361 }
362 g_free (custom_binary);
363 g_free (custom_args);
364
365 return FALSE;
366 }
367
368 g_free (custom_binary);
369 g_free (custom_args);
370
371 return TRUE;
372 }
373
374 static void
camel_sendmail_transport_class_init(CamelSendmailTransportClass * class)375 camel_sendmail_transport_class_init (CamelSendmailTransportClass *class)
376 {
377 CamelServiceClass *service_class;
378 CamelTransportClass *transport_class;
379
380 service_class = CAMEL_SERVICE_CLASS (class);
381 service_class->get_name = sendmail_get_name;
382 service_class->settings_type = CAMEL_TYPE_SENDMAIL_SETTINGS;
383
384 transport_class = CAMEL_TRANSPORT_CLASS (class);
385 transport_class->send_to_sync = sendmail_send_to_sync;
386 }
387
388 static void
camel_sendmail_transport_init(CamelSendmailTransport * sendmail_transport)389 camel_sendmail_transport_init (CamelSendmailTransport *sendmail_transport)
390 {
391 }
392
393