2 * a simple smtp mail sender
4 * Copyright (C) 2010-2013 Ali Gholami Rudi
6 * This program is released under the modified BSD license.
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
];
27 static char mail
[MAILLEN
];
29 static struct conn
*conn
;
32 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
34 static void b64_num(char *s
, unsigned long num
)
36 s
[3] = b64
[num
& 0x3f];
38 s
[2] = b64
[num
& 0x3f];
40 s
[1] = b64
[num
& 0x3f];
42 s
[0] = b64
[num
& 0x3f];
45 static char *putb64(char *dst
, char *src
, int len
)
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
);
60 for (i
= len
% 3; i
< 3; i
++)
61 dst
[4 * len
- 3 + i
] = '=';
66 static char *putstr(char *dst
, char *src
)
68 int len
= strchr(src
, '\0') - src
;
69 memcpy(dst
, src
, len
+ 1);
73 static char *cutaddr(char *dst
, char *s
, int len
)
75 static char *addrseps
= "<>()%!~* \t\r\n,\"'%";
77 while (s
< end
&& *s
&& strchr(addrseps
, *s
))
79 while (s
< end
&& *s
&& !strchr(addrseps
, *s
))
85 static char *find_hdr(char *hdr
)
88 int len
= strlen(hdr
);
89 char *end
= mail
+ mail_len
- len
;
92 if (!strncasecmp(s
, hdr
, len
))
94 r
= memchr(s
, '\n', end
- s
);
102 static int hdr_len(char *hdr
)
106 char *r
= strchr(s
, '\n');
108 return strchr(s
, '\0') - hdr
;
116 static int xread(int fd
, char *buf
, int len
)
120 int ret
= read(fd
, buf
+ nr
, len
- nr
);
121 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
130 static int reply_line(char *dst
, int len
)
136 if (!buf_cur
|| buf_cur
>= buf_end
) {
137 int buf_len
= conn_read(conn
, buf
, sizeof(buf
));
140 DPRINT(buf
, buf_len
);
142 buf_end
= buf
+ buf_len
;
144 ml
= MIN(buf_end
- buf_cur
, len
- nr
);
145 if ((nl
= memchr(buf_cur
, '\n', ml
))) {
147 memcpy(dst
+ nr
, buf_cur
, nl
- buf_cur
);
152 memcpy(dst
+ nr
, buf_cur
, ml
);
159 static int smtp_xwrite(char *buf
, int len
)
163 int ret
= conn_write(conn
, buf
+ nw
, len
- nw
);
164 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
168 DPRINT(buf
+ nw
, ret
);
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)
189 len
= reply_line(line
, sizeof(line
));
190 send_cmd("EHLO " HOSTNAME
"\r\n");
192 len
= reply_line(line
, sizeof(line
));
193 } while (!is_eoc(line
, len
));
196 static void login(char *user
, char *pass
)
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");
206 len
= reply_line(line
, sizeof(line
));
208 s
= putb64(s
, pass
, strlen(pass
));
209 s
= putstr(s
, "\r\n");
211 len
= reply_line(line
, sizeof(line
));
214 static int write_mail(struct account
*account
)
218 char *to_hdrs
[] = {"to:", "cc:", "bcc:"};
220 sprintf(line
, "MAIL FROM:<%s>\r\n", account
->from
);
222 len
= reply_line(line
, sizeof(line
));
224 for (i
= 0; i
< ARRAY_SIZE(to_hdrs
); i
++) {
227 if (!(hdr
= find_hdr(to_hdrs
[i
])))
229 end
= hdr
+ hdr_len(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
);
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
)
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
));
252 static struct account
*choose_account(void)
254 char *from
= find_hdr("from:");
255 char *end
= from
+ hdr_len(from
);
259 for (i
= 0; i
< ARRAY_SIZE(accounts
); i
++) {
260 char *pat
= accounts
[i
].from
;
261 int len
= strlen(pat
);
263 while (s
+ len
< end
&& (s
= memchr(s
, pat
[0], end
- s
)))
264 if (!strncmp(pat
, s
++, len
))
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
))
276 account
= choose_account();
277 conn
= conn_connect(account
->server
, account
->port
, account
->cert
);
281 login(account
->user
, account
->pass
);