conn: move tls handshake to conn_tls()
[pop3.git] / pop3.c
blobc9582f264381a64df573f33a0ae2ad6fc195eb6e
1 /*
2 * pop3 - a simple pop3 mail client
4 * Copyright (C) 2010-2016 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 "conf.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 << 6];
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_get(char *dst, int len)
52 int i = 0;
53 int c;
54 while (i < len - 1) {
55 c = pop3_read();
56 if (c < 0)
57 return -1;
58 dst[i++] = c;
59 if (c == '\n')
60 break;
62 dst[i] = '\0';
63 DPRINT(dst, i);
64 return i;
67 static void pop3_cmd(char *cmd)
69 conn_write(conn, cmd, strlen(cmd));
70 DPRINT(cmd, strlen(cmd));
73 static int pop3_iseoc(char *line, int len)
75 return len >= 2 && len <= 3 && line[0] == '.' &&
76 (line[1] == '\r' || line[1] == '\n');
79 static void pop3_login(char *user, char *pass)
81 char line[BUFFSIZE];
82 sprintf(line, "USER %s\r\n", user);
83 pop3_cmd(line);
84 pop3_get(line, sizeof(line));
85 sprintf(line, "PASS %s\r\n", pass);
86 pop3_cmd(line);
87 pop3_get(line, sizeof(line));
90 static void pop3_stat(void)
92 char line[BUFFSIZE];
93 int len;
94 pop3_cmd("STAT\r\n");
95 len = pop3_get(line, sizeof(line));
96 PRINT(line, len);
97 pop3_cmd("LIST\r\n");
98 len = pop3_get(line, sizeof(line));
99 while ((len = pop3_get(line, sizeof(line))) >= 0) {
100 struct mailinfo *mail;
101 if (pop3_iseoc(line, len))
102 break;
103 mail = &mails[nmails++];
104 sscanf(line, "%s %d", mail->name, &mail->size);
108 static void pop3_uidl(void)
110 char line[BUFFSIZE];
111 char name[128];
112 int len;
113 int i = 0;
114 pop3_cmd("UIDL\r\n");
115 len = pop3_get(line, sizeof(line));
116 while ((len = pop3_get(line, sizeof(line))) > 0 && !pop3_iseoc(line, len))
117 sscanf(line, "%s %s", name, mails[i++].id);
120 static void pop3_retr(int i)
122 char cmd[100];
123 sprintf(cmd, "RETR %s\r\n", mails[i].name);
124 pop3_cmd(cmd);
127 static char *mail_dst(char *hdr, int len)
129 int i;
130 hdr[len] = '\0';
131 for (i = 0; i < ARRAY_SIZE(filters); i++)
132 if (!strncmp(filters[i].hdr, hdr, strlen(filters[i].hdr)) &&
133 strstr(hdr, filters[i].val))
134 return filters[i].dst;
135 return NULL;
138 static int xwrite(int fd, char *buf, int len)
140 int nw = 0;
141 while (nw < len) {
142 int ret = write(fd, buf + nw, len - nw);
143 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
144 continue;
145 if (ret < 0)
146 break;
147 nw += ret;
149 return nw;
152 static int mail_write(char *dst, char *mail, int len)
154 int fd = open(dst, O_WRONLY | O_APPEND | O_CREAT, 0600);
155 if (fd < 0)
156 return 1;
157 if (xwrite(fd, mail, len) != len)
158 return 1;
159 close(fd);
160 return 0;
163 static int mail_from_(char *s)
165 char date[128];
166 time_t t;
167 time(&t);
168 strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y", localtime(&t));
169 return sprintf(s, "From %s %s\n", getenv("USER") ? getenv("USER") : "root", date);
172 static int pop3_lonefrom_(char *s)
174 while (*s == '>')
175 s++;
176 return !strncmp("From ", s, 5);
179 static int fetch_one(int i)
181 char line[BUFFSIZE];
182 char *s = mailbuf;
183 int len = pop3_get(line, sizeof(line));
184 char *dst = NULL;
185 int hdr = 1;
186 int ret;
187 PRINT(mails[i].name, strlen(mails[i].name));
188 s += mail_from_(s);
189 while (1) {
190 len = pop3_get(line, sizeof(line));
191 if (len <= 0) /* end of stream or error */
192 return 1;
193 if (pop3_iseoc(line, len))
194 break;
195 if (len > 1 && line[len - 2] == '\r')
196 line[len-- - 2] = '\n';
197 if (line[0] == '\n')
198 hdr = 0;
199 if (hdr && !dst)
200 dst = mail_dst(line, len);
201 if (pop3_lonefrom_(line))
202 *s++ = '>';
203 memcpy(s, line, len);
204 s += len;
206 *s++ = '\n';
207 if (!dst)
208 dst = SPOOL;
209 ret = mail_write(dst, mailbuf, s - mailbuf);
210 sprintf(line, " -> %s%s\n", dst, ret ? " [failed]" : "");
211 PRINT(line, strlen(line));
212 return ret;
215 static void pop3_del(int i)
217 char cmd[100];
218 sprintf(cmd, "DELE %s\r\n", mails[i].name);
219 pop3_cmd(cmd);
222 static int size_ok(int i)
224 return mails[i].size + 100 < MAXSIZE;
227 static int uidl_new(int i)
229 return !uidl || !uidl_find(uidl, mails[i].id);
232 static int fetch_mails(int beg, int end, int del)
234 char line[BUFFSIZE];
235 int i;
236 for (i = beg; i < end; i++)
237 if (size_ok(i) && uidl_new(i))
238 pop3_retr(i);
239 for (i = beg; i < end; i++) {
240 if (size_ok(i) && uidl_new(i)) {
241 if (fetch_one(i))
242 return 1;
243 if (uidl)
244 uidl_add(uidl, mails[i].id);
247 if (del) {
248 for (i = beg; i < end; i++)
249 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
250 pop3_del(i);
251 for (i = beg; i < end; i++)
252 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
253 pop3_get(line, sizeof(line));
255 return 0;
258 static int fetch(struct account *account)
260 char line[BUFFSIZE];
261 int batch;
262 int failed = 0;
263 int i;
264 nmails = 0;
265 conn = conn_connect(account->server, account->port);
266 if (!conn)
267 return 1;
268 if (conn_tls(conn, account->cert)) {
269 conn_close(conn);
270 return 1;
272 buf_pos = 0;
273 buf_len = 0;
274 if (account->uidl)
275 uidl = uidl_read(account->uidl);
276 sprintf(line, "fetching %s@%s\n", account->user, account->server);
277 PRINT(line, strlen(line));
278 pop3_get(line, sizeof(line));
279 pop3_login(account->user, account->pass);
280 pop3_stat();
281 if (account->uidl)
282 pop3_uidl();
283 batch = account->nopipe ? 1 : nmails;
284 for (i = 0; i < nmails; i += batch)
285 if ((failed = fetch_mails(i, MIN(nmails, i + batch), account->del)))
286 break;
287 if (!failed) {
288 pop3_cmd("QUIT\r\n");
289 pop3_get(line, sizeof(line));
291 conn_close(conn);
292 if (uidl)
293 uidl_save(uidl);
294 uidl = NULL;
295 return failed;
298 static void sigint(int sig)
300 if (uidl)
301 uidl_save(uidl);
302 exit(1);
305 int main(int argc, char *argv[])
307 int i;
308 signal(SIGINT, sigint);
309 mailbuf = malloc(MAXSIZE);
310 for (i = 0; i < ARRAY_SIZE(accounts); i++)
311 if (fetch(&accounts[i]))
312 return 1;
313 free(mailbuf);
314 return 0;