use \r\n to end cmds
[smtp.git] / smtp.c
blob68c765a390d1d4b6b9d7559c550a07b70bbc5618
1 /*
2 * a simple smtp mail sender
4 * Copyright (C) 2010 Ali Gholami Rudi
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License, as published by the
8 * Free Software Foundation.
9 */
10 #include <arpa/inet.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <netdb.h>
15 #include <netinet/in.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <strings.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <time.h>
24 #include <unistd.h>
25 #include "config.h"
27 #define BUFFSIZE (1 << 12)
28 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
29 #define MIN(a, b) ((a) < (b) ? (a) : (b))
31 static char buf[BUFFSIZE];
32 static char *buf_cur;
33 static char *buf_end;
34 static int fd;
35 static char mail[MAILLEN];
36 static int mail_len;
38 #ifdef SSL
39 #include <polarssl/ssl.h>
40 #include <polarssl/havege.h>
42 static ssl_context ssl;
43 static ssl_session ssn;
44 static havege_state hs;
46 static int ps_send(void *ctx, unsigned char *buf, int len)
48 return write(*(int *) ctx, buf, len);
51 static int ps_recv(void *ctx, unsigned char *buf, int len)
53 return read(*(int *) ctx, buf, len);
56 #endif
58 static char *b64 =
59 "ABCDEFGHIJGLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
61 static void b64_num(char *s, unsigned long num)
63 s[3] = b64[num & 0x3f];
64 num >>= 6;
65 s[2] = b64[num & 0x3f];
66 num >>= 6;
67 s[1] = b64[num & 0x3f];
68 num >>= 6;
69 s[0] = b64[num & 0x3f];
72 static char *putb64(char *dst, char *src, int len)
74 int n = len / 3;
75 int i;
76 if (n * 3 != len)
77 n++;
78 for (i = 0; i < n; i++) {
79 char *s = src + 3 * i;
80 unsigned c0 = (unsigned) s[0];
81 unsigned c1 = s + 1 < src + len ? (unsigned) s[1] : 0;
82 unsigned c2 = s + 2 < src + len ? (unsigned) s[2] : 0;
83 unsigned long word = (c0 << 16) | (c1 << 8) | c2;
84 b64_num(dst + 4 * i, word);
86 if (len % 3)
87 for (i = len % 3; i < 3; i++)
88 dst[4 * len - 3 + i] = '=';
89 dst[n * 4] = '\0';
90 return dst + n * 4;
93 static char *putstr(char *dst, char *src)
95 int len = strchr(src, '\0') - src;
96 memcpy(dst, src, len + 1);
97 return dst + len;
100 static char *cutaddr(char *dst, char *s, int len)
102 static char *addrseps = "<>()%!~* \t\r\n,\"'%";
103 char *end = s + len;
104 while (s < end && *s && strchr(addrseps, *s))
105 s++;
106 while (s < end && *s && !strchr(addrseps, *s))
107 *dst++ = *s++;
108 *dst = '\0';
109 return s;
112 static char *find_hdr(char *hdr)
114 char *s = mail;
115 int len = strlen(hdr);
116 char *end = mail + mail_len - len;
117 while (s < end) {
118 char *r;
119 if (!strncasecmp(s, hdr, len))
120 return s;
121 r = memchr(s, '\n', end - s);
122 if (!r || r == s)
123 return NULL;
124 s = r + 1;
126 return s;
129 static int hdr_len(char *hdr)
131 char *s = hdr;
132 while (*s) {
133 char *r = strchr(s, '\n');
134 if (!r)
135 return strchr(s, '\0') - hdr;
136 s = r + 1;
137 if (!isspace(*s))
138 return s - hdr;
140 return 0;
143 static int xread(int fd, char *buf, int len)
145 int nr = 0;
146 while (nr < len) {
147 int ret = read(fd, buf + nr, len - nr);
148 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
149 continue;
150 if (ret <= 0)
151 break;
152 nr += ret;
154 return nr;
157 static void print(char *buf, int len)
159 write(STDOUT_FILENO, buf, len);
162 static int smtp_connect(char *addr, char *port)
164 struct addrinfo hints, *addrinfo;
165 int fd;
167 memset(&hints, 0, sizeof(hints));
168 hints.ai_family = AF_UNSPEC;
169 hints.ai_socktype = SOCK_STREAM;
170 hints.ai_flags = AI_PASSIVE;
172 getaddrinfo(addr, port, &hints, &addrinfo);
173 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
174 addrinfo->ai_protocol);
176 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1) {
177 close(fd);
178 freeaddrinfo(addrinfo);
179 return -1;
181 freeaddrinfo(addrinfo);
182 return fd;
185 static int reply_line(char *dst, int len)
187 int nr = 0;
188 char *nl;
189 while (nr < len) {
190 int ml;
191 if (!buf_cur || buf_cur >= buf_end) {
192 #ifdef SSL
193 int buf_len = ssl_read(&ssl, (unsigned char *) buf,
194 sizeof(buf));
195 #else
196 int buf_len = read(fd, buf, sizeof(buf));
197 #endif
198 if (buf_len <= 0)
199 return -1;
200 #ifdef DEBUG
201 print(buf, buf_len);
202 #endif
203 buf_cur = buf;
204 buf_end = buf + buf_len;
206 ml = MIN(buf_end - buf_cur, len - nr);
207 if ((nl = memchr(buf_cur, '\n', ml))) {
208 nl++;
209 memcpy(dst + nr, buf_cur, nl - buf_cur);
210 nr += nl - buf_cur;
211 buf_cur = nl;
212 return nr;
214 memcpy(dst + nr, buf_cur, ml);
215 nr += ml;
216 buf_cur += ml;
218 return nr;
221 static int smtp_write(char *s, int len)
223 #ifdef DEBUG
224 print(s, len);
225 #endif
226 #ifdef SSL
227 return ssl_write(&ssl, (unsigned char *) s, len);
228 #else
229 return write(fd, s, len);
230 #endif
233 static int smtp_xwrite(char *buf, int len)
235 int nw = 0;
236 while (nw < len) {
237 int ret = smtp_write(buf + nw, len - nw);
238 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
239 continue;
240 if (ret < 0)
241 break;
242 nw += ret;
244 fsync(fd);
245 return nw;
248 static void send_cmd(char *cmd)
250 smtp_write(cmd, strlen(cmd));
253 static int is_eoc(char *s, int len)
255 return isdigit(s[0]) && isdigit(s[1]) && isdigit(s[2]) && isspace(s[3]);
258 static void ehlo(void)
260 char line[BUFFSIZE];
261 int len;
262 len = reply_line(line, sizeof(line));
263 send_cmd("EHLO " HOSTNAME "\r\n");
264 do {
265 len = reply_line(line, sizeof(line));
266 } while (!is_eoc(line, len));
269 static void login(char *user, char *pass)
271 char line[BUFFSIZE];
272 int len;
273 char *s = line;
274 send_cmd("AUTH LOGIN\r\n");
275 len = reply_line(line, sizeof(line));
276 s = putb64(s, user, strlen(user));
277 s = putstr(s, "\r\n");
278 send_cmd(line);
279 len = reply_line(line, sizeof(line));
280 s = line;
281 s = putb64(s, pass, strlen(pass));
282 s = putstr(s, "\r\n");
283 send_cmd(line);
284 len = reply_line(line, sizeof(line));
287 static int write_mail(struct account *account)
289 char line[BUFFSIZE];
290 int len;
291 char *s = line;
292 char *to_hdrs[] = {"to:", "cc:", "bcc:"};
293 int i;
294 s = putstr(s, "MAIL FROM:<");
295 s = putstr(s, account->from);
296 s = putstr(s, ">\r\n");
297 send_cmd(line);
298 len = reply_line(line, sizeof(line));
300 for (i = 0; i < ARRAY_SIZE(to_hdrs); i++) {
301 char *hdr, *end;
302 char addr[BUFFSIZE];
303 if (!(hdr = find_hdr(to_hdrs[i])))
304 continue;
305 end = hdr + hdr_len(hdr);
306 s = hdr;
307 while ((s = cutaddr(addr, s, end - s)) && s < end) {
308 char *at = strchr(addr, '@');
309 if (at && at > addr && *(at + 1)) {
310 char *r = line;
311 r = putstr(r, "RCPT TO:<");
312 r = putstr(r, addr);
313 r = putstr(r, ">\r\n");
314 send_cmd(line);
315 len = reply_line(line, sizeof(line));
320 send_cmd("DATA\r\n");
321 len = reply_line(line, sizeof(line));
322 if (smtp_xwrite(mail, mail_len) != mail_len)
323 return -1;
324 send_cmd("\r\n.\r\n");
325 len = reply_line(line, sizeof(line));
326 send_cmd("QUIT\r\n");
327 len = reply_line(line, sizeof(line));
328 return 0;
331 static struct account *choose_account(void)
333 char *from = find_hdr("from:");
334 char *end = from + hdr_len(from);
335 int i;
336 if (!from)
337 return &accounts[0];
338 for (i = 0; i < ARRAY_SIZE(accounts); i++) {
339 char *pat = accounts[i].from;
340 int len = strlen(pat);
341 char *s = from;
342 while (s + len < end && (s = memchr(s, pat[0], end - s)))
343 if (!strncmp(pat, s++, len))
344 return &accounts[i];
346 return &accounts[0];
349 int main(int argc, char *argv[])
351 struct account *account;
352 if ((mail_len = xread(STDIN_FILENO, mail, sizeof(mail))) == -1)
353 return 1;
354 account = choose_account();
355 fd = smtp_connect(account->server, account->port);
356 #ifdef SSL
357 havege_init(&hs);
358 memset(&ssn, 0, sizeof(ssn));
359 if (ssl_init(&ssl))
360 return 1;
361 ssl_set_endpoint(&ssl, SSL_IS_CLIENT);
362 ssl_set_authmode(&ssl, SSL_VERIFY_NONE);
363 ssl_set_rng(&ssl, havege_rand, &hs);
364 ssl_set_bio(&ssl, ps_recv, &fd, ps_send, &fd);
365 ssl_set_ciphers(&ssl, ssl_default_ciphers);
366 ssl_set_session(&ssl, 1, 600, &ssn);
367 #endif
368 ehlo();
369 login(account->user, account->pass);
370 write_mail(account);
372 #ifdef SSL
373 ssl_close_notify(&ssl);
374 #endif
375 close(fd);
376 #ifdef SSL
377 ssl_free(&ssl);
378 #endif
379 return 0;