pop3: starttls support
[pop3.git] / pop3.c
blobb9a79558f94326157652f13345040153dc3d86fe
1 /*
2 * A NEAT POP3 MAIL CLIENT
4 * Copyright (C) 2010-2017 Ali Gholami Rudi
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <netdb.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include "conf.h"
29 #include "uidl.h"
30 #include "conn.h"
32 #define BUFFSIZE (1 << 12)
33 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
34 #define MIN(a, b) ((a) < (b) ? (a) : (b))
35 #define PRINT(s, l) (write(1, (s), (l)))
37 static struct mailinfo {
38 char name[1 << 4];
39 char id[1 << 6];
40 int size;
41 } mails[MAXMAILS];
42 static int nmails;
43 static struct uidl *uidl;
45 static char buf[BUFFSIZE];
46 static int buf_len;
47 static int buf_pos;
48 static struct conn *conn;
49 static char *mailbuf;
51 static int pop3_read(void)
53 if (buf_pos == buf_len) {
54 buf_len = conn_read(conn, buf, sizeof(buf));
55 buf_pos = 0;
57 return buf_pos < buf_len ? (unsigned char) buf[buf_pos++] : -1;
60 static int pop3_get(char *dst, int len)
62 int i = 0;
63 int c;
64 while (i < len - 1) {
65 c = pop3_read();
66 if (c < 0)
67 return -1;
68 dst[i++] = c;
69 if (c == '\n')
70 break;
72 dst[i] = '\0';
73 DPRINT(dst, i);
74 return i;
77 static void pop3_cmd(char *cmd)
79 conn_write(conn, cmd, strlen(cmd));
80 DPRINT(cmd, strlen(cmd));
83 static int pop3_iseoc(char *line, int len)
85 return len >= 2 && len <= 3 && line[0] == '.' &&
86 (line[1] == '\r' || line[1] == '\n');
89 static void pop3_login(char *user, char *pass)
91 char line[BUFFSIZE];
92 sprintf(line, "USER %s\r\n", user);
93 pop3_cmd(line);
94 pop3_get(line, sizeof(line));
95 sprintf(line, "PASS %s\r\n", pass);
96 pop3_cmd(line);
97 pop3_get(line, sizeof(line));
100 static void pop3_stat(void)
102 char line[BUFFSIZE];
103 int len;
104 pop3_cmd("STAT\r\n");
105 len = pop3_get(line, sizeof(line));
106 PRINT(line, len);
107 pop3_cmd("LIST\r\n");
108 len = pop3_get(line, sizeof(line));
109 while ((len = pop3_get(line, sizeof(line))) >= 0) {
110 struct mailinfo *mail;
111 if (pop3_iseoc(line, len))
112 break;
113 mail = &mails[nmails++];
114 sscanf(line, "%s %d", mail->name, &mail->size);
118 static void pop3_uidl(void)
120 char line[BUFFSIZE];
121 char name[128];
122 int len;
123 int i = 0;
124 pop3_cmd("UIDL\r\n");
125 len = pop3_get(line, sizeof(line));
126 while ((len = pop3_get(line, sizeof(line))) > 0 && !pop3_iseoc(line, len))
127 sscanf(line, "%s %s", name, mails[i++].id);
130 static void pop3_retr(int i)
132 char cmd[100];
133 sprintf(cmd, "RETR %s\r\n", mails[i].name);
134 pop3_cmd(cmd);
137 static char *mail_dst(char *hdr, int len)
139 int i;
140 hdr[len] = '\0';
141 for (i = 0; i < ARRAY_SIZE(filters); i++)
142 if (!strncmp(filters[i].hdr, hdr, strlen(filters[i].hdr)) &&
143 strstr(hdr, filters[i].val))
144 return filters[i].dst;
145 return NULL;
148 static int xwrite(int fd, char *buf, int len)
150 int nw = 0;
151 while (nw < len) {
152 int ret = write(fd, buf + nw, len - nw);
153 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
154 continue;
155 if (ret < 0)
156 break;
157 nw += ret;
159 return nw;
162 static int mail_write(char *dst, char *mail, int len)
164 int fd = open(dst, O_WRONLY | O_APPEND | O_CREAT, 0600);
165 if (fd < 0)
166 return 1;
167 if (xwrite(fd, mail, len) != len)
168 return 1;
169 close(fd);
170 return 0;
173 static int mail_from_(char *s)
175 char date[128];
176 time_t t;
177 time(&t);
178 strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y", localtime(&t));
179 return sprintf(s, "From %s %s\n", getenv("USER") ? getenv("USER") : "root", date);
182 static int pop3_lonefrom_(char *s)
184 while (*s == '>')
185 s++;
186 return !strncmp("From ", s, 5);
189 static int fetch_one(int i)
191 char line[BUFFSIZE];
192 char *s = mailbuf;
193 int len = pop3_get(line, sizeof(line));
194 char *dst = NULL;
195 int hdr = 1;
196 int ret;
197 PRINT(mails[i].name, strlen(mails[i].name));
198 s += mail_from_(s);
199 while (1) {
200 len = pop3_get(line, sizeof(line));
201 if (len <= 0) /* end of stream or error */
202 return 1;
203 if (pop3_iseoc(line, len))
204 break;
205 if (len > 1 && line[len - 2] == '\r')
206 line[len-- - 2] = '\n';
207 if (line[0] == '\n')
208 hdr = 0;
209 if (hdr && !dst)
210 dst = mail_dst(line, len);
211 if (pop3_lonefrom_(line))
212 *s++ = '>';
213 memcpy(s, line, len);
214 s += len;
216 *s++ = '\n';
217 if (!dst)
218 dst = SPOOL;
219 ret = mail_write(dst, mailbuf, s - mailbuf);
220 sprintf(line, " -> %s%s\n", dst, ret ? " [failed]" : "");
221 PRINT(line, strlen(line));
222 return ret;
225 static void pop3_del(int i)
227 char cmd[100];
228 sprintf(cmd, "DELE %s\r\n", mails[i].name);
229 pop3_cmd(cmd);
232 static int size_ok(int i)
234 return mails[i].size + 100 < MAXSIZE;
237 static int uidl_new(int i)
239 return !uidl || !uidl_find(uidl, mails[i].id);
242 static int fetch_mails(int beg, int end, int del)
244 char line[BUFFSIZE];
245 int i;
246 for (i = beg; i < end; i++)
247 if (size_ok(i) && uidl_new(i))
248 pop3_retr(i);
249 for (i = beg; i < end; i++) {
250 if (size_ok(i) && uidl_new(i)) {
251 if (fetch_one(i))
252 return 1;
253 if (uidl)
254 uidl_add(uidl, mails[i].id);
257 if (del) {
258 for (i = beg; i < end; i++)
259 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
260 pop3_del(i);
261 for (i = beg; i < end; i++)
262 if ((!uidl && size_ok(i)) || (uidl && !uidl_new(i)))
263 pop3_get(line, sizeof(line));
265 return 0;
268 static int fetch(struct account *account)
270 char line[BUFFSIZE];
271 int batch;
272 int failed = 0;
273 int i;
274 nmails = 0;
275 conn = conn_connect(account->server, account->port);
276 if (!conn)
277 return 1;
278 if (account->stls) {
279 pop3_get(line, sizeof(line));
280 pop3_cmd("STLS\r\n");
281 pop3_get(line, sizeof(line));
283 if (conn_tls(conn, account->cert)) {
284 conn_close(conn);
285 return 1;
287 buf_pos = 0;
288 buf_len = 0;
289 if (account->uidl)
290 uidl = uidl_read(account->uidl);
291 sprintf(line, "fetching %s@%s\n", account->user, account->server);
292 PRINT(line, strlen(line));
293 if (!account->stls)
294 pop3_get(line, sizeof(line));
295 pop3_login(account->user, account->pass);
296 pop3_stat();
297 if (account->uidl)
298 pop3_uidl();
299 batch = account->nopipe ? 1 : nmails;
300 for (i = 0; i < nmails; i += batch)
301 if ((failed = fetch_mails(i, MIN(nmails, i + batch), account->del)))
302 break;
303 if (!failed) {
304 pop3_cmd("QUIT\r\n");
305 pop3_get(line, sizeof(line));
307 conn_close(conn);
308 if (uidl)
309 uidl_save(uidl);
310 uidl = NULL;
311 return failed;
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]))
328 return 1;
329 free(mailbuf);
330 return 0;