2 * This file is part of DisOrder
3 * Copyright (C) 2007-2008 Richard Kettlewell
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /** @file lib/sendmail.c
19 * @brief Sending email
21 * Can send email either via a local MTA or by connecting to an SMTP TCP port.
22 * The former is preferred as there is a strong implication that the local MTA
23 * will queue for you than some random TCP SMTP server you found.
29 #include <sys/socket.h>
34 #include <netinet/in.h>
38 #include "configuration.h"
39 #include "inputline.h"
46 /** @brief Get a server response
47 * @param tag Server name
48 * @param in Input stream
49 * @return Response code 0-999 or -1 on error
51 static int getresponse(const char *tag
, FILE *in
) {
54 while(!inputline(tag
, in
, &line
, CRLF
)) {
55 if(line
[0] >= '0' && line
[0] <= '9'
56 && line
[1] >= '0' && line
[1] <= '9'
57 && line
[2] >= '0' && line
[2] <= '9') {
58 const int rc
= 10 * (10 * line
[0] + line
[1]) + line
[2] - 111 * '0';
59 if(rc
>= 400 && rc
<= 599)
60 disorder_error(0, "%s: %s", tag
, line
);
64 /* else go round for further response lines */
66 disorder_error(0, "%s: malformed response: %s", tag
, line
);
71 disorder_error(errno
, "%s: read error", tag
);
73 disorder_error(0, "%s: server closed connection", tag
);
77 /** @brief Send a command to the server
78 * @param tag Server name
79 * @param out stream to send commands to
80 * @param fmt Format string
81 * @return 0 on success, non-0 on error
83 static int sendcommand(const char *tag
, FILE *out
, const char *fmt
, ...) {
88 rc
= vfprintf(out
, fmt
, ap
);
91 rc
= fputs("\r\n", out
);
96 disorder_error(errno
, "%s: write error", tag
);
100 /** @brief Send a mail message
101 * @param tag Server name
102 * @param in Stream to read responses from
103 * @param out Stream to send commands to
104 * @param sender Sender address (can be "")
105 * @param pubsender Visible sender address (must not be "")
106 * @param recipient Recipient address
107 * @param subject Subject string
108 * @param encoding Body encoding
109 * @param content_type Content-type of body
110 * @param body Text of body (encoded, but \n for newline)
111 * @return 0 on success, non-0 on error
113 static int sendmailfp(const char *tag
, FILE *in
, FILE *out
,
115 const char *pubsender
,
116 const char *recipient
,
118 const char *encoding
,
119 const char *content_type
,
131 strftime(date
, sizeof date
, "%a, %d %b %Y %H:%M:%S +0000", &ut
);
132 gcry_create_nonce(idbuf
, sizeof idbuf
);
133 id
= mime_to_base64(idbuf
, sizeof idbuf
);
134 if(getresponse(tag
, in
) / 100 != 2)
136 if(sendcommand(tag
, out
, "HELO %s", local_hostname()))
138 if(getresponse(tag
, in
) / 100 != 2)
140 if(sendcommand(tag
, out
, "MAIL FROM:<%s>", sender
))
142 if(getresponse(tag
, in
) / 100 != 2)
144 if(sendcommand(tag
, out
, "RCPT TO:<%s>", recipient
))
146 if(getresponse(tag
, in
) / 100 != 2)
148 if(sendcommand(tag
, out
, "DATA", sender
))
150 if(getresponse(tag
, in
) / 100 != 3)
152 if(fprintf(out
, "From: %s\r\n", pubsender
) < 0
153 || fprintf(out
, "To: %s\r\n", recipient
) < 0
154 || fprintf(out
, "Subject: %s\r\n", subject
) < 0
155 || fprintf(out
, "Message-ID: <%s@%s>\r\n", id
, local_hostname()) < 0
156 || fprintf(out
, "MIME-Version: 1.0\r\n") < 0
157 || fprintf(out
, "Content-Type: %s\r\n", content_type
) < 0
158 || fprintf(out
, "Content-Transfer-Encoding: %s\r\n", encoding
) < 0
159 || fprintf(out
, "Date: %s\r\n", date
) < 0
160 || fprintf(out
, "\r\n") < 0) {
162 disorder_error(errno
, "%s: write error", tag
);
165 for(ptr
= body
; *ptr
; ++ptr
) {
166 if(sol
&& *ptr
== '.')
167 if(fputc('.', out
) < 0)
170 if(fputc('\r', out
) < 0)
175 if(fputc(*ptr
, out
) < 0)
179 if(fputs("\r\n", out
) < 0)
181 if(fprintf(out
, ".\r\n") < 0
184 if(getresponse(tag
, in
) / 100 != 2)
189 /** @brief Send a mail message
190 * @param sender Sender address (can be "")
191 * @param pubsender Visible sender address (must not be "")
192 * @param recipient Recipient address
193 * @param subject Subject string
194 * @param encoding Body encoding
195 * @param content_type Content-type of body
196 * @param body Text of body (encoded, but \n for newline)
197 * @return 0 on success, non-0 on error
199 * See mime_encode_text() for encoding of text bodies.
201 int sendmail(const char *sender
,
202 const char *pubsender
,
203 const char *recipient
,
205 const char *encoding
,
206 const char *content_type
,
216 static const struct addrinfo pref
= {
218 .ai_family
= PF_INET
,
219 .ai_socktype
= SOCK_STREAM
,
220 .ai_protocol
= IPPROTO_TCP
,
223 /* Find the SMTP server */
224 if(config
->sendmail
&& config
->sendmail
[0]) {
225 int inpipe
[2], outpipe
[2];
227 /* If it's a path name, use -bs mode. Exim, Postfix and Sendmail all claim
228 * to support this. */
229 xpipe(inpipe
); /* sendmail's stdin */
230 xpipe(outpipe
); /* sendmail's stdout */
231 if(!(pid
= xfork())) {
233 signal(SIGPIPE
, SIG_DFL
);
237 xdup2(outpipe
[1], 1);
238 execlp(config
->sendmail
,
239 config
->sendmail
, "-bs", (char *)0);
240 disorder_fatal(errno
, "executing %s", config
->sendmail
);
244 fdin
= outpipe
[0]; /* read from sendmail's stdout */
245 fdout
= inpipe
[1]; /* write to sendmail's stdin */
246 tag
= config
->sendmail
;
250 s
[0] = config
->smtp_server
;
251 s
[1] = (char *)"smtp";
252 if(!(ai
= get_address(&a
, &pref
, &tag
)))
254 fdin
= xsocket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
255 if(connect(fdin
, ai
->ai_addr
, ai
->ai_addrlen
) < 0) {
256 disorder_error(errno
, "error connecting to %s", tag
);
260 if((fdout
= dup(fdin
)) < 0)
261 disorder_fatal(errno
, "error calling dup2");
263 if(!(in
= fdopen(fdin
, "rb")))
264 disorder_fatal(errno
, "error calling fdopen");
265 if(!(out
= fdopen(fdout
, "wb")))
266 disorder_fatal(errno
, "error calling fdopen");
267 rc
= sendmailfp(tag
, in
, out
, sender
, pubsender
, recipient
, subject
,
268 encoding
, content_type
, body
);
274 while(waitpid(pid
, &w
, 0) < 0 && errno
== EINTR
)
277 disorder_fatal(errno
, "error calling waitpid");
279 disorder_info("warning: %s -bs: %s", config
->sendmail
, wstat(w
));
280 /* Not fatal - we determine success/failure from the SMTP conversation.
281 * Some MTAs exit nonzero if you don't QUIT, which is just stupidly
287 /** @brief Start a subproces to send a mail message
288 * @param sender Sender address (can be "")
289 * @param pubsender Visible sender address (must not be "")
290 * @param recipient Recipient address
291 * @param subject Subject string
292 * @param encoding Body encoding
293 * @param content_type Content-type of body
294 * @param body Text of body (encoded, but \n for newline)
295 * @return Subprocess PID on success, -1 on error
297 pid_t
sendmail_subprocess(const char *sender
,
298 const char *pubsender
,
299 const char *recipient
,
301 const char *encoding
,
302 const char *content_type
,
306 if(!(pid
= fork())) {
308 if(sendmail(sender
, pubsender
, recipient
, subject
,
309 encoding
, content_type
, body
))
314 disorder_error(errno
, "error calling fork");