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.
10 #include <arpa/inet.h>
15 #include <netinet/in.h>
20 #include <sys/socket.h>
22 #include <sys/types.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
];
35 static char mail
[MAILLEN
];
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
);
59 "ABCDEFGHIJGLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
61 static void b64_num(char *s
, unsigned long num
)
63 s
[3] = b64
[num
& 0x3f];
65 s
[2] = b64
[num
& 0x3f];
67 s
[1] = b64
[num
& 0x3f];
69 s
[0] = b64
[num
& 0x3f];
72 static char *putb64(char *dst
, char *src
, int len
)
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
);
87 for (i
= len
% 3; i
< 3; i
++)
88 dst
[4 * len
- 3 + i
] = '=';
93 static char *putstr(char *dst
, char *src
)
95 int len
= strchr(src
, '\0') - src
;
96 memcpy(dst
, src
, len
+ 1);
100 static char *cutaddr(char *dst
, char *s
, int len
)
102 static char *addrseps
= "<>()%!~* \t\r\n,\"'%";
104 while (s
< end
&& *s
&& strchr(addrseps
, *s
))
106 while (s
< end
&& *s
&& !strchr(addrseps
, *s
))
112 static char *find_hdr(char *hdr
)
115 int len
= strlen(hdr
);
116 char *end
= mail
+ mail_len
- len
;
119 if (!strncasecmp(s
, hdr
, len
))
121 r
= memchr(s
, '\n', end
- s
);
129 static int hdr_len(char *hdr
)
133 char *r
= strchr(s
, '\n');
135 return strchr(s
, '\0') - hdr
;
143 static int xread(int fd
, char *buf
, int len
)
147 int ret
= read(fd
, buf
+ nr
, len
- nr
);
148 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
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
;
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) {
178 freeaddrinfo(addrinfo
);
181 freeaddrinfo(addrinfo
);
185 static int reply_line(char *dst
, int len
)
191 if (!buf_cur
|| buf_cur
>= buf_end
) {
193 int buf_len
= ssl_read(&ssl
, (unsigned char *) buf
,
196 int buf_len
= read(fd
, buf
, sizeof(buf
));
204 buf_end
= buf
+ buf_len
;
206 ml
= MIN(buf_end
- buf_cur
, len
- nr
);
207 if ((nl
= memchr(buf_cur
, '\n', ml
))) {
209 memcpy(dst
+ nr
, buf_cur
, nl
- buf_cur
);
214 memcpy(dst
+ nr
, buf_cur
, ml
);
221 static int smtp_write(char *s
, int len
)
227 return ssl_write(&ssl
, (unsigned char *) s
, len
);
229 return write(fd
, s
, len
);
233 static int smtp_xwrite(char *buf
, int len
)
237 int ret
= smtp_write(buf
+ nw
, len
- nw
);
238 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
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)
262 len
= reply_line(line
, sizeof(line
));
263 send_cmd("EHLO " HOSTNAME
"\r\n");
265 len
= reply_line(line
, sizeof(line
));
266 } while (!is_eoc(line
, len
));
269 static void login(char *user
, char *pass
)
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");
279 len
= reply_line(line
, sizeof(line
));
281 s
= putb64(s
, pass
, strlen(pass
));
282 s
= putstr(s
, "\r\n");
284 len
= reply_line(line
, sizeof(line
));
287 static int write_mail(struct account
*account
)
292 char *to_hdrs
[] = {"to:", "cc:", "bcc:"};
294 s
= putstr(s
, "MAIL FROM:<");
295 s
= putstr(s
, account
->from
);
296 s
= putstr(s
, ">\r\n");
298 len
= reply_line(line
, sizeof(line
));
300 for (i
= 0; i
< ARRAY_SIZE(to_hdrs
); i
++) {
303 if (!(hdr
= find_hdr(to_hdrs
[i
])))
305 end
= hdr
+ hdr_len(hdr
);
307 while ((s
= cutaddr(addr
, s
, end
- s
)) && s
< end
) {
308 char *at
= strchr(addr
, '@');
309 if (at
&& at
> addr
&& *(at
+ 1)) {
311 r
= putstr(r
, "RCPT TO:<");
313 r
= putstr(r
, ">\r\n");
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
)
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
));
331 static struct account
*choose_account(void)
333 char *from
= find_hdr("from:");
334 char *end
= from
+ hdr_len(from
);
338 for (i
= 0; i
< ARRAY_SIZE(accounts
); i
++) {
339 char *pat
= accounts
[i
].from
;
340 int len
= strlen(pat
);
342 while (s
+ len
< end
&& (s
= memchr(s
, pat
[0], end
- s
)))
343 if (!strncmp(pat
, s
++, len
))
349 int main(int argc
, char *argv
[])
351 struct account
*account
;
352 if ((mail_len
= xread(STDIN_FILENO
, mail
, sizeof(mail
))) == -1)
354 account
= choose_account();
355 fd
= smtp_connect(account
->server
, account
->port
);
358 memset(&ssn
, 0, sizeof(ssn
));
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
);
369 login(account
->user
, account
->pass
);
373 ssl_close_notify(&ssl
);