1d5116e6c53d4f5582b6d1337164e7109339d3db
[claws.git] / src / pop.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999,2000 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 "md5ify.h"
35 #include "prefs_account.h"
36 #include "utils.h"
37 #include "inc.h"
38 #include "recv.h"
39
40 static gint pop3_ok(gint sock, gchar *argbuf);
41 static void pop3_gen_send(gint sock, const gchar *format, ...);
42 static gint pop3_gen_recv(gint sock, gchar *buf, gint size);
43
44 gint pop3_greeting_recv(gint sock, gpointer data)
45 {
46         Pop3State *state = (Pop3State *)data;
47         gchar buf[POPBUFSIZE];
48
49         if (pop3_ok(sock, buf) == PS_SUCCESS) {
50                 if (state->ac_prefs->protocol == A_APOP) {
51                         state->greeting = g_strdup(buf);
52                         return POP3_GETAUTH_APOP_SEND;
53                 } else
54                         return POP3_GETAUTH_USER_SEND;
55         } else
56                 return -1;
57 }
58
59 gint pop3_getauth_user_send(gint sock, gpointer data)
60 {
61         Pop3State *state = (Pop3State *)data;
62
63         g_return_val_if_fail(state->user != NULL, -1);
64
65         inc_progress_update(state, POP3_GETAUTH_USER_SEND);
66
67         pop3_gen_send(sock, "USER %s", state->user);
68
69         return POP3_GETAUTH_USER_RECV;
70 }
71
72 gint pop3_getauth_user_recv(gint sock, gpointer data)
73 {
74         if (pop3_ok(sock, NULL) == PS_SUCCESS)
75                 return POP3_GETAUTH_PASS_SEND;
76         else
77                 return -1;
78 }
79
80 gint pop3_getauth_pass_send(gint sock, gpointer data)
81 {
82         Pop3State *state = (Pop3State *)data;
83
84         g_return_val_if_fail(state->pass != NULL, -1);
85
86         pop3_gen_send(sock, "PASS %s", state->pass);
87
88         return POP3_GETAUTH_PASS_RECV;
89 }
90
91 gint pop3_getauth_pass_recv(gint sock, gpointer data)
92 {
93         Pop3State *state = (Pop3State *)data;
94
95         if (pop3_ok(sock, NULL) == PS_SUCCESS)
96                 return POP3_GETRANGE_STAT_SEND;
97         else {
98                 log_warning(_("error occurred on authorization\n"));
99                 state->error_val = PS_AUTHFAIL;
100                 return -1;
101         }
102 }
103
104 gint pop3_getauth_apop_send(gint sock, gpointer data)
105 {
106         Pop3State *state = (Pop3State *)data;
107         gchar *start, *end;
108         gchar *apop_str, *md5sum;
109
110         g_return_val_if_fail(state->user != NULL, -1);
111         g_return_val_if_fail(state->pass != NULL, -1);
112
113         inc_progress_update(state, POP3_GETAUTH_APOP_SEND);
114
115         if ((start = strchr(state->greeting, '<')) == NULL) {
116                 log_warning(_("Required APOP timestamp not found "
117                               "in greeting\n"));
118                 return -1;
119         }
120
121         if ((end = strchr(start, '>')) == NULL || end == start + 1) {
122                 log_warning(_("Timestamp syntax error in greeting\n"));
123                 return -1;
124         }
125
126         *(end + 1) = '\0';
127
128         apop_str = g_strconcat(start, state->pass, NULL);
129         md5sum = MD5Digest(apop_str);
130         g_free(apop_str);
131
132         pop3_gen_send(sock, "APOP %s %s", state->user, md5sum);
133
134         return POP3_GETAUTH_APOP_RECV;
135 }
136
137 gint pop3_getauth_apop_recv(gint sock, gpointer data)
138 {
139         Pop3State *state = (Pop3State *)data;
140
141         if (pop3_ok(sock, NULL) == PS_SUCCESS)
142                 return POP3_GETRANGE_STAT_SEND;
143         else {
144                 log_warning(_("error occurred on authorization\n"));
145                 state->error_val = PS_AUTHFAIL;
146                 return -1;
147         }
148 }
149
150 gint pop3_getrange_stat_send(gint sock, gpointer data)
151 {
152         Pop3State *state = (Pop3State *)data;
153
154         inc_progress_update(state, POP3_GETRANGE_STAT_SEND);
155
156         pop3_gen_send(sock, "STAT");
157
158         return POP3_GETRANGE_STAT_RECV;
159 }
160
161 gint pop3_getrange_stat_recv(gint sock, gpointer data)
162 {
163         Pop3State *state = (Pop3State *)data;
164         gchar buf[POPBUFSIZE + 1];
165         gint ok;
166
167         if ((ok = pop3_ok(sock, buf)) == PS_SUCCESS) {
168                 if (sscanf(buf, "%d %d", &state->count, &state->bytes) != 2) {
169                         log_warning(_("POP3 protocol error\n"));
170                         return -1;
171                 } else {
172                         if (state->count == 0)
173                                 return POP3_LOGOUT_SEND;
174                         else {
175                                 state->cur_msg = 1;
176                                 state->new = state->count;
177                                 if (state->ac_prefs->rmmail ||
178                                     state->ac_prefs->getall)
179                                         return POP3_RETR_SEND;
180                                 else
181                                         return POP3_GETRANGE_UIDL_SEND;
182                         }
183                 }
184         } else if (ok == PS_PROTOCOL)
185                 return POP3_LOGOUT_SEND;
186         else
187                 return -1;
188 }
189
190 gint pop3_getrange_last_send(gint sock, gpointer data)
191 {
192         pop3_gen_send(sock, "LAST");
193
194         return POP3_GETRANGE_LAST_RECV;
195 }
196
197 gint pop3_getrange_last_recv(gint sock, gpointer data)
198 {
199         Pop3State *state = (Pop3State *)data;
200         gchar buf[POPBUFSIZE + 1];
201
202         if (pop3_ok(sock, buf) == PS_SUCCESS) {
203                 gint last;
204
205                 if (sscanf(buf, "%d", &last) == 0) {
206                         log_warning(_("POP3 protocol error\n"));
207                         return -1;
208                 } else {
209                         if (state->count == last)
210                                 return POP3_LOGOUT_SEND;
211                         else {
212                                 state->new = state->count - last;
213                                 state->cur_msg = last + 1;
214                                 return POP3_RETR_SEND;
215                         }
216                 }
217         } else
218                 return POP3_RETR_SEND;
219 }
220
221 gint pop3_getrange_uidl_send(gint sock, gpointer data)
222 {
223         pop3_gen_send(sock, "UIDL");
224
225         return POP3_GETRANGE_UIDL_RECV;
226 }
227
228 gint pop3_getrange_uidl_recv(gint sock, gpointer data)
229 {
230         Pop3State *state = (Pop3State *)data;
231         gboolean nb;
232         gboolean new = FALSE;
233         gchar buf[POPBUFSIZE];
234         gchar id[IDLEN + 1];
235
236         if (pop3_ok(sock, NULL) != PS_SUCCESS) return POP3_GETRANGE_LAST_SEND;
237
238         if (!state->id_table) new = TRUE;
239
240         nb = sock_is_nonblocking_mode(sock);
241         if (nb && (sock_set_nonblocking_mode(sock, FALSE) < 0)) return -1;
242
243         while (sock_read(sock, buf, sizeof(buf)) >= 0) {
244                 gint num;
245
246                 if (buf[0] == '.') break;
247                 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
248                         continue;
249
250                 if (new == FALSE &&
251                     g_hash_table_lookup(state->id_table, id) == NULL) {
252                         state->new = state->count - num + 1;
253                         state->cur_msg = num;
254                         new = TRUE;
255                 }
256
257                 if (new == TRUE)
258                         state->new_id_list = g_slist_append
259                                 (state->new_id_list, g_strdup(id));
260                 else
261                         state->id_list = g_slist_append
262                                 (state->id_list, g_strdup(id));
263         }
264
265         if (nb && (sock_set_nonblocking_mode(sock, TRUE) < 0)) return -1;
266
267         if (new == TRUE)
268                 return POP3_RETR_SEND;
269         else
270                 return POP3_LOGOUT_SEND;
271 }
272
273 gint pop3_retr_send(gint sock, gpointer data)
274 {
275         Pop3State *state = (Pop3State *)data;
276
277         inc_progress_update(state, POP3_RETR_SEND);
278
279         pop3_gen_send(sock, "RETR %d", state->cur_msg);
280
281         return POP3_RETR_RECV;
282 }
283
284 gint pop3_retr_recv(gint sock, gpointer data)
285 {
286         Pop3State *state = (Pop3State *)data;
287         const gchar *file;
288         gint ok, drop_ok;
289
290         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
291                 if (recv_write_to_file(sock, (file = get_tmp_file())) < 0) {
292                         state->inc_state = INC_NOSPACE;
293                         return -1;
294                 }
295                 if ((drop_ok = inc_drop_message(file, state)) < 0) {
296                         state->inc_state = INC_ERROR;
297                         return -1;
298                 }
299                 if (drop_ok == 0 && state->ac_prefs->rmmail)
300                         return POP3_DELETE_SEND;
301
302                 if (state->new_id_list) {
303                         state->id_list = g_slist_append
304                                 (state->id_list, state->new_id_list->data);
305                         state->new_id_list =
306                                 g_slist_remove(state->new_id_list,
307                                                state->new_id_list->data);
308                 }
309
310                 if (state->cur_msg < state->count) {
311                         state->cur_msg++;
312                         return POP3_RETR_SEND;
313                 } else
314                         return POP3_LOGOUT_SEND;
315         } else if (ok == PS_PROTOCOL)
316                 return POP3_LOGOUT_SEND;
317         else
318                 return -1;
319 }
320
321 gint pop3_delete_send(gint sock, gpointer data)
322 {
323         Pop3State *state = (Pop3State *)data;
324
325         //inc_progress_update(state, POP3_DELETE_SEND);
326
327         pop3_gen_send(sock, "DELE %d", state->cur_msg);
328
329         return POP3_DELETE_RECV;
330 }
331
332 gint pop3_delete_recv(gint sock, gpointer data)
333 {
334         Pop3State *state = (Pop3State *)data;
335         gint ok;
336
337         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
338                 if (state->cur_msg < state->count) {
339                         state->cur_msg++;
340                         return POP3_RETR_SEND;
341                 } else
342                         return POP3_LOGOUT_SEND;
343         } else if (ok == PS_PROTOCOL)
344                 return POP3_LOGOUT_SEND;
345         else
346                 return -1;
347 }
348
349 gint pop3_logout_send(gint sock, gpointer data)
350 {
351         Pop3State *state = (Pop3State *)data;
352
353         inc_progress_update(state, POP3_LOGOUT_SEND);
354
355         pop3_gen_send(sock, "QUIT");
356
357         return POP3_LOGOUT_RECV;
358 }
359
360 gint pop3_logout_recv(gint sock, gpointer data)
361 {
362         if (pop3_ok(sock, NULL) == PS_SUCCESS)
363                 return -1;
364         else
365                 return -1;
366 }
367
368 static gint pop3_ok(gint sock, gchar *argbuf)
369 {
370         gint ok;
371         gchar buf[POPBUFSIZE + 1];
372         gchar *bufp;
373
374         if ((ok = pop3_gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS) {
375                 bufp = buf;
376                 if (*bufp == '+' || *bufp == '-')
377                         bufp++;
378                 else
379                         return PS_PROTOCOL;
380
381                 while (isalpha(*bufp))
382                         bufp++;
383
384                 if (*bufp)
385                         *(bufp++) = '\0';
386
387                 if (!strcmp(buf, "+OK"))
388                         ok = PS_SUCCESS;
389                 else if (!strncmp(buf, "-ERR", 4)) {
390                         if (strstr(bufp, "lock") ||
391                                  strstr(bufp, "Lock") ||
392                                  strstr(bufp, "LOCK") ||
393                                  strstr(bufp, "wait"))
394                                 ok = PS_LOCKBUSY;
395                         else
396                                 ok = PS_PROTOCOL;
397
398                         if (*bufp)
399                                 fprintf(stderr, "POP3: %s\n", bufp);
400                 } else
401                         ok = PS_PROTOCOL;
402
403                 if (argbuf)
404                         strcpy(argbuf, bufp);
405         }
406
407         return ok;
408 }
409
410 static void pop3_gen_send(gint sock, const gchar *format, ...)
411 {
412         gchar buf[POPBUFSIZE + 1];
413         va_list args;
414
415         va_start(args, format);
416         g_vsnprintf(buf, sizeof(buf) - 2, format, args);
417         va_end(args);
418
419         if (!strncasecmp(buf, "PASS ", 5))
420                 log_print("POP3> PASS ********\n");
421         else
422                 log_print("POP3> %s\n", buf);
423
424         strcat(buf, "\r\n");
425         sock_write(sock, buf, strlen(buf));
426 }
427
428 static gint pop3_gen_recv(gint sock, gchar *buf, gint size)
429 {
430         gboolean nb;
431
432         nb = sock_is_nonblocking_mode(sock);
433
434         if (nb && (sock_set_nonblocking_mode(sock, FALSE) < 0))
435                 return PS_SOCKET;
436         if (sock_read(sock, buf, size) < 0) {
437                 return PS_SOCKET;
438         } else {
439                 strretchomp(buf);
440                 log_print("POP3< %s\n", buf);
441                 if (nb && (sock_set_nonblocking_mode(sock, TRUE) < 0))
442                         return PS_SOCKET;
443
444                 return PS_SUCCESS;
445         }
446 }