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 smtp_read(void)
132 if (buf_pos
== buf_len
) {
133 buf_len
= conn_read(conn
, buf
, sizeof(buf
));
136 return buf_pos
< buf_len
? (unsigned char) buf
[buf_pos
++] : -1;
139 static int smtp_line(char *dst
, int len
)
154 static int smtp_xwrite(char *buf
, int len
)
158 int ret
= conn_write(conn
, buf
+ nw
, len
- nw
);
159 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
163 DPRINT(buf
+ nw
, ret
);
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)
185 len
= smtp_line(line
, sizeof(line
));
186 send_cmd("EHLO " HOSTNAME
"\r\n");
188 len
= smtp_line(line
, sizeof(line
));
189 } while (len
> 0 && !is_eoc(line
, len
));
193 static int login(char *user
, char *pass
)
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");
202 smtp_line(line
, sizeof(line
));
204 s
= putb64(s
, pass
, strlen(pass
));
205 s
= putstr(s
, "\r\n");
207 return smtp_line(line
, sizeof(line
)) <= 0;
210 static int write_mail(struct account
*account
)
213 char *to_hdrs
[] = {"to:", "cc:", "bcc:"};
215 sprintf(line
, "MAIL FROM:<%s>\r\n", account
->from
);
217 smtp_line(line
, sizeof(line
));
219 for (i
= 0; i
< ARRAY_SIZE(to_hdrs
); i
++) {
222 if (!(hdr
= find_hdr(to_hdrs
[i
])))
224 end
= hdr
+ hdr_len(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
);
231 smtp_line(line
, sizeof(line
));
236 send_cmd("DATA\r\n");
237 if (smtp_line(line
, sizeof(line
)) <= 0)
239 if (smtp_xwrite(mail
, mail_len
) != mail_len
)
241 send_cmd("\r\n.\r\n");
242 if (smtp_line(line
, sizeof(line
)) <= 0)
244 send_cmd("QUIT\r\n");
245 smtp_line(line
, sizeof(line
));
249 static struct account
*choose_account(void)
251 char *from
= find_hdr("from:");
252 char *end
= from
+ hdr_len(from
);
254 for (i
= 0; i
< ARRAY_SIZE(accounts
) && from
; i
++) {
255 char *pat
= accounts
[i
].from
;
256 int len
= strlen(pat
);
258 while (s
+ len
< end
&& (s
= memchr(s
, pat
[0], end
- s
)))
259 if (!strncmp(pat
, s
++, len
))
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
))
271 account
= choose_account();
274 conn
= conn_connect(account
->server
, account
->port
, account
->cert
);
279 if (login(account
->user
, account
->pass
))
281 if (write_mail(account
))