Statistics
| Revision:

root / libsylph / pop.c @ 3053

History | View | Annotate | Download (21.8 kB)

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