2 * This file is part of DisOrder
3 * Copyright (C) 2007 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 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/socket.h>
35 #include "configuration.h"
36 #include "inputline.h"
42 /** @brief Get a server response
43 * @param tag Server name
44 * @param in Input stream
45 * @return Response code 0-999 or -1 on error
47 static int getresponse(const char *tag
, FILE *in
) {
50 while(!inputline(tag
, in
, &line
, CRLF
)) {
51 if(line
[0] >= '0' && line
[0] <= '9'
52 && line
[1] >= '0' && line
[1] <= '9'
53 && line
[2] >= '0' && line
[2] <= '9') {
54 const int rc
= 10 * (10 * line
[0] + line
[1]) + line
[2] - 111 * '0';
55 if(rc
>= 400 && rc
<= 599)
56 error(0, "%s: %s", tag
, line
);
60 /* else go round for further response lines */
62 error(0, "%s: malformed response: %s", tag
, line
);
67 error(errno
, "%s: read error", tag
);
69 error(0, "%s: server closed connection", tag
);
73 /** @brief Send a command to the server
74 * @param tag Server name
75 * @param out stream to send commands to
76 * @param fmt Format string
77 * @return 0 on success, non-0 on error
79 static int sendcommand(const char *tag
, FILE *out
, const char *fmt
, ...) {
84 rc
= vfprintf(out
, fmt
, ap
);
87 rc
= fputs("\r\n", out
);
92 error(errno
, "%s: write error", tag
);
96 /** @brief Send a mail message
97 * @param tag Server name
98 * @param in Stream to read responses from
99 * @param out Stream to send commands to
100 * @param sender Sender address (can be "")
101 * @param pubsender Visible sender address (must not be "")
102 * @param recipient Recipient address
103 * @param subject Subject string
104 * @param encoding Body encoding
105 * @param body_type Content-type of body
106 * @param body Text of body (encoded, but \n for newline)
107 * @return 0 on success, non-0 on error
109 static int sendmailfp(const char *tag
, FILE *in
, FILE *out
,
111 const char *pubsender
,
112 const char *recipient
,
114 const char *encoding
,
115 const char *content_type
,
127 strftime(date
, sizeof date
, "%a, %d %b %Y %H:%M:%S +0000", &ut
);
128 gcry_create_nonce(idbuf
, sizeof idbuf
);
129 id
= mime_to_base64(idbuf
, sizeof idbuf
);
130 if((rc
= getresponse(tag
, in
)) / 100 != 2)
132 if(sendcommand(tag
, out
, "HELO %s", local_hostname()))
134 if((rc
= getresponse(tag
, in
)) / 100 != 2)
136 if(sendcommand(tag
, out
, "MAIL FROM:<%s>", sender
))
138 if((rc
= getresponse(tag
, in
)) / 100 != 2)
140 if(sendcommand(tag
, out
, "RCPT TO:<%s>", recipient
))
142 if((rc
= getresponse(tag
, in
)) / 100 != 2)
144 if(sendcommand(tag
, out
, "DATA", sender
))
146 if((rc
= getresponse(tag
, in
)) / 100 != 3)
148 if(fprintf(out
, "From: %s\r\n", pubsender
) < 0
149 || fprintf(out
, "To: %s\r\n", recipient
) < 0
150 || fprintf(out
, "Subject: %s\r\n", subject
) < 0
151 || fprintf(out
, "Message-ID: <%s@%s>\r\n", id
, local_hostname()) < 0
152 || fprintf(out
, "MIME-Version: 1.0\r\n") < 0
153 || fprintf(out
, "Content-Type: %s\r\n", content_type
) < 0
154 || fprintf(out
, "Content-Transfer-Encoding: %s\r\n", encoding
) < 0
155 || fprintf(out
, "Date: %s\r\n", date
) < 0
156 || fprintf(out
, "\r\n") < 0) {
158 error(errno
, "%s: write error", tag
);
161 for(ptr
= body
; *ptr
; ++ptr
) {
162 if(sol
&& *ptr
== '.')
163 if(fputc('.', out
) < 0)
166 if(fputc('\r', out
) < 0)
171 if(fputc(*ptr
, out
) < 0)
175 if(fputs("\r\n", out
) < 0)
177 if(fprintf(out
, ".\r\n") < 0
180 if((rc
= getresponse(tag
, in
)) / 100 != 2)
185 /** @brief Send a mail message
186 * @param sender Sender address (can be "")
187 * @param pubsender Visible sender address (must not be "")
188 * @param recipient Recipient address
189 * @param subject Subject string
190 * @param encoding Body encoding
191 * @param content_type Content-type of body
192 * @param body Text of body (encoded, but \n for newline)
193 * @return 0 on success, non-0 on error
195 * See mime_encode_text() for encoding of text bodies.
197 int sendmail(const char *sender
,
198 const char *pubsender
,
199 const char *recipient
,
201 const char *encoding
,
202 const char *content_type
,
211 static const struct addrinfo pref
= {
222 /* Find the SMTP server */
225 s
[0] = config
->smtp_server
;
226 s
[1] = (char *)"smtp";
227 if(!(ai
= get_address(&a
, &pref
, &tag
)))
229 fdin
= xsocket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
230 if(connect(fdin
, ai
->ai_addr
, ai
->ai_addrlen
) < 0) {
231 error(errno
, "error connecting to %s", tag
);
235 if((fdout
= dup(fdin
)) < 0)
236 fatal(errno
, "error calling dup2");
237 if(!(in
= fdopen(fdin
, "rb")))
238 fatal(errno
, "error calling fdopen");
239 if(!(out
= fdopen(fdout
, "wb")))
240 fatal(errno
, "error calling fdopen");
241 rc
= sendmailfp(tag
, in
, out
, sender
, pubsender
, recipient
, subject
,
242 encoding
, content_type
, body
);