conn: update for polarssl-1.3.7
[smtp.git] / smtp.c
blobc20e2e2f82c0ed9386c66ce59c4903e50a01b893
1 /*
2 * a simple smtp mail sender
4 * Copyright (C) 2010-2013 Ali Gholami Rudi
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 "config.h"
18 #include "conn.h"
20 #define BUFFSIZE (1 << 12)
21 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
22 #define MIN(a, b) ((a) < (b) ? (a) : (b))
24 static char buf[BUFFSIZE];
25 static int buf_len;
26 static int buf_pos;
27 static char mail[MAILLEN];
28 static int mail_len;
29 static struct conn *conn;
31 static char *b64 =
32 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
34 static void b64_num(char *s, unsigned long num)
36 s[3] = b64[num & 0x3f];
37 num >>= 6;
38 s[2] = b64[num & 0x3f];
39 num >>= 6;
40 s[1] = b64[num & 0x3f];
41 num >>= 6;
42 s[0] = b64[num & 0x3f];
45 static char *putb64(char *dst, char *src, int len)
47 int n = len / 3;
48 int i;
49 if (n * 3 != len)
50 n++;
51 for (i = 0; i < n; i++) {
52 char *s = src + 3 * i;
53 unsigned c0 = (unsigned) s[0];
54 unsigned c1 = s + 1 < src + len ? (unsigned) s[1] : 0;
55 unsigned c2 = s + 2 < src + len ? (unsigned) s[2] : 0;
56 unsigned long word = (c0 << 16) | (c1 << 8) | c2;
57 b64_num(dst + 4 * i, word);
59 if (len % 3)
60 for (i = len % 3; i < 3; i++)
61 dst[4 * len - 3 + i] = '=';
62 dst[n * 4] = '\0';
63 return dst + n * 4;
66 static char *putstr(char *dst, char *src)
68 int len = strchr(src, '\0') - src;
69 memcpy(dst, src, len + 1);
70 return dst + len;
73 static char *cutaddr(char *dst, char *s, int len)
75 static char *addrseps = "<>()%!~* \t\r\n,\"'%";
76 char *end = s + len;
77 while (s < end && *s && strchr(addrseps, *s))
78 s++;
79 while (s < end && *s && !strchr(addrseps, *s))
80 *dst++ = *s++;
81 *dst = '\0';
82 return s;
85 static char *find_hdr(char *hdr)
87 char *s = mail;
88 int len = strlen(hdr);
89 char *end = mail + mail_len - len;
90 while (s < end) {
91 char *r;
92 if (!strncasecmp(s, hdr, len))
93 return s;
94 r = memchr(s, '\n', end - s);
95 if (!r || r == s)
96 return NULL;
97 s = r + 1;
99 return s;
102 static int hdr_len(char *hdr)
104 char *s = hdr;
105 while (*s) {
106 char *r = strchr(s, '\n');
107 if (!r)
108 return strchr(s, '\0') - hdr;
109 s = r + 1;
110 if (!isspace(*s))
111 return s - hdr;
113 return 0;
116 static int xread(int fd, char *buf, int len)
118 int nr = 0;
119 while (nr < len) {
120 int ret = read(fd, buf + nr, len - nr);
121 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
122 continue;
123 if (ret <= 0)
124 break;
125 nr += ret;
127 return nr;
130 static int smtp_read(void)
132 if (buf_pos == buf_len) {
133 buf_len = conn_read(conn, buf, sizeof(buf));
134 buf_pos = 0;
136 return buf_pos < buf_len ? (unsigned char) buf[buf_pos++] : -1;
139 static int smtp_line(char *dst, int len)
141 int i = 0;
142 int c;
143 while (i < len) {
144 c = smtp_read();
145 if (c < 0)
146 return -1;
147 dst[i++] = c;
148 if (c == '\n')
149 break;
151 DPRINT(dst, i);
152 return i;
154 static int smtp_xwrite(char *buf, int len)
156 int nw = 0;
157 while (nw < len) {
158 int ret = conn_write(conn, buf + nw, len - nw);
159 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
160 continue;
161 if (ret < 0)
162 break;
163 DPRINT(buf + nw, ret);
164 nw += ret;
166 return nw;
169 static void send_cmd(char *cmd)
171 conn_write(conn, cmd, strlen(cmd));
172 DPRINT(cmd, strlen(cmd));
175 static int is_eoc(char *s, int len)
177 return len > 3 && isdigit(s[0]) && isdigit(s[1]) &&
178 isdigit(s[2]) && isspace(s[3]);
181 static int ehlo(void)
183 char line[BUFFSIZE];
184 int len;
185 len = smtp_line(line, sizeof(line));
186 send_cmd("EHLO " HOSTNAME "\r\n");
187 do {
188 len = smtp_line(line, sizeof(line));
189 } while (len > 0 && !is_eoc(line, len));
190 return len <= 0;
193 static int login(char *user, char *pass)
195 char line[BUFFSIZE];
196 char *s = line;
197 send_cmd("AUTH LOGIN\r\n");
198 smtp_line(line, sizeof(line));
199 s = putb64(s, user, strlen(user));
200 s = putstr(s, "\r\n");
201 send_cmd(line);
202 smtp_line(line, sizeof(line));
203 s = line;
204 s = putb64(s, pass, strlen(pass));
205 s = putstr(s, "\r\n");
206 send_cmd(line);
207 return smtp_line(line, sizeof(line)) <= 0;
210 static int write_mail(struct account *account)
212 char line[BUFFSIZE];
213 char *to_hdrs[] = {"to:", "cc:", "bcc:"};
214 int i;
215 sprintf(line, "MAIL FROM:<%s>\r\n", account->from);
216 send_cmd(line);
217 smtp_line(line, sizeof(line));
219 for (i = 0; i < ARRAY_SIZE(to_hdrs); i++) {
220 char *hdr, *end, *s;
221 char addr[BUFFSIZE];
222 if (!(hdr = find_hdr(to_hdrs[i])))
223 continue;
224 end = hdr + hdr_len(hdr);
225 s = hdr;
226 while ((s = cutaddr(addr, s, end - s)) && s < end) {
227 char *at = strchr(addr, '@');
228 if (at && at > addr && *(at + 1)) {
229 sprintf(line, "RCPT TO:<%s>\r\n", addr);
230 send_cmd(line);
231 smtp_line(line, sizeof(line));
236 send_cmd("DATA\r\n");
237 if (smtp_line(line, sizeof(line)) <= 0)
238 return 1;
239 if (smtp_xwrite(mail, mail_len) != mail_len)
240 return 1;
241 send_cmd("\r\n.\r\n");
242 if (smtp_line(line, sizeof(line)) <= 0)
243 return 1;
244 send_cmd("QUIT\r\n");
245 smtp_line(line, sizeof(line));
246 return 0;
249 static struct account *choose_account(void)
251 char *from = find_hdr("from:");
252 char *end = from + hdr_len(from);
253 int i;
254 for (i = 0; i < ARRAY_SIZE(accounts) && from; i++) {
255 char *pat = accounts[i].from;
256 int len = strlen(pat);
257 char *s = from;
258 while (s + len < end && (s = memchr(s, pat[0], end - s)))
259 if (!strncmp(pat, s++, len))
260 return &accounts[i];
262 return NULL;
265 int main(int argc, char *argv[])
267 struct account *account;
268 mail_len = xread(STDIN_FILENO, mail, sizeof(mail));
269 if (mail_len < 0 || mail_len >= sizeof(mail))
270 return 1;
271 account = choose_account();
272 if (!account)
273 return 1;
274 conn = conn_connect(account->server, account->port, account->cert);
275 if (!conn)
276 return 1;
277 if (ehlo())
278 goto fail;
279 if (login(account->user, account->pass))
280 goto fail;
281 if (write_mail(account))
282 goto fail;
283 conn_close(conn);
284 return 0;
285 fail:
286 conn_close(conn);
287 return 1;