conn: initialize conn struct correctly
[smtp.git] / smtp.c
blob80cf9ba969fd887d3b33d350f006b738ae88cbad
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 char *buf_cur;
26 static char *buf_end;
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 reply_line(char *dst, int len)
132 int nr = 0;
133 char *nl;
134 while (nr < len) {
135 int ml;
136 if (!buf_cur || buf_cur >= buf_end) {
137 int buf_len = conn_read(conn, buf, sizeof(buf));
138 if (buf_len <= 0)
139 return -1;
140 DPRINT(buf, buf_len);
141 buf_cur = buf;
142 buf_end = buf + buf_len;
144 ml = MIN(buf_end - buf_cur, len - nr);
145 if ((nl = memchr(buf_cur, '\n', ml))) {
146 nl++;
147 memcpy(dst + nr, buf_cur, nl - buf_cur);
148 nr += nl - buf_cur;
149 buf_cur = nl;
150 return nr;
152 memcpy(dst + nr, buf_cur, ml);
153 nr += ml;
154 buf_cur += ml;
156 return nr;
159 static int smtp_xwrite(char *buf, int len)
161 int nw = 0;
162 while (nw < len) {
163 int ret = conn_write(conn, buf + nw, len - nw);
164 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
165 continue;
166 if (ret < 0)
167 break;
168 DPRINT(buf + nw, ret);
169 nw += ret;
171 return nw;
174 static void send_cmd(char *cmd)
176 conn_write(conn, cmd, strlen(cmd));
177 DPRINT(cmd, strlen(cmd));
180 static int is_eoc(char *s, int len)
182 return isdigit(s[0]) && isdigit(s[1]) && isdigit(s[2]) && isspace(s[3]);
185 static void ehlo(void)
187 char line[BUFFSIZE];
188 int len;
189 len = reply_line(line, sizeof(line));
190 send_cmd("EHLO " HOSTNAME "\r\n");
191 do {
192 len = reply_line(line, sizeof(line));
193 } while (!is_eoc(line, len));
196 static void login(char *user, char *pass)
198 char line[BUFFSIZE];
199 int len;
200 char *s = line;
201 send_cmd("AUTH LOGIN\r\n");
202 len = reply_line(line, sizeof(line));
203 s = putb64(s, user, strlen(user));
204 s = putstr(s, "\r\n");
205 send_cmd(line);
206 len = reply_line(line, sizeof(line));
207 s = line;
208 s = putb64(s, pass, strlen(pass));
209 s = putstr(s, "\r\n");
210 send_cmd(line);
211 len = reply_line(line, sizeof(line));
214 static int write_mail(struct account *account)
216 char line[BUFFSIZE];
217 int len;
218 char *to_hdrs[] = {"to:", "cc:", "bcc:"};
219 int i;
220 sprintf(line, "MAIL FROM:<%s>\r\n", account->from);
221 send_cmd(line);
222 len = reply_line(line, sizeof(line));
224 for (i = 0; i < ARRAY_SIZE(to_hdrs); i++) {
225 char *hdr, *end, *s;
226 char addr[BUFFSIZE];
227 if (!(hdr = find_hdr(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(line, "RCPT TO:<%s>\r\n", addr);
235 send_cmd(line);
236 len = reply_line(line, sizeof(line));
241 send_cmd("DATA\r\n");
242 len = reply_line(line, sizeof(line));
243 if (smtp_xwrite(mail, mail_len) != mail_len)
244 return -1;
245 send_cmd("\r\n.\r\n");
246 len = reply_line(line, sizeof(line));
247 send_cmd("QUIT\r\n");
248 len = reply_line(line, sizeof(line));
249 return 0;
252 static struct account *choose_account(void)
254 char *from = find_hdr("from:");
255 char *end = from + hdr_len(from);
256 int i;
257 if (!from)
258 return &accounts[0];
259 for (i = 0; i < ARRAY_SIZE(accounts); i++) {
260 char *pat = accounts[i].from;
261 int len = strlen(pat);
262 char *s = from;
263 while (s + len < end && (s = memchr(s, pat[0], end - s)))
264 if (!strncmp(pat, s++, len))
265 return &accounts[i];
267 return &accounts[0];
270 int main(int argc, char *argv[])
272 struct account *account;
273 mail_len = xread(STDIN_FILENO, mail, sizeof(mail));
274 if (mail_len < 0 || mail_len >= sizeof(mail))
275 return 1;
276 account = choose_account();
277 conn = conn_connect(account->server, account->port, account->cert);
278 if (!conn)
279 return 1;
280 ehlo();
281 login(account->user, account->pass);
282 write_mail(account);
284 conn_close(conn);
285 return 0;