0951490b5b6d9b79a13c554cef432397273e202f
[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 #include "selective_download.h"
40 #if USE_SSL
41 #  include "ssl.h"
42 #endif
43
44 #define LOOKUP_NEXT_MSG() \
45         for (;;) { \
46                 gint size = state->msg[state->cur_msg].size; \
47                 gboolean size_limit_over = \
48                     (state->ac_prefs->enable_size_limit && \
49                      state->ac_prefs->size_limit > 0 && \
50                      size > state->ac_prefs->size_limit * 1024); \
51  \
52                 if (size_limit_over) \
53                         log_print(_("POP3: Skipping message %d (%d bytes)\n"), \
54                                   state->cur_msg, size); \
55  \
56                 if (size == 0 || state->msg[state->cur_msg].received || \
57                     size_limit_over) { \
58                         if (size > 0) \
59                                 state->cur_total_bytes += size; \
60                         if (state->cur_msg == state->count) \
61                                 return POP3_LOGOUT_SEND; \
62                         else \
63                                 state->cur_msg++; \
64                 } else \
65                         break; \
66         }
67
68 static gint pop3_ok(SockInfo *sock, gchar *argbuf);
69 static void pop3_gen_send(SockInfo *sock, const gchar *format, ...);
70 static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size);
71 static gboolean pop3_delete_header (Pop3State *state);
72 static gboolean should_delete (const char *uidl, gpointer data); 
73
74 gint pop3_greeting_recv(SockInfo *sock, gpointer data)
75 {
76         Pop3State *state = (Pop3State *)data;
77         gchar buf[POPBUFSIZE];
78
79         if (pop3_ok(sock, buf) == PS_SUCCESS) {
80 #if USE_SSL
81                 if (state->ac_prefs->ssl_pop == SSL_STARTTLS) {
82                         state->greeting = g_strdup(buf);
83                         return POP3_STLS_SEND;
84                 }
85 #endif
86                 if (state->ac_prefs->protocol == A_APOP) {
87                         state->greeting = g_strdup(buf);
88                         return POP3_GETAUTH_APOP_SEND;
89                 } else
90                         return POP3_GETAUTH_USER_SEND;
91         } else
92                 return -1;
93 }
94
95 #if USE_SSL
96 gint pop3_stls_send(SockInfo *sock, gpointer data)
97 {
98         pop3_gen_send(sock, "STLS");
99
100         return POP3_STLS_RECV;
101 }
102
103 gint pop3_stls_recv(SockInfo *sock, gpointer data)
104 {
105         Pop3State *state = (Pop3State *)data;
106         gint ok;
107
108         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
109                 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1))
110                         return -1;
111                 if (state->ac_prefs->protocol == A_APOP) {
112                         return POP3_GETAUTH_APOP_SEND;
113                 } else
114                         return POP3_GETAUTH_USER_SEND;
115         } else if (ok == PS_PROTOCOL) {
116                 log_warning(_("can't start TLS session\n"));
117                 state->error_val = PS_PROTOCOL;
118                 state->inc_state = INC_ERROR;
119                 return POP3_LOGOUT_SEND;
120         } else
121                 return -1;
122 }
123 #endif /* USE_SSL */
124
125 gint pop3_getauth_user_send(SockInfo *sock, gpointer data)
126 {
127         Pop3State *state = (Pop3State *)data;
128
129         g_return_val_if_fail(state->user != NULL, -1);
130
131         pop3_gen_send(sock, "USER %s", state->user);
132
133         return POP3_GETAUTH_USER_RECV;
134 }
135
136 gint pop3_getauth_user_recv(SockInfo *sock, gpointer data)
137 {
138         Pop3State *state = (Pop3State *)data;
139
140         if (pop3_ok(sock, NULL) == PS_SUCCESS)
141                 return POP3_GETAUTH_PASS_SEND;
142         else {
143                 log_warning(_("error occurred on authentication\n"));
144                 state->error_val = PS_AUTHFAIL;
145                 state->inc_state = INC_AUTH_FAILED;
146                 return -1;
147         }
148 }
149
150 gint pop3_getauth_pass_send(SockInfo *sock, gpointer data)
151 {
152         Pop3State *state = (Pop3State *)data;
153
154         g_return_val_if_fail(state->pass != NULL, -1);
155
156         pop3_gen_send(sock, "PASS %s", state->pass);
157
158         return POP3_GETAUTH_PASS_RECV;
159 }
160
161 gint pop3_getauth_pass_recv(SockInfo *sock, gpointer data)
162 {
163         Pop3State *state = (Pop3State *)data;
164
165         if (pop3_ok(sock, NULL) == PS_SUCCESS) {
166                 
167                 if (pop3_delete_header(state) == TRUE)
168                         return POP3_DELETE_SEND;
169                 else
170                         return POP3_GETRANGE_STAT_SEND;
171         }
172         else {
173                 log_warning(_("error occurred on authentication\n"));
174                 state->error_val = PS_AUTHFAIL;
175                 state->inc_state = INC_AUTH_FAILED;
176                 return -1;
177         }
178 }
179
180 gint pop3_getauth_apop_send(SockInfo *sock, gpointer data)
181 {
182         Pop3State *state = (Pop3State *)data;
183         gchar *start, *end;
184         gchar *apop_str;
185         gchar md5sum[33];
186
187         g_return_val_if_fail(state->user != NULL, -1);
188         g_return_val_if_fail(state->pass != NULL, -1);
189
190         if ((start = strchr(state->greeting, '<')) == NULL) {
191                 log_warning(_("Required APOP timestamp not found "
192                               "in greeting\n"));
193                 return -1;
194         }
195
196         if ((end = strchr(start, '>')) == NULL || end == start + 1) {
197                 log_warning(_("Timestamp syntax error in greeting\n"));
198                 return -1;
199         }
200
201         *(end + 1) = '\0';
202
203         apop_str = g_strconcat(start, state->pass, NULL);
204         md5_hex_digest(md5sum, apop_str);
205         g_free(apop_str);
206
207         pop3_gen_send(sock, "APOP %s %s", state->user, md5sum);
208
209         return POP3_GETAUTH_APOP_RECV;
210 }
211
212 gint pop3_getauth_apop_recv(SockInfo *sock, gpointer data)
213 {
214         Pop3State *state = (Pop3State *)data;
215
216         if (pop3_ok(sock, NULL) == PS_SUCCESS) {
217                 
218                 if (pop3_delete_header(state) == TRUE)
219                         return POP3_DELETE_SEND;
220                 else
221                 return POP3_GETRANGE_STAT_SEND;
222         }
223
224         else {
225                 log_warning(_("error occurred on authentication\n"));
226                 state->error_val = PS_AUTHFAIL;
227                 state->inc_state = INC_AUTH_FAILED;
228                 return -1;
229         }
230 }
231
232 gint pop3_getrange_stat_send(SockInfo *sock, gpointer data)
233 {
234         pop3_gen_send(sock, "STAT");
235
236         return POP3_GETRANGE_STAT_RECV;
237 }
238
239 gint pop3_getrange_stat_recv(SockInfo *sock, gpointer data)
240 {
241         Pop3State *state = (Pop3State *)data;
242         gchar buf[POPBUFSIZE + 1];
243         gint ok;
244
245         if ((ok = pop3_ok(sock, buf)) == PS_SUCCESS) {
246                 if (sscanf(buf, "%d %d", &state->count, &state->total_bytes)
247                     != 2) {
248                         log_warning(_("POP3 protocol error\n"));
249                         return -1;
250                 } else {
251                         if (state->count == 0) {
252                                 state->uidl_is_valid = TRUE;
253                                 return POP3_LOGOUT_SEND;
254                         } else {
255                                 state->msg = g_new0
256                                         (Pop3MsgInfo, state->count + 1);
257                                 state->cur_msg = 1;
258                                 return POP3_GETRANGE_UIDL_SEND;
259                         }
260                 }
261         } else if (ok == PS_PROTOCOL)
262                 return POP3_LOGOUT_SEND;
263         else
264                 return -1;
265 }
266
267 gint pop3_getrange_last_send(SockInfo *sock, gpointer data)
268 {
269         pop3_gen_send(sock, "LAST");
270
271         return POP3_GETRANGE_LAST_RECV;
272 }
273
274 gint pop3_getrange_last_recv(SockInfo *sock, gpointer data)
275 {
276         Pop3State *state = (Pop3State *)data;
277         gchar buf[POPBUFSIZE + 1];
278
279         if (pop3_ok(sock, buf) == PS_SUCCESS) {
280                 gint last;
281
282                 if (sscanf(buf, "%d", &last) == 0) {
283                         log_warning(_("POP3 protocol error\n"));
284                         return -1;
285                 } else {
286                         if (state->count == last)
287                                 return POP3_LOGOUT_SEND;
288                         else {
289                                 state->cur_msg = last + 1;
290                                 return POP3_GETSIZE_LIST_SEND;
291                         }
292                 }
293         } else
294                 return POP3_GETSIZE_LIST_SEND;
295 }
296
297 gint pop3_getrange_uidl_send(SockInfo *sock, gpointer data)
298 {
299         pop3_gen_send(sock, "UIDL");
300
301         return POP3_GETRANGE_UIDL_RECV;
302 }
303
304 gint pop3_getrange_uidl_recv(SockInfo *sock, gpointer data)
305 {
306         Pop3State *state = (Pop3State *)data;
307         gboolean new = FALSE;
308         gboolean get_all = FALSE;
309         gchar buf[POPBUFSIZE];
310         gchar id[IDLEN + 1];
311
312         if (!state->uidl_table) new = TRUE;
313         if (state->ac_prefs->getall)
314                 get_all = TRUE;
315
316         if (pop3_ok(sock, NULL) != PS_SUCCESS) {
317                 /* UIDL is not supported */
318                 if (!get_all)
319                         return POP3_GETRANGE_LAST_SEND;
320                 else
321                         return POP3_GETSIZE_LIST_SEND;
322         }
323
324         while (sock_gets(sock, buf, sizeof(buf)) >= 0) {
325                 gint num;
326
327                 if (buf[0] == '.') break;
328                 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
329                         continue;
330                 if (num <= 0 || num > state->count) continue;
331
332                 state->msg[num].uidl = g_strdup(id);
333
334                 if (state->uidl_table) {
335                         if (!get_all &&
336                             g_hash_table_lookup(state->uidl_table, id) != NULL)
337                                 state->msg[num].received = TRUE;
338                         else {
339                                 if (new == FALSE) {
340                                         state->cur_msg = num;
341                                         new = TRUE;
342                                 }
343                         }
344                 }
345
346                 if (should_delete(buf, (Pop3State *) state))
347                         state->uidl_todelete_list = g_slist_append
348                                         (state->uidl_todelete_list, g_strdup(buf));             
349                 
350         }
351
352         state->uidl_is_valid = TRUE;
353
354         if (new == TRUE)
355                 return POP3_GETSIZE_LIST_SEND;
356         else
357                 return POP3_LOGOUT_SEND;
358 }
359
360 static gboolean should_delete(const char *uidl, gpointer data) 
361 {
362         /* answer[0] will contain id
363          * answer[0] will contain uidl */
364         Pop3State *state = (Pop3State *) data;
365         gchar **answer;
366         int  id;
367         gboolean result;
368         int tdate, keep_for, today, nb_days;
369         const gchar *sdate;
370         GDate curdate;
371         gchar *tuidl;
372
373         if (!state->ac_prefs->rmmail || !strchr(uidl, ' '))
374                 return FALSE;
375
376         /* remove \r\n */
377         tuidl  = g_strndup(uidl, strlen(uidl) - 2);
378         answer = g_strsplit(tuidl, " ", 2);
379         id     = atoi(answer[0]);
380
381         if (NULL != (sdate = g_hash_table_lookup(state->uidl_table, answer[1]))) {
382                 tdate    = atoi(sdate);
383                 keep_for = atoi(state->ac_prefs->leave_time); /* FIXME: leave time should be an int */
384
385                 g_date_clear(&curdate, 1);
386                 g_date_set_time(&curdate, time(NULL));
387                 today = g_date_day_of_year(&curdate);
388                 
389                 nb_days = g_date_is_leap_year(g_date_year(&curdate)) ? 366 : 365;
390                 result  = ((tdate + keep_for) % nb_days <= today);
391         } else
392                 result = FALSE;
393
394         g_free(tuidl);
395         g_strfreev(answer);
396         
397         return result;
398 }
399
400 gint pop3_getsize_list_send(SockInfo *sock, gpointer data)
401 {
402         pop3_gen_send(sock, "LIST");
403
404         return POP3_GETSIZE_LIST_RECV;
405 }
406
407 gint pop3_getsize_list_recv(SockInfo *sock, gpointer data)
408 {
409         Pop3State *state = (Pop3State *)data;
410         gchar buf[POPBUFSIZE];
411
412         if (pop3_ok(sock, NULL) != PS_SUCCESS) return POP3_LOGOUT_SEND;
413
414         state->cur_total_bytes = 0;
415
416         while (sock_gets(sock, buf, sizeof(buf)) >= 0) {
417                 guint num, size;
418
419                 if (buf[0] == '.') break;
420                 if (sscanf(buf, "%u %u", &num, &size) != 2)
421                         return -1;
422
423                 if (num > 0 && num <= state->count)
424                         state->msg[num].size = size;
425                 if (num > 0 && num < state->cur_msg)
426                         state->cur_total_bytes += size;
427         }
428
429         LOOKUP_NEXT_MSG();
430         if (state->ac_prefs->session_type == RETR_HEADER) 
431                 return POP3_TOP_SEND;
432         else
433                 return POP3_RETR_SEND;
434  }
435  
436 gint pop3_top_send(SockInfo *sock, gpointer data)
437 {
438         Pop3State *state = (Pop3State *)data;
439
440         inc_progress_update(state, POP3_TOP_SEND); 
441
442         pop3_gen_send(sock, "TOP %i 0", state->cur_msg );
443
444         return POP3_TOP_RECV;
445 }
446
447 gint pop3_top_recv(SockInfo *sock, gpointer data)
448 {
449         Pop3State *state = (Pop3State *)data;
450         FILE  *fp;
451         gchar buf[POPBUFSIZE];
452         gchar *header;
453         gchar *filename, *path;
454         
455         inc_progress_update(state, POP3_TOP_RECV); 
456
457         if (pop3_ok(sock, NULL) != PS_SUCCESS) 
458                 return POP3_LOGOUT_SEND;
459
460         path = g_strconcat(get_header_cache_dir(), G_DIR_SEPARATOR_S, NULL);
461
462         if ( !is_dir_exist(path) )
463                 make_dir_hier(path);
464         
465         filename = g_strdup_printf("%s%i", path, state->cur_msg);
466                                    
467         if (recv_write_to_file(sock, filename) < 0) {
468                 state->inc_state = INC_NOSPACE;
469                 return -1;
470         }
471         /* we add a Complete-Size Header Item ...
472            note: overwrites first line  --> this is dirty */
473         if ( (fp = fopen(filename, "rb+")) != NULL ) {
474                 gchar *buf = g_strdup_printf("%s%i", SIZE_HEADER, 
475                                              state->msg[state->cur_msg].size);
476         
477                 if (change_file_mode_rw(fp, filename) == 0) 
478                         fprintf(fp, "%s\n", buf);
479                 g_free(buf);    
480                 fclose(fp);
481         }
482         
483         g_free(path);
484         g_free(filename);
485
486         if (state->cur_msg < state->count) {
487                 state->cur_msg++;
488                 return POP3_TOP_SEND;
489         } else
490                 return POP3_LOGOUT_SEND;
491 }
492
493 gint pop3_retr_send(SockInfo *sock, gpointer data)
494 {
495         Pop3State *state = (Pop3State *)data;
496
497         pop3_gen_send(sock, "RETR %d", state->cur_msg);
498
499         return POP3_RETR_RECV;
500 }
501
502 gint pop3_retr_recv(SockInfo *sock, gpointer data)
503 {
504         Pop3State *state = (Pop3State *)data;
505         const gchar *file;
506         gint ok, drop_ok;
507         int keep_for;
508         
509         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
510                 if (recv_write_to_file(sock, (file = get_tmp_file())) < 0) {
511                         if (state->inc_state == INC_SUCCESS)
512                                 state->inc_state = INC_NOSPACE;
513                         return -1;
514                 }
515
516                 if ((drop_ok = inc_drop_message(file, state)) < 0) {
517                         state->inc_state = INC_ERROR;
518                         return -1;
519                 }
520         
521                 state->cur_total_bytes += state->msg[state->cur_msg].size;
522                 state->cur_total_num++;
523
524                 keep_for = (state->ac_prefs && state->ac_prefs->leave_time) ? 
525                            atoi(state->ac_prefs->leave_time) : 0;
526
527                 if (drop_ok == 0 && state->ac_prefs->rmmail && keep_for == 0)
528                         return POP3_DELETE_SEND;
529
530                 state->msg[state->cur_msg].received = TRUE;
531
532                 if (state->cur_msg < state->count) {
533                         state->cur_msg++;
534                         LOOKUP_NEXT_MSG();
535                         return POP3_RETR_SEND;
536                 } else
537                         return POP3_LOGOUT_SEND;
538         } else if (ok == PS_PROTOCOL)
539                 return POP3_LOGOUT_SEND;
540         else
541                 return -1;
542 }
543
544 gint pop3_delete_send(SockInfo *sock, gpointer data)
545 {
546         Pop3State *state = (Pop3State *)data;
547
548         pop3_gen_send(sock, "DELE %d", state->cur_msg);
549
550         return POP3_DELETE_RECV;
551 }
552
553 gint pop3_delete_recv(SockInfo *sock, gpointer data)
554 {
555         Pop3State *state = (Pop3State *)data;
556         gint ok;
557
558         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
559                 if (state->ac_prefs->session_type == RETR_NORMAL)
560                         state->msg[state->cur_msg].deleted = TRUE;
561
562                 if (pop3_delete_header(state) == TRUE) 
563                         return POP3_DELETE_SEND;
564
565                 if (state->cur_msg < state->count) {
566                         state->cur_msg++;
567                         LOOKUP_NEXT_MSG();
568                         return POP3_RETR_SEND;
569                 } else
570                         return POP3_LOGOUT_SEND;
571         } else if (ok == PS_PROTOCOL)
572                 return POP3_LOGOUT_SEND;
573         else
574                 return -1;
575 }
576
577 gint pop3_logout_send(SockInfo *sock, gpointer data)
578 {
579         Pop3State *state = (Pop3State *)data;
580         gchar **parts;
581         
582         while (state->uidl_todelete_list != NULL) {
583                 /*
584                  * FIXME: doesn't feel right - no checks for parts
585                  */
586                 parts = g_strsplit((gchar *) state->uidl_todelete_list->data, " ", 2);
587                 state->uidl_todelete_list = g_slist_remove
588                         (state->uidl_todelete_list, state->uidl_todelete_list->data);
589                 pop3_gen_send(sock, "DELE %s", parts[0]);
590                 if (pop3_ok(sock, NULL) != PS_SUCCESS)
591                         log_warning(_("error occurred on DELE\n"));
592                 g_strfreev(parts);      
593         }
594         
595         pop3_gen_send(sock, "QUIT");
596
597         return POP3_LOGOUT_RECV;
598 }
599
600 gint pop3_logout_recv(SockInfo *sock, gpointer data)
601 {
602         if (pop3_ok(sock, NULL) == PS_SUCCESS)
603                 return -1;
604         else
605                 return -1;
606 }
607
608 static gint pop3_ok(SockInfo *sock, gchar *argbuf)
609 {
610         gint ok;
611         gchar buf[POPBUFSIZE + 1];
612         gchar *bufp;
613
614         if ((ok = pop3_gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS) {
615                 bufp = buf;
616                 if (*bufp == '+' || *bufp == '-')
617                         bufp++;
618                 else
619                         return PS_PROTOCOL;
620
621                 while (isalpha(*bufp))
622                         bufp++;
623
624                 if (*bufp)
625                         *(bufp++) = '\0';
626
627                 if (!strcmp(buf, "+OK"))
628                         ok = PS_SUCCESS;
629                 else if (!strncmp(buf, "-ERR", 4)) {
630                         if (strstr(bufp, "lock") ||
631                                  strstr(bufp, "Lock") ||
632                                  strstr(bufp, "LOCK") ||
633                                  strstr(bufp, "wait"))
634                                 ok = PS_LOCKBUSY;
635                         else
636                                 ok = PS_PROTOCOL;
637
638                         if (*bufp)
639                                 fprintf(stderr, "POP3: %s\n", bufp);
640                 } else
641                         ok = PS_PROTOCOL;
642
643                 if (argbuf)
644                         strcpy(argbuf, bufp);
645         }
646
647         return ok;
648 }
649
650 static void pop3_gen_send(SockInfo *sock, const gchar *format, ...)
651 {
652         gchar buf[POPBUFSIZE + 1];
653         va_list args;
654
655         va_start(args, format);
656         g_vsnprintf(buf, sizeof(buf) - 2, format, args);
657         va_end(args);
658
659         if (!strncasecmp(buf, "PASS ", 5))
660                 log_print("POP3> PASS ********\n");
661         else
662                 log_print("POP3> %s\n", buf);
663
664         strcat(buf, "\r\n");
665         sock_write(sock, buf, strlen(buf));
666 }
667
668 static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size)
669 {
670         if (sock_gets(sock, buf, size) < 0) {
671                 return PS_SOCKET;
672         } else {
673                 strretchomp(buf);
674                 log_print("POP3< %s\n", buf);
675
676                 return PS_SUCCESS;
677         }
678 }
679
680 gboolean pop3_delete_header (Pop3State *state)
681 {
682         
683         if ( (state->ac_prefs->session_type == DELE_HEADER) &&
684              (g_slist_length(state->ac_prefs->to_delete) > 0) ) {
685
686                 state->cur_msg = (gint) state->ac_prefs->to_delete->data;
687                 debug_print(_("next to delete %i\n"), state->cur_msg);
688                 state->ac_prefs->to_delete = g_slist_remove 
689                         (state->ac_prefs->to_delete, state->ac_prefs->to_delete->data);
690                 return TRUE;
691         }
692         return FALSE;
693 }