conn: with null certfile, do not verify the certificate
[smtp.git] / smtp.c
bloba74ee58c5b82655ef26e67b5fc101c70b800342b
1 /*
2 * A simple SMTP mail sender
4 * Copyright (C) 2010-2016 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
7 */
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <strings.h>
15 #include <time.h>
16 #include <unistd.h>
17 #include "conf.h"
18 #include "conn.h"
20 #define LNLEN (1 << 12)
21 #define HDLEN (1 << 15)
22 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
23 #define MIN(a, b) ((a) < (b) ? (a) : (b))
25 static char buf[LNLEN]; /* SMTP reply buffer */
26 static int buf_len;
27 static int buf_pos;
28 static char mail[HDLEN]; /* the first HDLEN bytes of the mail */
29 static int mail_len;
30 static struct conn *conn; /* the SMTP connection */
32 static char *b64_chr =
33 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
35 /* encode 3 bytes in base64 */
36 static void b64_word(char *s, unsigned num)
38 s[3] = b64_chr[num & 0x3f];
39 num >>= 6;
40 s[2] = b64_chr[num & 0x3f];
41 num >>= 6;
42 s[1] = b64_chr[num & 0x3f];
43 num >>= 6;
44 s[0] = b64_chr[num & 0x3f];
47 /* base64 encoding; returns a static buffer */
48 static char *b64(char *src, int len)
50 static char dst[LNLEN];
51 int n = (len + 2) / 3;
52 int i;
53 for (i = 0; i < n; i++) {
54 char *s = src + 3 * i;
55 unsigned c0 = (unsigned char) s[0];
56 unsigned c1 = s + 1 < src + len ? (unsigned char) s[1] : 0;
57 unsigned c2 = s + 2 < src + len ? (unsigned char) s[2] : 0;
58 b64_word(dst + 4 * i, (c0 << 16) | (c1 << 8) | c2);
60 if (len % 3 >= 1)
61 dst[4 * n - 1] = '=';
62 if (len % 3 == 1)
63 dst[4 * n - 2] = '=';
64 dst[n * 4] = '\0';
65 return dst;
68 /* copy the address in s to dst */
69 static char *cutaddr(char *dst, char *s, int len)
71 static char *addrseps = "<>()%!~* \t\r\n,\"'%";
72 char *end = s + len;
73 while (s < end && *s && strchr(addrseps, *s))
74 s++;
75 while (s < end && *s && !strchr(addrseps, *s))
76 *dst++ = *s++;
77 *dst = '\0';
78 return s;
81 static char *hdr_val(char *hdr)
83 char *s = mail;
84 int len = strlen(hdr);
85 char *end = mail + mail_len - len;
86 while (s < end && strncasecmp(s, hdr, len)) {
87 char *r = memchr(s, '\n', end - s);
88 if (!r || r == s)
89 return NULL;
90 s = r + 1;
92 return s < end ? s : NULL;
95 static int hdr_len(char *hdr)
97 char *s = hdr;
98 while (*s) {
99 char *r = strchr(s, '\n');
100 if (!r)
101 return strchr(s, '\0') - hdr;
102 s = r + 1;
103 if (!isspace(*s))
104 return s - hdr;
106 return 0;
109 static int xread(int fd, char *buf, int len)
111 int nr = 0;
112 while (nr < len) {
113 int ret = read(fd, buf + nr, len - nr);
114 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
115 continue;
116 if (ret <= 0)
117 break;
118 nr += ret;
120 return nr;
123 /* read one character from the SMTP server */
124 static int smtp_read(void)
126 if (buf_pos == buf_len) {
127 buf_len = conn_read(conn, buf, sizeof(buf));
128 buf_pos = 0;
130 return buf_pos < buf_len ? (unsigned char) buf[buf_pos++] : -1;
133 /* read a line from the SMTP server; returns a static buffer */
134 static char *smtp_line(void)
136 static char dst[LNLEN];
137 int i = 0;
138 int c;
139 while (i < sizeof(dst)) {
140 c = smtp_read();
141 if (c < 0)
142 return NULL;
143 dst[i++] = c;
144 if (c == '\n')
145 break;
147 dst[i] = '\0';
148 DPRINT(dst, i);
149 return dst;
152 /* check the error code of an SMTP reply */
153 static int smtp_ok(char *s)
155 return s && atoi(s) < 400;
158 /* check if s is the last line of an SMTP reply */
159 static int smtp_eoc(char *s)
161 return isdigit((unsigned char) s[0]) && isdigit((unsigned char) s[1]) &&
162 isdigit((unsigned char) s[2]) && isspace((unsigned char) s[3]);
165 static int smtp_xwrite(char *buf, int len)
167 int nw = 0;
168 while (nw < len) {
169 int ret = conn_write(conn, buf + nw, len - nw);
170 if (ret < 0 && (errno == EAGAIN || errno == EINTR))
171 continue;
172 if (ret < 0)
173 break;
174 DPRINT(buf + nw, ret);
175 nw += ret;
177 return nw;
180 /* send a command to the SMTP server */
181 static void smtp_cmd(char *cmd)
183 conn_write(conn, cmd, strlen(cmd));
184 DPRINT(cmd, strlen(cmd));
187 static int ehlo(void)
189 char *ret;
190 if (!smtp_ok(smtp_line()))
191 return 1;
192 smtp_cmd("EHLO " HOSTNAME "\r\n");
193 do {
194 ret = smtp_line();
195 } while (ret && !smtp_eoc(ret));
196 return !smtp_ok(ret);
199 static int login(char *user, char *pass)
201 char cmd[LNLEN];
202 smtp_cmd("AUTH LOGIN\r\n");
203 if (!smtp_ok(smtp_line()))
204 return 1;
205 sprintf(cmd, "%s\r\n", b64(user, strlen(user)));
206 smtp_cmd(cmd);
207 if (!smtp_ok(smtp_line()))
208 return 1;
209 sprintf(cmd, "%s\r\n", b64(pass, strlen(pass)));
210 smtp_cmd(cmd);
211 return !smtp_ok(smtp_line());
214 static int mail_data(struct account *account)
216 char buf[HDLEN];
217 char cmd[LNLEN];
218 char *to_hdrs[] = {"to:", "cc:", "bcc:"};
219 int i, buflen;
220 sprintf(cmd, "MAIL FROM:<%s>\r\n", account->from);
221 smtp_cmd(cmd);
222 if (!smtp_ok(smtp_line()))
223 return 1;
224 for (i = 0; i < ARRAY_SIZE(to_hdrs); i++) {
225 char *hdr, *end, *s;
226 char addr[LNLEN];
227 if (!(hdr = hdr_val(to_hdrs[i])))
228 continue;
229 end = hdr + hdr_len(hdr);
230 s = hdr;
231 while ((s = cutaddr(addr, s, end - s)) && s < end) {
232 char *at = strchr(addr, '@');
233 if (at && at > addr && *(at + 1)) {
234 sprintf(cmd, "RCPT TO:<%s>\r\n", addr);
235 smtp_cmd(cmd);
236 if (!smtp_ok(smtp_line()))
237 return 1;
241 smtp_cmd("DATA\r\n");
242 if (!smtp_ok(smtp_line()))
243 return 1;
244 memcpy(buf, mail, mail_len);
245 buflen = mail_len;
246 do {
247 if (smtp_xwrite(buf, buflen) != buflen)
248 return 1;
249 } while ((buflen = xread(0, buf, sizeof(buf))) > 0);
250 smtp_cmd("\r\n.\r\n");
251 if (!smtp_ok(smtp_line()))
252 return 1;
253 smtp_cmd("QUIT\r\n");
254 smtp_line();
255 return 0;
258 static struct account *choose_account(void)
260 char *from = hdr_val("from:");
261 char *end = from + hdr_len(from);
262 int i;
263 for (i = 0; i < ARRAY_SIZE(accounts) && from; i++) {
264 char *pat = accounts[i].from;
265 int len = strlen(pat);
266 char *s = from;
267 while (s + len < end && (s = memchr(s, pat[0], end - s)))
268 if (!strncmp(pat, s++, len))
269 return &accounts[i];
271 return NULL;
274 int main(int argc, char *argv[])
276 struct account *account;
277 mail_len = xread(0, mail, sizeof(mail));
278 if (mail_len <= 0)
279 return 1;
280 account = choose_account();
281 if (!account)
282 return 1;
283 conn = conn_connect(account->server, account->port, account->cert);
284 if (!conn)
285 return 1;
286 if (ehlo())
287 goto fail;
288 if (login(account->user, account->pass))
289 goto fail;
290 if (mail_data(account))
291 goto fail;
292 conn_close(conn);
293 return 0;
294 fail:
295 conn_close(conn);
296 return 1;