conn: initialize conn struct correctly
[pop3.git] / pop3.c
blob66bbda1616c838f8bfcbf73b238ac48c8ddce9f8
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))
26 static struct mailinfo {
27 char name[1 << 4];
28 char id[1 << 5];
29 int size;
30 } mails[MAXMAILS];
31 static int nmails;
32 static struct uidl *uidl;
34 static char buf[BUFFSIZE];
35 static char *buf_cur;
36 static char *buf_end;
37 static struct conn *conn;
38 static char *mailbuf;
40 static void print(char *buf, int len)
42 write(STDOUT_FILENO, buf, len);
45 static int reply_line(char *dst, int len)
47 int nr = 0;
48 char *nl;
49 while (nr < len) {
50 int ml;
51 if (!buf_cur || buf_cur >= buf_end) {
52 int buf_len = conn_read(conn, buf, sizeof(buf));
53 if (buf_len <= 0)
54 return -1;
55 DPRINT(buf, buf_len);
56 buf_cur = buf;
57 buf_end = buf + buf_len;
59 ml = MIN(buf_end - buf_cur, len - nr);
60 if ((nl = memchr(buf_cur, '\n', ml))) {
61 nl++;
62 memcpy(dst + nr, buf_cur, nl - buf_cur);
63 nr += nl - buf_cur;
64 buf_cur = nl;
65 return nr;
67 memcpy(dst + nr, buf_cur, ml);
68 nr += ml;
69 buf_cur += ml;
71 return nr;
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);
89 return dst + len;
92 static char *cutword(char *dst, char *s)
94 while (*s && isspace(*s))
95 s++;
96 while (*s && !isspace(*s))
97 *dst++ = *s++;
98 *dst = '\0';
99 return s;
102 static char *putstr(char *dst, char *src)
104 int len = strchr(src, '\0') - src;
105 memcpy(dst, src, len + 1);
106 return dst + len;
109 static void login(char *user, char *pass)
111 char line[BUFFSIZE];
112 int len;
113 sprintf(line, "USER %s\r\n", user);
114 send_cmd(line);
115 len = reply_line(line, sizeof(line));
116 sprintf(line, "PASS %s\r\n", pass);
117 send_cmd(line);
118 len = reply_line(line, sizeof(line));
121 static void mail_stat(void)
123 char line[BUFFSIZE];
124 int len;
125 send_cmd("STAT\r\n");
126 len = reply_line(line, sizeof(line));
127 print(line, len);
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;
132 char *s = line;
133 if (is_eoc(line, len))
134 break;
135 mail = &mails[nmails++];
136 s = cutword(mail->name, s);
137 mail->size = atoi(s);
141 static void mail_uidl(void)
143 char line[BUFFSIZE];
144 int len;
145 int i = 0;
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)) {
150 char name[100];
151 char *s = line;
152 s = cutword(name, s);
153 s = cutword(mails[i++].id, s);
157 static void req_mail(int i)
159 char cmd[100];
160 sprintf(cmd, "RETR %s\r\n", mails[i].name);
161 send_cmd(cmd);
164 static char *mail_dst(char *line, int len)
166 int i;
167 line[len] = '\0';
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;
174 return NULL;
177 static int xwrite(int fd, char *buf, int len)
179 int nw = 0;
180 while (nw < len) {
181 int ret = write(fd, buf + nw, len - nw);
182 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
183 continue;
184 if (ret < 0)
185 break;
186 nw += ret;
188 return nw;
191 static int mail_write(char *dst, char *mail, int len)
193 int fd = open(dst, O_WRONLY | O_APPEND | O_CREAT, 0600);
194 if (fd == -1)
195 return -1;
196 if (xwrite(fd, mail, len) != len)
197 return -1;
198 close(fd);
199 return 0;
202 static char *put_from_(char *s)
204 time_t t;
205 time(&t);
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));
209 return s;
212 static int lone_from(char *s)
214 while (*s == '>')
215 s++;
216 return !strncmp("From ", s, 5);
219 static int ret_mail(int i)
221 char line[BUFFSIZE];
222 char *s = mailbuf;
223 int len = reply_line(line, sizeof(line));
224 char *dst = NULL;
225 int hdr = 1;
226 int ret;
227 print(mails[i].name, strlen(mails[i].name));
228 s = put_from_(s);
229 while ((len = reply_line(line, sizeof(line))) != -1) {
230 if (is_eoc(line, len))
231 break;
232 if (len > 1 && line[len - 2] == '\r')
233 line[len-- - 2] = '\n';
234 if (line[0] == '\n')
235 hdr = 0;
236 if (hdr && !dst)
237 dst = mail_dst(line, len);
238 if (lone_from(line))
239 *s++ = '>';
240 s = putmem(s, line, len);
242 *s++ = '\n';
243 if (!dst)
244 dst = SPOOL;
245 ret = mail_write(dst, mailbuf, s - mailbuf);
246 sprintf(line, " -> %s\n", dst);
247 print(line, strlen(line));
248 return ret;
251 static void del_mail(int i)
253 char cmd[100];
254 sprintf(cmd, "DELE %s\r\n", mails[i].name);
255 send_cmd(cmd);
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)
270 char line[BUFFSIZE];
271 int i;
272 for (i = beg; i < end; i++)
273 if (size_ok(i) && uidl_new(i))
274 req_mail(i);
275 for (i = beg; i < end; i++) {
276 if (size_ok(i) && uidl_new(i)) {
277 if (ret_mail(i) == -1)
278 return -1;
279 if (uidl)
280 uidl_add(uidl, mails[i].id);
283 if (del) {
284 for (i = beg; i < end; i++)
285 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
286 del_mail(i);
287 for (i = beg; i < end; i++)
288 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
289 reply_line(line, sizeof(line));
291 return 0;
294 static int fetch(struct account *account)
296 char line[BUFFSIZE];
297 int len;
298 int batch;
299 int i;
300 nmails = 0;
301 conn = conn_connect(account->server, account->port, account->cert);
302 if (!conn)
303 return -1;
304 buf_cur = buf;
305 buf_end = buf;
306 if (account->uidl)
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);
312 mail_stat();
313 if (account->uidl)
314 mail_uidl();
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));
320 conn_close(conn);
321 if (uidl)
322 uidl_save(uidl);
323 uidl = NULL;
324 return 0;
327 static void sigint(int sig)
329 if (uidl)
330 uidl_save(uidl);
331 exit(1);
334 int main(int argc, char *argv[])
336 int i;
337 signal(SIGINT, sigint);
338 mailbuf = malloc(MAXSIZE);
339 for (i = 0; i < ARRAY_SIZE(accounts); i++)
340 if (fetch(&accounts[i]) == -1)
341 return 1;
342 free(mailbuf);
343 return 0;