6065fb95f4fbd6aec3f9634632b0fd07e73b2c2d
[claws.git] / src / pop.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <ctype.h>
29 #include <unistd.h>
30
31 #include "intl.h"
32 #include "pop.h"
33 #include "socket.h"
34 #include "md5.h"
35 #include "prefs_account.h"
36 #include "utils.h"
37 #include "inc.h"
38 #include "recv.h"
39
40 #define LOOKUP_NEXT_MSG() \
41         for (;;) { \
42                 gint size = state->sizes[state->cur_msg]; \
43  \
44                 if (size == 0 || \
45                     (state->ac_prefs->enable_size_limit && \
46                      state->ac_prefs->size_limit > 0 && \
47                      size > state->ac_prefs->size_limit * 1024)) { \
48                         log_print(_("Skipping message %d\n"), state->cur_msg); \
49                         if (size > 0) { \
50                                 state->cur_total_bytes += size; \
51                                 state->cur_total_num++; \
52                                 if (state->new_id_list) { \
53                                         state->id_list = g_slist_append(state->id_list, state->new_id_list->data); \
54                                         state->new_id_list = g_slist_remove(state->new_id_list, state->new_id_list->data); \
55                                 } \
56                         } \
57                         if (state->cur_msg == state->count) \
58                                 return POP3_LOGOUT_SEND; \
59                         else \
60                                 state->cur_msg++; \
61                 } else \
62                         break; \
63         }
64
65 static gint pop3_ok(SockInfo *sock, gchar *argbuf);
66 static void pop3_gen_send(SockInfo *sock, const gchar *format, ...);
67 static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size);
68
69 gint pop3_greeting_recv(SockInfo *sock, gpointer data)
70 {
71         Pop3State *state = (Pop3State *)data;
72         gchar buf[POPBUFSIZE];
73
74         if (pop3_ok(sock, buf) == PS_SUCCESS) {
75                 if (state->ac_prefs->protocol == A_APOP) {
76                         state->greeting = g_strdup(buf);
77                         return POP3_GETAUTH_APOP_SEND;
78                 } else
79                         return POP3_GETAUTH_USER_SEND;
80         } else
81                 return -1;
82 }
83
84 gint pop3_getauth_user_send(SockInfo *sock, gpointer data)
85 {
86         Pop3State *state = (Pop3State *)data;
87
88         g_return_val_if_fail(state->user != NULL, -1);
89
90         inc_progress_update(state, POP3_GETAUTH_USER_SEND);
91
92         pop3_gen_send(sock, "USER %s", state->user);
93
94         return POP3_GETAUTH_USER_RECV;
95 }
96
97 gint pop3_getauth_user_recv(SockInfo *sock, gpointer data)
98 {
99         Pop3State *state = (Pop3State *)data;
100
101         if (pop3_ok(sock, NULL) == PS_SUCCESS)
102                 return POP3_GETAUTH_PASS_SEND;
103         else {
104                 log_warning(_("error occurred on authentication\n"));
105                 state->error_val = PS_AUTHFAIL;
106                 state->inc_state = INC_AUTH_FAILED;
107                 return -1;
108         }
109 }
110
111 gint pop3_getauth_pass_send(SockInfo *sock, gpointer data)
112 {
113         Pop3State *state = (Pop3State *)data;
114
115         g_return_val_if_fail(state->pass != NULL, -1);
116
117         pop3_gen_send(sock, "PASS %s", state->pass);
118
119         return POP3_GETAUTH_PASS_RECV;
120 }
121
122 gint pop3_getauth_pass_recv(SockInfo *sock, gpointer data)
123 {
124         Pop3State *state = (Pop3State *)data;
125
126         if (pop3_ok(sock, NULL) == PS_SUCCESS)
127                 return POP3_GETRANGE_STAT_SEND;
128         else {
129                 log_warning(_("error occurred on authentication\n"));
130                 state->error_val = PS_AUTHFAIL;
131                 state->inc_state = INC_AUTH_FAILED;
132                 return -1;
133         }
134 }
135
136 gint pop3_getauth_apop_send(SockInfo *sock, gpointer data)
137 {
138         Pop3State *state = (Pop3State *)data;
139         gchar *start, *end;
140         gchar *apop_str;
141         gchar md5sum[33];
142
143         g_return_val_if_fail(state->user != NULL, -1);
144         g_return_val_if_fail(state->pass != NULL, -1);
145
146         inc_progress_update(state, POP3_GETAUTH_APOP_SEND);
147
148         if ((start = strchr(state->greeting, '<')) == NULL) {
149                 log_warning(_("Required APOP timestamp not found "
150                               "in greeting\n"));
151                 return -1;
152         }
153
154         if ((end = strchr(start, '>')) == NULL || end == start + 1) {
155                 log_warning(_("Timestamp syntax error in greeting\n"));
156                 return -1;
157         }
158
159         *(end + 1) = '\0';
160
161         apop_str = g_strconcat(start, state->pass, NULL);
162         md5_hex_digest(md5sum, apop_str);
163         g_free(apop_str);
164
165         pop3_gen_send(sock, "APOP %s %s", state->user, md5sum);
166
167         return POP3_GETAUTH_APOP_RECV;
168 }
169
170 gint pop3_getauth_apop_recv(SockInfo *sock, gpointer data)
171 {
172         Pop3State *state = (Pop3State *)data;
173
174         if (pop3_ok(sock, NULL) == PS_SUCCESS)
175                 return POP3_GETRANGE_STAT_SEND;
176         else {
177                 log_warning(_("error occurred on authentication\n"));
178                 state->error_val = PS_AUTHFAIL;
179                 state->inc_state = INC_AUTH_FAILED;
180                 return -1;
181         }
182 }
183
184 gint pop3_getrange_stat_send(SockInfo *sock, gpointer data)
185 {
186         Pop3State *state = (Pop3State *)data;
187
188         inc_progress_update(state, POP3_GETRANGE_STAT_SEND);
189
190         pop3_gen_send(sock, "STAT");
191
192         return POP3_GETRANGE_STAT_RECV;
193 }
194
195 gint pop3_getrange_stat_recv(SockInfo *sock, gpointer data)
196 {
197         Pop3State *state = (Pop3State *)data;
198         gchar buf[POPBUFSIZE + 1];
199         gint ok;
200
201         if ((ok = pop3_ok(sock, buf)) == PS_SUCCESS) {
202                 if (sscanf(buf, "%d %d", &state->count, &state->total_bytes)
203                     != 2) {
204                         log_warning(_("POP3 protocol error\n"));
205                         return -1;
206                 } else {
207                         if (state->count == 0)
208                                 return POP3_LOGOUT_SEND;
209                         else {
210                                 state->cur_msg = 1;
211                                 state->new = state->count;
212                                 if (state->ac_prefs->rmmail ||
213                                     state->ac_prefs->getall)
214                                         return POP3_GETSIZE_LIST_SEND;
215                                 else
216                                         return POP3_GETRANGE_UIDL_SEND;
217                         }
218                 }
219         } else if (ok == PS_PROTOCOL)
220                 return POP3_LOGOUT_SEND;
221         else
222                 return -1;
223 }
224
225 gint pop3_getrange_last_send(SockInfo *sock, gpointer data)
226 {
227         Pop3State *state = (Pop3State *)data;
228
229         inc_progress_update(state, POP3_GETRANGE_LAST_SEND);
230
231         pop3_gen_send(sock, "LAST");
232
233         return POP3_GETRANGE_LAST_RECV;
234 }
235
236 gint pop3_getrange_last_recv(SockInfo *sock, gpointer data)
237 {
238         Pop3State *state = (Pop3State *)data;
239         gchar buf[POPBUFSIZE + 1];
240
241         if (pop3_ok(sock, buf) == PS_SUCCESS) {
242                 gint last;
243
244                 if (sscanf(buf, "%d", &last) == 0) {
245                         log_warning(_("POP3 protocol error\n"));
246                         return -1;
247                 } else {
248                         if (state->count == last)
249                                 return POP3_LOGOUT_SEND;
250                         else {
251                                 state->new = state->count - last;
252                                 state->cur_msg = last + 1;
253                                 return POP3_GETSIZE_LIST_SEND;
254                         }
255                 }
256         } else
257                 return POP3_GETSIZE_LIST_SEND;
258 }
259
260 gint pop3_getrange_uidl_send(SockInfo *sock, gpointer data)
261 {
262         Pop3State *state = (Pop3State *)data;
263
264         inc_progress_update(state, POP3_GETRANGE_UIDL_SEND);
265
266         pop3_gen_send(sock, "UIDL");
267
268         return POP3_GETRANGE_UIDL_RECV;
269 }
270
271 gint pop3_getrange_uidl_recv(SockInfo *sock, gpointer data)
272 {
273         Pop3State *state = (Pop3State *)data;
274         gboolean new = FALSE;
275         gchar buf[POPBUFSIZE];
276         gchar id[IDLEN + 1];
277
278         if (pop3_ok(sock, NULL) != PS_SUCCESS) return POP3_GETRANGE_LAST_SEND;
279
280         if (!state->id_table) new = TRUE;
281
282         while (sock_gets(sock, buf, sizeof(buf)) >= 0) {
283                 gint num;
284
285                 if (buf[0] == '.') break;
286                 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
287                         continue;
288
289                 if (new == FALSE &&
290                     g_hash_table_lookup(state->id_table, id) == NULL) {
291                         state->new = state->count - num + 1;
292                         state->cur_msg = num;
293                         new = TRUE;
294                 }
295
296                 if (new == TRUE)
297                         state->new_id_list = g_slist_append
298                                 (state->new_id_list, g_strdup(id));
299                 else
300                         state->id_list = g_slist_append
301                                 (state->id_list, g_strdup(id));
302         }
303
304         if (new == TRUE)
305                 return POP3_GETSIZE_LIST_SEND;
306         else
307                 return POP3_LOGOUT_SEND;
308 }
309
310 gint pop3_getsize_list_send(SockInfo *sock, gpointer data)
311 {
312         Pop3State *state = (Pop3State *)data;
313
314         inc_progress_update(state, POP3_GETSIZE_LIST_SEND);
315
316         pop3_gen_send(sock, "LIST");
317
318         return POP3_GETSIZE_LIST_RECV;
319 }
320
321 gint pop3_getsize_list_recv(SockInfo *sock, gpointer data)
322 {
323         Pop3State *state = (Pop3State *)data;
324         gchar buf[POPBUFSIZE];
325
326         if (pop3_ok(sock, NULL) != PS_SUCCESS) return POP3_LOGOUT_SEND;
327
328         state->sizes = g_new0(gint, state->count + 1);
329         state->cur_total_bytes = 0;
330
331         while (sock_gets(sock, buf, sizeof(buf)) >= 0) {
332                 guint num, size;
333
334                 if (buf[0] == '.') break;
335                 if (sscanf(buf, "%u %u", &num, &size) != 2)
336                         return -1;
337
338                 if (num > 0 && num <= state->count)
339                         state->sizes[num] = size;
340                 if (num > 0 && num < state->cur_msg)
341                         state->cur_total_bytes += size;
342         }
343
344         LOOKUP_NEXT_MSG();
345         return POP3_RETR_SEND;
346 }
347
348 gint pop3_retr_send(SockInfo *sock, gpointer data)
349 {
350         Pop3State *state = (Pop3State *)data;
351
352         inc_progress_update(state, POP3_RETR_SEND);
353
354         pop3_gen_send(sock, "RETR %d", state->cur_msg);
355
356         return POP3_RETR_RECV;
357 }
358
359 gint pop3_retr_recv(SockInfo *sock, gpointer data)
360 {
361         Pop3State *state = (Pop3State *)data;
362         const gchar *file;
363         gint ok, drop_ok;
364
365         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
366                 if (recv_write_to_file(sock, (file = get_tmp_file())) < 0) {
367                         state->inc_state = INC_NOSPACE;
368                         return -1;
369                 }
370
371                 if ((drop_ok = inc_drop_message(file, state)) < 0) {
372                         state->inc_state = INC_ERROR;
373                         return -1;
374                 }
375
376                 state->cur_total_bytes += state->sizes[state->cur_msg];
377                 state->cur_total_num++;
378
379                 if (drop_ok == 0 && state->ac_prefs->rmmail)
380                         return POP3_DELETE_SEND;
381
382                 if (state->new_id_list) {
383                         state->id_list = g_slist_append
384                                 (state->id_list, state->new_id_list->data);
385                         state->new_id_list =
386                                 g_slist_remove(state->new_id_list,
387                                                state->new_id_list->data);
388                 }
389
390                 if (state->cur_msg < state->count) {
391                         state->cur_msg++;
392                         LOOKUP_NEXT_MSG();
393                         return POP3_RETR_SEND;
394                 } else
395                         return POP3_LOGOUT_SEND;
396         } else if (ok == PS_PROTOCOL)
397                 return POP3_LOGOUT_SEND;
398         else
399                 return -1;
400 }
401
402 gint pop3_delete_send(SockInfo *sock, gpointer data)
403 {
404         Pop3State *state = (Pop3State *)data;
405
406         /* inc_progress_update(state, POP3_DELETE_SEND); */
407
408         pop3_gen_send(sock, "DELE %d", state->cur_msg);
409
410         return POP3_DELETE_RECV;
411 }
412
413 gint pop3_delete_recv(SockInfo *sock, gpointer data)
414 {
415         Pop3State *state = (Pop3State *)data;
416         gint ok;
417
418         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
419                 if (state->cur_msg < state->count) {
420                         state->cur_msg++;
421                         LOOKUP_NEXT_MSG();
422                         return POP3_RETR_SEND;
423                 } else
424                         return POP3_LOGOUT_SEND;
425         } else if (ok == PS_PROTOCOL)
426                 return POP3_LOGOUT_SEND;
427         else
428                 return -1;
429 }
430
431 gint pop3_logout_send(SockInfo *sock, gpointer data)
432 {
433         Pop3State *state = (Pop3State *)data;
434
435         inc_progress_update(state, POP3_LOGOUT_SEND);
436
437         pop3_gen_send(sock, "QUIT");
438
439         return POP3_LOGOUT_RECV;
440 }
441
442 gint pop3_logout_recv(SockInfo *sock, gpointer data)
443 {
444         if (pop3_ok(sock, NULL) == PS_SUCCESS)
445                 return -1;
446         else
447                 return -1;
448 }
449
450 static gint pop3_ok(SockInfo *sock, gchar *argbuf)
451 {
452         gint ok;
453         gchar buf[POPBUFSIZE + 1];
454         gchar *bufp;
455
456         if ((ok = pop3_gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS) {
457                 bufp = buf;
458                 if (*bufp == '+' || *bufp == '-')
459                         bufp++;
460                 else
461                         return PS_PROTOCOL;
462
463                 while (isalpha(*bufp))
464                         bufp++;
465
466                 if (*bufp)
467                         *(bufp++) = '\0';
468
469                 if (!strcmp(buf, "+OK"))
470                         ok = PS_SUCCESS;
471                 else if (!strncmp(buf, "-ERR", 4)) {
472                         if (strstr(bufp, "lock") ||
473                                  strstr(bufp, "Lock") ||
474                                  strstr(bufp, "LOCK") ||
475                                  strstr(bufp, "wait"))
476                                 ok = PS_LOCKBUSY;
477                         else
478                                 ok = PS_PROTOCOL;
479
480                         if (*bufp)
481                                 fprintf(stderr, "POP3: %s\n", bufp);
482                 } else
483                         ok = PS_PROTOCOL;
484
485                 if (argbuf)
486                         strcpy(argbuf, bufp);
487         }
488
489         return ok;
490 }
491
492 static void pop3_gen_send(SockInfo *sock, const gchar *format, ...)
493 {
494         gchar buf[POPBUFSIZE + 1];
495         va_list args;
496
497         va_start(args, format);
498         g_vsnprintf(buf, sizeof(buf) - 2, format, args);
499         va_end(args);
500
501         if (!strncasecmp(buf, "PASS ", 5))
502                 log_print("POP3> PASS ********\n");
503         else
504                 log_print("POP3> %s\n", buf);
505
506         strcat(buf, "\r\n");
507         sock_write(sock, buf, strlen(buf));
508 }
509
510 static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size)
511 {
512         if (sock_gets(sock, buf, size) < 0) {
513                 return PS_SOCKET;
514         } else {
515                 strretchomp(buf);
516                 log_print("POP3< %s\n", buf);
517
518                 return PS_SUCCESS;
519         }
520 }