Statistics
| Revision:

root / src / pop.c @ 358

History | View | Annotate | Download (20.6 kB)

1 1 hiro
/*
2 1 hiro
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 1 hiro
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4 1 hiro
 *
5 1 hiro
 * This program is free software; you can redistribute it and/or modify
6 1 hiro
 * it under the terms of the GNU General Public License as published by
7 1 hiro
 * the Free Software Foundation; either version 2 of the License, or
8 1 hiro
 * (at your option) any later version.
9 1 hiro
 *
10 1 hiro
 * This program is distributed in the hope that it will be useful,
11 1 hiro
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 1 hiro
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 1 hiro
 * GNU General Public License for more details.
14 1 hiro
 *
15 1 hiro
 * You should have received a copy of the GNU General Public License
16 1 hiro
 * along with this program; if not, write to the Free Software
17 1 hiro
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 1 hiro
 */
19 1 hiro
20 1 hiro
#ifdef HAVE_CONFIG_H
21 1 hiro
#  include "config.h"
22 1 hiro
#endif
23 1 hiro
24 37 hiro
#include "defs.h"
25 37 hiro
26 1 hiro
#include <glib.h>
27 92 hiro
#include <glib/gi18n.h>
28 1 hiro
#include <stdio.h>
29 1 hiro
#include <string.h>
30 1 hiro
#include <stdarg.h>
31 1 hiro
#include <ctype.h>
32 1 hiro
#include <unistd.h>
33 1 hiro
#include <time.h>
34 1 hiro
#include <errno.h>
35 1 hiro
36 1 hiro
#include "pop.h"
37 1 hiro
#include "md5.h"
38 1 hiro
#include "prefs_account.h"
39 1 hiro
#include "utils.h"
40 1 hiro
#include "recv.h"
41 1 hiro
42 1 hiro
static gint pop3_greeting_recv                (Pop3Session *session,
43 1 hiro
                                         const gchar *msg);
44 1 hiro
static gint pop3_getauth_user_send        (Pop3Session *session);
45 1 hiro
static gint pop3_getauth_pass_send        (Pop3Session *session);
46 1 hiro
static gint pop3_getauth_apop_send        (Pop3Session *session);
47 1 hiro
#if USE_SSL
48 1 hiro
static gint pop3_stls_send                (Pop3Session *session);
49 1 hiro
static gint pop3_stls_recv                (Pop3Session *session);
50 1 hiro
#endif
51 1 hiro
static gint pop3_getrange_stat_send        (Pop3Session *session);
52 1 hiro
static gint pop3_getrange_stat_recv        (Pop3Session *session,
53 1 hiro
                                         const gchar *msg);
54 1 hiro
static gint pop3_getrange_last_send        (Pop3Session *session);
55 1 hiro
static gint pop3_getrange_last_recv        (Pop3Session *session,
56 1 hiro
                                         const gchar *msg);
57 1 hiro
static gint pop3_getrange_uidl_send        (Pop3Session *session);
58 1 hiro
static gint pop3_getrange_uidl_recv        (Pop3Session *session,
59 1 hiro
                                         const gchar *data,
60 1 hiro
                                         guint        len);
61 1 hiro
static gint pop3_getsize_list_send        (Pop3Session *session);
62 1 hiro
static gint pop3_getsize_list_recv        (Pop3Session *session,
63 1 hiro
                                         const gchar *data,
64 1 hiro
                                         guint        len);
65 1 hiro
static gint pop3_retr_send                (Pop3Session *session);
66 1 hiro
static gint pop3_retr_recv                (Pop3Session *session,
67 1 hiro
                                         const gchar *data,
68 1 hiro
                                         guint        len);
69 1 hiro
static gint pop3_delete_send                (Pop3Session *session);
70 1 hiro
static gint pop3_delete_recv                (Pop3Session *session);
71 1 hiro
static gint pop3_logout_send                (Pop3Session *session);
72 1 hiro
73 1 hiro
static void pop3_gen_send                (Pop3Session        *session,
74 1 hiro
                                         const gchar        *format, ...);
75 1 hiro
76 1 hiro
static void pop3_session_destroy        (Session        *session);
77 1 hiro
78 1 hiro
static gint pop3_write_msg_to_file        (const gchar        *file,
79 1 hiro
                                         const gchar        *data,
80 1 hiro
                                         guint                 len);
81 1 hiro
82 1 hiro
static Pop3State pop3_lookup_next        (Pop3Session        *session);
83 1 hiro
static Pop3ErrorValue pop3_ok                (Pop3Session        *session,
84 1 hiro
                                         const gchar        *msg);
85 1 hiro
86 1 hiro
static gint pop3_session_recv_msg                (Session        *session,
87 1 hiro
                                                 const gchar        *msg);
88 1 hiro
static gint pop3_session_recv_data_finished        (Session        *session,
89 1 hiro
                                                 guchar                *data,
90 1 hiro
                                                 guint                 len);
91 1 hiro
92 1 hiro
93 1 hiro
static gint pop3_greeting_recv(Pop3Session *session, const gchar *msg)
94 1 hiro
{
95 1 hiro
        session->state = POP3_GREETING;
96 1 hiro
97 1 hiro
        session->greeting = g_strdup(msg);
98 1 hiro
        return PS_SUCCESS;
99 1 hiro
}
100 1 hiro
101 1 hiro
#if USE_SSL
102 1 hiro
static gint pop3_stls_send(Pop3Session *session)
103 1 hiro
{
104 1 hiro
        session->state = POP3_STLS;
105 1 hiro
        pop3_gen_send(session, "STLS");
106 1 hiro
        return PS_SUCCESS;
107 1 hiro
}
108 1 hiro
109 1 hiro
static gint pop3_stls_recv(Pop3Session *session)
110 1 hiro
{
111 1 hiro
        if (session_start_tls(SESSION(session)) < 0) {
112 1 hiro
                session->error_val = PS_SOCKET;
113 1 hiro
                return -1;
114 1 hiro
        }
115 1 hiro
        return PS_SUCCESS;
116 1 hiro
}
117 1 hiro
#endif /* USE_SSL */
118 1 hiro
119 1 hiro
static gint pop3_getauth_user_send(Pop3Session *session)
120 1 hiro
{
121 1 hiro
        g_return_val_if_fail(session->user != NULL, -1);
122 1 hiro
123 1 hiro
        session->state = POP3_GETAUTH_USER;
124 1 hiro
        pop3_gen_send(session, "USER %s", session->user);
125 1 hiro
        return PS_SUCCESS;
126 1 hiro
}
127 1 hiro
128 1 hiro
static gint pop3_getauth_pass_send(Pop3Session *session)
129 1 hiro
{
130 1 hiro
        g_return_val_if_fail(session->pass != NULL, -1);
131 1 hiro
132 1 hiro
        session->state = POP3_GETAUTH_PASS;
133 1 hiro
        pop3_gen_send(session, "PASS %s", session->pass);
134 1 hiro
        return PS_SUCCESS;
135 1 hiro
}
136 1 hiro
137 1 hiro
static gint pop3_getauth_apop_send(Pop3Session *session)
138 1 hiro
{
139 1 hiro
        gchar *start, *end;
140 1 hiro
        gchar *apop_str;
141 1 hiro
        gchar md5sum[33];
142 1 hiro
143 1 hiro
        g_return_val_if_fail(session->user != NULL, -1);
144 1 hiro
        g_return_val_if_fail(session->pass != NULL, -1);
145 1 hiro
146 1 hiro
        session->state = POP3_GETAUTH_APOP;
147 1 hiro
148 1 hiro
        if ((start = strchr(session->greeting, '<')) == NULL) {
149 1 hiro
                log_warning(_("Required APOP timestamp not found "
150 1 hiro
                              "in greeting\n"));
151 1 hiro
                session->error_val = PS_PROTOCOL;
152 1 hiro
                return -1;
153 1 hiro
        }
154 1 hiro
155 1 hiro
        if ((end = strchr(start, '>')) == NULL || end == start + 1) {
156 1 hiro
                log_warning(_("Timestamp syntax error in greeting\n"));
157 1 hiro
                session->error_val = PS_PROTOCOL;
158 1 hiro
                return -1;
159 1 hiro
        }
160 1 hiro
161 1 hiro
        *(end + 1) = '\0';
162 1 hiro
163 1 hiro
        apop_str = g_strconcat(start, session->pass, NULL);
164 1 hiro
        md5_hex_digest(md5sum, apop_str);
165 1 hiro
        g_free(apop_str);
166 1 hiro
167 1 hiro
        pop3_gen_send(session, "APOP %s %s", session->user, md5sum);
168 1 hiro
169 1 hiro
        return PS_SUCCESS;
170 1 hiro
}
171 1 hiro
172 1 hiro
static gint pop3_getrange_stat_send(Pop3Session *session)
173 1 hiro
{
174 1 hiro
        session->state = POP3_GETRANGE_STAT;
175 1 hiro
        pop3_gen_send(session, "STAT");
176 1 hiro
        return PS_SUCCESS;
177 1 hiro
}
178 1 hiro
179 1 hiro
static gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg)
180 1 hiro
{
181 1 hiro
        if (sscanf(msg, "%d %d", &session->count, &session->total_bytes) != 2) {
182 1 hiro
                log_warning(_("POP3 protocol error\n"));
183 1 hiro
                session->error_val = PS_PROTOCOL;
184 1 hiro
                return -1;
185 1 hiro
        } else {
186 1 hiro
                if (session->count == 0) {
187 1 hiro
                        session->uidl_is_valid = TRUE;
188 1 hiro
                } else {
189 1 hiro
                        session->msg = g_new0(Pop3MsgInfo, session->count + 1);
190 1 hiro
                        session->cur_msg = 1;
191 1 hiro
                }
192 1 hiro
        }
193 1 hiro
194 1 hiro
        return PS_SUCCESS;
195 1 hiro
}
196 1 hiro
197 1 hiro
static gint pop3_getrange_last_send(Pop3Session *session)
198 1 hiro
{
199 1 hiro
        session->state = POP3_GETRANGE_LAST;
200 1 hiro
        pop3_gen_send(session, "LAST");
201 1 hiro
        return PS_SUCCESS;
202 1 hiro
}
203 1 hiro
204 1 hiro
static gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg)
205 1 hiro
{
206 1 hiro
        gint last;
207 1 hiro
208 1 hiro
        if (sscanf(msg, "%d", &last) == 0) {
209 1 hiro
                log_warning(_("POP3 protocol error\n"));
210 1 hiro
                session->error_val = PS_PROTOCOL;
211 1 hiro
                return -1;
212 1 hiro
        } else {
213 1 hiro
                if (session->count > last) {
214 1 hiro
                        session->new_msg_exist = TRUE;
215 1 hiro
                        session->cur_msg = last + 1;
216 1 hiro
                } else
217 1 hiro
                        session->cur_msg = 0;
218 1 hiro
        }
219 1 hiro
220 1 hiro
        return PS_SUCCESS;
221 1 hiro
}
222 1 hiro
223 1 hiro
static gint pop3_getrange_uidl_send(Pop3Session *session)
224 1 hiro
{
225 1 hiro
        session->state = POP3_GETRANGE_UIDL;
226 1 hiro
        pop3_gen_send(session, "UIDL");
227 1 hiro
        return PS_SUCCESS;
228 1 hiro
}
229 1 hiro
230 1 hiro
static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data,
231 1 hiro
                                    guint len)
232 1 hiro
{
233 1 hiro
        gchar id[IDLEN + 1];
234 1 hiro
        gchar buf[POPBUFSIZE];
235 1 hiro
        gint buf_len;
236 1 hiro
        gint num;
237 1 hiro
        time_t recv_time;
238 1 hiro
        const gchar *p = data;
239 1 hiro
        const gchar *lastp = data + len;
240 1 hiro
        const gchar *newline;
241 1 hiro
242 1 hiro
        while (p < lastp) {
243 1 hiro
                if ((newline = memchr(p, '\r', lastp - p)) == NULL)
244 1 hiro
                        return -1;
245 1 hiro
                buf_len = MIN(newline - p, sizeof(buf) - 1);
246 1 hiro
                memcpy(buf, p, buf_len);
247 1 hiro
                buf[buf_len] = '\0';
248 1 hiro
249 1 hiro
                p = newline + 1;
250 1 hiro
                if (p < lastp && *p == '\n') p++;
251 1 hiro
252 1 hiro
                if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2 ||
253 1 hiro
                    num <= 0 || num > session->count) {
254 1 hiro
                        log_warning(_("invalid UIDL response: %s\n"), buf);
255 1 hiro
                        continue;
256 1 hiro
                }
257 1 hiro
258 1 hiro
                session->msg[num].uidl = g_strdup(id);
259 1 hiro
260 1 hiro
                recv_time = (time_t)g_hash_table_lookup(session->uidl_table, id);
261 1 hiro
                session->msg[num].recv_time = recv_time;
262 1 hiro
263 1 hiro
                if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE)
264 1 hiro
                        session->msg[num].received = TRUE;
265 1 hiro
266 1 hiro
                if (!session->new_msg_exist &&
267 1 hiro
                    (session->ac_prefs->getall || recv_time == RECV_TIME_NONE ||
268 1 hiro
                     session->ac_prefs->rmmail)) {
269 1 hiro
                        session->cur_msg = num;
270 1 hiro
                        session->new_msg_exist = TRUE;
271 1 hiro
                }
272 1 hiro
        }
273 1 hiro
274 1 hiro
        session->uidl_is_valid = TRUE;
275 1 hiro
        return PS_SUCCESS;
276 1 hiro
}
277 1 hiro
278 1 hiro
static gint pop3_getsize_list_send(Pop3Session *session)
279 1 hiro
{
280 1 hiro
        session->state = POP3_GETSIZE_LIST;
281 1 hiro
        pop3_gen_send(session, "LIST");
282 1 hiro
        return PS_SUCCESS;
283 1 hiro
}
284 1 hiro
285 1 hiro
static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data,
286 1 hiro
                                   guint len)
287 1 hiro
{
288 1 hiro
        gchar buf[POPBUFSIZE];
289 1 hiro
        gint buf_len;
290 1 hiro
        guint num, size;
291 1 hiro
        const gchar *p = data;
292 1 hiro
        const gchar *lastp = data + len;
293 1 hiro
        const gchar *newline;
294 1 hiro
295 1 hiro
        while (p < lastp) {
296 1 hiro
                if ((newline = memchr(p, '\r', lastp - p)) == NULL)
297 1 hiro
                        return -1;
298 1 hiro
                buf_len = MIN(newline - p, sizeof(buf) - 1);
299 1 hiro
                memcpy(buf, p, buf_len);
300 1 hiro
                buf[buf_len] = '\0';
301 1 hiro
302 1 hiro
                p = newline + 1;
303 1 hiro
                if (p < lastp && *p == '\n') p++;
304 1 hiro
305 1 hiro
                if (sscanf(buf, "%u %u", &num, &size) != 2) {
306 1 hiro
                        session->error_val = PS_PROTOCOL;
307 1 hiro
                        return -1;
308 1 hiro
                }
309 1 hiro
310 1 hiro
                if (num > 0 && num <= session->count)
311 1 hiro
                        session->msg[num].size = size;
312 1 hiro
                if (num > 0 && num < session->cur_msg)
313 1 hiro
                        session->cur_total_bytes += size;
314 1 hiro
        }
315 1 hiro
316 1 hiro
        return PS_SUCCESS;
317 1 hiro
}
318 1 hiro
319 1 hiro
static gint pop3_retr_send(Pop3Session *session)
320 1 hiro
{
321 1 hiro
        session->state = POP3_RETR;
322 1 hiro
        pop3_gen_send(session, "RETR %d", session->cur_msg);
323 1 hiro
        return PS_SUCCESS;
324 1 hiro
}
325 1 hiro
326 1 hiro
static gint pop3_retr_recv(Pop3Session *session, const gchar *data, guint len)
327 1 hiro
{
328 1 hiro
        gchar *file;
329 1 hiro
        gint drop_ok;
330 1 hiro
331 1 hiro
        file = get_tmp_file();
332 1 hiro
        if (pop3_write_msg_to_file(file, data, len) < 0) {
333 1 hiro
                g_free(file);
334 1 hiro
                session->error_val = PS_IOERR;
335 1 hiro
                return -1;
336 1 hiro
        }
337 1 hiro
338 1 hiro
        drop_ok = session->drop_message(session, file);
339 1 hiro
        unlink(file);
340 1 hiro
        g_free(file);
341 1 hiro
        if (drop_ok < 0) {
342 1 hiro
                session->error_val = PS_IOERR;
343 1 hiro
                return -1;
344 1 hiro
        }
345 1 hiro
346 1 hiro
        session->cur_total_bytes += session->msg[session->cur_msg].size;
347 1 hiro
        session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
348 1 hiro
        session->cur_total_num++;
349 1 hiro
350 1 hiro
        session->msg[session->cur_msg].received = TRUE;
351 1 hiro
        session->msg[session->cur_msg].recv_time =
352 1 hiro
                drop_ok == DROP_DONT_RECEIVE ? RECV_TIME_KEEP
353 1 hiro
                        : drop_ok == DROP_DELETE ? RECV_TIME_DELETE
354 1 hiro
                        : session->current_time;
355 1 hiro
356 1 hiro
        return PS_SUCCESS;
357 1 hiro
}
358 1 hiro
359 1 hiro
static gint pop3_delete_send(Pop3Session *session)
360 1 hiro
{
361 1 hiro
        session->state = POP3_DELETE;
362 1 hiro
        pop3_gen_send(session, "DELE %d", session->cur_msg);
363 1 hiro
        return PS_SUCCESS;
364 1 hiro
}
365 1 hiro
366 1 hiro
static gint pop3_delete_recv(Pop3Session *session)
367 1 hiro
{
368 1 hiro
        session->msg[session->cur_msg].deleted = TRUE;
369 1 hiro
        return PS_SUCCESS;
370 1 hiro
}
371 1 hiro
372 1 hiro
static gint pop3_logout_send(Pop3Session *session)
373 1 hiro
{
374 1 hiro
        session->state = POP3_LOGOUT;
375 1 hiro
        pop3_gen_send(session, "QUIT");
376 1 hiro
        return PS_SUCCESS;
377 1 hiro
}
378 1 hiro
379 1 hiro
static void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
380 1 hiro
{
381 1 hiro
        gchar buf[POPBUFSIZE + 1];
382 1 hiro
        va_list args;
383 1 hiro
384 1 hiro
        va_start(args, format);
385 1 hiro
        g_vsnprintf(buf, sizeof(buf) - 2, format, args);
386 1 hiro
        va_end(args);
387 1 hiro
388 333 hiro
        if (!g_ascii_strncasecmp(buf, "PASS ", 5))
389 1 hiro
                log_print("POP3> PASS ********\n");
390 1 hiro
        else
391 1 hiro
                log_print("POP3> %s\n", buf);
392 1 hiro
393 1 hiro
        session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
394 1 hiro
}
395 1 hiro
396 1 hiro
Session *pop3_session_new(PrefsAccount *account)
397 1 hiro
{
398 1 hiro
        Pop3Session *session;
399 1 hiro
400 1 hiro
        g_return_val_if_fail(account != NULL, NULL);
401 1 hiro
402 1 hiro
        session = g_new0(Pop3Session, 1);
403 1 hiro
404 1 hiro
        session_init(SESSION(session));
405 1 hiro
406 1 hiro
        SESSION(session)->type = SESSION_POP3;
407 1 hiro
408 1 hiro
        SESSION(session)->recv_msg = pop3_session_recv_msg;
409 1 hiro
        SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
410 1 hiro
        SESSION(session)->send_data_finished = NULL;
411 1 hiro
412 1 hiro
        SESSION(session)->destroy = pop3_session_destroy;
413 1 hiro
414 1 hiro
        session->state = POP3_READY;
415 1 hiro
        session->ac_prefs = account;
416 1 hiro
        session->uidl_table = pop3_get_uidl_table(account);
417 1 hiro
        session->current_time = time(NULL);
418 1 hiro
        session->error_val = PS_SUCCESS;
419 1 hiro
        session->error_msg = NULL;
420 1 hiro
421 1 hiro
        return SESSION(session);
422 1 hiro
}
423 1 hiro
424 1 hiro
static void pop3_session_destroy(Session *session)
425 1 hiro
{
426 1 hiro
        Pop3Session *pop3_session = POP3_SESSION(session);
427 1 hiro
        gint n;
428 1 hiro
429 1 hiro
        g_return_if_fail(session != NULL);
430 1 hiro
431 1 hiro
        for (n = 1; n <= pop3_session->count; n++)
432 1 hiro
                g_free(pop3_session->msg[n].uidl);
433 1 hiro
        g_free(pop3_session->msg);
434 1 hiro
435 1 hiro
        if (pop3_session->uidl_table) {
436 1 hiro
                hash_free_strings(pop3_session->uidl_table);
437 1 hiro
                g_hash_table_destroy(pop3_session->uidl_table);
438 1 hiro
        }
439 1 hiro
440 1 hiro
        g_free(pop3_session->greeting);
441 1 hiro
        g_free(pop3_session->user);
442 1 hiro
        g_free(pop3_session->pass);
443 1 hiro
        g_free(pop3_session->error_msg);
444 1 hiro
}
445 1 hiro
446 1 hiro
GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs)
447 1 hiro
{
448 1 hiro
        GHashTable *table;
449 1 hiro
        gchar *path;
450 1 hiro
        FILE *fp;
451 1 hiro
        gchar buf[POPBUFSIZE];
452 1 hiro
        gchar uidl[POPBUFSIZE];
453 1 hiro
        time_t recv_time;
454 1 hiro
        time_t now;
455 1 hiro
456 1 hiro
        table = g_hash_table_new(g_str_hash, g_str_equal);
457 1 hiro
458 1 hiro
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
459 37 hiro
                           UIDL_DIR, G_DIR_SEPARATOR_S, ac_prefs->recv_server,
460 1 hiro
                           "-", ac_prefs->userid, NULL);
461 1 hiro
        if ((fp = fopen(path, "rb")) == NULL) {
462 1 hiro
                if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
463 1 hiro
                g_free(path);
464 37 hiro
                return table;
465 1 hiro
        }
466 1 hiro
        g_free(path);
467 1 hiro
468 1 hiro
        now = time(NULL);
469 1 hiro
470 1 hiro
        while (fgets(buf, sizeof(buf), fp) != NULL) {
471 1 hiro
                strretchomp(buf);
472 1 hiro
                recv_time = RECV_TIME_NONE;
473 1 hiro
                if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) {
474 1 hiro
                        if (sscanf(buf, "%s", uidl) != 1)
475 1 hiro
                                continue;
476 1 hiro
                        else
477 1 hiro
                                recv_time = now;
478 1 hiro
                }
479 1 hiro
                if (recv_time == RECV_TIME_NONE)
480 1 hiro
                        recv_time = RECV_TIME_RECEIVED;
481 1 hiro
                g_hash_table_insert(table, g_strdup(uidl),
482 1 hiro
                                    GINT_TO_POINTER(recv_time));
483 1 hiro
        }
484 1 hiro
485 1 hiro
        fclose(fp);
486 1 hiro
        return table;
487 1 hiro
}
488 1 hiro
489 1 hiro
gint pop3_write_uidl_list(Pop3Session *session)
490 1 hiro
{
491 1 hiro
        gchar *path;
492 1 hiro
        FILE *fp;
493 1 hiro
        Pop3MsgInfo *msg;
494 1 hiro
        gint n;
495 1 hiro
496 1 hiro
        if (!session->uidl_is_valid) return 0;
497 1 hiro
498 1 hiro
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
499 1 hiro
                           "uidl", G_DIR_SEPARATOR_S,
500 1 hiro
                           session->ac_prefs->recv_server,
501 1 hiro
                           "-", session->ac_prefs->userid, NULL);
502 1 hiro
        if ((fp = fopen(path, "wb")) == NULL) {
503 1 hiro
                FILE_OP_ERROR(path, "fopen");
504 1 hiro
                g_free(path);
505 1 hiro
                return -1;
506 1 hiro
        }
507 1 hiro
508 1 hiro
        for (n = 1; n <= session->count; n++) {
509 1 hiro
                msg = &session->msg[n];
510 1 hiro
                if (msg->uidl && msg->received && !msg->deleted)
511 1 hiro
                        fprintf(fp, "%s\t%ld\n", msg->uidl, msg->recv_time);
512 1 hiro
        }
513 1 hiro
514 1 hiro
        if (fclose(fp) == EOF) FILE_OP_ERROR(path, "fclose");
515 1 hiro
        g_free(path);
516 1 hiro
517 1 hiro
        return 0;
518 1 hiro
}
519 1 hiro
520 1 hiro
static gint pop3_write_msg_to_file(const gchar *file, const gchar *data,
521 1 hiro
                                   guint len)
522 1 hiro
{
523 1 hiro
        FILE *fp;
524 1 hiro
        const gchar *prev, *cur;
525 1 hiro
526 1 hiro
        g_return_val_if_fail(file != NULL, -1);
527 1 hiro
528 1 hiro
        if ((fp = fopen(file, "wb")) == NULL) {
529 1 hiro
                FILE_OP_ERROR(file, "fopen");
530 1 hiro
                return -1;
531 1 hiro
        }
532 1 hiro
533 1 hiro
        if (change_file_mode_rw(fp, file) < 0)
534 1 hiro
                FILE_OP_ERROR(file, "chmod");
535 1 hiro
536 1 hiro
        /* +------------------+----------------+--------------------------+ *
537 1 hiro
         * ^data              ^prev            ^cur             data+len-1^ */
538 1 hiro
539 1 hiro
        prev = data;
540 1 hiro
        while ((cur = (gchar *)my_memmem(prev, len - (prev - data), "\r\n", 2))
541 1 hiro
               != NULL) {
542 1 hiro
                if ((cur > prev && fwrite(prev, cur - prev, 1, fp) < 1) ||
543 1 hiro
                    fputc('\n', fp) == EOF) {
544 1 hiro
                        FILE_OP_ERROR(file, "fwrite");
545 1 hiro
                        g_warning("can't write to file: %s\n", file);
546 1 hiro
                        fclose(fp);
547 1 hiro
                        unlink(file);
548 1 hiro
                        return -1;
549 1 hiro
                }
550 1 hiro
551 1 hiro
                if (cur == data + len - 1) {
552 1 hiro
                        prev = cur + 1;
553 1 hiro
                        break;
554 1 hiro
                }
555 1 hiro
556 1 hiro
                if (*(cur + 1) == '\n')
557 1 hiro
                        prev = cur + 2;
558 1 hiro
                else
559 1 hiro
                        prev = cur + 1;
560 1 hiro
561 1 hiro
                if (prev - data < len - 1 && *prev == '.' && *(prev + 1) == '.')
562 1 hiro
                        prev++;
563 1 hiro
564 1 hiro
                if (prev - data >= len)
565 1 hiro
                        break;
566 1 hiro
        }
567 1 hiro
568 1 hiro
        if (prev - data < len &&
569 1 hiro
            fwrite(prev, len - (prev - data), 1, fp) < 1) {
570 1 hiro
                FILE_OP_ERROR(file, "fwrite");
571 1 hiro
                g_warning("can't write to file: %s\n", file);
572 1 hiro
                fclose(fp);
573 1 hiro
                unlink(file);
574 1 hiro
                return -1;
575 1 hiro
        }
576 1 hiro
        if (data[len - 1] != '\r' && data[len - 1] != '\n') {
577 1 hiro
                if (fputc('\n', fp) == EOF) {
578 1 hiro
                        FILE_OP_ERROR(file, "fputc");
579 1 hiro
                        g_warning("can't write to file: %s\n", file);
580 1 hiro
                        fclose(fp);
581 1 hiro
                        unlink(file);
582 1 hiro
                        return -1;
583 1 hiro
                }
584 1 hiro
        }
585 1 hiro
586 1 hiro
        if (fclose(fp) == EOF) {
587 1 hiro
                FILE_OP_ERROR(file, "fclose");
588 1 hiro
                unlink(file);
589 1 hiro
                return -1;
590 1 hiro
        }
591 1 hiro
592 1 hiro
        return 0;
593 1 hiro
}
594 1 hiro
595 1 hiro
static Pop3State pop3_lookup_next(Pop3Session *session)
596 1 hiro
{
597 1 hiro
        Pop3MsgInfo *msg;
598 1 hiro
        PrefsAccount *ac = session->ac_prefs;
599 1 hiro
        gint size;
600 1 hiro
        gboolean size_limit_over;
601 1 hiro
602 1 hiro
        for (;;) {
603 1 hiro
                msg = &session->msg[session->cur_msg];
604 1 hiro
                size = msg->size;
605 1 hiro
                size_limit_over =
606 1 hiro
                    (ac->enable_size_limit &&
607 1 hiro
                     ac->size_limit > 0 &&
608 1 hiro
                     size > ac->size_limit * 1024);
609 1 hiro
610 1 hiro
                if (msg->recv_time == RECV_TIME_DELETE ||
611 1 hiro
                    (ac->rmmail &&
612 1 hiro
                     msg->recv_time != RECV_TIME_NONE &&
613 1 hiro
                     msg->recv_time != RECV_TIME_KEEP &&
614 1 hiro
                     session->current_time - msg->recv_time >=
615 1 hiro
                     ac->msg_leave_time * 24 * 60 * 60)) {
616 1 hiro
                        log_print(_("POP3: Deleting expired message %d\n"),
617 1 hiro
                                  session->cur_msg);
618 1 hiro
                        pop3_delete_send(session);
619 1 hiro
                        return POP3_DELETE;
620 1 hiro
                }
621 1 hiro
622 1 hiro
                if (size_limit_over)
623 1 hiro
                        log_print
624 1 hiro
                                (_("POP3: Skipping message %d (%d bytes)\n"),
625 1 hiro
                                  session->cur_msg, size);
626 1 hiro
627 1 hiro
                if (size == 0 || msg->received || size_limit_over) {
628 1 hiro
                        session->cur_total_bytes += size;
629 1 hiro
                        if (session->cur_msg == session->count) {
630 1 hiro
                                pop3_logout_send(session);
631 1 hiro
                                return POP3_LOGOUT;
632 1 hiro
                        } else
633 1 hiro
                                session->cur_msg++;
634 1 hiro
                } else
635 1 hiro
                        break;
636 1 hiro
        }
637 1 hiro
638 1 hiro
        pop3_retr_send(session);
639 1 hiro
        return POP3_RETR;
640 1 hiro
}
641 1 hiro
642 1 hiro
static Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg)
643 1 hiro
{
644 1 hiro
        Pop3ErrorValue ok;
645 1 hiro
646 1 hiro
        log_print("POP3< %s\n", msg);
647 1 hiro
648 1 hiro
        if (!strncmp(msg, "+OK", 3))
649 1 hiro
                ok = PS_SUCCESS;
650 1 hiro
        else if (!strncmp(msg, "-ERR", 4)) {
651 1 hiro
                if (strstr(msg + 4, "lock") ||
652 1 hiro
                    strstr(msg + 4, "Lock") ||
653 1 hiro
                    strstr(msg + 4, "LOCK") ||
654 1 hiro
                    strstr(msg + 4, "wait")) {
655 1 hiro
                        log_warning(_("mailbox is locked\n"));
656 1 hiro
                        ok = PS_LOCKBUSY;
657 1 hiro
                } else if (strcasestr(msg + 4, "timeout")) {
658 1 hiro
                        log_warning(_("session timeout\n"));
659 1 hiro
                        ok = PS_ERROR;
660 1 hiro
                } else {
661 1 hiro
                        switch (session->state) {
662 1 hiro
#if USE_SSL
663 1 hiro
                        case POP3_STLS:
664 1 hiro
                                log_warning(_("can't start TLS session\n"));
665 1 hiro
                                ok = PS_ERROR;
666 1 hiro
                                break;
667 1 hiro
#endif
668 1 hiro
                        case POP3_GETAUTH_USER:
669 1 hiro
                        case POP3_GETAUTH_PASS:
670 1 hiro
                        case POP3_GETAUTH_APOP:
671 1 hiro
                                log_warning(_("error occurred on authentication\n"));
672 1 hiro
                                ok = PS_AUTHFAIL;
673 1 hiro
                                break;
674 1 hiro
                        case POP3_GETRANGE_LAST:
675 1 hiro
                        case POP3_GETRANGE_UIDL:
676 1 hiro
                                log_warning(_("command not supported\n"));
677 1 hiro
                                ok = PS_NOTSUPPORTED;
678 1 hiro
                                break;
679 1 hiro
                        default:
680 1 hiro
                                log_warning(_("error occurred on POP3 session\n"));
681 1 hiro
                                ok = PS_ERROR;
682 1 hiro
                        }
683 1 hiro
                }
684 1 hiro
685 1 hiro
                g_free(session->error_msg);
686 1 hiro
                session->error_msg = g_strdup(msg);
687 1 hiro
                fprintf(stderr, "POP3: %s\n", msg);
688 1 hiro
        } else
689 1 hiro
                ok = PS_PROTOCOL;
690 1 hiro
691 1 hiro
        session->error_val = ok;
692 1 hiro
        return ok;
693 1 hiro
}
694 1 hiro
695 1 hiro
static gint pop3_session_recv_msg(Session *session, const gchar *msg)
696 1 hiro
{
697 1 hiro
        Pop3Session *pop3_session = POP3_SESSION(session);
698 1 hiro
        Pop3ErrorValue val = PS_SUCCESS;
699 1 hiro
        const guchar *body;
700 1 hiro
701 1 hiro
        body = msg;
702 1 hiro
        if (pop3_session->state != POP3_GETRANGE_UIDL_RECV &&
703 1 hiro
            pop3_session->state != POP3_GETSIZE_LIST_RECV) {
704 1 hiro
                val = pop3_ok(pop3_session, msg);
705 1 hiro
                if (val != PS_SUCCESS) {
706 1 hiro
                        if (val != PS_NOTSUPPORTED) {
707 1 hiro
                                pop3_session->state = POP3_ERROR;
708 1 hiro
                                return -1;
709 1 hiro
                        }
710 1 hiro
                }
711 1 hiro
712 1 hiro
                if (*body == '+' || *body == '-')
713 1 hiro
                        body++;
714 1 hiro
                while (isalpha(*body))
715 1 hiro
                        body++;
716 1 hiro
                while (isspace(*body))
717 1 hiro
                        body++;
718 1 hiro
        }
719 1 hiro
720 1 hiro
        switch (pop3_session->state) {
721 1 hiro
        case POP3_READY:
722 1 hiro
        case POP3_GREETING:
723 1 hiro
                pop3_greeting_recv(pop3_session, body);
724 1 hiro
#if USE_SSL
725 1 hiro
                if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS)
726 1 hiro
                        pop3_stls_send(pop3_session);
727 1 hiro
                else
728 1 hiro
#endif
729 1 hiro
                if (pop3_session->ac_prefs->use_apop_auth)
730 1 hiro
                        pop3_getauth_apop_send(pop3_session);
731 1 hiro
                else
732 1 hiro
                        pop3_getauth_user_send(pop3_session);
733 1 hiro
                break;
734 1 hiro
#if USE_SSL
735 1 hiro
        case POP3_STLS:
736 1 hiro
                if (pop3_stls_recv(pop3_session) != PS_SUCCESS)
737 1 hiro
                        return -1;
738 1 hiro
                if (pop3_session->ac_prefs->use_apop_auth)
739 1 hiro
                        pop3_getauth_apop_send(pop3_session);
740 1 hiro
                else
741 1 hiro
                        pop3_getauth_user_send(pop3_session);
742 1 hiro
                break;
743 1 hiro
#endif
744 1 hiro
        case POP3_GETAUTH_USER:
745 1 hiro
                pop3_getauth_pass_send(pop3_session);
746 1 hiro
                break;
747 1 hiro
        case POP3_GETAUTH_PASS:
748 1 hiro
        case POP3_GETAUTH_APOP:
749 1 hiro
                pop3_getrange_stat_send(pop3_session);
750 1 hiro
                break;
751 1 hiro
        case POP3_GETRANGE_STAT:
752 1 hiro
                if (pop3_getrange_stat_recv(pop3_session, body) < 0)
753 1 hiro
                        return -1;
754 1 hiro
                if (pop3_session->count > 0)
755 1 hiro
                        pop3_getrange_uidl_send(pop3_session);
756 1 hiro
                else
757 1 hiro
                        pop3_logout_send(pop3_session);
758 1 hiro
                break;
759 1 hiro
        case POP3_GETRANGE_LAST:
760 1 hiro
                if (val == PS_NOTSUPPORTED)
761 1 hiro
                        pop3_session->error_val = PS_SUCCESS;
762 1 hiro
                else if (pop3_getrange_last_recv(pop3_session, body) < 0)
763 1 hiro
                        return -1;
764 1 hiro
                if (pop3_session->cur_msg > 0)
765 1 hiro
                        pop3_getsize_list_send(pop3_session);
766 1 hiro
                else
767 1 hiro
                        pop3_logout_send(pop3_session);
768 1 hiro
                break;
769 1 hiro
        case POP3_GETRANGE_UIDL:
770 1 hiro
                if (val == PS_NOTSUPPORTED) {
771 1 hiro
                        pop3_session->error_val = PS_SUCCESS;
772 1 hiro
                        pop3_getrange_last_send(pop3_session);
773 1 hiro
                } else {
774 1 hiro
                        pop3_session->state = POP3_GETRANGE_UIDL_RECV;
775 1 hiro
                        session_recv_data(session, 0, ".\r\n");
776 1 hiro
                }
777 1 hiro
                break;
778 1 hiro
        case POP3_GETSIZE_LIST:
779 1 hiro
                pop3_session->state = POP3_GETSIZE_LIST_RECV;
780 1 hiro
                session_recv_data(session, 0, ".\r\n");
781 1 hiro
                break;
782 1 hiro
        case POP3_RETR:
783 1 hiro
                pop3_session->state = POP3_RETR_RECV;
784 1 hiro
                session_recv_data(session, 0, ".\r\n");
785 1 hiro
                break;
786 1 hiro
        case POP3_DELETE:
787 1 hiro
                pop3_delete_recv(pop3_session);
788 1 hiro
                if (pop3_session->cur_msg == pop3_session->count)
789 1 hiro
                        pop3_logout_send(pop3_session);
790 1 hiro
                else {
791 1 hiro
                        pop3_session->cur_msg++;
792 1 hiro
                        if (pop3_lookup_next(pop3_session) == POP3_ERROR)
793 1 hiro
                                return -1;
794 1 hiro
                }
795 1 hiro
                break;
796 1 hiro
        case POP3_LOGOUT:
797 1 hiro
                session_disconnect(session);
798 1 hiro
                break;
799 1 hiro
        case POP3_ERROR:
800 1 hiro
        default:
801 1 hiro
                return -1;
802 1 hiro
        }
803 1 hiro
804 1 hiro
        return 0;
805 1 hiro
}
806 1 hiro
807 1 hiro
static gint pop3_session_recv_data_finished(Session *session, guchar *data,
808 1 hiro
                                            guint len)
809 1 hiro
{
810 1 hiro
        Pop3Session *pop3_session = POP3_SESSION(session);
811 1 hiro
        Pop3ErrorValue val = PS_SUCCESS;
812 1 hiro
813 1 hiro
        switch (pop3_session->state) {
814 1 hiro
        case POP3_GETRANGE_UIDL_RECV:
815 1 hiro
                val = pop3_getrange_uidl_recv(pop3_session, data, len);
816 1 hiro
                if (val == PS_SUCCESS) {
817 1 hiro
                        if (pop3_session->new_msg_exist)
818 1 hiro
                                pop3_getsize_list_send(pop3_session);
819 1 hiro
                        else
820 1 hiro
                                pop3_logout_send(pop3_session);
821 1 hiro
                } else
822 1 hiro
                        return -1;
823 1 hiro
                break;
824 1 hiro
        case POP3_GETSIZE_LIST_RECV:
825 1 hiro
                val = pop3_getsize_list_recv(pop3_session, data, len);
826 1 hiro
                if (val == PS_SUCCESS) {
827 1 hiro
                        if (pop3_lookup_next(pop3_session) == POP3_ERROR)
828 1 hiro
                                return -1;
829 1 hiro
                } else
830 1 hiro
                        return -1;
831 1 hiro
                break;
832 1 hiro
        case POP3_RETR_RECV:
833 1 hiro
                if (pop3_retr_recv(pop3_session, data, len) < 0)
834 1 hiro
                        return -1;
835 1 hiro
836 1 hiro
                if (pop3_session->msg[pop3_session->cur_msg].recv_time
837 1 hiro
                    == RECV_TIME_DELETE ||
838 1 hiro
                    (pop3_session->ac_prefs->rmmail &&
839 1 hiro
                     pop3_session->ac_prefs->msg_leave_time == 0 &&
840 1 hiro
                     pop3_session->msg[pop3_session->cur_msg].recv_time
841 1 hiro
                     != RECV_TIME_KEEP))
842 1 hiro
                        pop3_delete_send(pop3_session);
843 1 hiro
                else if (pop3_session->cur_msg == pop3_session->count)
844 1 hiro
                        pop3_logout_send(pop3_session);
845 1 hiro
                else {
846 1 hiro
                        pop3_session->cur_msg++;
847 1 hiro
                        if (pop3_lookup_next(pop3_session) == POP3_ERROR)
848 1 hiro
                                return -1;
849 1 hiro
                }
850 1 hiro
                break;
851 1 hiro
        case POP3_ERROR:
852 1 hiro
        default:
853 1 hiro
                return -1;
854 1 hiro
        }
855 1 hiro
856 1 hiro
        return 0;
857 1 hiro
}