2 * pop3 - a simple pop3 mail client
4 * Copyright (C) 2010-2013 Ali Gholami Rudi
6 * This program is released under the modified BSD license.
22 #define BUFFSIZE (1 << 12)
23 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
24 #define MIN(a, b) ((a) < (b) ? (a) : (b))
26 static struct mailinfo
{
32 static struct uidl
*uidl
;
34 static char buf
[BUFFSIZE
];
37 static struct conn
*conn
;
40 static void print(char *buf
, int len
)
42 write(STDOUT_FILENO
, buf
, len
);
45 static int reply_line(char *dst
, int len
)
51 if (!buf_cur
|| buf_cur
>= buf_end
) {
52 int buf_len
= conn_read(conn
, buf
, sizeof(buf
));
57 buf_end
= buf
+ buf_len
;
59 ml
= MIN(buf_end
- buf_cur
, len
- nr
);
60 if ((nl
= memchr(buf_cur
, '\n', ml
))) {
62 memcpy(dst
+ nr
, buf_cur
, nl
- buf_cur
);
67 memcpy(dst
+ nr
, buf_cur
, ml
);
74 static void send_cmd(char *cmd
)
76 conn_write(conn
, cmd
, strlen(cmd
));
77 DPRINT(cmd
, strlen(cmd
));
80 static int is_eoc(char *line
, int len
)
82 return len
< 4 && line
[0] == '.' &&
83 (line
[1] == '\r' || line
[1] == '\n');
86 static char *putmem(char *dst
, char *src
, int len
)
88 memcpy(dst
, src
, len
);
92 static char *cutword(char *dst
, char *s
)
94 while (*s
&& isspace(*s
))
96 while (*s
&& !isspace(*s
))
102 static char *putstr(char *dst
, char *src
)
104 int len
= strchr(src
, '\0') - src
;
105 memcpy(dst
, src
, len
+ 1);
109 static void login(char *user
, char *pass
)
113 sprintf(line
, "USER %s\r\n", user
);
115 len
= reply_line(line
, sizeof(line
));
116 sprintf(line
, "PASS %s\r\n", pass
);
118 len
= reply_line(line
, sizeof(line
));
121 static void mail_stat(void)
125 send_cmd("STAT\r\n");
126 len
= reply_line(line
, sizeof(line
));
128 send_cmd("LIST\r\n");
129 len
= reply_line(line
, sizeof(line
));
130 while ((len
= reply_line(line
, sizeof(line
))) != -1) {
131 struct mailinfo
*mail
;
133 if (is_eoc(line
, len
))
135 mail
= &mails
[nmails
++];
136 s
= cutword(mail
->name
, s
);
137 mail
->size
= atoi(s
);
141 static void mail_uidl(void)
146 send_cmd("UIDL\r\n");
147 len
= reply_line(line
, sizeof(line
));
148 while ((len
= reply_line(line
, sizeof(line
))) != -1 &&
149 !is_eoc(line
, len
)) {
152 s
= cutword(name
, s
);
153 s
= cutword(mails
[i
++].id
, s
);
157 static void req_mail(int i
)
160 sprintf(cmd
, "RETR %s\r\n", mails
[i
].name
);
164 static char *mail_dst(char *line
, int len
)
168 for (i
= 0; i
< ARRAY_SIZE(filters
); i
++) {
169 char *hdr
= filters
[i
].hdr
;
170 if (!strncmp(hdr
, line
, strlen(hdr
)) &&
171 strstr(line
, filters
[i
].val
))
172 return filters
[i
].dst
;
177 static int xwrite(int fd
, char *buf
, int len
)
181 int ret
= write(fd
, buf
+ nw
, len
- nw
);
182 if (ret
== -1 && (errno
== EAGAIN
|| errno
== EINTR
))
191 static int mail_write(char *dst
, char *mail
, int len
)
193 int fd
= open(dst
, O_WRONLY
| O_APPEND
| O_CREAT
, 0600);
196 if (xwrite(fd
, mail
, len
) != len
)
202 static char *put_from_(char *s
)
206 s
= putstr(s
, "From ");
207 s
= putstr(s
, getenv("USER") ? getenv("USER") : "root");
208 s
+= strftime(s
, MAXSIZE
, " %a %b %d %H:%M:%S %Y\n", localtime(&t
));
212 static int lone_from(char *s
)
216 return !strncmp("From ", s
, 5);
219 static int ret_mail(int i
)
223 int len
= reply_line(line
, sizeof(line
));
227 print(mails
[i
].name
, strlen(mails
[i
].name
));
229 while ((len
= reply_line(line
, sizeof(line
))) != -1) {
230 if (is_eoc(line
, len
))
232 if (len
> 1 && line
[len
- 2] == '\r')
233 line
[len
-- - 2] = '\n';
237 dst
= mail_dst(line
, len
);
240 s
= putmem(s
, line
, len
);
245 ret
= mail_write(dst
, mailbuf
, s
- mailbuf
);
246 sprintf(line
, " -> %s\n", dst
);
247 print(line
, strlen(line
));
251 static void del_mail(int i
)
254 sprintf(cmd
, "DELE %s\r\n", mails
[i
].name
);
258 static int size_ok(int i
)
260 return mails
[i
].size
+ 100 < MAXSIZE
;
263 static int uidl_new(int i
)
265 return !uidl
|| !uidl_find(uidl
, mails
[i
].id
);
268 static int ret_mails(int beg
, int end
, int del
)
272 for (i
= beg
; i
< end
; i
++)
273 if (size_ok(i
) && uidl_new(i
))
275 for (i
= beg
; i
< end
; i
++) {
276 if (size_ok(i
) && uidl_new(i
)) {
277 if (ret_mail(i
) == -1)
280 uidl_add(uidl
, mails
[i
].id
);
284 for (i
= beg
; i
< end
; i
++)
285 if ((!uidl
&& size_ok(i
)) || (uidl
&& !uidl_new(i
)))
287 for (i
= beg
; i
< end
; i
++)
288 if ((!uidl
&& size_ok(i
)) || (uidl
&& !uidl_new(i
)))
289 reply_line(line
, sizeof(line
));
294 static int fetch(struct account
*account
)
301 conn
= conn_connect(account
->server
, account
->port
, account
->cert
);
307 uidl
= uidl_read(account
->uidl
);
308 sprintf(line
, "fetching %s@%s\n", account
->user
, account
->server
);
309 print(line
, strlen(line
));
310 len
= reply_line(line
, sizeof(line
));
311 login(account
->user
, account
->pass
);
315 batch
= account
->nopipe
? 1 : nmails
;
316 for (i
= 0; i
< nmails
; i
+= batch
)
317 ret_mails(i
, MIN(nmails
, i
+ batch
), account
->del
);
318 send_cmd("QUIT\r\n");
319 len
= reply_line(line
, sizeof(line
));
327 static void sigint(int sig
)
334 int main(int argc
, char *argv
[])
337 signal(SIGINT
, sigint
);
338 mailbuf
= malloc(MAXSIZE
);
339 for (i
= 0; i
< ARRAY_SIZE(accounts
); i
++)
340 if (fetch(&accounts
[i
]) == -1)