Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / sendmail / camel-sendmail-transport.c
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 /* 
5  *
6  * Authors: Dan Winship <danw@ximian.com>
7  *
8  * Copyright 2000 Ximian, Inc. (www.ximian.com)
9  *
10  * This program is free software; you can redistribute it and/or 
11  * modify it under the terms of version 2 of the GNU Lesser General Public 
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/wait.h>
35
36 #include <glib/gi18n-lib.h>
37
38 #include "camel-data-wrapper.h"
39 #include "camel-exception.h"
40 #include "camel-mime-filter-crlf.h"
41 #include "camel-mime-message.h"
42 #include "camel-sendmail-transport.h"
43 #include "camel-stream-filter.h"
44 #include "camel-stream-fs.h"
45
46 static char *get_name (CamelService *service, gboolean brief);
47
48 static gboolean sendmail_send_to (CamelTransport *transport,
49                                   CamelMimeMessage *message,
50                                   CamelAddress *from, CamelAddress *recipients,
51                                   CamelException *ex);
52
53
54 static void
55 camel_sendmail_transport_class_init (CamelSendmailTransportClass *camel_sendmail_transport_class)
56 {
57         CamelTransportClass *camel_transport_class =
58                 CAMEL_TRANSPORT_CLASS (camel_sendmail_transport_class);
59         CamelServiceClass *camel_service_class =
60                 CAMEL_SERVICE_CLASS (camel_sendmail_transport_class);
61
62         /* virtual method overload */
63         camel_service_class->get_name = get_name;
64         camel_transport_class->send_to = sendmail_send_to;
65 }
66
67 CamelType
68 camel_sendmail_transport_get_type (void)
69 {
70         static CamelType camel_sendmail_transport_type = CAMEL_INVALID_TYPE;
71         
72         if (camel_sendmail_transport_type == CAMEL_INVALID_TYPE)        {
73                 camel_sendmail_transport_type =
74                         camel_type_register (CAMEL_TRANSPORT_TYPE, "CamelSendmailTransport",
75                                              sizeof (CamelSendmailTransport),
76                                              sizeof (CamelSendmailTransportClass),
77                                              (CamelObjectClassInitFunc) camel_sendmail_transport_class_init,
78                                              NULL,
79                                              (CamelObjectInitFunc) NULL,
80                                              NULL);
81         }
82         
83         return camel_sendmail_transport_type;
84 }
85
86
87 static gboolean
88 sendmail_send_to (CamelTransport *transport, CamelMimeMessage *message,
89                   CamelAddress *from, CamelAddress *recipients,
90                   CamelException *ex)
91 {
92         struct _camel_header_raw *header, *savedbcc, *n, *tail;
93         const char *from_addr, *addr, **argv;
94         int i, len, fd[2], nullfd, wstat;
95         CamelStreamFilter *filter;
96         CamelMimeFilter *crlf;
97         sigset_t mask, omask;
98         CamelStream *out;
99         pid_t pid;
100         
101         if (!camel_internet_address_get (CAMEL_INTERNET_ADDRESS (from), 0, NULL, &from_addr))
102                 return FALSE;
103         
104         len = camel_address_length (recipients);
105         argv = g_malloc ((len + 6) * sizeof (char *));
106         argv[0] = "sendmail";
107         argv[1] = "-i";
108         argv[2] = "-f";
109         argv[3] = from_addr;
110         argv[4] = "--";
111         
112         for (i = 0; i < len; i++) {
113                 if (!camel_internet_address_get (CAMEL_INTERNET_ADDRESS (recipients), i, NULL, &addr)) {
114                         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
115                                              _("Could not parse recipient list"));
116                         g_free (argv);
117                         return FALSE;
118                 }
119                 
120                 argv[i + 5] = addr;
121         }
122         
123         argv[i + 5] = NULL;
124         
125         /* unlink the bcc headers */
126         savedbcc = NULL;
127         tail = (struct _camel_header_raw *) &savedbcc;
128         
129         header = (struct _camel_header_raw *) &CAMEL_MIME_PART (message)->headers;
130         n = header->next;
131         while (n != NULL) {
132                 if (!g_ascii_strcasecmp (n->name, "Bcc")) {
133                         header->next = n->next;
134                         tail->next = n;
135                         n->next = NULL;
136                         tail = n;
137                 } else {
138                         header = n;
139                 }
140                 
141                 n = header->next;
142         }
143         
144         if (pipe (fd) == -1) {
145                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
146                                       _("Could not create pipe to sendmail: "
147                                         "%s: mail not sent"),
148                                       g_strerror (errno));
149                 
150                 /* restore the bcc headers */
151                 header->next = savedbcc;
152                 
153                 return FALSE;
154         }
155         
156         /* Block SIGCHLD so the calling application doesn't notice
157          * sendmail exiting before we do.
158          */
159         sigemptyset (&mask);
160         sigaddset (&mask, SIGCHLD);
161         sigprocmask (SIG_BLOCK, &mask, &omask);
162         
163         pid = fork ();
164         switch (pid) {
165         case -1:
166                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
167                                       _("Could not fork sendmail: "
168                                         "%s: mail not sent"),
169                                       g_strerror (errno));
170                 close (fd[0]);
171                 close (fd[1]);
172                 sigprocmask (SIG_SETMASK, &omask, NULL);
173                 g_free (argv);
174                 
175                 /* restore the bcc headers */
176                 header->next = savedbcc;
177                 
178                 return FALSE;
179         case 0:
180                 /* Child process */
181                 nullfd = open ("/dev/null", O_RDWR);
182                 dup2 (fd[0], STDIN_FILENO);
183                 /*dup2 (nullfd, STDOUT_FILENO);
184                   dup2 (nullfd, STDERR_FILENO);*/
185                 close (nullfd);
186                 close (fd[1]);
187                 
188                 execv (SENDMAIL_PATH, (char **)argv);
189                 _exit (255);
190         }
191         g_free (argv);
192         
193         /* Parent process. Write the message out. */
194         close (fd[0]);
195         out = camel_stream_fs_new_with_fd (fd[1]);
196         
197         /* workaround for lame sendmail implementations that can't handle CRLF eoln sequences */
198         filter = camel_stream_filter_new_with_stream (out);
199         crlf = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_DECODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
200         camel_stream_filter_add (filter, crlf);
201         camel_object_unref (crlf);
202         camel_object_unref (out);
203         
204         out = (CamelStream *) filter;
205         if (camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), out) == -1
206             || camel_stream_close (out) == -1) {
207                 camel_object_unref (CAMEL_OBJECT (out));
208                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
209                                       _("Could not send message: %s"),
210                                       g_strerror (errno));
211                 
212                 /* Wait for sendmail to exit. */
213                 while (waitpid (pid, &wstat, 0) == -1 && errno == EINTR)
214                         ;
215                 
216                 sigprocmask (SIG_SETMASK, &omask, NULL);
217                 
218                 /* restore the bcc headers */
219                 header->next = savedbcc;
220                 
221                 return FALSE;
222         }
223         
224         camel_object_unref (CAMEL_OBJECT (out));
225         
226         /* Wait for sendmail to exit. */
227         while (waitpid (pid, &wstat, 0) == -1 && errno == EINTR)
228                 ;
229         
230         sigprocmask (SIG_SETMASK, &omask, NULL);
231         
232         /* restore the bcc headers */
233         header->next = savedbcc;
234         
235         if (!WIFEXITED (wstat)) {
236                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
237                                       _("sendmail exited with signal %s: "
238                                         "mail not sent."),
239                                       g_strsignal (WTERMSIG (wstat)));
240                 return FALSE;
241         } else if (WEXITSTATUS (wstat) != 0) {
242                 if (WEXITSTATUS (wstat) == 255) {
243                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
244                                               _("Could not execute %s: "
245                                                 "mail not sent."),
246                                               SENDMAIL_PATH);
247                 } else {
248                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
249                                               _("sendmail exited with status "
250                                                 "%d: mail not sent."),
251                                               WEXITSTATUS (wstat));
252                 }
253                 return FALSE;
254         }
255         
256         return TRUE;
257 }
258
259 static char *
260 get_name (CamelService *service, gboolean brief)
261 {
262         if (brief)
263                 return g_strdup (_("sendmail"));
264         else
265                 return g_strdup (_("Mail delivery via the sendmail program"));
266 }