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>
34 #include "configuration.h"
35 #include "inputline.h"
41 /** @brief Get a server response
42 * @param tag Server name
43 * @param in Input stream
44 * @return Response code 0-999 or -1 on error
46 static int getresponse(const char *tag, FILE *in) {
49 while(!inputline(tag, in, &line, CRLF)) {
50 if(line[0] >= '0' && line[0] <= '9'
51 && line[1] >= '0' && line[1] <= '9'
52 && line[2] >= '0' && line[2] <= '9') {
53 const int rc = 10 * (10 * line[0] + line[1]) + line[2] - 111 * '0';
54 if(rc >= 400 && rc <= 599)
55 error(0, "%s: %s", tag, line);
59 /* else go round for further response lines */
61 error(0, "%s: malformed response: %s", tag, line);
66 error(errno, "%s: read error", tag);
68 error(0, "%s: server closed connection", tag);
72 /** @brief Send a command to the server
73 * @param tag Server name
74 * @param out stream to send commands to
75 * @param fmt Format string
76 * @return 0 on success, non-0 on error
78 static int sendcommand(const char *tag, FILE *out, const char *fmt, ...) {
83 rc = vfprintf(out, fmt, ap);
86 rc = fputs("\r\n", out);
91 error(errno, "%s: write error", tag);
95 /** @brief Send a mail message
96 * @param tag Server name
97 * @param in Stream to read responses from
98 * @param out Stream to send commands to
99 * @param sender Sender address (can be "")
100 * @param pubsender Visible sender address (must not be "")
101 * @param recipient Recipient address
102 * @param subject Subject string
103 * @param encoding Body encoding
104 * @param body_type Content-type of body
105 * @param body Text of body (encoded, but \n for newline)
106 * @return 0 on success, non-0 on error
108 static int sendmailfp(const char *tag, FILE *in, FILE *out,
110 const char *pubsender,
111 const char *recipient,
113 const char *encoding,
114 const char *content_type,
121 gcry_create_nonce(idbuf, sizeof idbuf);
122 id = mime_to_base64(idbuf, sizeof idbuf);
123 if((rc = getresponse(tag, in)) / 100 != 2)
125 if(sendcommand(tag, out, "HELO %s", local_hostname()))
127 if((rc = getresponse(tag, in)) / 100 != 2)
129 if(sendcommand(tag, out, "MAIL FROM:<%s>", sender))
131 if((rc = getresponse(tag, in)) / 100 != 2)
133 if(sendcommand(tag, out, "RCPT TO:<%s>", recipient))
135 if((rc = getresponse(tag, in)) / 100 != 2)
137 if(sendcommand(tag, out, "DATA", sender))
139 if((rc = getresponse(tag, in)) / 100 != 3)
141 if(fprintf(out, "From: %s\r\n", pubsender) < 0
142 || fprintf(out, "To: %s\r\n", recipient) < 0
143 || fprintf(out, "Subject: %s\r\n", subject) < 0
144 || fprintf(out, "Message-ID: <%s@%s>\r\n", id, local_hostname()) < 0
145 || fprintf(out, "MIME-Version: 1.0\r\n") < 0
146 || fprintf(out, "Content-Type: %s\r\n", content_type) < 0
147 || fprintf(out, "Content-Transfer-Encoding: %s\r\n", encoding) < 0
148 || fprintf(out, "\r\n") < 0) {
150 error(errno, "%s: write error", tag);
153 for(ptr = body; *ptr; ++ptr) {
154 if(sol && *ptr == '.')
155 if(fputc('.', out) < 0)
158 if(fputc('\r', out) < 0)
163 if(fputc(*ptr, out) < 0)
167 if(fputs("\r\n", out) < 0)
169 if(fprintf(out, ".\r\n") < 0
172 if((rc = getresponse(tag, in)) / 100 != 2)
177 /** @brief Send a mail message
178 * @param sender Sender address (can be "")
179 * @param pubsender Visible sender address (must not be "")
180 * @param recipient Recipient address
181 * @param subject Subject string
182 * @param encoding Body encoding
183 * @param content_type Content-type of body
184 * @param body Text of body (encoded, but \n for newline)
185 * @return 0 on success, non-0 on error
187 * See mime_encode_text() for encoding of text bodies.
189 int sendmail(const char *sender,
190 const char *pubsender,
191 const char *recipient,
193 const char *encoding,
194 const char *content_type,
203 static const struct addrinfo pref = {
214 /* Find the SMTP server */
217 s[0] = config->smtp_server;
218 s[1] = (char *)"smtp";
219 if(!(ai = get_address(&a, &pref, &tag)))
221 fdin = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
222 if(connect(fdin, ai->ai_addr, ai->ai_addrlen) < 0) {
223 error(errno, "error connecting to %s", tag);
227 if((fdout = dup(fdin)) < 0)
228 fatal(errno, "error calling dup2");
229 if(!(in = fdopen(fdin, "rb")))
230 fatal(errno, "error calling fdopen");
231 if(!(out = fdopen(fdout, "wb")))
232 fatal(errno, "error calling fdopen");
233 rc = sendmailfp(tag, in, out, sender, pubsender, recipient, subject,
234 encoding, content_type, body);