pop3: simplify pop3_line() and exit if server hangs up prematurely
[pop3.git] / pop3.c
blobd25b30de3d8dae8cfe179af923c5ff07300d3a31
1 /*
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.
7 */
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <netdb.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17 #include <unistd.h>
18 #include "config.h"
19 #include "uidl.h"
20 #include "conn.h"
22 #define BUFFSIZE (1 << 12)
23 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
24 #define MIN(a, b) ((a) < (b) ? (a) : (b))
25 #define PRINT(s, l) (write(1, (s), (l)))
27 static struct mailinfo {
28 char name[1 << 4];
29 char id[1 << 5];
30 int size;
31 } mails[MAXMAILS];
32 static int nmails;
33 static struct uidl *uidl;
35 static char buf[BUFFSIZE];
36 static int buf_len;
37 static int buf_pos;
38 static struct conn *conn;
39 static char *mailbuf;
41 static int pop3_read(void)
43 if (buf_pos == buf_len) {
44 buf_len = conn_read(conn, buf, sizeof(buf));
45 buf_pos = 0;
47 return buf_pos < buf_len ? (unsigned char) buf[buf_pos++] : -1;
50 static int pop3_line(char *dst, int len)
52 int i = 0;
53 int c;
54 while (i < len) {
55 c = pop3_read();
56 if (c < 0)
57 return -1;
58 dst[i++] = c;
59 if (c == '\n')
60 break;
62 DPRINT(dst, i);
63 return i;
66 static void send_cmd(char *cmd)
68 conn_write(conn, cmd, strlen(cmd));
69 DPRINT(cmd, strlen(cmd));
72 static int is_eoc(char *line, int len)
74 return len >= 2 && len <= 3 && line[0] == '.' &&
75 (line[1] == '\r' || line[1] == '\n');
78 static char *cutword(char *dst, char *s)
80 while (*s && isspace(*s))
81 s++;
82 while (*s && !isspace(*s))
83 *dst++ = *s++;
84 *dst = '\0';
85 return s;
88 static void login(char *user, char *pass)
90 char line[BUFFSIZE];
91 sprintf(line, "USER %s\r\n", user);
92 send_cmd(line);
93 pop3_line(line, sizeof(line));
94 sprintf(line, "PASS %s\r\n", pass);
95 send_cmd(line);
96 pop3_line(line, sizeof(line));
99 static void mail_stat(void)
101 char line[BUFFSIZE];
102 int len;
103 send_cmd("STAT\r\n");
104 len = pop3_line(line, sizeof(line));
105 PRINT(line, len);
106 send_cmd("LIST\r\n");
107 len = pop3_line(line, sizeof(line));
108 while ((len = pop3_line(line, sizeof(line))) != -1) {
109 struct mailinfo *mail;
110 char *s = line;
111 if (is_eoc(line, len))
112 break;
113 mail = &mails[nmails++];
114 s = cutword(mail->name, s);
115 mail->size = atoi(s);
119 static void mail_uidl(void)
121 char line[BUFFSIZE];
122 char name[100];
123 int len;
124 int i = 0;
125 send_cmd("UIDL\r\n");
126 len = pop3_line(line, sizeof(line));
127 while ((len = pop3_line(line, sizeof(line))) > 0 && !is_eoc(line, len)) {
128 char *s = line;
129 s = cutword(name, s);
130 s = cutword(mails[i++].id, s);
134 static void req_mail(int i)
136 char cmd[100];
137 sprintf(cmd, "RETR %s\r\n", mails[i].name);
138 send_cmd(cmd);
141 static char *mail_dst(char *line, int len)
143 int i;
144 line[len] = '\0';
145 for (i = 0; i < ARRAY_SIZE(filters); i++) {
146 char *hdr = filters[i].hdr;
147 if (!strncmp(hdr, line, strlen(hdr)) &&
148 strstr(line, filters[i].val))
149 return filters[i].dst;
151 return NULL;
154 static int xwrite(int fd, char *buf, int len)
156 int nw = 0;
157 while (nw < len) {
158 int ret = write(fd, buf + nw, len - nw);
159 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
160 continue;
161 if (ret < 0)
162 break;
163 nw += ret;
165 return nw;
168 static int mail_write(char *dst, char *mail, int len)
170 int fd = open(dst, O_WRONLY | O_APPEND | O_CREAT, 0600);
171 if (fd == -1)
172 return -1;
173 if (xwrite(fd, mail, len) != len)
174 return -1;
175 close(fd);
176 return 0;
179 static char *putstr(char *dst, char *src)
181 int len = strchr(src, '\0') - src;
182 memcpy(dst, src, len + 1);
183 return dst + len;
186 static char *put_from_(char *s)
188 time_t t;
189 time(&t);
190 s = putstr(s, "From ");
191 s = putstr(s, getenv("USER") ? getenv("USER") : "root");
192 s += strftime(s, MAXSIZE, " %a %b %d %H:%M:%S %Y\n", localtime(&t));
193 return s;
196 static int lone_from(char *s)
198 while (*s == '>')
199 s++;
200 return !strncmp("From ", s, 5);
203 static int ret_mail(int i)
205 char line[BUFFSIZE];
206 char *s = mailbuf;
207 int len = pop3_line(line, sizeof(line));
208 char *dst = NULL;
209 int hdr = 1;
210 int ret;
211 PRINT(mails[i].name, strlen(mails[i].name));
212 s = put_from_(s);
213 while (1) {
214 len = pop3_line(line, sizeof(line));
215 if (len <= 0) /* end of stream or error */
216 return -1;
217 if (is_eoc(line, len))
218 break;
219 if (len > 1 && line[len - 2] == '\r')
220 line[len-- - 2] = '\n';
221 if (line[0] == '\n')
222 hdr = 0;
223 if (hdr && !dst)
224 dst = mail_dst(line, len);
225 if (lone_from(line))
226 *s++ = '>';
227 memcpy(s, line, len);
228 s += len;
230 *s++ = '\n';
231 if (!dst)
232 dst = SPOOL;
233 ret = mail_write(dst, mailbuf, s - mailbuf);
234 sprintf(line, " -> %s\n", dst);
235 PRINT(line, strlen(line));
236 return ret;
239 static void del_mail(int i)
241 char cmd[100];
242 sprintf(cmd, "DELE %s\r\n", mails[i].name);
243 send_cmd(cmd);
246 static int size_ok(int i)
248 return mails[i].size + 100 < MAXSIZE;
251 static int uidl_new(int i)
253 return !uidl || !uidl_find(uidl, mails[i].id);
256 static int ret_mails(int beg, int end, int del)
258 char line[BUFFSIZE];
259 int i;
260 for (i = beg; i < end; i++)
261 if (size_ok(i) && uidl_new(i))
262 req_mail(i);
263 for (i = beg; i < end; i++) {
264 if (size_ok(i) && uidl_new(i)) {
265 if (ret_mail(i) == -1)
266 return -1;
267 if (uidl)
268 uidl_add(uidl, mails[i].id);
271 if (del) {
272 for (i = beg; i < end; i++)
273 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
274 del_mail(i);
275 for (i = beg; i < end; i++)
276 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
277 pop3_line(line, sizeof(line));
279 return 0;
282 static int fetch(struct account *account)
284 char line[BUFFSIZE];
285 int batch;
286 int i;
287 nmails = 0;
288 conn = conn_connect(account->server, account->port, account->cert);
289 if (!conn)
290 return -1;
291 buf_pos = 0;
292 buf_len = 0;
293 if (account->uidl)
294 uidl = uidl_read(account->uidl);
295 sprintf(line, "fetching %s@%s\n", account->user, account->server);
296 PRINT(line, strlen(line));
297 pop3_line(line, sizeof(line));
298 login(account->user, account->pass);
299 mail_stat();
300 if (account->uidl)
301 mail_uidl();
302 batch = account->nopipe ? 1 : nmails;
303 for (i = 0; i < nmails; i += batch)
304 ret_mails(i, MIN(nmails, i + batch), account->del);
305 send_cmd("QUIT\r\n");
306 pop3_line(line, sizeof(line));
307 conn_close(conn);
308 if (uidl)
309 uidl_save(uidl);
310 uidl = NULL;
311 return 0;
314 static void sigint(int sig)
316 if (uidl)
317 uidl_save(uidl);
318 exit(1);
321 int main(int argc, char *argv[])
323 int i;
324 signal(SIGINT, sigint);
325 mailbuf = malloc(MAXSIZE);
326 for (i = 0; i < ARRAY_SIZE(accounts); i++)
327 if (fetch(&accounts[i]) == -1)
328 return 1;
329 free(mailbuf);
330 return 0;