Statistics
| Revision:

root / libsylph / pop.c @ 3195

History | View | Annotate | Download (22.2 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2008 Hiroyuki Yamamoto
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library 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 GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <stdio.h>
29
#include <string.h>
30
#include <stdarg.h>
31
#include <ctype.h>
32
#include <unistd.h>
33
#include <time.h>
34
#include <errno.h>
35

    
36
#include "pop.h"
37
#include "md5.h"
38
#include "prefs.h"
39
#include "prefs_account.h"
40
#include "utils.h"
41
#include "recv.h"
42

    
43
gint pop3_greeting_recv                (Pop3Session *session,
44
                                 const gchar *msg);
45
gint pop3_getauth_user_send        (Pop3Session *session);
46
gint pop3_getauth_pass_send        (Pop3Session *session);
47
gint pop3_getauth_apop_send        (Pop3Session *session);
48
#if USE_SSL
49
gint pop3_stls_send                (Pop3Session *session);
50
gint pop3_stls_recv                (Pop3Session *session);
51
#endif
52
gint pop3_getrange_stat_send        (Pop3Session *session);
53
gint pop3_getrange_stat_recv        (Pop3Session *session,
54
                                 const gchar *msg);
55
gint pop3_getrange_last_send        (Pop3Session *session);
56
gint pop3_getrange_last_recv        (Pop3Session *session,
57
                                 const gchar *msg);
58
gint pop3_getrange_uidl_send        (Pop3Session *session);
59
gint pop3_getrange_uidl_recv        (Pop3Session *session,
60
                                 const gchar *data,
61
                                 guint        len);
62
gint pop3_getsize_list_send        (Pop3Session *session);
63
gint pop3_getsize_list_recv        (Pop3Session *session,
64
                                 const gchar *data,
65
                                 guint        len);
66
gint pop3_retr_send                (Pop3Session *session);
67
gint pop3_retr_recv                (Pop3Session *session,
68
                                 FILE             *fp,
69
                                 guint        len);
70
gint pop3_delete_send                (Pop3Session *session);
71
gint pop3_delete_recv                (Pop3Session *session);
72
gint pop3_logout_send                (Pop3Session *session);
73

    
74
void pop3_gen_send                (Pop3Session        *session,
75
                                 const gchar        *format, ...);
76

    
77
static void pop3_session_destroy        (Session        *session);
78

    
79
gint pop3_write_msg_to_file        (const gchar        *file,
80
                                 FILE                *src_fp,
81
                                 guint                 len);
82

    
83
static Pop3State pop3_lookup_next        (Pop3Session        *session);
84

    
85
Pop3ErrorValue pop3_ok                (Pop3Session        *session,
86
                                 const gchar        *msg);
87

    
88
static gint pop3_session_recv_msg                (Session        *session,
89
                                                 const gchar        *msg);
90
static gint pop3_session_recv_data_finished        (Session        *session,
91
                                                 guchar                *data,
92
                                                 guint                 len);
93
static gint pop3_session_recv_data_as_file_finished
94
                                                (Session        *session,
95
                                                 FILE                *fp,
96
                                                 guint                 len);
97

    
98

    
99
gint pop3_greeting_recv(Pop3Session *session, const gchar *msg)
100
{
101
        session->state = POP3_GREETING;
102

    
103
        session->greeting = g_strdup(msg);
104
        return PS_SUCCESS;
105
}
106

    
107
#if USE_SSL
108
gint pop3_stls_send(Pop3Session *session)
109
{
110
        session->state = POP3_STLS;
111
        pop3_gen_send(session, "STLS");
112
        return PS_SUCCESS;
113
}
114

    
115
gint pop3_stls_recv(Pop3Session *session)
116
{
117
        if (session_start_tls(SESSION(session)) < 0) {
118
                session->error_val = PS_SOCKET;
119
                return PS_SOCKET;
120
        }
121
        return PS_SUCCESS;
122
}
123
#endif /* USE_SSL */
124

    
125
gint pop3_getauth_user_send(Pop3Session *session)
126
{
127
        g_return_val_if_fail(session->user != NULL, -1);
128

    
129
        session->state = POP3_GETAUTH_USER;
130
        pop3_gen_send(session, "USER %s", session->user);
131
        return PS_SUCCESS;
132
}
133

    
134
gint pop3_getauth_pass_send(Pop3Session *session)
135
{
136
        g_return_val_if_fail(session->pass != NULL, -1);
137

    
138
        session->state = POP3_GETAUTH_PASS;
139
        pop3_gen_send(session, "PASS %s", session->pass);
140
        return PS_SUCCESS;
141
}
142

    
143
gint pop3_getauth_apop_send(Pop3Session *session)
144
{
145
        gchar *start, *end;
146
        gchar *apop_str;
147
        SMD5 *md5;
148
        gchar *md5sum;
149

    
150
        g_return_val_if_fail(session->user != NULL, -1);
151
        g_return_val_if_fail(session->pass != NULL, -1);
152

    
153
        session->state = POP3_GETAUTH_APOP;
154

    
155
        if ((start = strchr(session->greeting, '<')) == NULL) {
156
                log_warning(_("Required APOP timestamp not found "
157
                              "in greeting\n"));
158
                session->error_val = PS_PROTOCOL;
159
                return PS_PROTOCOL;
160
        }
161

    
162
        if ((end = strchr(start, '>')) == NULL || end == start + 1) {
163
                log_warning(_("Timestamp syntax error in greeting\n"));
164
                session->error_val = PS_PROTOCOL;
165
                return PS_PROTOCOL;
166
        }
167

    
168
        *(end + 1) = '\0';
169

    
170
        if (!is_ascii_str(start) || strchr(start, '@') == NULL) {
171
                log_warning(_("Invalid timestamp in greeting\n"));
172
                session->error_val = PS_PROTOCOL;
173
                return PS_PROTOCOL;
174
        }
175

    
176
        apop_str = g_strconcat(start, session->pass, NULL);
177
        md5 = s_gnet_md5_new((guchar *)apop_str, strlen(apop_str));
178
        md5sum = s_gnet_md5_get_string(md5);
179

    
180
        pop3_gen_send(session, "APOP %s %s", session->user, md5sum);
181

    
182
        g_free(md5sum);
183
        s_gnet_md5_delete(md5);
184
        g_free(apop_str);
185

    
186
        return PS_SUCCESS;
187
}
188

    
189
gint pop3_getrange_stat_send(Pop3Session *session)
190
{
191
        session->state = POP3_GETRANGE_STAT;
192
        pop3_gen_send(session, "STAT");
193
        return PS_SUCCESS;
194
}
195

    
196
gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg)
197
{
198
        if (sscanf(msg, "%d %lld", &session->count, &session->total_bytes) != 2) {
199
                log_warning(_("POP3 protocol error\n"));
200
                session->error_val = PS_PROTOCOL;
201
                return PS_PROTOCOL;
202
        } else {
203
                if (session->count == 0) {
204
                        session->uidl_is_valid = TRUE;
205
                } else {
206
                        session->msg = g_new0(Pop3MsgInfo, session->count + 1);
207
                        session->cur_msg = 1;
208
                }
209
        }
210

    
211
        return PS_SUCCESS;
212
}
213

    
214
gint pop3_getrange_last_send(Pop3Session *session)
215
{
216
        session->state = POP3_GETRANGE_LAST;
217
        pop3_gen_send(session, "LAST");
218
        return PS_SUCCESS;
219
}
220

    
221
gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg)
222
{
223
        gint last;
224

    
225
        if (sscanf(msg, "%d", &last) == 0) {
226
                log_warning(_("POP3 protocol error\n"));
227
                session->error_val = PS_PROTOCOL;
228
                return PS_PROTOCOL;
229
        } else {
230
                if (session->count > last) {
231
                        session->new_msg_exist = TRUE;
232
                        session->cur_msg = last + 1;
233
                } else
234
                        session->cur_msg = 0;
235
        }
236

    
237
        return PS_SUCCESS;
238
}
239

    
240
gint pop3_getrange_uidl_send(Pop3Session *session)
241
{
242
        session->state = POP3_GETRANGE_UIDL;
243
        pop3_gen_send(session, "UIDL");
244
        return PS_SUCCESS;
245
}
246

    
247
gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data, guint len)
248
{
249
        gchar id[IDLEN + 1];
250
        gchar buf[POPBUFSIZE];
251
        gint buf_len;
252
        gint num;
253
        time_t recv_time;
254
        const gchar *p = data;
255
        const gchar *lastp = data + len;
256
        const gchar *newline;
257

    
258
        while (p < lastp) {
259
                if ((newline = memchr(p, '\r', lastp - p)) == NULL)
260
                        return PS_PROTOCOL;
261
                buf_len = MIN(newline - p, sizeof(buf) - 1);
262
                memcpy(buf, p, buf_len);
263
                buf[buf_len] = '\0';
264

    
265
                p = newline + 1;
266
                if (p < lastp && *p == '\n') p++;
267

    
268
                if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2 ||
269
                    num <= 0 || num > session->count) {
270
                        log_warning(_("invalid UIDL response: %s\n"), buf);
271
                        continue;
272
                }
273

    
274
                session->msg[num].uidl = g_strdup(id);
275

    
276
                recv_time = (time_t)g_hash_table_lookup(session->uidl_table, id);
277
                session->msg[num].recv_time = recv_time;
278

    
279
                if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE)
280
                        session->msg[num].received = TRUE;
281

    
282
                if (!session->new_msg_exist &&
283
                    (session->ac_prefs->getall || recv_time == RECV_TIME_NONE ||
284
                     session->ac_prefs->rmmail)) {
285
                        session->cur_msg = num;
286
                        session->new_msg_exist = TRUE;
287
                }
288
        }
289

    
290
        session->uidl_is_valid = TRUE;
291
        return PS_SUCCESS;
292
}
293

    
294
gint pop3_getsize_list_send(Pop3Session *session)
295
{
296
        session->state = POP3_GETSIZE_LIST;
297
        pop3_gen_send(session, "LIST");
298
        return PS_SUCCESS;
299
}
300

    
301
gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data, guint len)
302
{
303
        gchar buf[POPBUFSIZE];
304
        gint buf_len;
305
        guint num, size;
306
        const gchar *p = data;
307
        const gchar *lastp = data + len;
308
        const gchar *newline;
309

    
310
        while (p < lastp) {
311
                if ((newline = memchr(p, '\r', lastp - p)) == NULL)
312
                        return PS_PROTOCOL;
313
                buf_len = MIN(newline - p, sizeof(buf) - 1);
314
                memcpy(buf, p, buf_len);
315
                buf[buf_len] = '\0';
316

    
317
                p = newline + 1;
318
                if (p < lastp && *p == '\n') p++;
319

    
320
                if (sscanf(buf, "%u %u", &num, &size) != 2) {
321
                        session->error_val = PS_PROTOCOL;
322
                        return PS_PROTOCOL;
323
                }
324

    
325
                if (num > 0 && num <= session->count)
326
                        session->msg[num].size = size;
327
                if (num > 0 && num < session->cur_msg)
328
                        session->cur_total_bytes += size;
329
        }
330

    
331
        return PS_SUCCESS;
332
}
333

    
334
gint pop3_retr_send(Pop3Session *session)
335
{
336
        session->state = POP3_RETR;
337
        pop3_gen_send(session, "RETR %d", session->cur_msg);
338
        return PS_SUCCESS;
339
}
340

    
341
gint pop3_retr_recv(Pop3Session *session, FILE *fp, guint len)
342
{
343
        gchar *file;
344
        gint drop_ok;
345

    
346
        file = get_tmp_file();
347
        if (pop3_write_msg_to_file(file, fp, len) < 0) {
348
                g_free(file);
349
                session->error_val = PS_IOERR;
350
                return PS_IOERR;
351
        }
352

    
353
        drop_ok = session->drop_message(session, file);
354
        g_unlink(file);
355
        g_free(file);
356
        if (drop_ok < 0) {
357
                session->error_val = PS_IOERR;
358
                return PS_IOERR;
359
        }
360

    
361
        session->cur_total_bytes += session->msg[session->cur_msg].size;
362
        session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
363
        session->cur_total_num++;
364

    
365
        session->msg[session->cur_msg].received = TRUE;
366
        session->msg[session->cur_msg].recv_time =
367
                drop_ok == DROP_DONT_RECEIVE ? RECV_TIME_KEEP
368
                        : drop_ok == DROP_DELETE ? RECV_TIME_DELETE
369
                        : session->current_time;
370

    
371
        return PS_SUCCESS;
372
}
373

    
374
gint pop3_delete_send(Pop3Session *session)
375
{
376
        session->state = POP3_DELETE;
377
        pop3_gen_send(session, "DELE %d", session->cur_msg);
378
        return PS_SUCCESS;
379
}
380

    
381
gint pop3_delete_recv(Pop3Session *session)
382
{
383
        session->msg[session->cur_msg].recv_time = RECV_TIME_DELETE;
384
        session->msg[session->cur_msg].deleted = TRUE;
385
        return PS_SUCCESS;
386
}
387

    
388
gint pop3_logout_send(Pop3Session *session)
389
{
390
        session->state = POP3_LOGOUT;
391
        pop3_gen_send(session, "QUIT");
392
        return PS_SUCCESS;
393
}
394

    
395
void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
396
{
397
        gchar buf[POPBUFSIZE + 1];
398
        va_list args;
399

    
400
        va_start(args, format);
401
        g_vsnprintf(buf, sizeof(buf) - 2, format, args);
402
        va_end(args);
403

    
404
        if (!g_ascii_strncasecmp(buf, "PASS ", 5))
405
                log_print("POP3> PASS ********\n");
406
        else
407
                log_print("POP3> %s\n", buf);
408

    
409
        session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
410
}
411

    
412
Session *pop3_session_new(PrefsAccount *account)
413
{
414
        Pop3Session *session;
415

    
416
        g_return_val_if_fail(account != NULL, NULL);
417

    
418
        session = g_new0(Pop3Session, 1);
419

    
420
        session_init(SESSION(session));
421

    
422
        SESSION(session)->type = SESSION_POP3;
423

    
424
        SESSION(session)->recv_msg = pop3_session_recv_msg;
425
        SESSION(session)->send_data_finished = NULL;
426
        SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
427
        SESSION(session)->recv_data_as_file_finished =
428
                pop3_session_recv_data_as_file_finished;
429

    
430
        SESSION(session)->destroy = pop3_session_destroy;
431

    
432
        session->state = POP3_READY;
433
        session->ac_prefs = account;
434
        session->uidl_table = pop3_get_uidl_table(account);
435
        session->current_time = time(NULL);
436
        session->error_val = PS_SUCCESS;
437
        session->error_msg = NULL;
438

    
439
        session->user = g_strdup(account->userid);
440
        session->pass = account->passwd ? g_strdup(account->passwd) :
441
                account->tmp_pass ? g_strdup(account->tmp_pass) : NULL;
442

    
443
        SESSION(session)->server = g_strdup(account->recv_server);
444

    
445
#if USE_SSL
446
        SESSION(session)->port = account->set_popport ?
447
                account->popport : account->ssl_pop == SSL_TUNNEL ? 995 : 110;
448
        SESSION(session)->ssl_type = account->ssl_pop;
449
        if (account->ssl_pop != SSL_NONE)
450
                SESSION(session)->nonblocking = account->use_nonblocking_ssl;
451
#else
452
        SESSION(session)->port = account->set_popport ? account->popport : 110;
453
#endif
454

    
455
        return SESSION(session);
456
}
457

    
458
static void pop3_session_destroy(Session *session)
459
{
460
        Pop3Session *pop3_session = POP3_SESSION(session);
461
        gint n;
462

    
463
        g_return_if_fail(session != NULL);
464

    
465
        for (n = 1; n <= pop3_session->count; n++)
466
                g_free(pop3_session->msg[n].uidl);
467
        g_free(pop3_session->msg);
468

    
469
        if (pop3_session->uidl_table) {
470
                hash_free_strings(pop3_session->uidl_table);
471
                g_hash_table_destroy(pop3_session->uidl_table);
472
        }
473

    
474
        g_free(pop3_session->greeting);
475
        g_free(pop3_session->user);
476
        g_free(pop3_session->pass);
477
        g_free(pop3_session->error_msg);
478
}
479

    
480
GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs)
481
{
482
        GHashTable *table;
483
        gchar *path;
484
        FILE *fp;
485
        gchar buf[POPBUFSIZE];
486
        gchar uidl[POPBUFSIZE];
487
        time_t recv_time;
488
        time_t now;
489
        gchar *uid;
490

    
491
        table = g_hash_table_new(g_str_hash, g_str_equal);
492

    
493
        uid = uriencode_for_filename(ac_prefs->userid);
494
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
495
                           UIDL_DIR, G_DIR_SEPARATOR_S, ac_prefs->recv_server,
496
                           "-", uid, NULL);
497
        g_free(uid);
498
        if ((fp = g_fopen(path, "rb")) == NULL) {
499
                if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
500
                g_free(path);
501
                return table;
502
        }
503
        g_free(path);
504

    
505
        now = time(NULL);
506

    
507
        while (fgets(buf, sizeof(buf), fp) != NULL) {
508
                strretchomp(buf);
509
                recv_time = RECV_TIME_NONE;
510
                if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) {
511
                        if (sscanf(buf, "%s", uidl) != 1)
512
                                continue;
513
                        else
514
                                recv_time = now;
515
                }
516
                if (recv_time == RECV_TIME_NONE)
517
                        recv_time = RECV_TIME_RECEIVED;
518
                g_hash_table_insert(table, g_strdup(uidl),
519
                                    GINT_TO_POINTER(recv_time));
520
        }
521

    
522
        fclose(fp);
523
        return table;
524
}
525

    
526
gint pop3_write_uidl_list(Pop3Session *session)
527
{
528
        gchar *path;
529
        PrefFile *pfile;
530
        Pop3MsgInfo *msg;
531
        gint n;
532
        gchar *uid;
533

    
534
        if (!session->uidl_is_valid) return 0;
535

    
536
        uid = uriencode_for_filename(session->ac_prefs->userid);
537
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
538
                           UIDL_DIR, G_DIR_SEPARATOR_S,
539
                           session->ac_prefs->recv_server,
540
                           "-", uid, NULL);
541
        g_free(uid);
542
        if ((pfile = prefs_file_open(path)) == NULL) {
543
                g_free(path);
544
                return -1;
545
        }
546
        prefs_file_set_backup_generation(pfile, 0);
547

    
548
        for (n = 1; n <= session->count; n++) {
549
                msg = &session->msg[n];
550
                if (!msg->uidl || !msg->received)
551
                        continue;
552
                if (session->state == POP3_DONE && msg->deleted)
553
                        continue;
554
                fprintf(pfile->fp, "%s\t%ld\n", msg->uidl, msg->recv_time);
555
        }
556

    
557
        if (prefs_file_close(pfile) < 0)
558
                g_warning("%s: failed to write UIDL list.\n", path);
559

    
560
        g_free(path);
561

    
562
        return 0;
563
}
564

    
565
gint pop3_write_msg_to_file(const gchar *file, FILE *src_fp, guint len)
566
{
567
        FILE *fp;
568
        gchar buf[BUFFSIZE];
569
        gchar last_ch = '\0';
570

    
571
        g_return_val_if_fail(file != NULL, -1);
572

    
573
        if ((fp = g_fopen(file, "wb")) == NULL) {
574
                FILE_OP_ERROR(file, "fopen");
575
                return -1;
576
        }
577

    
578
        if (change_file_mode_rw(fp, file) < 0)
579
                FILE_OP_ERROR(file, "chmod");
580

    
581
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
582
                gchar *p = buf;
583
                gint len;
584

    
585
                len = strlen(buf);
586
                if (len > 0) {
587
                        last_ch = buf[len - 1];
588
                        if (last_ch == '\n' && len > 1 &&
589
                            buf[len - 2] == '\r') {
590
                                buf[len - 2] = '\n';
591
                                buf[len - 1] = '\0';
592
                        } else if (last_ch == '\r')
593
                                buf[len - 1] = '\0';
594
                } else
595
                        last_ch = '\0';
596

    
597
                if ((last_ch == '\0' || last_ch == '\n') &&
598
                    *p == '.' && *(p + 1) == '.')
599
                        p++;
600

    
601
                if (fputs(p, fp) == EOF) {
602
                        FILE_OP_ERROR(file, "fputs");
603
                        g_warning("can't write to file: %s\n", file);
604
                        fclose(fp);
605
                        g_unlink(file);
606
                        return -1;
607
                }
608
        }
609

    
610
        if (ferror(src_fp)) {
611
                FILE_OP_ERROR(file, "fgets");
612
                fclose(fp);
613
                g_unlink(file);
614
                return -1;
615
        }
616

    
617
        if (fclose(fp) == EOF) {
618
                FILE_OP_ERROR(file, "fclose");
619
                g_unlink(file);
620
                return -1;
621
        }
622

    
623
        return 0;
624
}
625

    
626
static Pop3State pop3_lookup_next(Pop3Session *session)
627
{
628
        Pop3MsgInfo *msg;
629
        PrefsAccount *ac = session->ac_prefs;
630
        gint size;
631
        gboolean size_limit_over;
632

    
633
        for (;;) {
634
                msg = &session->msg[session->cur_msg];
635
                size = msg->size;
636
                size_limit_over =
637
                    (ac->enable_size_limit &&
638
                     ac->size_limit > 0 &&
639
                     size > ac->size_limit * 1024);
640

    
641
                if (msg->recv_time == RECV_TIME_DELETE ||
642
                    (ac->rmmail &&
643
                     msg->recv_time != RECV_TIME_NONE &&
644
                     msg->recv_time != RECV_TIME_KEEP &&
645
                     session->current_time - msg->recv_time >=
646
                     ac->msg_leave_time * 24 * 60 * 60)) {
647
                        log_print(_("POP3: Deleting expired message %d\n"),
648
                                  session->cur_msg);
649
                        session->cur_total_bytes += size;
650
                        pop3_delete_send(session);
651
                        return POP3_DELETE;
652
                }
653

    
654
                if (size_limit_over && !msg->received) {
655
                        log_print
656
                                (_("POP3: Skipping message %d (%d bytes)\n"),
657
                                  session->cur_msg, size);
658
                        session->skipped_num++;
659
                }
660

    
661
                if (size == 0 || msg->received || size_limit_over) {
662
                        session->cur_total_bytes += size;
663
                        if (session->cur_msg == session->count) {
664
                                pop3_logout_send(session);
665
                                return POP3_LOGOUT;
666
                        } else
667
                                session->cur_msg++;
668
                } else
669
                        break;
670
        }
671

    
672
        pop3_retr_send(session);
673

    
674
        return POP3_RETR;
675
}
676

    
677
Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg)
678
{
679
        Pop3ErrorValue ok;
680

    
681
        log_print("POP3< %s\n", msg);
682

    
683
        if (!strncmp(msg, "+OK", 3))
684
                ok = PS_SUCCESS;
685
        else if (!strncmp(msg, "-ERR", 4)) {
686
                if (strstr(msg + 4, "lock") ||
687
                    strstr(msg + 4, "Lock") ||
688
                    strstr(msg + 4, "LOCK") ||
689
                    strstr(msg + 4, "wait")) {
690
                        log_warning(_("mailbox is locked\n"));
691
                        ok = PS_LOCKBUSY;
692
                } else if (strcasestr(msg + 4, "timeout")) {
693
                        log_warning(_("session timeout\n"));
694
                        ok = PS_ERROR;
695
                } else {
696
                        switch (session->state) {
697
#if USE_SSL
698
                        case POP3_STLS:
699
                                log_warning(_("can't start TLS session\n"));
700
                                ok = PS_ERROR;
701
                                break;
702
#endif
703
                        case POP3_GETAUTH_USER:
704
                        case POP3_GETAUTH_PASS:
705
                        case POP3_GETAUTH_APOP:
706
                                log_warning(_("error occurred on authentication\n"));
707
                                ok = PS_AUTHFAIL;
708
                                break;
709
                        case POP3_GETRANGE_LAST:
710
                        case POP3_GETRANGE_UIDL:
711
                                log_warning(_("command not supported\n"));
712
                                ok = PS_NOTSUPPORTED;
713
                                break;
714
                        default:
715
                                log_warning(_("error occurred on POP3 session\n"));
716
                                ok = PS_ERROR;
717
                        }
718
                }
719

    
720
                g_free(session->error_msg);
721
                session->error_msg = g_strdup(msg);
722
                fprintf(stderr, "POP3: %s\n", msg);
723
        } else
724
                ok = PS_PROTOCOL;
725

    
726
        /* don't overwrite previous error on logout */
727
        if (session->state != POP3_LOGOUT)
728
                session->error_val = ok;
729

    
730
        return ok;
731
}
732

    
733
static gint pop3_session_recv_msg(Session *session, const gchar *msg)
734
{
735
        Pop3Session *pop3_session = POP3_SESSION(session);
736
        gint val = PS_SUCCESS;
737
        const gchar *body;
738

    
739
        body = msg;
740
        if (pop3_session->state != POP3_GETRANGE_UIDL_RECV &&
741
            pop3_session->state != POP3_GETSIZE_LIST_RECV) {
742
                val = pop3_ok(pop3_session, msg);
743
                if (val != PS_SUCCESS) {
744
                        if (val == PS_SOCKET) {
745
                                pop3_session->state = POP3_ERROR;
746
                                return -1;
747
                        }
748
                        if (val != PS_NOTSUPPORTED) {
749
                                if (pop3_session->state != POP3_LOGOUT) {
750
                                        if (pop3_logout_send(pop3_session) == PS_SUCCESS)
751
                                                return 0;
752
                                        else
753
                                                return -1;
754
                                }
755
                        }
756
                }
757

    
758
                if (*body == '+' || *body == '-')
759
                        body++;
760
                while (g_ascii_isalpha(*body))
761
                        body++;
762
                while (g_ascii_isspace(*body))
763
                        body++;
764
        }
765

    
766
        switch (pop3_session->state) {
767
        case POP3_READY:
768
        case POP3_GREETING:
769
                val = pop3_greeting_recv(pop3_session, body);
770
#if USE_SSL
771
                if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS)
772
                        val = pop3_stls_send(pop3_session);
773
                else
774
#endif
775
                if (pop3_session->ac_prefs->use_apop_auth)
776
                        val = pop3_getauth_apop_send(pop3_session);
777
                else
778
                        val = pop3_getauth_user_send(pop3_session);
779
                break;
780
#if USE_SSL
781
        case POP3_STLS:
782
                if ((val = pop3_stls_recv(pop3_session)) != PS_SUCCESS)
783
                        return -1;
784
                if (pop3_session->ac_prefs->use_apop_auth)
785
                        val = pop3_getauth_apop_send(pop3_session);
786
                else
787
                        val = pop3_getauth_user_send(pop3_session);
788
                break;
789
#endif
790
        case POP3_GETAUTH_USER:
791
                val = pop3_getauth_pass_send(pop3_session);
792
                break;
793
        case POP3_GETAUTH_PASS:
794
        case POP3_GETAUTH_APOP:
795
                if (pop3_session->auth_only)
796
                        val = pop3_logout_send(pop3_session);
797
                else
798
                        val = pop3_getrange_stat_send(pop3_session);
799
                break;
800
        case POP3_GETRANGE_STAT:
801
                if ((val = pop3_getrange_stat_recv(pop3_session, body)) < 0)
802
                        return -1;
803
                if (pop3_session->count > 0)
804
                        val = pop3_getrange_uidl_send(pop3_session);
805
                else
806
                        val = pop3_logout_send(pop3_session);
807
                break;
808
        case POP3_GETRANGE_LAST:
809
                if (val == PS_NOTSUPPORTED)
810
                        pop3_session->error_val = PS_SUCCESS;
811
                else if ((val = pop3_getrange_last_recv
812
                                (pop3_session, body)) < 0)
813
                        return -1;
814
                if (pop3_session->cur_msg > 0)
815
                        val = pop3_getsize_list_send(pop3_session);
816
                else
817
                        val = pop3_logout_send(pop3_session);
818
                break;
819
        case POP3_GETRANGE_UIDL:
820
                if (val == PS_NOTSUPPORTED) {
821
                        pop3_session->error_val = PS_SUCCESS;
822
                        val = pop3_getrange_last_send(pop3_session);
823
                } else {
824
                        pop3_session->state = POP3_GETRANGE_UIDL_RECV;
825
                        val = session_recv_data(session, 0, ".\r\n");
826
                }
827
                break;
828
        case POP3_GETSIZE_LIST:
829
                pop3_session->state = POP3_GETSIZE_LIST_RECV;
830
                val = session_recv_data(session, 0, ".\r\n");
831
                break;
832
        case POP3_RETR:
833
                pop3_session->state = POP3_RETR_RECV;
834
                val = session_recv_data_as_file(session, 0, ".\r\n");
835
                break;
836
        case POP3_DELETE:
837
                val = pop3_delete_recv(pop3_session);
838
                if (pop3_session->cur_msg == pop3_session->count)
839
                        val = pop3_logout_send(pop3_session);
840
                else {
841
                        pop3_session->cur_msg++;
842
                        if (pop3_lookup_next(pop3_session) == POP3_ERROR)
843
                                return -1;
844
                }
845
                break;
846
        case POP3_LOGOUT:
847
                if (val == PS_SUCCESS)
848
                        pop3_session->state = POP3_DONE;
849
                else
850
                        pop3_session->state = POP3_ERROR;
851
                session_disconnect(session);
852
                break;
853
        case POP3_ERROR:
854
        default:
855
                return -1;
856
        }
857

    
858
        if (val == PS_SUCCESS)
859
                return 0;
860
        else
861
                return -1;
862
}
863

    
864
static gint pop3_session_recv_data_finished(Session *session, guchar *data,
865
                                            guint len)
866
{
867
        Pop3Session *pop3_session = POP3_SESSION(session);
868
        Pop3ErrorValue val = PS_SUCCESS;
869

    
870
        switch (pop3_session->state) {
871
        case POP3_GETRANGE_UIDL_RECV:
872
                val = pop3_getrange_uidl_recv(pop3_session, (gchar *)data, len);
873
                if (val == PS_SUCCESS) {
874
                        if (pop3_session->new_msg_exist)
875
                                pop3_getsize_list_send(pop3_session);
876
                        else
877
                                pop3_logout_send(pop3_session);
878
                } else
879
                        return -1;
880
                break;
881
        case POP3_GETSIZE_LIST_RECV:
882
                val = pop3_getsize_list_recv(pop3_session, (gchar *)data, len);
883
                if (val == PS_SUCCESS) {
884
                        if (pop3_lookup_next(pop3_session) == POP3_ERROR)
885
                                return -1;
886
                } else
887
                        return -1;
888
                break;
889
        case POP3_ERROR:
890
        default:
891
                return -1;
892
        }
893

    
894
        return 0;
895
}
896

    
897
static gint pop3_session_recv_data_as_file_finished(Session *session, FILE *fp,
898
                                                    guint len)
899
{
900
        Pop3Session *pop3_session = POP3_SESSION(session);
901

    
902
        g_return_val_if_fail(pop3_session->state == POP3_RETR_RECV, -1);
903

    
904
        if (pop3_retr_recv(pop3_session, fp, len) < 0)
905
                return -1;
906

    
907
        /* disconnected? */
908
        if (!session->sock)
909
                return -1;
910

    
911
        if (pop3_session->msg[pop3_session->cur_msg].recv_time
912
            == RECV_TIME_DELETE ||
913
            (pop3_session->ac_prefs->rmmail &&
914
             pop3_session->ac_prefs->msg_leave_time == 0 &&
915
             pop3_session->msg[pop3_session->cur_msg].recv_time
916
             != RECV_TIME_KEEP))
917
                pop3_delete_send(pop3_session);
918
        else if (pop3_session->cur_msg == pop3_session->count)
919
                pop3_logout_send(pop3_session);
920
        else {
921
                pop3_session->cur_msg++;
922
                if (pop3_lookup_next(pop3_session) == POP3_ERROR)
923
                        return -1;
924
        }
925

    
926
        return 0;
927
}