2 * pop3 - a simple pop3 mail client
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>
19 #include <sys/socket.h>
21 #include <sys/types.h>
26 #define BUFFSIZE (1 << 12)
27 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
28 #define MIN(a, b) ((a) < (b) ? (a) : (b))
37 static char buf
[BUFFSIZE
];
42 #include <polarssl/ssl.h>
43 #include <polarssl/havege.h>
45 static ssl_context ssl
;
46 static ssl_session ssn
;
47 static havege_state hs
;
49 static int ps_send(void *ctx
, unsigned char *buf
, int len
)
51 return write(*(int *) ctx
, buf
, len
);
54 static int ps_recv(void *ctx
, unsigned char *buf
, int len
)
56 return read(*(int *) ctx
, buf
, len
);
61 static int pop3_connect(char *addr
, char *port
)
63 struct addrinfo hints
, *addrinfo
;
66 memset(&hints
, 0, sizeof(hints
));
67 hints
.ai_family
= AF_UNSPEC
;
68 hints
.ai_socktype
= SOCK_STREAM
;
69 hints
.ai_flags
= AI_PASSIVE
;
71 getaddrinfo(addr
, port
, &hints
, &addrinfo
);
72 fd
= socket(addrinfo
->ai_family
, addrinfo
->ai_socktype
,
73 addrinfo
->ai_protocol
);
75 if (connect(fd
, addrinfo
->ai_addr
, addrinfo
->ai_addrlen
) == -1) {
77 freeaddrinfo(addrinfo
);
80 freeaddrinfo(addrinfo
);
84 static void print(char *buf
, int len
)
86 write(STDOUT_FILENO
, buf
, len
);
89 static int reply_line(char *dst
, int len
)
95 if (!buf_cur
|| buf_cur
>= buf_end
) {
97 int buf_len
= ssl_read(&ssl
, (unsigned char *) buf
,
100 int buf_len
= read(fd
, buf
, sizeof(buf
));
108 buf_end
= buf
+ buf_len
;
110 ml
= MIN(buf_end
- buf_cur
, len
- nr
);
111 if ((nl
= memchr(buf_cur
, '\n', ml
))) {
113 memcpy(dst
+ nr
, buf_cur
, nl
- buf_cur
);
118 memcpy(dst
+ nr
, buf_cur
, ml
);
125 static void send_cmd(char *cmd
)
128 ssl_write(&ssl
, (unsigned char *) cmd
, strlen(cmd
));
130 write(fd
, cmd
, strlen(cmd
));
134 print(cmd
, strlen(cmd
));
138 static int is_eoc(char *line
, int len
)
140 return len
< 4 && line
[0] == '.' &&
141 (line
[1] == '\r' || line
[1] == '\n');
144 static char *putmem(char *dst
, char *src
, int len
)
146 memcpy(dst
, src
, len
);
150 static char *cutword(char *dst
, char *s
)
152 while (*s
&& isspace(*s
))
154 while (*s
&& !isspace(*s
))
160 static char *putstr(char *dst
, char *src
)
162 int len
= strchr(src
, '\0') - src
;
163 memcpy(dst
, src
, len
+ 1);
167 static void login(char *user
, char *pass
)
172 s
= putstr(s
, "USER ");
176 len
= reply_line(line
, sizeof(line
));
178 s
= putstr(s
, "PASS ");
182 len
= reply_line(line
, sizeof(line
));
185 static void mail_stat(void)
190 len
= reply_line(line
, sizeof(line
));
193 len
= reply_line(line
, sizeof(line
));
194 while ((len
= reply_line(line
, sizeof(line
))) != -1) {
195 struct mailinfo
*mail
;
197 if (is_eoc(line
, len
))
199 mail
= &mails
[nmails
++];
200 s
= cutword(mail
->name
, s
);
201 mail
->size
= atoi(s
);
205 static void req_mail(int i
)
209 s
= putstr(s
, "RETR ");
210 s
= putstr(s
, mails
[i
].name
);
215 static char *mail_dst(char *line
, int len
)
219 for (i
= 0; i
< ARRAY_SIZE(filters
); i
++) {
220 char *hdr
= filters
[i
].hdr
;
221 if (!strncmp(hdr
, line
, strlen(hdr
)) &&
222 strstr(line
, filters
[i
].val
))
223 return filters
[i
].dst
;
228 static int xwrite(int fd
, char *buf
, int len
)
232 int ret
= write(fd
, buf
+ nw
, len
- nw
);
233 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
242 static int mail_write(char *dst
, char *mail
, int len
)
244 int fd
= open(dst
, O_WRONLY
| O_APPEND
| O_CREAT
, 0600);
247 if (xwrite(fd
, mail
, len
) != len
)
253 static char *put_from_(char *s
)
257 s
= putstr(s
, "From ");
258 s
= putstr(s
, getlogin());
259 s
+= strftime(s
, MAXSIZE
, " %a %b %d %H:%M:%S %Y\n", localtime(&t
));
263 static int lone_from(char *s
)
267 return !strncmp("From ", s
, 5);
270 static int ret_mail(int i
)
275 int len
= reply_line(line
, sizeof(line
));
279 if (mails
[i
].size
+ 100 > sizeof(mail
))
281 print(mails
[i
].name
, strlen(mails
[i
].name
));
283 while ((len
= reply_line(line
, sizeof(line
))) != -1) {
284 if (is_eoc(line
, len
))
286 if (len
> 1 && line
[len
- 2] == '\r')
287 line
[len
-- - 2] = '\n';
291 dst
= mail_dst(line
, len
);
294 s
= putmem(s
, line
, len
);
299 ret
= mail_write(dst
, mail
, s
- mail
);
301 s
= putstr(s
, " -> ");
304 print(line
, s
- line
);
308 static void del_mail(int i
)
312 s
= putstr(s
, "DELE ");
313 s
= putstr(s
, mails
[i
].name
);
318 static int ret_mails_nopipe(int beg
)
321 for (i
= beg
; i
< nmails
; i
++) {
323 if (ret_mail(i
) == -1)
329 static int ret_mails(int beg
)
332 for (i
= beg
; i
< nmails
; i
++)
334 for (i
= beg
; i
< nmails
; i
++)
335 if (ret_mail(i
) == -1)
340 static int del_mails_nopipe(int beg
)
344 for (i
= beg
; i
< nmails
; i
++) {
346 reply_line(line
, sizeof(line
));
351 static int del_mails(int beg
)
355 for (i
= beg
; i
< nmails
; i
++)
357 for (i
= beg
; i
< nmails
; i
++)
358 reply_line(line
, sizeof(line
));
362 static int fetch(struct account
*account
, int beg
)
368 if ((fd
= pop3_connect(account
->server
, account
->port
)) == -1)
372 s
= putstr(s
, "fetching ");
373 s
= putstr(s
, account
->user
);
375 s
= putstr(s
, account
->server
);
377 print(line
, s
- line
);
380 memset(&ssn
, 0, sizeof(ssn
));
383 ssl_set_endpoint(&ssl
, SSL_IS_CLIENT
);
384 ssl_set_authmode(&ssl
, SSL_VERIFY_NONE
);
385 ssl_set_rng(&ssl
, havege_rand
, &hs
);
386 ssl_set_bio(&ssl
, ps_recv
, &fd
, ps_send
, &fd
);
387 ssl_set_ciphers(&ssl
, ssl_default_ciphers
);
388 ssl_set_session(&ssl
, 1, 600, &ssn
);
390 len
= reply_line(line
, sizeof(line
));
391 login(account
->user
, account
->pass
);
393 if ((account
->nopipe
? ret_mails_nopipe
: ret_mails
)(beg
))
396 if ((account
->nopipe
? del_mails_nopipe
: del_mails
)(beg
))
399 len
= reply_line(line
, sizeof(line
));
401 ssl_close_notify(&ssl
);
410 int main(int argc
, char *argv
[])
413 for (i
= 0; i
< ARRAY_SIZE(accounts
); i
++) {
415 if (argc
> i
+ 1 && isdigit(argv
[i
+ 1][0]))
416 beg
= atoi(argv
[i
+ 1]);
417 if (fetch(&accounts
[i
], beg
) == -1)