Statistics
| Revision:

root / libsylph / imap.c @ 2026

History | View | Annotate | Download (108.7 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 <stdlib.h>
31
#include <dirent.h>
32
#include <unistd.h>
33
#include <ctype.h>
34
#include <time.h>
35
#if HAVE_ICONV
36
#  include <iconv.h>
37
#endif
38
39
#include "imap.h"
40
#include "socket.h"
41
#include "ssl.h"
42
#include "recv.h"
43
#include "procmsg.h"
44
#include "procheader.h"
45
#include "folder.h"
46
#include "prefs_account.h"
47
#include "codeconv.h"
48
#include "md5_hmac.h"
49
#include "base64.h"
50
#include "utils.h"
51
#include "prefs_common.h"
52
#include "virtual.h"
53
54
#define IMAP4_PORT        143
55
#if USE_SSL
56
#define IMAPS_PORT        993
57
#endif
58
59
#define IMAP_COPY_LIMIT        200
60
#define IMAP_CMD_LIMIT        1000
61
62
#define QUOTE_IF_REQUIRED(out, str)                                        \
63
{                                                                        \
64
        if (*str != '"' && strpbrk(str, " \t(){}[]%&*") != NULL) {        \
65
                gchar *__tmp;                                                \
66
                gint len;                                                \
67
                                                                        \
68
                len = strlen(str) + 3;                                        \
69
                Xalloca(__tmp, len, return IMAP_ERROR);                        \
70
                g_snprintf(__tmp, len, "\"%s\"", str);                        \
71
                out = __tmp;                                                \
72
        } else {                                                        \
73
                Xstrdup_a(out, str, return IMAP_ERROR);                        \
74
        }                                                                \
75
}
76
77
static GList *session_list = NULL;
78
79
static void imap_folder_init                (Folder                *folder,
80
                                         const gchar        *name,
81
                                         const gchar        *path);
82
83
static Folder        *imap_folder_new        (const gchar        *name,
84
                                         const gchar        *path);
85
static void         imap_folder_destroy        (Folder                *folder);
86
87
static Session *imap_session_new        (PrefsAccount        *account);
88
static gint imap_session_connect        (IMAPSession        *session);
89
static gint imap_session_reconnect        (IMAPSession        *session);
90
static void imap_session_destroy        (Session        *session);
91
/* static void imap_session_destroy_all        (void); */
92
93
static gint imap_search_flags                (IMAPSession        *session,
94
                                         GArray               **uids,
95
                                         GHashTable    **flags_table);
96
static gint imap_fetch_flags                (IMAPSession        *session,
97
                                         GArray               **uids,
98
                                         GHashTable    **flags_table);
99
100
static GSList *imap_get_msg_list        (Folder                *folder,
101
                                         FolderItem        *item,
102
                                         gboolean         use_cache);
103
static GSList *imap_get_uncached_msg_list
104
                                        (Folder                *folder,
105
                                         FolderItem        *item);
106
static gchar *imap_fetch_msg                (Folder                *folder,
107
                                         FolderItem        *item,
108
                                         gint                 uid);
109
static MsgInfo *imap_get_msginfo        (Folder                *folder,
110
                                         FolderItem        *item,
111
                                         gint                 uid);
112
static gint imap_add_msg                (Folder                *folder,
113
                                         FolderItem        *dest,
114
                                         const gchar        *file,
115
                                         MsgFlags        *flags,
116
                                         gboolean         remove_source);
117
static gint imap_add_msgs                (Folder                *folder,
118
                                         FolderItem        *dest,
119
                                         GSList                *file_list,
120
                                         gboolean         remove_source,
121
                                         gint                *first);
122
123
static gint imap_move_msg                (Folder                *folder,
124
                                         FolderItem        *dest,
125
                                         MsgInfo        *msginfo);
126
static gint imap_move_msgs                (Folder                *folder,
127
                                         FolderItem        *dest,
128
                                         GSList                *msglist);
129
static gint imap_copy_msg                (Folder                *folder,
130
                                         FolderItem        *dest,
131
                                         MsgInfo        *msginfo);
132
static gint imap_copy_msgs                (Folder                *folder,
133
                                         FolderItem        *dest,
134
                                         GSList                *msglist);
135
136
static gint imap_remove_msg                (Folder                *folder,
137
                                         FolderItem        *item,
138
                                         MsgInfo        *msginfo);
139
static gint imap_remove_msgs                (Folder                *folder,
140
                                         FolderItem        *item,
141
                                         GSList                *msglist);
142
static gint imap_remove_all_msg                (Folder                *folder,
143
                                         FolderItem        *item);
144
145
static gboolean imap_is_msg_changed        (Folder                *folder,
146
                                         FolderItem        *item,
147
                                         MsgInfo        *msginfo);
148
149
static gint imap_close                        (Folder                *folder,
150
                                         FolderItem        *item);
151
152
static gint imap_scan_folder                (Folder                *folder,
153
                                         FolderItem        *item);
154
static gint imap_scan_tree                (Folder                *folder);
155
156
static gint imap_create_tree                (Folder                *folder);
157
158
static FolderItem *imap_create_folder        (Folder                *folder,
159
                                         FolderItem        *parent,
160
                                         const gchar        *name);
161
static gint imap_rename_folder                (Folder                *folder,
162
                                         FolderItem        *item,
163
                                         const gchar        *name);
164
static gint imap_move_folder                (Folder                *folder,
165
                                         FolderItem        *item,
166
                                         FolderItem        *new_parent);
167
static gint imap_remove_folder                (Folder                *folder,
168
                                         FolderItem        *item);
169
170
static IMAPSession *imap_session_get        (Folder                *folder);
171
172
static gint imap_greeting                (IMAPSession        *session);
173
static gint imap_auth                        (IMAPSession        *session,
174
                                         const gchar        *user,
175
                                         const gchar        *pass,
176
                                         IMAPAuthType         type);
177
178
static gint imap_scan_tree_recursive        (IMAPSession        *session,
179
                                         FolderItem        *item,
180
                                         GSList                *item_list);
181
static GSList *imap_get_folder_list        (IMAPSession        *session,
182
                                         FolderItem        *item);
183
static GSList *imap_parse_list                (IMAPSession        *session,
184
                                         const gchar        *real_path,
185
                                         gchar                *separator);
186
static GSList *imap_add_inter_folders        (GSList                *item_list,
187
                                         const gchar        *root_path);
188
static GSList *imap_get_part_folder_list(GSList                *item_list,
189
                                         FolderItem        *item);
190
191
static void imap_create_missing_folders        (Folder                        *folder);
192
static FolderItem *imap_create_special_folder
193
                                        (Folder                        *folder,
194
                                         SpecialFolderItemType         stype,
195
                                         const gchar                *name);
196
197
static gint imap_do_copy_msgs                (Folder                *folder,
198
                                         FolderItem        *dest, 
199
                                         GSList                *msglist,
200
                                         gboolean         remove_source);
201
static gint imap_remove_msgs_by_seq_set        (Folder                *folder,
202
                                         FolderItem        *item,
203
                                         GSList                *seq_list);
204
205
static GSList *imap_get_uncached_messages        (IMAPSession        *session,
206
                                                 FolderItem        *item,
207
                                                 guint32         first_uid,
208
                                                 guint32         last_uid,
209
                                                 gint                 exists,
210
                                                 gboolean         update_count);
211
static void imap_delete_cached_message                (FolderItem        *item,
212
                                                 guint32         uid);
213
static GSList *imap_delete_cached_messages        (GSList                *mlist,
214
                                                 FolderItem        *item,
215
                                                 guint32         first_uid,
216
                                                 guint32         last_uid);
217
static void imap_delete_all_cached_messages        (FolderItem        *item);
218
219
#if USE_SSL
220
static SockInfo *imap_open                (const gchar        *server,
221
                                         gushort         port,
222
                                         SSLType         ssl_type);
223
#else
224
static SockInfo *imap_open                (const gchar        *server,
225
                                         gushort         port);
226
#endif
227
228
static gint imap_msg_list_change_perm_flags        (GSList                *msglist,
229
                                                 MsgPermFlags         flags,
230
                                                 gboolean         is_set);
231
static gchar *imap_get_flag_str                        (IMAPFlags         flags);
232
static gint imap_set_message_flags                (IMAPSession        *session,
233
                                                 const gchar        *seq_set,
234
                                                 IMAPFlags         flags,
235
                                                 gboolean         is_set);
236
static gint imap_select                                (IMAPSession        *session,
237
                                                 IMAPFolder        *folder,
238
                                                 const gchar        *path,
239
                                                 gint                *exists,
240
                                                 gint                *recent,
241
                                                 gint                *unseen,
242
                                                 guint32        *uid_validity);
243
static gint imap_status                                (IMAPSession        *session,
244
                                                 IMAPFolder        *folder,
245
                                                 const gchar        *path,
246
                                                 gint                *messages,
247
                                                 gint                *recent,
248
                                                 guint32        *uid_next,
249
                                                 guint32        *uid_validity,
250
                                                 gint                *unseen);
251
252
static void imap_parse_namespace                (IMAPSession        *session,
253
                                                 IMAPFolder        *folder);
254
static void imap_get_namespace_by_list                (IMAPSession        *session,
255
                                                 IMAPFolder        *folder);
256
static IMAPNameSpace *imap_find_namespace        (IMAPFolder        *folder,
257
                                                 const gchar        *path);
258
static gchar imap_get_path_separator                (IMAPFolder        *folder,
259
                                                 const gchar        *path);
260
static gchar *imap_get_real_path                (IMAPFolder        *folder,
261
                                                 const gchar        *path);
262
263
static gchar *imap_parse_atom                (IMAPSession        *session,
264
                                         gchar                *src,
265
                                         gchar                *dest,
266
                                         gint                 dest_len,
267
                                         GString        *str);
268
static MsgFlags imap_parse_flags        (const gchar        *flag_str);
269
static IMAPFlags imap_parse_imap_flags        (const gchar        *flag_str);
270
static MsgInfo *imap_parse_envelope        (IMAPSession        *session,
271
                                         FolderItem        *item,
272
                                         GString        *line_str);
273
274
static gboolean imap_has_capability        (IMAPSession        *session,
275
                                         const gchar        *capability);
276
static void imap_capability_free        (IMAPSession        *session);
277
278
/* low-level IMAP4rev1 commands */
279
static gint imap_cmd_capability        (IMAPSession        *session);
280
static gint imap_cmd_authenticate
281
                                (IMAPSession        *session,
282
                                 const gchar        *user,
283
                                 const gchar        *pass,
284
                                 IMAPAuthType         type);
285
static gint imap_cmd_login        (IMAPSession        *session,
286
                                 const gchar        *user,
287
                                 const gchar        *pass);
288
static gint imap_cmd_logout        (IMAPSession        *session);
289
static gint imap_cmd_noop        (IMAPSession        *session);
290
#if USE_SSL
291
static gint imap_cmd_starttls        (IMAPSession        *session);
292
#endif
293
static gint imap_cmd_namespace        (IMAPSession        *session,
294
                                 gchar               **ns_str);
295
static gint imap_cmd_list        (IMAPSession        *session,
296
                                 const gchar        *ref,
297
                                 const gchar        *mailbox,
298
                                 GPtrArray        *argbuf);
299
static gint imap_cmd_do_select        (IMAPSession        *session,
300
                                 const gchar        *folder,
301
                                 gboolean         examine,
302
                                 gint                *exists,
303
                                 gint                *recent,
304
                                 gint                *unseen,
305
                                 guint32        *uid_validity);
306
static gint imap_cmd_select        (IMAPSession        *session,
307
                                 const gchar        *folder,
308
                                 gint                *exists,
309
                                 gint                *recent,
310
                                 gint                *unseen,
311
                                 guint32        *uid_validity);
312
static gint imap_cmd_examine        (IMAPSession        *session,
313
                                 const gchar        *folder,
314
                                 gint                *exists,
315
                                 gint                *recent,
316
                                 gint                *unseen,
317
                                 guint32        *uid_validity);
318
static gint imap_cmd_create        (IMAPSession        *session,
319
                                 const gchar        *folder);
320
static gint imap_cmd_rename        (IMAPSession        *session,
321
                                 const gchar        *oldfolder,
322
                                 const gchar        *newfolder);
323
static gint imap_cmd_delete        (IMAPSession        *session,
324
                                 const gchar        *folder);
325
static gint imap_cmd_envelope        (IMAPSession        *session,
326
                                 const gchar        *seq_set);
327
static gint imap_cmd_search        (IMAPSession        *session,
328
                                 const gchar        *criteria,
329
                                 GArray        **result);
330
static gint imap_cmd_fetch        (IMAPSession        *session,
331
                                 guint32         uid,
332
                                 const gchar        *filename);
333
static gint imap_cmd_append        (IMAPSession        *session,
334
                                 const gchar        *destfolder,
335
                                 const gchar        *file,
336
                                 IMAPFlags         flags,
337
                                 guint32        *new_uid);
338
static gint imap_cmd_copy        (IMAPSession        *session,
339
                                 const gchar        *seq_set,
340
                                 const gchar        *destfolder);
341
static gint imap_cmd_store        (IMAPSession        *session,
342
                                 const gchar        *seq_set,
343
                                 const gchar        *sub_cmd);
344
static gint imap_cmd_expunge        (IMAPSession        *session);
345
static gint imap_cmd_close        (IMAPSession        *session);
346
347
static gint imap_cmd_ok                (IMAPSession        *session,
348
                                 GPtrArray        *argbuf);
349
static void imap_cmd_gen_send        (IMAPSession        *session,
350
                                 const gchar        *format, ...);
351
static gint imap_cmd_gen_recv        (IMAPSession        *session,
352
                                 gchar               **ret);
353
354
/* misc utility functions */
355
static gchar *strchr_cpy                        (const gchar        *src,
356
                                                 gchar                 ch,
357
                                                 gchar                *dest,
358
                                                 gint                 len);
359
static gchar *get_quoted                        (const gchar        *src,
360
                                                 gchar                 ch,
361
                                                 gchar                *dest,
362
                                                 gint                 len);
363
static gchar *search_array_contain_str                (GPtrArray        *array,
364
                                                 gchar                *str);
365
static gchar *search_array_str                        (GPtrArray        *array,
366
                                                 gchar                *str);
367
static void imap_path_separator_subst                (gchar                *str,
368
                                                 gchar                 separator);
369
370
static gchar *imap_modified_utf7_to_utf8        (const gchar        *mutf7_str);
371
static gchar *imap_utf8_to_modified_utf7        (const gchar        *from);
372
373
static GSList *imap_get_seq_set_from_msglist        (GSList                *msglist,
374
                                                 gint                 limit);
375
static gint imap_seq_set_get_count                (const gchar        *seq_set);
376
static void imap_seq_set_free                        (GSList                *seq_list);
377
378
static GHashTable *imap_get_uid_table                (GArray                *array);
379
380
static gboolean imap_rename_folder_func                (GNode                *node,
381
                                                 gpointer         data);
382
383
static FolderClass imap_class =
384
{
385
        F_IMAP,
386
387
        imap_folder_new,
388
        imap_folder_destroy,
389
390
        imap_scan_tree,
391
        imap_create_tree,
392
393
        imap_get_msg_list,
394
        imap_get_uncached_msg_list,
395
        imap_fetch_msg,
396
        imap_get_msginfo,
397
        imap_add_msg,
398
        imap_add_msgs,
399
        imap_move_msg,
400
        imap_move_msgs,
401
        imap_copy_msg,
402
        imap_copy_msgs,
403
        imap_remove_msg,
404
        imap_remove_msgs,
405
        imap_remove_all_msg,
406
        imap_is_msg_changed,
407
        imap_close,
408
        imap_scan_folder,
409
410
        imap_create_folder,
411
        imap_rename_folder,
412
        imap_move_folder,
413
        imap_remove_folder
414
};
415
416
417
FolderClass *imap_get_class(void)
418
{
419
        return &imap_class;
420
}
421
422
static Folder *imap_folder_new(const gchar *name, const gchar *path)
423
{
424
        Folder *folder;
425
426
        folder = (Folder *)g_new0(IMAPFolder, 1);
427
        imap_folder_init(folder, name, path);
428
429
        return folder;
430
}
431
432
static void imap_folder_destroy(Folder *folder)
433
{
434
        gchar *dir;
435
436
        dir = folder_get_path(folder);
437
        if (is_dir_exist(dir))
438
                remove_dir_recursive(dir);
439
        g_free(dir);
440
441
        folder_remote_folder_destroy(REMOTE_FOLDER(folder));
442
}
443
444
static void imap_folder_init(Folder *folder, const gchar *name,
445
                             const gchar *path)
446
{
447
        folder->klass = imap_get_class();
448
        folder_remote_folder_init(folder, name, path);
449
}
450
451
static IMAPSession *imap_session_get(Folder *folder)
452
{
453
        RemoteFolder *rfolder = REMOTE_FOLDER(folder);
454
455
        g_return_val_if_fail(folder != NULL, NULL);
456
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
457
        g_return_val_if_fail(folder->account != NULL, NULL);
458
459
        if (!prefs_common.online_mode)
460
                return NULL;
461
462
        if (!rfolder->session) {
463
                rfolder->session = imap_session_new(folder->account);
464
                if (rfolder->session)
465
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
466
                                             IMAP_FOLDER(folder));
467
                return IMAP_SESSION(rfolder->session);
468
        }
469
470
        if (time(NULL) - rfolder->session->last_access_time <
471
                SESSION_TIMEOUT_INTERVAL) {
472
                return IMAP_SESSION(rfolder->session);
473
        }
474
475
        if (imap_cmd_noop(IMAP_SESSION(rfolder->session)) != IMAP_SUCCESS) {
476
                log_warning(_("IMAP4 connection to %s has been"
477
                              " disconnected. Reconnecting...\n"),
478
                            folder->account->recv_server);
479
                if (imap_session_reconnect(IMAP_SESSION(rfolder->session))
480
                    == IMAP_SUCCESS)
481
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
482
                                             IMAP_FOLDER(folder));
483
                else {
484
                        session_destroy(rfolder->session);
485
                        rfolder->session = NULL;
486
                }
487
        }
488
489
        return IMAP_SESSION(rfolder->session);
490
}
491
492
static gint imap_greeting(IMAPSession *session)
493
{
494
        gchar *greeting;
495
        gint ok;
496
497
        if ((ok = imap_cmd_gen_recv(session, &greeting)) != IMAP_SUCCESS)
498
                return ok;
499
500
        if (greeting[0] != '*' || greeting[1] != ' ')
501
                ok = IMAP_ERROR;
502
        else if (!strncmp(greeting + 2, "OK", 2))
503
                ok = IMAP_SUCCESS;
504
        else if (!strncmp(greeting + 2, "PREAUTH", 7)) {
505
                session->authenticated = TRUE;
506
                ok = IMAP_SUCCESS;
507
        } else
508
                ok = IMAP_ERROR;
509
510
        g_free(greeting);
511
        return ok;
512
}
513
514
static gint imap_auth(IMAPSession *session, const gchar *user,
515
                      const gchar *pass, IMAPAuthType type)
516
{
517
        gboolean nologin;
518
        gint ok = IMAP_AUTHFAIL;
519
520
        nologin = imap_has_capability(session, "LOGINDISABLED");
521
522
        switch (type) {
523
        case 0:
524
                if (imap_has_capability(session, "AUTH=CRAM-MD5"))
525
                        ok = imap_cmd_authenticate(session, user, pass,
526
                                                   IMAP_AUTH_CRAM_MD5);
527
                else if (imap_has_capability(session, "AUTH=PLAIN"))
528
                        ok = imap_cmd_authenticate(session, user, pass,
529
                                                   IMAP_AUTH_PLAIN);
530
                else if (nologin)
531
                        log_print(_("IMAP4 server disables LOGIN.\n"));
532
                else
533
                        ok = imap_cmd_login(session, user, pass);
534
                break;
535
        case IMAP_AUTH_LOGIN:
536
                if (nologin)
537
                        log_warning(_("IMAP4 server disables LOGIN.\n"));
538
                else
539
                        ok = imap_cmd_login(session, user, pass);
540
                break;
541
        case IMAP_AUTH_CRAM_MD5:
542
        case IMAP_AUTH_PLAIN:
543
                ok = imap_cmd_authenticate(session, user, pass, type);
544
                break;
545
        default:
546
                break;
547
        }
548
549
        if (ok == IMAP_SUCCESS)
550
                session->authenticated = TRUE;
551
552
        return ok;
553
}
554
555
static Session *imap_session_new(PrefsAccount *account)
556
{
557
        IMAPSession *session;
558
        gushort port;
559
560
        g_return_val_if_fail(account != NULL, NULL);
561
        g_return_val_if_fail(account->recv_server != NULL, NULL);
562
        g_return_val_if_fail(account->userid != NULL, NULL);
563
564
#if USE_SSL
565
        port = account->set_imapport ? account->imapport
566
                : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
567
#else
568
        port = account->set_imapport ? account->imapport : IMAP4_PORT;
569
#endif
570
571
        session = g_new0(IMAPSession, 1);
572
573
        session_init(SESSION(session));
574
575
        SESSION(session)->type             = SESSION_IMAP;
576
        SESSION(session)->sock             = NULL;
577
        SESSION(session)->server           = g_strdup(account->recv_server);
578
        SESSION(session)->port             = port;
579
#if USE_SSL
580
        SESSION(session)->ssl_type         = account->ssl_imap;
581
#endif
582
        SESSION(session)->last_access_time = time(NULL);
583
        SESSION(session)->data             = account;
584
585
        SESSION(session)->destroy          = imap_session_destroy;
586
587
        session->authenticated = FALSE;
588
        session->capability    = NULL;
589
        session->uidplus       = FALSE;
590
        session->mbox          = NULL;
591
        session->cmd_count     = 0;
592
593
        session_list = g_list_append(session_list, session);
594
595
        if (imap_session_connect(session) != IMAP_SUCCESS) {
596
                session_destroy(SESSION(session));
597
                return NULL;
598
        }
599
600
        return SESSION(session);
601
}
602
603
static gint imap_session_connect(IMAPSession *session)
604
{
605
        SockInfo *sock;
606
        PrefsAccount *account;
607
        const gchar *pass;
608
609
        g_return_val_if_fail(session != NULL, IMAP_ERROR);
610
611
        account = (PrefsAccount *)(SESSION(session)->data);
612
613
        log_message(_("creating IMAP4 connection to %s:%d ...\n"),
614
                    SESSION(session)->server, SESSION(session)->port);
615
616
        pass = account->passwd;
617
        if (!pass)
618
                pass = account->tmp_pass;
619
        if (!pass) {
620
                gchar *tmp_pass;
621
622
                tmp_pass = input_query_password(account->recv_server,
623
                                                account->userid);
624
                if (!tmp_pass)
625
                        return IMAP_ERROR;
626
627
                account->tmp_pass = tmp_pass;
628
                pass = account->tmp_pass;
629
        }
630
631
#if USE_SSL
632
        if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port,
633
                              SESSION(session)->ssl_type)) == NULL)
634
#else
635
        if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port))
636
            == NULL)
637
#endif
638
                return IMAP_ERROR;
639
640
        SESSION(session)->sock = sock;
641
642
        if (imap_greeting(session) != IMAP_SUCCESS)
643
                return IMAP_ERROR;
644
        if (imap_cmd_capability(session) != IMAP_SUCCESS)
645
                return IMAP_ERROR;
646
647
        if (imap_has_capability(session, "UIDPLUS"))
648
                session->uidplus = TRUE;
649
650
#if USE_SSL
651
        if (account->ssl_imap == SSL_STARTTLS &&
652
            imap_has_capability(session, "STARTTLS")) {
653
                gint ok;
654
655
                ok = imap_cmd_starttls(session);
656
                if (ok != IMAP_SUCCESS) {
657
                        log_warning(_("Can't start TLS session.\n"));
658
                        return IMAP_ERROR;
659
                }
660
                if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1))
661
                        return IMAP_SOCKET;
662
663
                /* capability can be changed after STARTTLS */
664
                if (imap_cmd_capability(session) != IMAP_SUCCESS)
665
                        return IMAP_ERROR;
666
        }
667
#endif
668
669
        if (!session->authenticated &&
670
            imap_auth(session, account->userid, pass, account->imap_auth_type)
671
            != IMAP_SUCCESS) {
672
                if (account->tmp_pass) {
673
                        g_free(account->tmp_pass);
674
                        account->tmp_pass = NULL;
675
                }
676
                imap_cmd_logout(session);
677
                return IMAP_AUTHFAIL;
678
        }
679
680
        return IMAP_SUCCESS;
681
}
682
683
static gint imap_session_reconnect(IMAPSession *session)
684
{
685
        g_return_val_if_fail(session != NULL, IMAP_ERROR);
686
687
        session_disconnect(SESSION(session));
688
689
        imap_capability_free(session);
690
        session->uidplus = FALSE;
691
        g_free(session->mbox);
692
        session->mbox = NULL;
693
        session->authenticated = FALSE;
694
        SESSION(session)->state = SESSION_READY;
695
696
        return imap_session_connect(session);
697
}
698
699
static void imap_session_destroy(Session *session)
700
{
701
        imap_capability_free(IMAP_SESSION(session));
702
        g_free(IMAP_SESSION(session)->mbox);
703
        session_list = g_list_remove(session_list, session);
704
}
705
706
#if 0
707
static void imap_session_destroy_all(void)
708
{
709
        while (session_list != NULL) {
710
                IMAPSession *session = (IMAPSession *)session_list->data;
711
712
                imap_cmd_logout(session);
713
                session_destroy(SESSION(session));
714
        }
715
}
716
#endif
717
718
#define THROW goto catch
719
720
static gint imap_search_flags(IMAPSession *session, GArray **uids,
721
                              GHashTable **flags_table)
722
{
723
        gint ok;
724
        gint i;
725
        GArray *flag_uids;
726
        GHashTable *unseen_table;
727
        GHashTable *flagged_table;
728
        GHashTable *answered_table;
729
        guint32 uid;
730
        IMAPFlags flags;
731
732
        ok = imap_cmd_search(session, "ALL", uids);
733
        if (ok != IMAP_SUCCESS) return ok;
734
735
        ok = imap_cmd_search(session, "UNSEEN", &flag_uids);
736
        if (ok != IMAP_SUCCESS) {
737
                g_array_free(*uids, TRUE);
738
                return ok;
739
        }
740
        unseen_table = imap_get_uid_table(flag_uids);
741
        g_array_free(flag_uids, TRUE);
742
        ok = imap_cmd_search(session, "FLAGGED", &flag_uids);
743
        if (ok != IMAP_SUCCESS) {
744
                g_hash_table_destroy(unseen_table);
745
                g_array_free(*uids, TRUE);
746
                return ok;
747
        }
748
        flagged_table = imap_get_uid_table(flag_uids);
749
        g_array_free(flag_uids, TRUE);
750
        ok = imap_cmd_search(session, "ANSWERED", &flag_uids);
751
        if (ok != IMAP_SUCCESS) {
752
                g_hash_table_destroy(flagged_table);
753
                g_hash_table_destroy(unseen_table);
754
                g_array_free(*uids, TRUE);
755
                return ok;
756
        }
757
        answered_table = imap_get_uid_table(flag_uids);
758
        g_array_free(flag_uids, TRUE);
759
760
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
761
762
        for (i = 0; i < (*uids)->len; i++) {
763
                uid = g_array_index(*uids, guint32, i);
764
                flags = IMAP_FLAG_DRAFT;
765
                if (!g_hash_table_lookup(unseen_table, GUINT_TO_POINTER(uid)))
766
                        flags |= IMAP_FLAG_SEEN;
767
                if (g_hash_table_lookup(flagged_table, GUINT_TO_POINTER(uid)))
768
                        flags |= IMAP_FLAG_FLAGGED;
769
                if (g_hash_table_lookup(answered_table, GUINT_TO_POINTER(uid)))
770
                        flags |= IMAP_FLAG_ANSWERED;
771
                g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
772
                                    GINT_TO_POINTER(flags));
773
        }
774
775
        g_hash_table_destroy(answered_table);
776
        g_hash_table_destroy(flagged_table);
777
        g_hash_table_destroy(unseen_table);
778
779
        return IMAP_SUCCESS;
780
}
781
782
static gint imap_fetch_flags(IMAPSession *session, GArray **uids,
783
                             GHashTable **flags_table)
784
{
785
        gint ok;
786
        gchar *tmp;
787
        gchar *cur_pos;
788
        gchar buf[IMAPBUFSIZE];
789
        guint32 uid;
790
        IMAPFlags flags;
791
792
        imap_cmd_gen_send(session, "UID FETCH 1:* (UID FLAGS)");
793
794
        *uids = g_array_new(FALSE, FALSE, sizeof(guint32));
795
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
796
797
        while ((ok = imap_cmd_gen_recv(session, &tmp)) == IMAP_SUCCESS) {
798
                if (tmp[0] != '*' || tmp[1] != ' ') {
799
                        g_free(tmp);
800
                        break;
801
                }
802
                cur_pos = tmp + 2;
803
804
#define PARSE_ONE_ELEMENT(ch)                                        \
805
{                                                                \
806
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
807
        if (cur_pos == NULL) {                                        \
808
                g_warning("cur_pos == NULL\n");                        \
809
                g_free(tmp);                                        \
810
                g_hash_table_destroy(*flags_table);                \
811
                g_array_free(*uids, TRUE);                        \
812
                return IMAP_ERROR;                                \
813
        }                                                        \
814
}
815
816
                PARSE_ONE_ELEMENT(' ');
817
                PARSE_ONE_ELEMENT(' ');
818
                if (strcmp(buf, "FETCH") != 0) {
819
                        g_free(tmp);
820
                        continue;
821
                }
822
                if (*cur_pos != '(') {
823
                        g_free(tmp);
824
                        continue;
825
                }
826
                cur_pos++;
827
                uid = 0;
828
                flags = 0;
829
830
                while (*cur_pos != '\0' && *cur_pos != ')') {
831
                        while (*cur_pos == ' ') cur_pos++;
832
833
                        if (!strncmp(cur_pos, "UID ", 4)) {
834
                                cur_pos += 4;
835
                                uid = strtoul(cur_pos, &cur_pos, 10);
836
                        } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
837
                                cur_pos += 6;
838
                                if (*cur_pos != '(') {
839
                                        g_warning("*cur_pos != '('\n");
840
                                        break;
841
                                }
842
                                cur_pos++;
843
                                PARSE_ONE_ELEMENT(')');
844
                                flags = imap_parse_imap_flags(buf);
845
                                flags |= IMAP_FLAG_DRAFT;
846
                        } else {
847
                                g_warning("invalid FETCH response: %s\n", cur_pos);
848
                                break;
849
                        }
850
                }
851
852
#undef PARSE_ONE_ELEMENT
853
854
                if (uid > 0) {
855
                        g_array_append_val(*uids, uid);
856
                        g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
857
                                            GINT_TO_POINTER(flags));
858
                }
859
860
                g_free(tmp);
861
        }
862
863
        if (ok != IMAP_SUCCESS) {
864
                g_hash_table_destroy(*flags_table);
865
                g_array_free(*uids, TRUE);
866
        }
867
868
        return ok;
869
}
870
871
static GSList *imap_get_msg_list_full(Folder *folder, FolderItem *item,
872
                                      gboolean use_cache,
873
                                      gboolean uncached_only)
874
{
875
        GSList *mlist = NULL;
876
        IMAPSession *session;
877
        gint ok, exists = 0, recent = 0, unseen = 0;
878
        guint32 uid_validity = 0;
879
        guint32 first_uid = 0, last_uid = 0;
880
        GSList *newlist = NULL;
881
882
        g_return_val_if_fail(folder != NULL, NULL);
883
        g_return_val_if_fail(item != NULL, NULL);
884
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
885
        g_return_val_if_fail(folder->account != NULL, NULL);
886
887
        item->new = item->unread = item->total = 0;
888
889
        session = imap_session_get(folder);
890
891
        if (!session) {
892
                if (uncached_only)
893
                        return NULL;
894
                mlist = procmsg_read_cache(item, FALSE);
895
                item->last_num = procmsg_get_last_num_in_msg_list(mlist);
896
                procmsg_set_flags(mlist, item);
897
                return mlist;
898
        }
899
900
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
901
                         &exists, &recent, &unseen, &uid_validity);
902
        if (ok != IMAP_SUCCESS) THROW;
903
904
        if (exists == 0) {
905
                imap_delete_all_cached_messages(item);
906
                return NULL;
907
        }
908
909
        /* invalidate current cache if UIDVALIDITY has been changed */
910
        if (item->mtime != uid_validity) {
911
                debug_print("imap_get_msg_list: "
912
                            "UIDVALIDITY has been changed.\n");
913
                use_cache = FALSE;
914
        }
915
916
        if (use_cache) {
917
                GArray *uids;
918
                GHashTable *msg_table;
919
                GHashTable *flags_table;
920
                guint32 cache_last;
921
                guint32 begin = 0;
922
                GSList *cur, *next = NULL;
923
                MsgInfo *msginfo;
924
                IMAPFlags imap_flags;
925
926
                /* get cache data */
927
                mlist = procmsg_read_cache(item, FALSE);
928
                procmsg_set_flags(mlist, item);
929
                cache_last = procmsg_get_last_num_in_msg_list(mlist);
930
931
                /* get all UID list and flags */
932
                ok = imap_search_flags(session, &uids, &flags_table);
933
                if (ok != IMAP_SUCCESS) {
934
                        if (ok == IMAP_SOCKET || ok == IMAP_IOERR) THROW;
935
                        ok = imap_fetch_flags(session, &uids, &flags_table);
936
                        if (ok != IMAP_SUCCESS) THROW;
937
                }
938
939
                if (uids->len > 0) {
940
                        first_uid = g_array_index(uids, guint32, 0);
941
                        last_uid = g_array_index(uids, guint32, uids->len - 1);
942
                } else {
943
                        g_array_free(uids, TRUE);
944
                        g_hash_table_destroy(flags_table);
945
                        THROW;
946
                }
947
948
                /* sync message flags with server */
949
                for (cur = mlist; cur != NULL; cur = next) {
950
                        msginfo = (MsgInfo *)cur->data;
951
                        next = cur->next;
952
                        imap_flags = GPOINTER_TO_INT(g_hash_table_lookup
953
                                (flags_table,
954
                                 GUINT_TO_POINTER(msginfo->msgnum)));
955
956
                        if (imap_flags == 0) {
957
                                debug_print("imap_get_msg_list: "
958
                                            "message %u has been deleted.\n",
959
                                            msginfo->msgnum);
960
                                imap_delete_cached_message
961
                                        (item, msginfo->msgnum);
962
                                if (MSG_IS_NEW(msginfo->flags))
963
                                        item->new--;
964
                                if (MSG_IS_UNREAD(msginfo->flags))
965
                                        item->unread--;
966
                                item->total--;
967
                                mlist = g_slist_remove(mlist, msginfo);
968
                                procmsg_msginfo_free(msginfo);
969
                                item->cache_dirty = TRUE;
970
                                item->mark_dirty = TRUE;
971
                                continue;
972
                        }
973
974
                        if (!IMAP_IS_SEEN(imap_flags)) {
975
                                if (!MSG_IS_UNREAD(msginfo->flags)) {
976
                                        item->unread++;
977
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
978
                                                           MSG_UNREAD);
979
                                        item->mark_dirty = TRUE;
980
                                }
981
                        } else {
982
                                if (MSG_IS_NEW(msginfo->flags)) {
983
                                        item->new--;
984
                                        item->mark_dirty = TRUE;
985
                                }
986
                                if (MSG_IS_UNREAD(msginfo->flags)) {
987
                                        item->unread--;
988
                                        item->mark_dirty = TRUE;
989
                                }
990
                                MSG_UNSET_PERM_FLAGS(msginfo->flags,
991
                                                     MSG_NEW|MSG_UNREAD);
992
                        }
993
994
                        if (IMAP_IS_FLAGGED(imap_flags)) {
995
                                if (!MSG_IS_MARKED(msginfo->flags)) {
996
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
997
                                                           MSG_MARKED);
998
                                        item->mark_dirty = TRUE;
999
                                }
1000
                        } else {
1001
                                if (MSG_IS_MARKED(msginfo->flags)) {
1002
                                        MSG_UNSET_PERM_FLAGS(msginfo->flags,
1003
                                                             MSG_MARKED);
1004
                                        item->mark_dirty = TRUE;
1005
                                }
1006
                        }
1007
                        if (IMAP_IS_ANSWERED(imap_flags)) {
1008
                                if (!MSG_IS_REPLIED(msginfo->flags)) {
1009
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
1010
                                                           MSG_REPLIED);
1011
                                        item->mark_dirty = TRUE;
1012
                                }
1013
                        } else {
1014
                                if (MSG_IS_REPLIED(msginfo->flags)) {
1015
                                        MSG_UNSET_PERM_FLAGS(msginfo->flags,
1016
                                                             MSG_REPLIED);
1017
                                        item->mark_dirty = TRUE;
1018
                                }
1019
                        }
1020
                }
1021
1022
                /* check for the first new message */
1023
                msg_table = procmsg_msg_hash_table_create(mlist);
1024
                if (msg_table == NULL)
1025
                        begin = first_uid;
1026
                else {
1027
                        gint i;
1028
1029
                        for (i = 0; i < uids->len; i++) {
1030
                                guint32 uid;
1031
1032
                                uid = g_array_index(uids, guint32, i);
1033
                                if (g_hash_table_lookup
1034
                                        (msg_table, GUINT_TO_POINTER(uid))
1035
                                        == NULL) {
1036
                                        debug_print("imap_get_msg_list: "
1037
                                                    "first new UID: %u\n", uid);
1038
                                        begin = uid;
1039
                                        break;
1040
                                }
1041
                        }
1042
                        g_hash_table_destroy(msg_table);
1043
                }
1044
1045
                g_array_free(uids, TRUE);
1046
                g_hash_table_destroy(flags_table);
1047
1048
                /* remove ununsed caches */
1049
                if (first_uid > 0 && last_uid > 0) {
1050
                        mlist = imap_delete_cached_messages
1051
                                (mlist, item, 0, first_uid - 1);
1052
                        mlist = imap_delete_cached_messages
1053
                                (mlist, item, begin > 0 ? begin : last_uid + 1,
1054
                                 UINT_MAX);
1055
                }
1056
1057
                if (begin > 0 && begin <= last_uid) {
1058
                        newlist = imap_get_uncached_messages
1059
                                (session, item, begin, last_uid,
1060
                                 exists - item->total, TRUE);
1061
                        if (newlist) {
1062
                                item->cache_dirty = TRUE;
1063
                                item->mark_dirty = TRUE;
1064
                        }
1065
                        mlist = g_slist_concat(mlist, newlist);
1066
                }
1067
        } else {
1068
                imap_delete_all_cached_messages(item);
1069
                mlist = imap_get_uncached_messages(session, item, 0, 0, exists,
1070
                                                   TRUE);
1071
                last_uid = procmsg_get_last_num_in_msg_list(mlist);
1072
                item->cache_dirty = TRUE;
1073
                item->mark_dirty = TRUE;
1074
                newlist = mlist;
1075
        }
1076
1077
        if (!uncached_only)
1078
                mlist = procmsg_sort_msg_list(mlist, item->sort_key,
1079
                                              item->sort_type);
1080
1081
        item->last_num = last_uid;
1082
1083
        if (item->mark_queue)
1084
                item->mark_dirty = TRUE;
1085
1086
        debug_print("cache_dirty: %d, mark_dirty: %d\n",
1087
                    item->cache_dirty, item->mark_dirty);
1088
1089
        if (!item->opened) {
1090
                item->mtime = uid_validity;
1091
                if (item->cache_dirty)
1092
                        procmsg_write_cache_list(item, mlist);
1093
                if (item->mark_dirty)
1094
                        procmsg_write_flags_list(item, mlist);
1095
        }
1096
1097
catch:
1098
        if (uncached_only) {
1099
                GSList *cur;
1100
1101
                if (newlist == NULL) {
1102
                        procmsg_msg_list_free(mlist);
1103
                        return NULL;
1104
                }
1105
                if (mlist == newlist)
1106
                        return newlist;
1107
                for (cur = mlist; cur != NULL; cur = cur->next) {
1108
                        if (cur->next == newlist) {
1109
                                cur->next = NULL;
1110
                                procmsg_msg_list_free(mlist);
1111
                                return newlist;
1112
                        }
1113
                }
1114
                procmsg_msg_list_free(mlist);
1115
                return NULL;
1116
        }
1117
1118
        return mlist;
1119
}
1120
1121
#undef THROW
1122
1123
static GSList *imap_get_msg_list(Folder *folder, FolderItem *item,
1124
                                 gboolean use_cache)
1125
{
1126
        return imap_get_msg_list_full(folder, item, use_cache, FALSE);
1127
}
1128
1129
static GSList *imap_get_uncached_msg_list(Folder *folder, FolderItem *item)
1130
{
1131
        return imap_get_msg_list_full(folder, item, TRUE, TRUE);
1132
}
1133
1134
static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1135
{
1136
        gchar *path, *filename;
1137
        IMAPSession *session;
1138
        gint ok;
1139
1140
        g_return_val_if_fail(folder != NULL, NULL);
1141
        g_return_val_if_fail(item != NULL, NULL);
1142
1143
        path = folder_item_get_path(item);
1144
        if (!is_dir_exist(path))
1145
                make_dir_hier(path);
1146
        filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1147
        g_free(path);
1148
1149
        if (is_file_exist(filename)) {
1150
                debug_print("message %d has been already cached.\n", uid);
1151
                return filename;
1152
        }
1153
1154
        session = imap_session_get(folder);
1155
        if (!session) {
1156
                g_free(filename);
1157
                return NULL;
1158
        }
1159
1160
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1161
                         NULL, NULL, NULL, NULL);
1162
        if (ok != IMAP_SUCCESS) {
1163
                g_warning("can't select mailbox %s\n", item->path);
1164
                g_free(filename);
1165
                return NULL;
1166
        }
1167
1168
        status_print(_("Getting message %d"), uid);
1169
        debug_print("getting message %d...\n", uid);
1170
        ok = imap_cmd_fetch(session, (guint32)uid, filename);
1171
1172
        if (ok != IMAP_SUCCESS) {
1173
                g_warning("can't fetch message %d\n", uid);
1174
                g_free(filename);
1175
                return NULL;
1176
        }
1177
1178
        return filename;
1179
}
1180
1181
static MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
1182
{
1183
        IMAPSession *session;
1184
        GSList *list;
1185
        MsgInfo *msginfo = NULL;
1186
1187
        g_return_val_if_fail(folder != NULL, NULL);
1188
        g_return_val_if_fail(item != NULL, NULL);
1189
1190
        session = imap_session_get(folder);
1191
        g_return_val_if_fail(session != NULL, NULL);
1192
1193
        list = imap_get_uncached_messages(session, item, uid, uid, 0, FALSE);
1194
        if (list) {
1195
                msginfo = (MsgInfo *)list->data;
1196
                list->data = NULL;
1197
        }
1198
        procmsg_msg_list_free(list);
1199
1200
        return msginfo;
1201
}
1202
1203
static gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1204
                         MsgFlags *flags, gboolean remove_source)
1205
{
1206
        GSList file_list;
1207
        MsgFileInfo fileinfo;
1208
1209
        g_return_val_if_fail(file != NULL, -1);
1210
1211
        fileinfo.file = (gchar *)file;
1212
        fileinfo.flags = flags;
1213
        file_list.data = &fileinfo;
1214
        file_list.next = NULL;
1215
1216
        return imap_add_msgs(folder, dest, &file_list, remove_source, NULL);
1217
}
1218
1219
static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1220
                          gboolean remove_source, gint *first)
1221
{
1222
        gchar *destdir;
1223
        IMAPSession *session;
1224
        gint messages, recent, unseen;
1225
        guint32 uid_next, uid_validity;
1226
        guint32 last_uid = 0;
1227
        GSList *cur;
1228
        MsgFileInfo *fileinfo;
1229
        gint count = 1;
1230
        gint total;
1231
        gint ok;
1232
        GTimeVal tv_prev, tv_cur;
1233
1234
        g_return_val_if_fail(folder != NULL, -1);
1235
        g_return_val_if_fail(dest != NULL, -1);
1236
        g_return_val_if_fail(file_list != NULL, -1);
1237
1238
        session = imap_session_get(folder);
1239
        if (!session) return -1;
1240
1241
        g_get_current_time(&tv_prev);
1242
        ui_update();
1243
1244
        ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
1245
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1246
        if (ok != IMAP_SUCCESS) {
1247
                g_warning("can't append messages\n");
1248
                return -1;
1249
        }
1250
1251
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1252
1253
        if (!session->uidplus)
1254
                last_uid = uid_next - 1;
1255
        if (first)
1256
                *first = uid_next;
1257
1258
        total = g_slist_length(file_list);
1259
1260
        for (cur = file_list; cur != NULL; cur = cur->next) {
1261
                IMAPFlags iflags = 0;
1262
                guint32 new_uid = 0;
1263
1264
                fileinfo = (MsgFileInfo *)cur->data;
1265
1266
                if (fileinfo->flags) {
1267
                        if (MSG_IS_MARKED(*fileinfo->flags))
1268
                                iflags |= IMAP_FLAG_FLAGGED;
1269
                        if (MSG_IS_REPLIED(*fileinfo->flags))
1270
                                iflags |= IMAP_FLAG_ANSWERED;
1271
                        if (!MSG_IS_UNREAD(*fileinfo->flags))
1272
                                iflags |= IMAP_FLAG_SEEN;
1273
                }
1274
1275
                if (dest->stype == F_OUTBOX ||
1276
                    dest->stype == F_QUEUE  ||
1277
                    dest->stype == F_DRAFT)
1278
                        iflags |= IMAP_FLAG_SEEN;
1279
1280
                g_get_current_time(&tv_cur);
1281
                if (tv_cur.tv_sec > tv_prev.tv_sec ||
1282
                    tv_cur.tv_usec - tv_prev.tv_usec >
1283
                    PROGRESS_UPDATE_INTERVAL * 1000) {
1284
                        status_print(_("Appending messages to %s (%d / %d)"),
1285
                                     dest->path, count, total);
1286
                        progress_show(count, total);
1287
                        ui_update();
1288
                        tv_prev = tv_cur;
1289
                }
1290
                ++count;
1291
1292
                ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1293
                                     &new_uid);
1294
1295
                if (ok != IMAP_SUCCESS) {
1296
                        g_warning("can't append message %s\n", fileinfo->file);
1297
                        g_free(destdir);
1298
                        progress_show(0, 0);
1299
                        return -1;
1300
                }
1301
1302
                if (!session->uidplus)
1303
                        last_uid++;
1304
                else if (last_uid < new_uid)
1305
                        last_uid = new_uid;
1306
1307
                dest->last_num = last_uid;
1308
                dest->total++;
1309
                dest->updated = TRUE;
1310
1311
                if (fileinfo->flags) {
1312
                        if (MSG_IS_UNREAD(*fileinfo->flags))
1313
                                dest->unread++;
1314
                } else
1315
                        dest->unread++;
1316
        }
1317
1318
        progress_show(0, 0);
1319
        g_free(destdir);
1320
1321
        if (remove_source) {
1322
                for (cur = file_list; cur != NULL; cur = cur->next) {
1323
                        fileinfo = (MsgFileInfo *)cur->data;
1324
                        if (g_unlink(fileinfo->file) < 0)
1325
                                 FILE_OP_ERROR(fileinfo->file, "unlink");
1326
                }
1327
        }
1328
1329
        return last_uid;
1330
}
1331
1332
static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist,
1333
                              gboolean remove_source)
1334
{
1335
        FolderItem *src;
1336
        gchar *destdir;
1337
        GSList *seq_list, *cur;
1338
        MsgInfo *msginfo;
1339
        IMAPSession *session;
1340
        gint count = 0, total;
1341
        gint ok = IMAP_SUCCESS;
1342
1343
        g_return_val_if_fail(folder != NULL, -1);
1344
        g_return_val_if_fail(dest != NULL, -1);
1345
        g_return_val_if_fail(msglist != NULL, -1);
1346
1347
        session = imap_session_get(folder);
1348
        if (!session) return -1;
1349
1350
        ui_update();
1351
1352
        msginfo = (MsgInfo *)msglist->data;
1353
1354
        src = msginfo->folder;
1355
        if (src == dest) {
1356
                g_warning("the src folder is identical to the dest.\n");
1357
                return -1;
1358
        }
1359
1360
        ok = imap_select(session, IMAP_FOLDER(folder), src->path,
1361
                         NULL, NULL, NULL, NULL);
1362
        if (ok != IMAP_SUCCESS)
1363
                return ok;
1364
1365
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1366
1367
        total = g_slist_length(msglist);
1368
        seq_list = imap_get_seq_set_from_msglist(msglist, IMAP_COPY_LIMIT);
1369
1370
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1371
                gchar *seq_set = (gchar *)cur->data;
1372
1373
                count += imap_seq_set_get_count(seq_set);
1374
1375
                if (remove_source) {
1376
                        status_print(_("Moving messages %s to %s ..."),
1377
                                     seq_set, dest->path);
1378
                        debug_print("Moving message %s/[%s] to %s ...\n",
1379
                                    src->path, seq_set, dest->path);
1380
                } else {
1381
                        status_print(_("Copying messages %s to %s ..."),
1382
                                     seq_set, dest->path);
1383
                        debug_print("Copying message %s/[%s] to %s ...\n",
1384
                                    src->path, seq_set, dest->path);
1385
                }
1386
                progress_show(count, total);
1387
                ui_update();
1388
1389
                ok = imap_cmd_copy(session, seq_set, destdir);
1390
                if (ok != IMAP_SUCCESS) {
1391
                        imap_seq_set_free(seq_list);
1392
                        progress_show(0, 0);
1393
                        return -1;
1394
                }
1395
        }
1396
1397
        progress_show(0, 0);
1398
1399
        dest->updated = TRUE;
1400
1401
        imap_seq_set_free(seq_list);
1402
        g_free(destdir);
1403
1404
        for (cur = msglist; cur != NULL; cur = cur->next) {
1405
                msginfo = (MsgInfo *)cur->data;
1406
                dest->total++;
1407
                if (MSG_IS_NEW(msginfo->flags))
1408
                        dest->new++;
1409
                if (MSG_IS_UNREAD(msginfo->flags))
1410
                        dest->unread++;
1411
        }
1412
1413
        if (remove_source) {
1414
                ok = imap_remove_msgs(folder, src, msglist);
1415
                if (ok != IMAP_SUCCESS)
1416
                        return ok;
1417
        }
1418
1419
        if (ok == IMAP_SUCCESS)
1420
                return 0;
1421
        else
1422
                return -1;
1423
}
1424
1425
static gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1426
{
1427
        GSList msglist;
1428
1429
        g_return_val_if_fail(msginfo != NULL, -1);
1430
1431
        msglist.data = msginfo;
1432
        msglist.next = NULL;
1433
1434
        return imap_move_msgs(folder, dest, &msglist);
1435
}
1436
1437
static gint imap_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1438
{
1439
        MsgInfo *msginfo;
1440
        GSList *file_list;
1441
        gint ret = 0;
1442
1443
        g_return_val_if_fail(folder != NULL, -1);
1444
        g_return_val_if_fail(dest != NULL, -1);
1445
        g_return_val_if_fail(msglist != NULL, -1);
1446
1447
        msginfo = (MsgInfo *)msglist->data;
1448
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1449
1450
        if (folder == msginfo->folder->folder)
1451
                return imap_do_copy_msgs(folder, dest, msglist, TRUE);
1452
1453
        file_list = procmsg_get_message_file_list(msglist);
1454
        g_return_val_if_fail(file_list != NULL, -1);
1455
1456
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1457
1458
        procmsg_message_file_list_free(file_list);
1459
1460
        if (ret != -1)
1461
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
1462
1463
        return ret;
1464
}
1465
1466
static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1467
{
1468
        GSList msglist;
1469
1470
        g_return_val_if_fail(msginfo != NULL, -1);
1471
1472
        msglist.data = msginfo;
1473
        msglist.next = NULL;
1474
1475
        return imap_copy_msgs(folder, dest, &msglist);
1476
}
1477
1478
static gint imap_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1479
{
1480
        MsgInfo *msginfo;
1481
        GSList *file_list;
1482
        gint ret;
1483
1484
        g_return_val_if_fail(folder != NULL, -1);
1485
        g_return_val_if_fail(dest != NULL, -1);
1486
        g_return_val_if_fail(msglist != NULL, -1);
1487
1488
        msginfo = (MsgInfo *)msglist->data;
1489
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1490
1491
        if (folder == msginfo->folder->folder)
1492
                return imap_do_copy_msgs(folder, dest, msglist, FALSE);
1493
1494
        file_list = procmsg_get_message_file_list(msglist);
1495
        g_return_val_if_fail(file_list != NULL, -1);
1496
1497
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1498
1499
        procmsg_message_file_list_free(file_list);
1500
1501
        return ret;
1502
}
1503
1504
static gint imap_remove_msgs_by_seq_set(Folder *folder, FolderItem *item,
1505
                                        GSList *seq_list)
1506
{
1507
        gint ok;
1508
        IMAPSession *session;
1509
        GSList *cur;
1510
1511
        g_return_val_if_fail(seq_list != NULL, -1);
1512
1513
        session = imap_session_get(folder);
1514
        if (!session) return -1;
1515
1516
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1517
                gchar *seq_set = (gchar *)cur->data;
1518
1519
                status_print(_("Removing messages %s"), seq_set);
1520
                ui_update();
1521
1522
                ok = imap_set_message_flags(session, seq_set, IMAP_FLAG_DELETED,
1523
                                            TRUE);
1524
                if (ok != IMAP_SUCCESS) {
1525
                        log_warning(_("can't set deleted flags: %s\n"),
1526
                                    seq_set);
1527
                        return ok;
1528
                }
1529
        }
1530
1531
        ok = imap_cmd_expunge(session);
1532
        if (ok != IMAP_SUCCESS)
1533
                log_warning(_("can't expunge\n"));
1534
1535
        item->updated = TRUE;
1536
1537
        return ok;
1538
}
1539
1540
static gint imap_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1541
{
1542
        GSList msglist;
1543
1544
        g_return_val_if_fail(msginfo != NULL, -1);
1545
1546
        msglist.data = msginfo;
1547
        msglist.next = NULL;
1548
1549
        return imap_remove_msgs(folder, item, &msglist);
1550
}
1551
1552
static gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
1553
{
1554
        gint ok;
1555
        IMAPSession *session;
1556
        GSList *seq_list, *cur;
1557
        gchar *dir;
1558
        gboolean dir_exist;
1559
1560
        g_return_val_if_fail(folder != NULL, -1);
1561
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
1562
        g_return_val_if_fail(item != NULL, -1);
1563
        g_return_val_if_fail(msglist != NULL, -1);
1564
1565
        session = imap_session_get(folder);
1566
        if (!session) return -1;
1567
1568
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1569
                         NULL, NULL, NULL, NULL);
1570
        if (ok != IMAP_SUCCESS)
1571
                return ok;
1572
1573
        seq_list = imap_get_seq_set_from_msglist(msglist, 0);
1574
        ok = imap_remove_msgs_by_seq_set(folder, item, seq_list);
1575
        imap_seq_set_free(seq_list);
1576
        if (ok != IMAP_SUCCESS)
1577
                return ok;
1578
1579
        dir = folder_item_get_path(item);
1580
        dir_exist = is_dir_exist(dir);
1581
        for (cur = msglist; cur != NULL; cur = cur->next) {
1582
                MsgInfo *msginfo = (MsgInfo *)cur->data;
1583
                guint32 uid = msginfo->msgnum;
1584
1585
                if (dir_exist)
1586
                        remove_numbered_files(dir, uid, uid);
1587
                item->total--;
1588
                if (MSG_IS_NEW(msginfo->flags))
1589
                        item->new--;
1590
                if (MSG_IS_UNREAD(msginfo->flags))
1591
                        item->unread--;
1592
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
1593
        }
1594
        g_free(dir);
1595
1596
        return IMAP_SUCCESS;
1597
}
1598
1599
static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1600
{
1601
        gint ok;
1602
        IMAPSession *session;
1603
        gchar *dir;
1604
1605
        g_return_val_if_fail(folder != NULL, -1);
1606
        g_return_val_if_fail(item != NULL, -1);
1607
1608
        session = imap_session_get(folder);
1609
        if (!session) return -1;
1610
1611
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1612
                         NULL, NULL, NULL, NULL);
1613
        if (ok != IMAP_SUCCESS)
1614
                return ok;
1615
1616
        status_print(_("Removing all messages in %s"), item->path);
1617
        ui_update();
1618
1619
        imap_cmd_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1620
        ok = imap_cmd_ok(session, NULL);
1621
        if (ok != IMAP_SUCCESS) {
1622
                log_warning(_("can't set deleted flags: 1:*\n"));
1623
                return ok;
1624
        }
1625
1626
        ok = imap_cmd_expunge(session);
1627
        if (ok != IMAP_SUCCESS) {
1628
                log_warning(_("can't expunge\n"));
1629
                return ok;
1630
        }
1631
1632
        item->new = item->unread = item->total = 0;
1633
        item->updated = TRUE;
1634
1635
        dir = folder_item_get_path(item);
1636
        if (is_dir_exist(dir))
1637
                remove_all_numbered_files(dir);
1638
        g_free(dir);
1639
1640
        return IMAP_SUCCESS;
1641
}
1642
1643
static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1644
                                    MsgInfo *msginfo)
1645
{
1646
        /* TODO: properly implement this method */
1647
        return FALSE;
1648
}
1649
1650
static gint imap_close(Folder *folder, FolderItem *item)
1651
{
1652
        gint ok;
1653
        IMAPSession *session;
1654
1655
        g_return_val_if_fail(folder != NULL, -1);
1656
1657
        if (!item->path) return 0;
1658
1659
        if (!REMOTE_FOLDER(folder)->session)
1660
                return 0;
1661
1662
        session = imap_session_get(folder);
1663
        if (!session) return -1;
1664
1665
        if (session->mbox) {
1666
                if (strcmp2(session->mbox, item->path) != 0) return -1;
1667
1668
                ok = imap_cmd_close(session);
1669
                if (ok != IMAP_SUCCESS)
1670
                        log_warning(_("can't close folder\n"));
1671
1672
                g_free(session->mbox);
1673
                session->mbox = NULL;
1674
1675
                return ok;
1676
        } else
1677
                return 0;
1678
}
1679
1680
static gint imap_scan_folder(Folder *folder, FolderItem *item)
1681
{
1682
        IMAPSession *session;
1683
        gint messages, recent, unseen;
1684
        guint32 uid_next, uid_validity;
1685
        gint ok;
1686
1687
        g_return_val_if_fail(folder != NULL, -1);
1688
        g_return_val_if_fail(item != NULL, -1);
1689
1690
        session = imap_session_get(folder);
1691
        if (!session) return -1;
1692
1693
        ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1694
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1695
        if (ok != IMAP_SUCCESS) return -1;
1696
1697
        item->new = unseen > 0 ? recent : 0;
1698
        item->unread = unseen;
1699
        item->total = messages;
1700
        item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1701
        /* item->mtime = uid_validity; */
1702
        item->updated = TRUE;
1703
1704
        return 0;
1705
}
1706
1707
static gint imap_scan_tree(Folder *folder)
1708
{
1709
        FolderItem *item = NULL;
1710
        IMAPSession *session;
1711
        gchar *root_folder = NULL;
1712
        GSList *item_list, *cur;
1713
1714
        g_return_val_if_fail(folder != NULL, -1);
1715
        g_return_val_if_fail(folder->account != NULL, -1);
1716
1717
        session = imap_session_get(folder);
1718
        if (!session) {
1719
                if (!folder->node) {
1720
                        folder_tree_destroy(folder);
1721
                        item = folder_item_new(folder->name, NULL);
1722
                        item->folder = folder;
1723
                        folder->node = item->node = g_node_new(item);
1724
                }
1725
                return -1;
1726
        }
1727
1728
        if (folder->account->imap_dir && *folder->account->imap_dir) {
1729
                gchar *real_path;
1730
                GPtrArray *argbuf;
1731
                gint ok;
1732
1733
                Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1734
                extract_quote(root_folder, '"');
1735
                subst_char(root_folder,
1736
                           imap_get_path_separator(IMAP_FOLDER(folder),
1737
                                                   root_folder),
1738
                           '/');
1739
                strtailchomp(root_folder, '/');
1740
                real_path = imap_get_real_path
1741
                        (IMAP_FOLDER(folder), root_folder);
1742
                debug_print("IMAP root directory: %s\n", real_path);
1743
1744
                /* check if root directory exist */
1745
                argbuf = g_ptr_array_new();
1746
                ok = imap_cmd_list(session, NULL, real_path, argbuf);
1747
                if (ok != IMAP_SUCCESS ||
1748
                    search_array_str(argbuf, "LIST ") == NULL) {
1749
                        log_warning(_("root folder %s not exist\n"), real_path);
1750
                        g_ptr_array_free(argbuf, TRUE);
1751
                        g_free(real_path);
1752
                        return -1;
1753
                }
1754
                g_ptr_array_free(argbuf, TRUE);
1755
                g_free(real_path);
1756
        }
1757
1758
        if (folder->node)
1759
                item = FOLDER_ITEM(folder->node->data);
1760
        if (!item || ((item->path || root_folder) &&
1761
                      strcmp2(item->path, root_folder) != 0)) {
1762
                folder_tree_destroy(folder);
1763
                item = folder_item_new(folder->name, root_folder);
1764
                item->folder = folder;
1765
                folder->node = item->node = g_node_new(item);
1766
        }
1767
1768
        item_list = imap_get_folder_list(session, item);
1769
        imap_scan_tree_recursive(session, item, item_list);
1770
        imap_create_missing_folders(folder);
1771
1772
        for (cur = item_list; cur != NULL; cur = cur->next)
1773
                folder_item_destroy(FOLDER_ITEM(cur->data));
1774
        g_slist_free(item_list);
1775
1776
        return 0;
1777
}
1778
1779
static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item,
1780
                                     GSList *item_list)
1781
{
1782
        Folder *folder;
1783
        FolderItem *new_item;
1784
        GSList *part_list, *cur;
1785
        GNode *node;
1786
1787
        g_return_val_if_fail(item != NULL, -1);
1788
        g_return_val_if_fail(item->folder != NULL, -1);
1789
        g_return_val_if_fail(item->no_sub == FALSE, -1);
1790
1791
        folder = item->folder;
1792
1793
        part_list = imap_get_part_folder_list(item_list, item);
1794
1795
        node = item->node->children;
1796
        while (node != NULL) {
1797
                FolderItem *old_item = FOLDER_ITEM(node->data);
1798
                GNode *next = node->next;
1799
1800
                new_item = NULL;
1801
1802
                for (cur = part_list; cur != NULL; cur = cur->next) {
1803
                        FolderItem *cur_item = FOLDER_ITEM(cur->data);
1804
                        if (!strcmp2(old_item->path, cur_item->path)) {
1805
                                new_item = cur_item;
1806
                                break;
1807
                        }
1808
                }
1809
                if (!new_item) {
1810
                        if (old_item->stype != F_VIRTUAL) {
1811
                                debug_print("folder '%s' not found. removing...\n", old_item->path);
1812
                                folder_item_remove(old_item);
1813
                        }
1814
                } else if (old_item->stype == F_VIRTUAL) {
1815
                                debug_print("IMAP4 folder found at the location of virtual folder '%s'. removing virtual folder...\n", old_item->path);
1816
                                virtual_get_class()->remove_folder
1817
                                        (folder, old_item);
1818
                } else {
1819
                        old_item->no_sub = new_item->no_sub;
1820
                        old_item->no_select = new_item->no_select;
1821
                        if (old_item->no_select == TRUE)
1822
                                old_item->new = old_item->unread =
1823
                                        old_item->total = 0;
1824
                        if (old_item->no_sub == TRUE && node->children) {
1825
                                debug_print("folder '%s' doesn't have "
1826
                                            "subfolders. removing...\n",
1827
                                            old_item->path);
1828
                                folder_item_remove_children(old_item);
1829
                        }
1830
                }
1831
1832
                node = next;
1833
        }
1834
1835
        for (cur = part_list; cur != NULL; cur = cur->next) {
1836
                FolderItem *cur_item = FOLDER_ITEM(cur->data);
1837
                new_item = NULL;
1838
                for (node = item->node->children; node != NULL;
1839
                     node = node->next) {
1840
                        if (!strcmp2(FOLDER_ITEM(node->data)->path,
1841
                                     cur_item->path)) {
1842
                                new_item = FOLDER_ITEM(node->data);
1843
                                cur_item = NULL;
1844
                                break;
1845
                        }
1846
                }
1847
                if (!new_item) {
1848
                        new_item = folder_item_copy(cur_item);
1849
                        debug_print("new folder '%s' found.\n", new_item->path);
1850
                        folder_item_append(item, new_item);
1851
                }
1852
1853
                if (!strcmp(new_item->path, "INBOX")) {
1854
                        new_item->stype = F_INBOX;
1855
                        folder->inbox = new_item;
1856
                } else if (!item->parent || item->stype == F_INBOX) {
1857
                        const gchar *base;
1858
1859
                        base = g_basename(new_item->path);
1860
1861
                        if (!folder->outbox &&
1862
                            !g_ascii_strcasecmp(base, "Sent")) {
1863
                                new_item->stype = F_OUTBOX;
1864
                                folder->outbox = new_item;
1865
                        } else if (!folder->draft &&
1866
                                   !g_ascii_strcasecmp(base, "Drafts")) {
1867
                                new_item->stype = F_DRAFT;
1868
                                folder->draft = new_item;
1869
                        } else if (!folder->queue &&
1870
                                   !g_ascii_strcasecmp(base, "Queue")) {
1871
                                new_item->stype = F_QUEUE;
1872
                                folder->queue = new_item;
1873
                        } else if (!folder->trash &&
1874
                                   !g_ascii_strcasecmp(base, "Trash")) {
1875
                                new_item->stype = F_TRASH;
1876
                                folder->trash = new_item;
1877
                        }
1878
                }
1879
1880
#if 0
1881
                if (new_item->no_select == FALSE)
1882
                        imap_scan_folder(folder, new_item);
1883
#endif
1884
                if (new_item->no_sub == FALSE)
1885
                        imap_scan_tree_recursive(session, new_item, item_list);
1886
        }
1887
1888
        g_slist_free(part_list);
1889
1890
        return IMAP_SUCCESS;
1891
}
1892
1893
static GSList *imap_get_folder_list(IMAPSession *session, FolderItem *item)
1894
{
1895
        Folder *folder;
1896
        IMAPFolder *imapfolder;
1897
        gchar *real_path;
1898
        gchar *wildcard_path;
1899
        gchar separator;
1900
        GSList *item_list;
1901
1902
        folder = item->folder;
1903
        imapfolder = IMAP_FOLDER(folder);
1904
1905
        separator = imap_get_path_separator(imapfolder, item->path);
1906
1907
        if (folder->ui_func)
1908
                folder->ui_func(folder, item, folder->ui_func_data);
1909
1910
        if (item->path) {
1911
                real_path = imap_get_real_path(imapfolder, item->path);
1912
                strtailchomp(real_path, separator);
1913
                wildcard_path = g_strdup_printf("%s%c*", real_path, separator);
1914
        } else {
1915
                real_path = g_strdup("");
1916
                wildcard_path = g_strdup("*");
1917
        }
1918
1919
        imap_cmd_gen_send(session, "LIST \"\" \"%s\"", wildcard_path);
1920
1921
        item_list = imap_parse_list(session, real_path, NULL);
1922
        item_list = imap_add_inter_folders(item_list, item->path);
1923
        g_free(real_path);
1924
        g_free(wildcard_path);
1925
1926
        return item_list;
1927
}
1928
1929
static GSList *imap_parse_list(IMAPSession *session, const gchar *real_path,
1930
                               gchar *separator)
1931
{
1932
        gchar buf[IMAPBUFSIZE];
1933
        gchar flags[256];
1934
        gchar separator_str[16];
1935
        gchar *p;
1936
        const gchar *name;
1937
        gchar *loc_name, *loc_path;
1938
        GSList *item_list = NULL;
1939
        GString *str;
1940
        FolderItem *new_item;
1941
1942
        debug_print("getting list of %s ...\n",
1943
                    *real_path ? real_path : "\"\"");
1944
1945
        str = g_string_new(NULL);
1946
1947
        for (;;) {
1948
                if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1949
                        log_warning(_("error occurred while getting LIST.\n"));
1950
                        break;
1951
                }
1952
                strretchomp(buf);
1953
                if (buf[0] != '*' || buf[1] != ' ') {
1954
                        log_print("IMAP4< %s\n", buf);
1955
                        if (sscanf(buf, "%*d %16s", buf) < 1 ||
1956
                            strcmp(buf, "OK") != 0)
1957
                                log_warning(_("error occurred while getting LIST.\n"));
1958
                                
1959
                        break;
1960
                }
1961
                debug_print("IMAP4< %s\n", buf);
1962
1963
                g_string_assign(str, buf);
1964
                p = str->str + 2;
1965
                if (strncmp(p, "LIST ", 5) != 0) continue;
1966
                p += 5;
1967
1968
                if (*p != '(') continue;
1969
                p++;
1970
                p = strchr_cpy(p, ')', flags, sizeof(flags));
1971
                if (!p) continue;
1972
                while (*p == ' ') p++;
1973
1974
                p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1975
                if (!p) continue;
1976
                extract_quote(separator_str, '"');
1977
                if (!strcmp(separator_str, "NIL"))
1978
                        separator_str[0] = '\0';
1979
                if (separator)
1980
                        *separator = separator_str[0];
1981
1982
                buf[0] = '\0';
1983
                while (*p == ' ') p++;
1984
                if ((*p == '~' && *(p + 1) == '{') || *p == '{' || *p == '"')
1985
                        p = imap_parse_atom(session, p, buf, sizeof(buf), str);
1986
                else
1987
                        strncpy2(buf, p, sizeof(buf));
1988
                strtailchomp(buf, separator_str[0]);
1989
                if (buf[0] == '\0') continue;
1990
                if (!strcmp(buf, real_path)) continue;
1991
1992
                if (separator_str[0] != '\0')
1993
                        subst_char(buf, separator_str[0], '/');
1994
                name = g_basename(buf);
1995
                if (name[0] == '.') continue;
1996
1997
                loc_name = imap_modified_utf7_to_utf8(name);
1998
                loc_path = imap_modified_utf7_to_utf8(buf);
1999
                new_item = folder_item_new(loc_name, loc_path);
2000
                if (strcasestr(flags, "\\Noinferiors") != NULL)
2001
                        new_item->no_sub = TRUE;
2002
                if (strcmp(buf, "INBOX") != 0 &&
2003
                    strcasestr(flags, "\\Noselect") != NULL)
2004
                        new_item->no_select = TRUE;
2005
2006
                item_list = g_slist_prepend(item_list, new_item);
2007
2008
                debug_print("folder '%s' found.\n", loc_path);
2009
                g_free(loc_path);
2010
                g_free(loc_name);
2011
        }
2012
2013
        g_string_free(str, TRUE);
2014
2015
        item_list = g_slist_reverse(item_list);
2016
        return item_list;
2017
}
2018
2019
static GSList *imap_add_inter_folders(GSList *item_list, const gchar *root_path)
2020
{
2021
        FolderItem *item;
2022
        GSList *cur;
2023
        GSList *add_list = NULL;
2024
        GHashTable *exist;
2025
        const gchar *p;
2026
        gint root_path_len = 0;
2027
2028
        if (root_path)
2029
                root_path_len = strlen(root_path);
2030
2031
        exist = g_hash_table_new(g_str_hash, g_str_equal);
2032
2033
        for (cur = item_list; cur != NULL; cur = cur->next) {
2034
                item = FOLDER_ITEM(cur->data);
2035
2036
                if (root_path_len > 0 &&
2037
                    strncmp(root_path, item->path, root_path_len) != 0)
2038
                        continue;
2039
                p = item->path + root_path_len;
2040
                while (*p == '/') p++;
2041
                if (*p == '\0') continue;
2042
                g_hash_table_insert(exist, (gpointer)p, GINT_TO_POINTER(1));
2043
        }
2044
2045
        for (cur = item_list; cur != NULL; cur = cur->next) {
2046
                const gchar *q, *r;
2047
                gchar *parent, *full_parent;
2048
                FolderItem *new_item;
2049
2050
                item = FOLDER_ITEM(cur->data);
2051
2052
                if (root_path_len > 0 &&
2053
                    strncmp(root_path, item->path, root_path_len) != 0)
2054
                        continue;
2055
                p = item->path + root_path_len;
2056
                while (*p == '/') p++;
2057
                if (*p == '\0') continue;
2058
2059
                q = p;
2060
                while ((q = strchr(q, '/')) != NULL) {
2061
                        parent = g_strndup(p, q - p);
2062
                        if (!g_hash_table_lookup(exist, parent)) {
2063
                                if (root_path_len > 0)
2064
                                        full_parent = g_strconcat
2065
                                                (root_path, "/", parent, NULL);
2066
                                else
2067
                                        full_parent = g_strdup(parent);
2068
                                new_item = folder_item_new(g_basename(parent),
2069
                                                           full_parent);
2070
                                new_item->no_select = TRUE;
2071
                                add_list = g_slist_prepend(add_list, new_item);
2072
                                r = new_item->path + root_path_len;
2073
                                while (*r == '/') r++;
2074
                                g_hash_table_insert(exist, (gpointer)r,
2075
                                                    GINT_TO_POINTER(1));
2076
                                debug_print("intermediate folder '%s' added\n",
2077
                                            full_parent);
2078
                                g_free(full_parent);
2079
                        }
2080
                        g_free(parent);
2081
                        while (*q == '/') q++;
2082
                }
2083
        }
2084
2085
        g_hash_table_destroy(exist);
2086
2087
        add_list = g_slist_reverse(add_list);
2088
        item_list = g_slist_concat(item_list, add_list);
2089
2090
        return item_list;
2091
}
2092
2093
static GSList *imap_get_part_folder_list(GSList *item_list, FolderItem *item)
2094
{
2095
        FolderItem *cur_item;
2096
        GSList *part_list = NULL, *cur;
2097
        gint len;
2098
2099
        if (!item->path) {
2100
                debug_print("imap_get_part_folder_list(): get root folders\n");
2101
                for (cur = item_list; cur != NULL; cur = cur->next) {
2102
                        cur_item = FOLDER_ITEM(cur->data);
2103
2104
                        if (!strchr(cur_item->path, '/')) {
2105
                                part_list = g_slist_prepend(part_list,
2106
                                                            cur_item);
2107
                                debug_print("append '%s'\n", cur_item->path);
2108
                        }
2109
                }
2110
                part_list = g_slist_reverse(part_list);
2111
                return part_list;
2112
        }
2113
2114
        len = strlen(item->path);
2115
        debug_print("imap_get_part_folder_list(): get folders under '%s'\n",
2116
                    item->path);
2117
2118
        for (cur = item_list; cur != NULL; cur = cur->next) {
2119
                cur_item = FOLDER_ITEM(cur->data);
2120
2121
                if (!strncmp(cur_item->path, item->path, len) &&
2122
                    cur_item->path[len] == '/' &&
2123
                    !strchr(cur_item->path + len + 1, '/')) {
2124
                        part_list = g_slist_prepend(part_list, cur_item);
2125
                        debug_print("append '%s'\n", cur_item->path);
2126
                }
2127
        }
2128
2129
        part_list = g_slist_reverse(part_list);
2130
        return part_list;
2131
}
2132
2133
static gint imap_create_tree(Folder *folder)
2134
{
2135
        g_return_val_if_fail(folder != NULL, -1);
2136
        g_return_val_if_fail(folder->node != NULL, -1);
2137
        g_return_val_if_fail(folder->node->data != NULL, -1);
2138
        g_return_val_if_fail(folder->account != NULL, -1);
2139
2140
        imap_scan_tree(folder);
2141
        imap_create_missing_folders(folder);
2142
2143
        return 0;
2144
}
2145
2146
static void imap_create_missing_folders(Folder *folder)
2147
{
2148
        g_return_if_fail(folder != NULL);
2149
2150
        if (!folder->inbox)
2151
                folder->inbox = imap_create_special_folder
2152
                        (folder, F_INBOX, "INBOX");
2153
#if 0
2154
        if (!folder->outbox)
2155
                folder->outbox = imap_create_special_folder
2156
                        (folder, F_OUTBOX, "Sent");
2157
        if (!folder->draft)
2158
                folder->draft = imap_create_special_folder
2159
                        (folder, F_DRAFT, "Drafts");
2160
        if (!folder->queue)
2161
                folder->queue = imap_create_special_folder
2162
                        (folder, F_QUEUE, "Queue");
2163
#endif
2164
        if (!folder->trash)
2165
                folder->trash = imap_create_special_folder
2166
                        (folder, F_TRASH, "Trash");
2167
}
2168
2169
static FolderItem *imap_create_special_folder(Folder *folder,
2170
                                              SpecialFolderItemType stype,
2171
                                              const gchar *name)
2172
{
2173
        FolderItem *item;
2174
        FolderItem *new_item;
2175
2176
        g_return_val_if_fail(folder != NULL, NULL);
2177
        g_return_val_if_fail(folder->node != NULL, NULL);
2178
        g_return_val_if_fail(folder->node->data != NULL, NULL);
2179
        g_return_val_if_fail(folder->account != NULL, NULL);
2180
        g_return_val_if_fail(name != NULL, NULL);
2181
2182
        item = FOLDER_ITEM(folder->node->data);
2183
        new_item = imap_create_folder(folder, item, name);
2184
2185
        if (!new_item) {
2186
                g_warning(_("Can't create '%s'\n"), name);
2187
                if (!folder->inbox) return NULL;
2188
2189
                new_item = imap_create_folder(folder, folder->inbox, name);
2190
                if (!new_item)
2191
                        g_warning(_("Can't create '%s' under INBOX\n"), name);
2192
                else
2193
                        new_item->stype = stype;
2194
        } else
2195
                new_item->stype = stype;
2196
2197
        return new_item;
2198
}
2199
2200
static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2201
                                      const gchar *name)
2202
{
2203
        gchar *dirpath, *imap_path;
2204
        IMAPSession *session;
2205
        FolderItem *new_item;
2206
        gchar separator;
2207
        gchar *new_name;
2208
        const gchar *p;
2209
        gboolean no_sub = FALSE, no_select = FALSE;
2210
        gint ok;
2211
2212
        g_return_val_if_fail(folder != NULL, NULL);
2213
        g_return_val_if_fail(folder->account != NULL, NULL);
2214
        g_return_val_if_fail(parent != NULL, NULL);
2215
        g_return_val_if_fail(name != NULL, NULL);
2216
2217
        session = imap_session_get(folder);
2218
        if (!session) return NULL;
2219
2220
        if (!parent->parent && strcmp(name, "INBOX") == 0)
2221
                dirpath = g_strdup(name);
2222
        else if (parent->path)
2223
                dirpath = g_strconcat(parent->path, "/", name, NULL);
2224
        else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2225
                dirpath = g_strdup(name);
2226
        else if (folder->account->imap_dir && *folder->account->imap_dir) {
2227
                gchar *imap_dir;
2228
2229
                Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
2230
                strtailchomp(imap_dir, '/');
2231
                dirpath = g_strconcat(imap_dir, "/", name, NULL);
2232
        } else
2233
                dirpath = g_strdup(name);
2234
2235
        /* keep trailing directory separator to create a folder that contains
2236
           sub folder */
2237
        imap_path = imap_utf8_to_modified_utf7(dirpath);
2238
        strtailchomp(dirpath, '/');
2239
        Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
2240
        strtailchomp(new_name, '/');
2241
        separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
2242
        imap_path_separator_subst(imap_path, separator);
2243
        subst_char(new_name, '/', separator);
2244
2245
        if (strcmp(name, "INBOX") != 0) {
2246
                GPtrArray *argbuf;
2247
                gint i;
2248
                gboolean exist = FALSE;
2249
2250
                argbuf = g_ptr_array_new();
2251
                ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2252
                if (ok != IMAP_SUCCESS) {
2253
                        log_warning(_("can't create mailbox: LIST failed\n"));
2254
                        g_free(imap_path);
2255
                        g_free(dirpath);
2256
                        g_ptr_array_free(argbuf, TRUE);
2257
                        return NULL;
2258
                }
2259
2260
                for (i = 0; i < argbuf->len; i++) {
2261
                        gchar *str;
2262
                        str = g_ptr_array_index(argbuf, i);
2263
                        if (!strncmp(str, "LIST ", 5)) {
2264
                                exist = TRUE;
2265
                                if (strcasestr(str + 5, "\\Noinferiors"))
2266
                                        no_sub = TRUE;
2267
                                if (strcasestr(str + 5, "\\Noselect"))
2268
                                        no_select = TRUE;
2269
                                break;
2270
                        }
2271
                }
2272
2273
                if (!exist) {
2274
                        ok = imap_cmd_create(session, imap_path);
2275
                        if (ok != IMAP_SUCCESS) {
2276
                                log_warning(_("can't create mailbox\n"));
2277
                                g_free(imap_path);
2278
                                g_free(dirpath);
2279
                                return NULL;
2280
                        }
2281
2282
                        g_ptr_array_free(argbuf, TRUE);
2283
                        argbuf = g_ptr_array_new();
2284
                        ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2285
                        if (ok != IMAP_SUCCESS) {
2286
                                log_warning("LIST failed\n");
2287
                                g_free(imap_path);
2288
                                g_free(dirpath);
2289
                                g_ptr_array_free(argbuf, TRUE);
2290
                                return NULL;
2291
                        }
2292
2293
                        for (i = 0; i < argbuf->len; i++) {
2294
                                gchar *str;
2295
                                str = g_ptr_array_index(argbuf, i);
2296
                                if (!strncmp(str, "LIST ", 5)) {
2297
                                        if (strcasestr(str + 5, "\\Noinferiors"))
2298
                                                no_sub = TRUE;
2299
                                        if (strcasestr(str + 5, "\\Noselect"))
2300
                                                no_select = TRUE;
2301
                                        break;
2302
                                }
2303
                        }
2304
                }
2305
2306
                g_ptr_array_free(argbuf, TRUE);
2307
        }
2308
2309
        new_item = folder_item_new(new_name, dirpath);
2310
        new_item->no_sub = no_sub;
2311
        new_item->no_select = no_select;
2312
        folder_item_append(parent, new_item);
2313
        g_free(imap_path);
2314
        g_free(dirpath);
2315
2316
        dirpath = folder_item_get_path(new_item);
2317
        if (!is_dir_exist(dirpath))
2318
                make_dir_hier(dirpath);
2319
        g_free(dirpath);
2320
2321
        return new_item;
2322
}
2323
2324
static gint imap_rename_folder_real(Folder *folder, FolderItem *item,
2325
                                    FolderItem *new_parent, const gchar *name)
2326
{
2327
        gchar *newpath;
2328
        gchar *real_oldpath;
2329
        gchar *real_newpath;
2330
        gchar *paths[2];
2331
        gchar *old_cache_dir;
2332
        gchar *new_cache_dir;
2333
        IMAPSession *session;
2334
        gchar separator;
2335
        gint ok;
2336
        gint exists, recent, unseen;
2337
        guint32 uid_validity;
2338
2339
        g_return_val_if_fail(folder != NULL, -1);
2340
        g_return_val_if_fail(item != NULL, -1);
2341
        g_return_val_if_fail(folder == item->folder, -1);
2342
        g_return_val_if_fail(item->path != NULL, -1);
2343
        g_return_val_if_fail(new_parent != NULL || name != NULL, -1);
2344
        if (new_parent) {
2345
                g_return_val_if_fail(item != new_parent, -1);
2346
                g_return_val_if_fail(item->parent != new_parent, -1);
2347
                g_return_val_if_fail(item->folder == new_parent->folder, -1);
2348
                if (g_node_is_ancestor(item->node, new_parent->node)) {
2349
                        g_warning("folder to be moved is ancestor of new parent\n");
2350
                        return -1;
2351
                }
2352
        }
2353
2354
        session = imap_session_get(folder);
2355
        if (!session) return -1;
2356
2357
        real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2358
2359
        g_free(session->mbox);
2360
        session->mbox = NULL;
2361
        ok = imap_cmd_examine(session, "INBOX",
2362
                              &exists, &recent, &unseen, &uid_validity);
2363
        if (ok != IMAP_SUCCESS) {
2364
                g_free(real_oldpath);
2365
                return -1;
2366
        }
2367
2368
        separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
2369
        if (new_parent) {
2370
                if (name) {
2371
                        if (new_parent->path)
2372
                                newpath = g_strconcat(new_parent->path,
2373
                                                      "/", name, NULL);
2374
                        else
2375
                                newpath = g_strdup(name);
2376
                } else {
2377
                        gchar *name_;
2378
2379
                        name_ = g_path_get_basename(item->path);
2380
                        if (new_parent->path)
2381
                                newpath = g_strconcat(new_parent->path,
2382
                                                      "/", name_, NULL);
2383
                        else
2384
                                newpath = g_strdup(name_);
2385
                        AUTORELEASE_STR(name_, );
2386
                        name = name_;
2387
                }
2388
        } else {
2389
                if (strchr(item->path, '/')) {
2390
                        gchar *dirpath;
2391
2392
                        dirpath = g_dirname(item->path);
2393
                        newpath = g_strconcat(dirpath, "/", name, NULL);
2394
                        g_free(dirpath);
2395
                } else
2396
                        newpath = g_strdup(name);
2397
        }
2398
2399
        real_newpath = imap_utf8_to_modified_utf7(newpath);
2400
        imap_path_separator_subst(real_newpath, separator);
2401
2402
        ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2403
        if (ok != IMAP_SUCCESS) {
2404
                log_warning(_("can't rename mailbox: %s to %s\n"),
2405
                            real_oldpath, real_newpath);
2406
                g_free(real_oldpath);
2407
                g_free(newpath);
2408
                g_free(real_newpath);
2409
                return -1;
2410
        }
2411
2412
        if (new_parent) {
2413
                g_node_unlink(item->node);
2414
                g_node_append(new_parent->node, item->node);
2415
                item->parent = new_parent;
2416
        }
2417
2418
        g_free(item->name);
2419
        item->name = g_strdup(name);
2420
2421
        old_cache_dir = folder_item_get_path(item);
2422
2423
        paths[0] = g_strdup(item->path);
2424
        paths[1] = newpath;
2425
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2426
                        imap_rename_folder_func, paths);
2427
2428
        if (is_dir_exist(old_cache_dir)) {
2429
                new_cache_dir = folder_item_get_path(item);
2430
                if (g_rename(old_cache_dir, new_cache_dir) < 0) {
2431
                        FILE_OP_ERROR(old_cache_dir, "rename");
2432
                }
2433
                g_free(new_cache_dir);
2434
        }
2435
2436
        g_free(old_cache_dir);
2437
        g_free(paths[0]);
2438
        g_free(newpath);
2439
        g_free(real_oldpath);
2440
        g_free(real_newpath);
2441
2442
        return 0;
2443
}
2444
2445
static gint imap_rename_folder(Folder *folder, FolderItem *item,
2446
                               const gchar *name)
2447
{
2448
        return imap_rename_folder_real(folder, item, NULL, name);
2449
}
2450
2451
static gint imap_move_folder(Folder *folder, FolderItem *item,
2452
                             FolderItem *new_parent)
2453
{
2454
        return imap_rename_folder_real(folder, item, new_parent, NULL);
2455
}
2456
2457
static gint imap_remove_folder(Folder *folder, FolderItem *item)
2458
{
2459
        gint ok;
2460
        IMAPSession *session;
2461
        gchar *path;
2462
        gchar *cache_dir;
2463
        gint exists, recent, unseen;
2464
        guint32 uid_validity;
2465
2466
        g_return_val_if_fail(folder != NULL, -1);
2467
        g_return_val_if_fail(item != NULL, -1);
2468
        g_return_val_if_fail(item->path != NULL, -1);
2469
2470
        session = imap_session_get(folder);
2471
        if (!session) return -1;
2472
2473
        path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2474
2475
        ok = imap_cmd_examine(session, "INBOX",
2476
                              &exists, &recent, &unseen, &uid_validity);
2477
        if (ok != IMAP_SUCCESS) {
2478
                g_free(path);
2479
                return -1;
2480
        }
2481
2482
        ok = imap_cmd_delete(session, path);
2483
        if (ok != IMAP_SUCCESS) {
2484
                log_warning(_("can't delete mailbox\n"));
2485
                g_free(path);
2486
                return -1;
2487
        }
2488
2489
        g_free(path);
2490
        cache_dir = folder_item_get_path(item);
2491
        if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2492
                g_warning("can't remove directory '%s'\n", cache_dir);
2493
        g_free(cache_dir);
2494
        folder_item_remove(item);
2495
2496
        return 0;
2497
}
2498
2499
static GSList *imap_get_uncached_messages(IMAPSession *session,
2500
                                          FolderItem *item,
2501
                                          guint32 first_uid, guint32 last_uid,
2502
                                          gint exists, gboolean update_count)
2503
{
2504
        gchar *tmp;
2505
        GSList *newlist = NULL;
2506
        GSList *llast = NULL;
2507
        GString *str;
2508
        MsgInfo *msginfo;
2509
        gchar seq_set[22];
2510
        gint count = 1;
2511
        GTimeVal tv_prev, tv_cur;
2512
2513
        g_return_val_if_fail(session != NULL, NULL);
2514
        g_return_val_if_fail(item != NULL, NULL);
2515
        g_return_val_if_fail(item->folder != NULL, NULL);
2516
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, NULL);
2517
        g_return_val_if_fail(first_uid <= last_uid, NULL);
2518
2519
        g_get_current_time(&tv_prev);
2520
        ui_update();
2521
2522
        if (first_uid == 0 && last_uid == 0)
2523
                strcpy(seq_set, "1:*");
2524
        else
2525
                g_snprintf(seq_set, sizeof(seq_set), "%u:%u",
2526
                           first_uid, last_uid);
2527
        if (imap_cmd_envelope(session, seq_set) != IMAP_SUCCESS) {
2528
                log_warning(_("can't get envelope\n"));
2529
                return NULL;
2530
        }
2531
2532
        str = g_string_new(NULL);
2533
2534
        for (;;) {
2535
                if (exists > 0 && count <= exists) {
2536
                        g_get_current_time(&tv_cur);
2537
                        if (tv_cur.tv_sec > tv_prev.tv_sec ||
2538
                            tv_cur.tv_usec - tv_prev.tv_usec >
2539
                            PROGRESS_UPDATE_INTERVAL * 1000) {
2540
                                status_print
2541
                                        (_("Getting message headers (%d / %d)"),
2542
                                         count, exists);
2543
                                progress_show(count, exists);
2544
                                ui_update();
2545
                                tv_prev = tv_cur;
2546
                        }
2547
                }
2548
                ++count;
2549
2550
                if (sock_getline(SESSION(session)->sock, &tmp) < 0) {
2551
                        log_warning(_("error occurred while getting envelope.\n"));
2552
                        g_string_free(str, TRUE);
2553
                        progress_show(0, 0);
2554
                        return newlist;
2555
                }
2556
                strretchomp(tmp);
2557
                if (tmp[0] != '*' || tmp[1] != ' ') {
2558
                        log_print("IMAP4< %s\n", tmp);
2559
                        g_free(tmp);
2560
                        break;
2561
                }
2562
                if (strstr(tmp, "FETCH") == NULL) {
2563
                        log_print("IMAP4< %s\n", tmp);
2564
                        g_free(tmp);
2565
                        continue;
2566
                }
2567
                log_print("IMAP4< %s\n", tmp);
2568
                g_string_assign(str, tmp);
2569
                g_free(tmp);
2570
2571
                msginfo = imap_parse_envelope(session, item, str);
2572
                if (!msginfo) {
2573
                        log_warning(_("can't parse envelope: %s\n"), str->str);
2574
                        continue;
2575
                }
2576
                if (update_count) {
2577
                        if (MSG_IS_NEW(msginfo->flags))
2578
                                item->new++;
2579
                        if (MSG_IS_UNREAD(msginfo->flags))
2580
                                item->unread++;
2581
                }
2582
                if (item->stype == F_QUEUE) {
2583
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2584
                } else if (item->stype == F_DRAFT) {
2585
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2586
                }
2587
2588
                msginfo->folder = item;
2589
2590
                if (!newlist)
2591
                        llast = newlist = g_slist_append(newlist, msginfo);
2592
                else {
2593
                        llast = g_slist_append(llast, msginfo);
2594
                        llast = llast->next;
2595
                }
2596
2597
                if (update_count)
2598
                        item->total++;
2599
        }
2600
2601
        progress_show(0, 0);
2602
        g_string_free(str, TRUE);
2603
2604
        session_set_access_time(SESSION(session));
2605
2606
        return newlist;
2607
}
2608
2609
static void imap_delete_cached_message(FolderItem *item, guint32 uid)
2610
{
2611
        gchar *dir;
2612
        gchar *file;
2613
2614
        g_return_if_fail(item != NULL);
2615
        g_return_if_fail(item->folder != NULL);
2616
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2617
2618
        dir = folder_item_get_path(item);
2619
        file = g_strdup_printf("%s%c%u", dir, G_DIR_SEPARATOR, uid);
2620
2621
        debug_print("Deleting cached message: %s\n", file);
2622
2623
        g_unlink(file);
2624
2625
        g_free(file);
2626
        g_free(dir);
2627
}
2628
2629
static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
2630
                                           guint32 first_uid, guint32 last_uid)
2631
{
2632
        GSList *cur, *next;
2633
        MsgInfo *msginfo;
2634
        gchar *dir;
2635
2636
        g_return_val_if_fail(item != NULL, mlist);
2637
        g_return_val_if_fail(item->folder != NULL, mlist);
2638
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, mlist);
2639
2640
        if (first_uid == 0 && last_uid == 0)
2641
                return mlist;
2642
2643
        debug_print("Deleting cached messages %u - %u ... ",
2644
                    first_uid, last_uid);
2645
2646
        dir = folder_item_get_path(item);
2647
        if (is_dir_exist(dir))
2648
                remove_numbered_files(dir, first_uid, last_uid);
2649
        g_free(dir);
2650
2651
        for (cur = mlist; cur != NULL; ) {
2652
                next = cur->next;
2653
2654
                msginfo = (MsgInfo *)cur->data;
2655
                if (msginfo != NULL && first_uid <= msginfo->msgnum &&
2656
                    msginfo->msgnum <= last_uid) {
2657
                        procmsg_msginfo_free(msginfo);
2658
                        mlist = g_slist_remove(mlist, msginfo);
2659
                }
2660
2661
                cur = next;
2662
        }
2663
2664
        debug_print("done.\n");
2665
2666
        return mlist;
2667
}
2668
2669
static void imap_delete_all_cached_messages(FolderItem *item)
2670
{
2671
        gchar *dir;
2672
2673
        g_return_if_fail(item != NULL);
2674
        g_return_if_fail(item->folder != NULL);
2675
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2676
2677
        debug_print("Deleting all cached messages... ");
2678
2679
        dir = folder_item_get_path(item);
2680
        if (is_dir_exist(dir))
2681
                remove_all_numbered_files(dir);
2682
        g_free(dir);
2683
2684
        debug_print("done.\n");
2685
}
2686
2687
#if USE_SSL
2688
static SockInfo *imap_open(const gchar *server, gushort port,
2689
                           SSLType ssl_type)
2690
#else
2691
static SockInfo *imap_open(const gchar *server, gushort port)
2692
#endif
2693
{
2694
        SockInfo *sock;
2695
2696
        if ((sock = sock_connect(server, port)) == NULL) {
2697
                log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2698
                            server, port);
2699
                return NULL;
2700
        }
2701
2702
#if USE_SSL
2703
        if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2704
                log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2705
                            server, port);
2706
                sock_close(sock);
2707
                return NULL;
2708
        }
2709
#endif
2710
2711
        return sock;
2712
}
2713
2714
static GList *imap_parse_namespace_str(gchar *str)
2715
{
2716
        gchar *p = str;
2717
        gchar *name;
2718
        gchar *separator;
2719
        IMAPNameSpace *namespace;
2720
        GList *ns_list = NULL;
2721
2722
        while (*p != '\0') {
2723
                /* parse ("#foo" "/") */
2724
2725
                while (*p && *p != '(') p++;
2726
                if (*p == '\0') break;
2727
                p++;
2728
2729
                while (*p && *p != '"') p++;
2730
                if (*p == '\0') break;
2731
                p++;
2732
                name = p;
2733
2734
                while (*p && *p != '"') p++;
2735
                if (*p == '\0') break;
2736
                *p = '\0';
2737
                p++;
2738
2739
                while (*p && g_ascii_isspace(*p)) p++;
2740
                if (*p == '\0') break;
2741
                if (strncmp(p, "NIL", 3) == 0)
2742
                        separator = NULL;
2743
                else if (*p == '"') {
2744
                        p++;
2745
                        separator = p;
2746
                        while (*p && *p != '"') p++;
2747
                        if (*p == '\0') break;
2748
                        *p = '\0';
2749
                        p++;
2750
                } else break;
2751
2752
                while (*p && *p != ')') p++;
2753
                if (*p == '\0') break;
2754
                p++;
2755
2756
                namespace = g_new(IMAPNameSpace, 1);
2757
                namespace->name = g_strdup(name);
2758
                namespace->separator = separator ? separator[0] : '\0';
2759
                ns_list = g_list_append(ns_list, namespace);
2760
        }
2761
2762
        return ns_list;
2763
}
2764
2765
static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2766
{
2767
        gchar *ns_str = NULL;
2768
        gchar **str_array;
2769
2770
        g_return_if_fail(session != NULL);
2771
        g_return_if_fail(folder != NULL);
2772
2773
        if (folder->ns_personal != NULL ||
2774
            folder->ns_others   != NULL ||
2775
            folder->ns_shared   != NULL)
2776
                return;
2777
2778
        if (imap_cmd_namespace(session, &ns_str) != IMAP_SUCCESS) {
2779
                log_warning(_("can't get namespace\n"));
2780
                imap_get_namespace_by_list(session, folder);
2781
                return;
2782
        }
2783
2784
        str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2785
        if (str_array[0])
2786
                folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2787
        if (str_array[0] && str_array[1])
2788
                folder->ns_others = imap_parse_namespace_str(str_array[1]);
2789
        if (str_array[0] && str_array[1] && str_array[2])
2790
                folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2791
        g_strfreev(str_array);
2792
        g_free(ns_str);
2793
}
2794
2795
static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2796
{
2797
        GSList *item_list, *cur;
2798
        gchar separator = '\0';
2799
        IMAPNameSpace *namespace;
2800
2801
        g_return_if_fail(session != NULL);
2802
        g_return_if_fail(folder != NULL);
2803
2804
        if (folder->ns_personal != NULL ||
2805
            folder->ns_others   != NULL ||
2806
            folder->ns_shared   != NULL)
2807
                return;
2808
2809
        imap_cmd_gen_send(session, "LIST \"\" \"\"");
2810
        item_list = imap_parse_list(session, "", &separator);
2811
        for (cur = item_list; cur != NULL; cur = cur->next)
2812
                folder_item_destroy(FOLDER_ITEM(cur->data));
2813
        g_slist_free(item_list);
2814
2815
        namespace = g_new(IMAPNameSpace, 1);
2816
        namespace->name = g_strdup("");
2817
        namespace->separator = separator;
2818
        folder->ns_personal = g_list_append(NULL, namespace);
2819
}
2820
2821
static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2822
                                                    const gchar *path)
2823
{
2824
        IMAPNameSpace *namespace = NULL;
2825
        gchar *tmp_path, *name;
2826
2827
        if (!path) path = "";
2828
2829
        for (; ns_list != NULL; ns_list = ns_list->next) {
2830
                IMAPNameSpace *tmp_ns = ns_list->data;
2831
2832
                Xstrcat_a(tmp_path, path, "/", return namespace);
2833
                Xstrdup_a(name, tmp_ns->name, return namespace);
2834
                if (tmp_ns->separator && tmp_ns->separator != '/') {
2835
                        subst_char(tmp_path, tmp_ns->separator, '/');
2836
                        subst_char(name, tmp_ns->separator, '/');
2837
                }
2838
                if (strncmp(tmp_path, name, strlen(name)) == 0)
2839
                        namespace = tmp_ns;
2840
        }
2841
2842
        return namespace;
2843
}
2844
2845
static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2846
                                          const gchar *path)
2847
{
2848
        IMAPNameSpace *namespace;
2849
2850
        g_return_val_if_fail(folder != NULL, NULL);
2851
2852
        namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2853
        if (namespace) return namespace;
2854
        namespace = imap_find_namespace_from_list(folder->ns_others, path);
2855
        if (namespace) return namespace;
2856
        namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2857
        if (namespace) return namespace;
2858
2859
        return NULL;
2860
}
2861
2862
static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2863
{
2864
        IMAPNameSpace *namespace;
2865
        gchar separator = '/';
2866
2867
        namespace = imap_find_namespace(folder, path);
2868
        if (namespace && namespace->separator)
2869
                separator = namespace->separator;
2870
2871
        return separator;
2872
}
2873
2874
static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2875
{
2876
        gchar *real_path;
2877
        gchar separator;
2878
2879
        g_return_val_if_fail(folder != NULL, NULL);
2880
        g_return_val_if_fail(path != NULL, NULL);
2881
2882
        real_path = imap_utf8_to_modified_utf7(path);
2883
        separator = imap_get_path_separator(folder, path);
2884
        imap_path_separator_subst(real_path, separator);
2885
2886
        return real_path;
2887
}
2888
2889
static gchar *imap_parse_atom(IMAPSession *session, gchar *src,
2890
                              gchar *dest, gint dest_len, GString *str)
2891
{
2892
        gchar *cur_pos = src;
2893
        gchar *nextline;
2894
2895
        g_return_val_if_fail(str != NULL, cur_pos);
2896
2897
        /* read the next line if the current response buffer is empty */
2898
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
2899
        while (*cur_pos == '\0') {
2900
                if (sock_getline(SESSION(session)->sock, &nextline) < 0)
2901
                        return cur_pos;
2902
                g_string_assign(str, nextline);
2903
                cur_pos = str->str;
2904
                strretchomp(nextline);
2905
                /* log_print("IMAP4< %s\n", nextline); */
2906
                debug_print("IMAP4< %s\n", nextline);
2907
                g_free(nextline);
2908
2909
                while (g_ascii_isspace(*cur_pos)) cur_pos++;
2910
        }
2911
2912
        if (*cur_pos == '~' && *(cur_pos + 1) == '{')
2913
                cur_pos++;
2914
2915
        if (!strncmp(cur_pos, "NIL", 3)) {
2916
                *dest = '\0';
2917
                cur_pos += 3;
2918
        } else if (*cur_pos == '\"') {
2919
                gchar *p;
2920
2921
                p = get_quoted(cur_pos, '\"', dest, dest_len);
2922
                cur_pos = p ? p : cur_pos + 2;
2923
        } else if (*cur_pos == '{') {
2924
                gchar buf[32];
2925
                gint len;
2926
                gint block_len = 0;
2927
2928
                cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2929
                len = atoi(buf);
2930
                g_return_val_if_fail(len >= 0, cur_pos);
2931
2932
                g_string_truncate(str, 0);
2933
                cur_pos = str->str;
2934
2935
                do {
2936
                        gint cur_len;
2937
2938
                        cur_len = sock_getline(SESSION(session)->sock,
2939
                                               &nextline);
2940
                        if (cur_len < 0)
2941
                                return cur_pos;
2942
                        block_len += cur_len;
2943
                        subst_null(nextline, cur_len, ' ');
2944
                        g_string_append(str, nextline);
2945
                        cur_pos = str->str;
2946
                        strretchomp(nextline);
2947
                        /* log_print("IMAP4< %s\n", nextline); */
2948
                        debug_print("IMAP4< %s\n", nextline);
2949
                        g_free(nextline);
2950
                } while (block_len < len);
2951
2952
                memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2953
                dest[MIN(len, dest_len - 1)] = '\0';
2954
                cur_pos += len;
2955
        }
2956
2957
        return cur_pos;
2958
}
2959
2960
static gchar *imap_get_header(IMAPSession *session, gchar *cur_pos,
2961
                              gchar **headers, GString *str)
2962
{
2963
        gchar *nextline;
2964
        gchar buf[32];
2965
        gint len;
2966
        gint block_len = 0;
2967
2968
        *headers = NULL;
2969
2970
        g_return_val_if_fail(str != NULL, cur_pos);
2971
2972
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
2973
        if (*cur_pos == '~' && *(cur_pos + 1) == '{')
2974
                cur_pos++;
2975
2976
        g_return_val_if_fail(*cur_pos == '{', cur_pos);
2977
2978
        cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2979
        len = atoi(buf);
2980
        g_return_val_if_fail(len >= 0, cur_pos);
2981
2982
        g_string_truncate(str, 0);
2983
        cur_pos = str->str;
2984
2985
        do {
2986
                gint cur_len;
2987
2988
                cur_len = sock_getline(SESSION(session)->sock, &nextline);
2989
                if (cur_len < 0)
2990
                        return cur_pos;
2991
                block_len += cur_len;
2992
                subst_null(nextline, cur_len, ' ');
2993
                g_string_append(str, nextline);
2994
                cur_pos = str->str;
2995
                /* strretchomp(nextline); */
2996
                /* debug_print("IMAP4< %s\n", nextline); */
2997
                g_free(nextline);
2998
        } while (block_len < len);
2999
3000
        debug_print("IMAP4< [contents of RFC822.HEADER]\n");
3001
3002
        *headers = g_strndup(cur_pos, len);
3003
        cur_pos += len;
3004
3005
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
3006
        while (*cur_pos == '\0') {
3007
                if (sock_getline(SESSION(session)->sock, &nextline) < 0)
3008
                        return cur_pos;
3009
                g_string_assign(str, nextline);
3010
                cur_pos = str->str;
3011
                strretchomp(nextline);
3012
                debug_print("IMAP4< %s\n", nextline);
3013
                g_free(nextline);
3014
3015
                while (g_ascii_isspace(*cur_pos)) cur_pos++;
3016
        }
3017
3018
        return cur_pos;
3019
}
3020
3021
static MsgFlags imap_parse_flags(const gchar *flag_str)  
3022
{
3023
        const gchar *p = flag_str;
3024
        MsgFlags flags = {0, 0};
3025
3026
        flags.perm_flags = MSG_UNREAD;
3027
3028
        while ((p = strchr(p, '\\')) != NULL) {
3029
                p++;
3030
3031
                if (g_ascii_strncasecmp(p, "Recent", 6) == 0 &&
3032
                    MSG_IS_UNREAD(flags)) {
3033
                        MSG_SET_PERM_FLAGS(flags, MSG_NEW);
3034
                } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
3035
                        MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
3036
                } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
3037
                        MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
3038
                } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
3039
                        MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
3040
                } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
3041
                        MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
3042
                }
3043
        }
3044
3045
        return flags;
3046
}
3047
3048
static IMAPFlags imap_parse_imap_flags(const gchar *flag_str)  
3049
{
3050
        const gchar *p = flag_str;
3051
        IMAPFlags flags = 0;
3052
3053
        while ((p = strchr(p, '\\')) != NULL) {
3054
                p++;
3055
3056
                if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
3057
                        flags |= IMAP_FLAG_SEEN;
3058
                } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
3059
                        flags |= IMAP_FLAG_DELETED;
3060
                } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
3061
                        flags |= IMAP_FLAG_FLAGGED;
3062
                } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
3063
                        flags |= IMAP_FLAG_ANSWERED;
3064
                }
3065
        }
3066
3067
        return flags;
3068
}
3069
3070
static MsgInfo *imap_parse_envelope(IMAPSession *session, FolderItem *item,
3071
                                    GString *line_str)
3072
{
3073
        gchar buf[IMAPBUFSIZE];
3074
        MsgInfo *msginfo = NULL;
3075
        gchar *cur_pos;
3076
        gint msgnum;
3077
        guint32 uid = 0;
3078
        size_t size = 0;
3079
        MsgFlags flags = {0, 0}, imap_flags = {0, 0};
3080
3081
        g_return_val_if_fail(line_str != NULL, NULL);
3082
        g_return_val_if_fail(line_str->str[0] == '*' &&
3083
                             line_str->str[1] == ' ', NULL);
3084
3085
        MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3086
        if (item->stype == F_QUEUE) {
3087
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3088
        } else if (item->stype == F_DRAFT) {
3089
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3090
        }
3091
3092
        cur_pos = line_str->str + 2;
3093
3094
#define PARSE_ONE_ELEMENT(ch)                                        \
3095
{                                                                \
3096
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
3097
        if (cur_pos == NULL) {                                        \
3098
                g_warning("cur_pos == NULL\n");                        \
3099
                procmsg_msginfo_free(msginfo);                        \
3100
                return NULL;                                        \
3101
        }                                                        \
3102
}
3103
3104
        PARSE_ONE_ELEMENT(' ');
3105
        msgnum = atoi(buf);
3106
3107
        PARSE_ONE_ELEMENT(' ');
3108
        g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
3109
3110
        g_return_val_if_fail(*cur_pos == '(', NULL);
3111
        cur_pos++;
3112
3113
        while (*cur_pos != '\0' && *cur_pos != ')') {
3114
                while (*cur_pos == ' ') cur_pos++;
3115
3116
                if (!strncmp(cur_pos, "UID ", 4)) {
3117
                        cur_pos += 4;
3118
                        uid = strtoul(cur_pos, &cur_pos, 10);
3119
                } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
3120
                        cur_pos += 6;
3121
                        if (*cur_pos != '(') {
3122
                                g_warning("*cur_pos != '('\n");
3123
                                procmsg_msginfo_free(msginfo);
3124
                                return NULL;
3125
                        }
3126
                        cur_pos++;
3127
                        PARSE_ONE_ELEMENT(')');
3128
                        imap_flags = imap_parse_flags(buf);
3129
                } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
3130
                        cur_pos += 12;
3131
                        size = strtol(cur_pos, &cur_pos, 10);
3132
                } else if (!strncmp(cur_pos, "RFC822.HEADER ", 14)) {
3133
                        gchar *headers;
3134
3135
                        cur_pos += 14;
3136
                        cur_pos = imap_get_header(session, cur_pos, &headers,
3137
                                                  line_str);
3138
                        msginfo = procheader_parse_str(headers, flags, FALSE);
3139
                        g_free(headers);
3140
                } else {
3141
                        g_warning("invalid FETCH response: %s\n", cur_pos);
3142
                        break;
3143
                }
3144
        }
3145
3146
#undef PARSE_ONE_ELEMENT
3147
3148
        if (msginfo) {
3149
                msginfo->msgnum = uid;
3150
                msginfo->size = size;
3151
                msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
3152
                msginfo->flags.perm_flags = imap_flags.perm_flags;
3153
        }
3154
3155
        return msginfo;
3156
}
3157
3158
static gint imap_msg_list_change_perm_flags(GSList *msglist, MsgPermFlags flags,
3159
                                            gboolean is_set)
3160
{
3161
        Folder *folder;
3162
        IMAPSession *session;
3163
        IMAPFlags iflags = 0;
3164
        MsgInfo *msginfo;
3165
        GSList *seq_list, *cur;
3166
        gint ok = IMAP_SUCCESS;
3167
3168
        if (msglist == NULL) return IMAP_SUCCESS;
3169
3170
        msginfo = (MsgInfo *)msglist->data;
3171
        g_return_val_if_fail(msginfo != NULL, -1);
3172
3173
        g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
3174
        g_return_val_if_fail(msginfo->folder != NULL, -1);
3175
        g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
3176
3177
        folder = msginfo->folder->folder;
3178
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
3179
3180
        session = imap_session_get(folder);
3181
        if (!session) return -1;
3182
3183
        ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3184
                         NULL, NULL, NULL, NULL);
3185
        if (ok != IMAP_SUCCESS)
3186
                return ok;
3187
3188
        seq_list = imap_get_seq_set_from_msglist(msglist, 0);
3189
3190
        if (flags & MSG_MARKED)  iflags |= IMAP_FLAG_FLAGGED;
3191
        if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
3192
3193
        for (cur = seq_list; cur != NULL; cur = cur->next) {
3194
                gchar *seq_set = (gchar *)cur->data;
3195
3196
                if (iflags) {
3197
                        ok = imap_set_message_flags(session, seq_set, iflags,
3198
                                                    is_set);
3199
                        if (ok != IMAP_SUCCESS) break;
3200
                }
3201
3202
                if (flags & MSG_UNREAD) {
3203
                        ok = imap_set_message_flags(session, seq_set,
3204
                                                    IMAP_FLAG_SEEN, !is_set);
3205
                        if (ok != IMAP_SUCCESS) break;
3206
                }
3207
        }
3208
3209
        imap_seq_set_free(seq_list);
3210
3211
        return ok;
3212
}
3213
3214
gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3215
{
3216
        GSList msglist;
3217
3218
        msglist.data = msginfo;
3219
        msglist.next = NULL;
3220
3221
        return imap_msg_list_change_perm_flags(&msglist, flags, TRUE);
3222
}
3223
3224
gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3225
{
3226
        GSList msglist;
3227
3228
        msglist.data = msginfo;
3229
        msglist.next = NULL;
3230
3231
        return imap_msg_list_change_perm_flags(&msglist, flags, FALSE);
3232
}
3233
3234
gint imap_msg_list_set_perm_flags(GSList *msglist, MsgPermFlags flags)
3235
{
3236
        return imap_msg_list_change_perm_flags(msglist, flags, TRUE);
3237
}
3238
3239
gint imap_msg_list_unset_perm_flags(GSList *msglist, MsgPermFlags flags)
3240
{
3241
        return imap_msg_list_change_perm_flags(msglist, flags, FALSE);
3242
}
3243
3244
static gchar *imap_get_flag_str(IMAPFlags flags)
3245
{
3246
        GString *str;
3247
        gchar *ret;
3248
3249
        str = g_string_new(NULL);
3250
3251
        if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
3252
        if (IMAP_IS_ANSWERED(flags))        g_string_append(str, "\\Answered ");
3253
        if (IMAP_IS_FLAGGED(flags))        g_string_append(str, "\\Flagged ");
3254
        if (IMAP_IS_DELETED(flags))        g_string_append(str, "\\Deleted ");
3255
        if (IMAP_IS_DRAFT(flags))        g_string_append(str, "\\Draft");
3256
3257
        if (str->len > 0 && str->str[str->len - 1] == ' ')
3258
                g_string_truncate(str, str->len - 1);
3259
3260
        ret = str->str;
3261
        g_string_free(str, FALSE);
3262
3263
        return ret;
3264
}
3265
3266
static gint imap_set_message_flags(IMAPSession *session,
3267
                                   const gchar *seq_set,
3268
                                   IMAPFlags flags,
3269
                                   gboolean is_set)
3270
{
3271
        gchar *cmd;
3272
        gchar *flag_str;
3273
        gint ok;
3274
3275
        flag_str = imap_get_flag_str(flags);
3276
        cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
3277
                          flag_str, ")", NULL);
3278
        g_free(flag_str);
3279
3280
        ok = imap_cmd_store(session, seq_set, cmd);
3281
        g_free(cmd);
3282
3283
        return ok;
3284
}
3285
3286
static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3287
                        const gchar *path,
3288
                        gint *exists, gint *recent, gint *unseen,
3289
                        guint32 *uid_validity)
3290
{
3291
        gchar *real_path;
3292
        gint ok;
3293
        gint exists_, recent_, unseen_;
3294
        guint32 uid_validity_;
3295
3296
        if (!exists || !recent || !unseen || !uid_validity) {
3297
                if (session->mbox && strcmp(session->mbox, path) == 0)
3298
                        return IMAP_SUCCESS;
3299
                exists = &exists_;
3300
                recent = &recent_;
3301
                unseen = &unseen_;
3302
                uid_validity = &uid_validity_;
3303
        }
3304
3305
        g_free(session->mbox);
3306
        session->mbox = NULL;
3307
3308
        real_path = imap_get_real_path(folder, path);
3309
        ok = imap_cmd_select(session, real_path,
3310
                             exists, recent, unseen, uid_validity);
3311
        if (ok != IMAP_SUCCESS)
3312
                log_warning(_("can't select folder: %s\n"), real_path);
3313
        else
3314
                session->mbox = g_strdup(path);
3315
        g_free(real_path);
3316
3317
        return ok;
3318
}
3319
3320
#define THROW(err) { ok = err; goto catch; }
3321
3322
static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3323
                        const gchar *path,
3324
                        gint *messages, gint *recent,
3325
                        guint32 *uid_next, guint32 *uid_validity,
3326
                        gint *unseen)
3327
{
3328
        gchar *real_path;
3329
        gchar *real_path_;
3330
        gint ok;
3331
        GPtrArray *argbuf = NULL;
3332
        gchar *str;
3333
3334
        if (messages && recent && uid_next && uid_validity && unseen) {
3335
                *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
3336
                argbuf = g_ptr_array_new();
3337
        }
3338
3339
        real_path = imap_get_real_path(folder, path);
3340
        QUOTE_IF_REQUIRED(real_path_, real_path);
3341
        imap_cmd_gen_send(session, "STATUS %s "
3342
                          "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
3343
                          real_path_);
3344
3345
        ok = imap_cmd_ok(session, argbuf);
3346
        if (ok != IMAP_SUCCESS)
3347
                log_warning(_("error on imap command: STATUS\n"));
3348
        if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
3349
3350
        str = search_array_str(argbuf, "STATUS");
3351
        if (!str) THROW(IMAP_ERROR);
3352
3353
        str = strchr(str, '(');
3354
        if (!str) THROW(IMAP_ERROR);
3355
        str++;
3356
        while (*str != '\0' && *str != ')') {
3357
                while (*str == ' ') str++;
3358
3359
                if (!strncmp(str, "MESSAGES ", 9)) {
3360
                        str += 9;
3361
                        *messages = strtol(str, &str, 10);
3362
                } else if (!strncmp(str, "RECENT ", 7)) {
3363
                        str += 7;
3364
                        *recent = strtol(str, &str, 10);
3365
                } else if (!strncmp(str, "UIDNEXT ", 8)) {
3366
                        str += 8;
3367
                        *uid_next = strtoul(str, &str, 10);
3368
                } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
3369
                        str += 12;
3370
                        *uid_validity = strtoul(str, &str, 10);
3371
                } else if (!strncmp(str, "UNSEEN ", 7)) {
3372
                        str += 7;
3373
                        *unseen = strtol(str, &str, 10);
3374
                } else {
3375
                        g_warning("invalid STATUS response: %s\n", str);
3376
                        break;
3377
                }
3378
        }
3379
3380
catch:
3381
        g_free(real_path);
3382
        if (argbuf) {
3383
                ptr_array_free_strings(argbuf);
3384
                g_ptr_array_free(argbuf, TRUE);
3385
        }
3386
3387
        return ok;
3388
}
3389
3390
#undef THROW
3391
3392
static gboolean imap_has_capability(IMAPSession        *session,
3393
                                    const gchar *capability)
3394
{
3395
        gchar **p;
3396
3397
        for (p = session->capability; *p != NULL; ++p) {
3398
                if (!g_ascii_strcasecmp(*p, capability))
3399
                        return TRUE;
3400
        }
3401
3402
        return FALSE;
3403
}
3404
3405
static void imap_capability_free(IMAPSession *session)
3406
{
3407
        if (session->capability) {
3408
                g_strfreev(session->capability);
3409
                session->capability = NULL;
3410
        }
3411
}
3412
3413
3414
/* low-level IMAP4rev1 commands */
3415
3416
#define THROW(err) { ok = err; goto catch; }
3417
3418
static gint imap_cmd_capability(IMAPSession *session)
3419
{
3420
        gint ok;
3421
        GPtrArray *argbuf;
3422
        gchar *capability;
3423
3424
        argbuf = g_ptr_array_new();
3425
3426
        imap_cmd_gen_send(session, "CAPABILITY");
3427
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3428
3429
        capability = search_array_str(argbuf, "CAPABILITY ");
3430
        if (!capability) THROW(IMAP_ERROR);
3431
3432
        capability += strlen("CAPABILITY ");
3433
3434
        imap_capability_free(session);
3435
        session->capability = g_strsplit(capability, " ", -1);
3436
3437
catch:
3438
        ptr_array_free_strings(argbuf);
3439
        g_ptr_array_free(argbuf, TRUE);
3440
3441
        return ok;
3442
}
3443
3444
#undef THROW
3445
3446
static gint imap_cmd_auth_plain(IMAPSession *session, const gchar *user,
3447
                                const gchar *pass)
3448
{
3449
        gchar *p;
3450
        gchar *response;
3451
        gchar *response64;
3452
        gint ok;
3453
3454
        p = response = g_malloc(strlen(user) * 2 + 2 + strlen(pass) + 1);
3455
        strcpy(p, user);
3456
        p += strlen(user) + 1;
3457
        strcpy(p, user);
3458
        p += strlen(user) + 1;
3459
        strcpy(p, pass);
3460
        p += strlen(pass);
3461
3462
        response64 = g_malloc((p - response) * 2 + 1);
3463
        base64_encode(response64, (guchar *)response, p - response);
3464
        g_free(response);
3465
3466
        log_print("IMAP4> ****************\n");
3467
        sock_puts(SESSION(session)->sock, response64);
3468
        ok = imap_cmd_ok(session, NULL);
3469
        if (ok != IMAP_SUCCESS)
3470
                log_warning(_("IMAP4 authentication failed.\n"));
3471
        g_free(response64);
3472
3473
        return ok;
3474
}
3475
3476
static gint imap_cmd_auth_cram_md5(IMAPSession *session, const gchar *user,
3477
                                   const gchar *pass, const gchar *challenge64)
3478
{
3479
        gchar *challenge;
3480
        gint challenge_len;
3481
        gchar hexdigest[33];
3482
        gchar *response;
3483
        gchar *response64;
3484
        gint ok;
3485
3486
        challenge = g_malloc(strlen(challenge64 + 2) + 1);
3487
        challenge_len = base64_decode((guchar *)challenge, challenge64 + 2, -1);
3488
        challenge[challenge_len] = '\0';
3489
        log_print("IMAP< [Decoded: %s]\n", challenge);
3490
3491
        md5_hex_hmac(hexdigest, (guchar *)challenge, challenge_len,
3492
                     (guchar *)pass, strlen(pass));
3493
        g_free(challenge);
3494
3495
        response = g_strdup_printf("%s %s", user, hexdigest);
3496
        log_print("IMAP> [Encoded: %s]\n", response);
3497
        response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3498
        base64_encode(response64, (guchar *)response, strlen(response));
3499
        g_free(response);
3500
3501
        log_print("IMAP> %s\n", response64);
3502
        sock_puts(SESSION(session)->sock, response64);
3503
        ok = imap_cmd_ok(session, NULL);
3504
        if (ok != IMAP_SUCCESS)
3505
                log_warning(_("IMAP4 authentication failed.\n"));
3506
3507
        return ok;
3508
}
3509
3510
static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
3511
                                  const gchar *pass, IMAPAuthType type)
3512
{
3513
        gchar *auth_type;
3514
        gint ok;
3515
        gchar *buf = NULL;
3516
3517
        g_return_val_if_fail((type == 0 || type == IMAP_AUTH_CRAM_MD5 ||
3518
                              type == IMAP_AUTH_PLAIN), IMAP_ERROR);
3519
3520
        if (type == IMAP_AUTH_PLAIN)
3521
                auth_type = "PLAIN";
3522
        else
3523
                auth_type = "CRAM-MD5";
3524
3525
        imap_cmd_gen_send(session, "AUTHENTICATE %s", auth_type);
3526
        ok = imap_cmd_gen_recv(session, &buf);
3527
        if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
3528
                g_free(buf);
3529
                return IMAP_ERROR;
3530
        }
3531
3532
        if (type == IMAP_AUTH_PLAIN)
3533
                ok = imap_cmd_auth_plain(session, user, pass);
3534
        else
3535
                ok = imap_cmd_auth_cram_md5(session, user, pass, buf);
3536
3537
        g_free(buf);
3538
3539
        return ok;
3540
}
3541
3542
static gint imap_cmd_login(IMAPSession *session,
3543
                           const gchar *user, const gchar *pass)
3544
{
3545
        gchar *user_, *pass_;
3546
        gint ok;
3547
3548
        QUOTE_IF_REQUIRED(user_, user);
3549
        QUOTE_IF_REQUIRED(pass_, pass);
3550
        imap_cmd_gen_send(session, "LOGIN %s %s", user_, pass_);
3551
3552
        ok = imap_cmd_ok(session, NULL);
3553
        if (ok != IMAP_SUCCESS)
3554
                log_warning(_("IMAP4 login failed.\n"));
3555
3556
        return ok;
3557
}
3558
3559
static gint imap_cmd_logout(IMAPSession *session)
3560
{
3561
        imap_cmd_gen_send(session, "LOGOUT");
3562
        return imap_cmd_ok(session, NULL);
3563
}
3564
3565
static gint imap_cmd_noop(IMAPSession *session)
3566
{
3567
        imap_cmd_gen_send(session, "NOOP");
3568
        return imap_cmd_ok(session, NULL);
3569
}
3570
3571
#if USE_SSL
3572
static gint imap_cmd_starttls(IMAPSession *session)
3573
{
3574
        imap_cmd_gen_send(session, "STARTTLS");
3575
        return imap_cmd_ok(session, NULL);
3576
}
3577
#endif
3578
3579
#define THROW(err) { ok = err; goto catch; }
3580
3581
static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3582
{
3583
        gint ok;
3584
        GPtrArray *argbuf;
3585
        gchar *str;
3586
3587
        argbuf = g_ptr_array_new();
3588
3589
        imap_cmd_gen_send(session, "NAMESPACE");
3590
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3591
3592
        str = search_array_str(argbuf, "NAMESPACE");
3593
        if (!str) THROW(IMAP_ERROR);
3594
3595
        *ns_str = g_strdup(str);
3596
3597
catch:
3598
        ptr_array_free_strings(argbuf);
3599
        g_ptr_array_free(argbuf, TRUE);
3600
3601
        return ok;
3602
}
3603
3604
#undef THROW
3605
3606
static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3607
                          const gchar *mailbox, GPtrArray *argbuf)
3608
{
3609
        gchar *ref_, *mailbox_;
3610
3611
        if (!ref) ref = "\"\"";
3612
        if (!mailbox) mailbox = "\"\"";
3613
3614
        QUOTE_IF_REQUIRED(ref_, ref);
3615
        QUOTE_IF_REQUIRED(mailbox_, mailbox);
3616
        imap_cmd_gen_send(session, "LIST %s %s", ref_, mailbox_);
3617
3618
        return imap_cmd_ok(session, argbuf);
3619
}
3620
3621
#define THROW goto catch
3622
3623
static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3624
                               gboolean examine,
3625
                               gint *exists, gint *recent, gint *unseen,
3626
                               guint32 *uid_validity)
3627
{
3628
        gint ok;
3629
        gchar *resp_str;
3630
        GPtrArray *argbuf;
3631
        gchar *select_cmd;
3632
        gchar *folder_;
3633
        guint uid_validity_;
3634
3635
        *exists = *recent = *unseen = *uid_validity = 0;
3636
        argbuf = g_ptr_array_new();
3637
3638
        if (examine)
3639
                select_cmd = "EXAMINE";
3640
        else
3641
                select_cmd = "SELECT";
3642
3643
        QUOTE_IF_REQUIRED(folder_, folder);
3644
        imap_cmd_gen_send(session, "%s %s", select_cmd, folder_);
3645
3646
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
3647
3648
        resp_str = search_array_contain_str(argbuf, "EXISTS");
3649
        if (resp_str) {
3650
                if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3651
                        g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3652
                        THROW;
3653
                }
3654
        }
3655
3656
        resp_str = search_array_contain_str(argbuf, "RECENT");
3657
        if (resp_str) {
3658
                if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3659
                        g_warning("imap_cmd_select(): invalid RECENT line.\n");
3660
                        THROW;
3661
                }
3662
        }
3663
3664
        resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3665
        if (resp_str) {
3666
                if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3667
                    != 1) {
3668
                        g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3669
                        THROW;
3670
                }
3671
                *uid_validity = uid_validity_;
3672
        }
3673
3674
        resp_str = search_array_contain_str(argbuf, "UNSEEN");
3675
        if (resp_str) {
3676
                if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3677
                        g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3678
                        THROW;
3679
                }
3680
        }
3681
3682
catch:
3683
        ptr_array_free_strings(argbuf);
3684
        g_ptr_array_free(argbuf, TRUE);
3685
3686
        return ok;
3687
}
3688
3689
static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3690
                            gint *exists, gint *recent, gint *unseen,
3691
                            guint32 *uid_validity)
3692
{
3693
        return imap_cmd_do_select(session, folder, FALSE,
3694
                                  exists, recent, unseen, uid_validity);
3695
}
3696
3697
static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3698
                             gint *exists, gint *recent, gint *unseen,
3699
                             guint32 *uid_validity)
3700
{
3701
        return imap_cmd_do_select(session, folder, TRUE,
3702
                                  exists, recent, unseen, uid_validity);
3703
}
3704
3705
#undef THROW
3706
3707
static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3708
{
3709
        gchar *folder_;
3710
3711
        QUOTE_IF_REQUIRED(folder_, folder);
3712
        imap_cmd_gen_send(session, "CREATE %s", folder_);
3713
3714
        return imap_cmd_ok(session, NULL);
3715
}
3716
3717
static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3718
                            const gchar *new_folder)
3719
{
3720
        gchar *old_folder_, *new_folder_;
3721
3722
        QUOTE_IF_REQUIRED(old_folder_, old_folder);
3723
        QUOTE_IF_REQUIRED(new_folder_, new_folder);
3724
        imap_cmd_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3725
3726
        return imap_cmd_ok(session, NULL);
3727
}
3728
3729
static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3730
{
3731
        gchar *folder_;
3732
3733
        QUOTE_IF_REQUIRED(folder_, folder);
3734
        imap_cmd_gen_send(session, "DELETE %s", folder_);
3735
3736
        return imap_cmd_ok(session, NULL);
3737
}
3738
3739
#define THROW(err) { ok = err; goto catch; }
3740
3741
static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3742
                            GArray **result)
3743
{
3744
        gint ok;
3745
        GPtrArray *argbuf;
3746
        GArray *array;
3747
        gchar *str;
3748
        gchar *p, *ep;
3749
        gint i;
3750
        guint32 uid;
3751
3752
        g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3753
        g_return_val_if_fail(result != NULL, IMAP_ERROR);
3754
3755
        argbuf = g_ptr_array_new();
3756
3757
        imap_cmd_gen_send(session, "UID SEARCH %s", criteria);
3758
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3759
3760
        array = g_array_new(FALSE, FALSE, sizeof(guint32));
3761
3762
        for (i = 0; i < argbuf->len; i++) {
3763
                str = g_ptr_array_index(argbuf, i);
3764
                if (strncmp(str, "SEARCH", 6) != 0)
3765
                        continue;
3766
3767
                p = str + 6;
3768
                while (*p != '\0') {
3769
                        uid = strtoul(p, &ep, 10);
3770
                        if (p < ep && uid > 0) {
3771
                                g_array_append_val(array, uid);
3772
                                p = ep;
3773
                        } else
3774
                                break;
3775
                }
3776
        }
3777
3778
        *result = array;
3779
3780
catch:
3781
        ptr_array_free_strings(argbuf);
3782
        g_ptr_array_free(argbuf, TRUE);
3783
3784
        return ok;
3785
}
3786
3787
static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3788
                           const gchar *filename)
3789
{
3790
        gint ok;
3791
        gchar *buf;
3792
        gchar *cur_pos;
3793
        gchar size_str[32];
3794
        glong size_num;
3795
        gint ret;
3796
3797
        g_return_val_if_fail(filename != NULL, IMAP_ERROR);
3798
3799
        imap_cmd_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3800
3801
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
3802
                if (buf[0] != '*' || buf[1] != ' ') {
3803
                        g_free(buf);
3804
                        return IMAP_ERROR;
3805
                }
3806
                if (strstr(buf, "FETCH") != NULL) break;
3807
                g_free(buf);
3808
        }
3809
        if (ok != IMAP_SUCCESS)
3810
                return ok;
3811
3812
#define RETURN_ERROR_IF_FAIL(cond)                 \
3813
        if (!(cond)) {                                 \
3814
                g_free(buf);                         \
3815
                ok = imap_cmd_ok(session, NULL); \
3816
                return IMAP_ERROR;                 \
3817
        }
3818
3819
        cur_pos = strchr(buf, '{');
3820
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3821
        cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3822
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3823
        size_num = atol(size_str);
3824
        RETURN_ERROR_IF_FAIL(size_num >= 0);
3825
3826
        RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3827
3828
#undef RETURN_ERROR_IF_FAIL
3829
3830
        g_free(buf);
3831
3832
        if ((ret = recv_bytes_write_to_file(SESSION(session)->sock,
3833
                                            size_num, filename)) != 0) {
3834
                if (ret == -2)
3835
                        return IMAP_SOCKET;
3836
        }
3837
3838
        if (imap_cmd_gen_recv(session, &buf) != IMAP_SUCCESS)
3839
                return IMAP_ERROR;
3840
3841
        if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3842
                g_free(buf);
3843
                return IMAP_ERROR;
3844
        }
3845
        g_free(buf);
3846
3847
        ok = imap_cmd_ok(session, NULL);
3848
3849
        if (ret != 0)
3850
                return IMAP_ERROR;
3851
3852
        return ok;
3853
}
3854
3855
static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3856
                            const gchar *file, IMAPFlags flags,
3857
                            guint32 *new_uid)
3858
{
3859
        gint ok;
3860
        gint size;
3861
        gchar *destfolder_;
3862
        gchar *flag_str;
3863
        guint new_uid_;
3864
        gchar *ret = NULL;
3865
        gchar buf[BUFFSIZE];
3866
        FILE *fp;
3867
        FILE *tmp;
3868
        size_t read_len;
3869
        GPtrArray *argbuf;
3870
        gchar *resp_str;
3871
3872
        g_return_val_if_fail(file != NULL, IMAP_ERROR);
3873
3874
        if ((fp = g_fopen(file, "rb")) == NULL) {
3875
                FILE_OP_ERROR(file, "fopen");
3876
                return -1;
3877
        }
3878
        tmp = canonicalize_file_stream(fp, &size);
3879
        fclose(fp);
3880
        if (!tmp)
3881
                return -1;
3882
3883
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
3884
        flag_str = imap_get_flag_str(flags);
3885
        imap_cmd_gen_send(session, "APPEND %s (%s) {%d}",
3886
                          destfolder_, flag_str, size);
3887
        g_free(flag_str);
3888
3889
        ok = imap_cmd_gen_recv(session, &ret);
3890
        if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3891
                log_warning(_("can't append %s to %s\n"), file, destfolder_);
3892
                g_free(ret);
3893
                fclose(tmp);
3894
                return IMAP_ERROR;
3895
        }
3896
        g_free(ret);
3897
3898
        log_print("IMAP4> %s\n", _("(sending file...)"));
3899
3900
        while ((read_len = fread(buf, 1, sizeof(buf), tmp)) > 0) {
3901
                if (read_len < sizeof(buf) && ferror(tmp))
3902
                        break;
3903
                if (sock_write_all(SESSION(session)->sock, buf, read_len) < 0) {
3904
                        fclose(tmp);
3905
                        return -1;
3906
                }
3907
        }
3908
3909
        if (ferror(tmp)) {
3910
                FILE_OP_ERROR(file, "fread");
3911
                fclose(tmp);
3912
                return -1;
3913
        }
3914
3915
        sock_puts(SESSION(session)->sock, "");
3916
3917
        fclose(tmp);
3918
3919
        if (new_uid != NULL)
3920
                *new_uid = 0;
3921
3922
        if (new_uid != NULL && session->uidplus) {
3923
                argbuf = g_ptr_array_new();
3924
3925
                ok = imap_cmd_ok(session, argbuf);
3926
                if (ok != IMAP_SUCCESS)
3927
                        log_warning(_("can't append message to %s\n"),
3928
                                    destfolder_);
3929
                else if (argbuf->len > 0) {
3930
                        resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3931
                        if (resp_str &&
3932
                            sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3933
                                   &new_uid_) == 1) {
3934
                                *new_uid = new_uid_;
3935
                        }
3936
                }
3937
3938
                ptr_array_free_strings(argbuf);
3939
                g_ptr_array_free(argbuf, TRUE);
3940
        } else
3941
                ok = imap_cmd_ok(session, NULL);
3942
3943
        return ok;
3944
}
3945
3946
static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3947
                          const gchar *destfolder)
3948
{
3949
        gint ok;
3950
        gchar *destfolder_;
3951
3952
        g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3953
3954
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
3955
        imap_cmd_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3956
3957
        ok = imap_cmd_ok(session, NULL);
3958
        if (ok != IMAP_SUCCESS) {
3959
                log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3960
                return -1;
3961
        }
3962
3963
        return ok;
3964
}
3965
3966
gint imap_cmd_envelope(IMAPSession *session, const gchar *seq_set)
3967
{
3968
        imap_cmd_gen_send
3969
                (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
3970
                 seq_set);
3971
3972
        return IMAP_SUCCESS;
3973
}
3974
3975
static gint imap_cmd_store(IMAPSession *session, const gchar *seq_set,
3976
                           const gchar *sub_cmd)
3977
{
3978
        gint ok;
3979
3980
        imap_cmd_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3981
3982
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3983
                log_warning(_("error while imap command: STORE %s %s\n"),
3984
                            seq_set, sub_cmd);
3985
                return ok;
3986
        }
3987
3988
        return IMAP_SUCCESS;
3989
}
3990
3991
static gint imap_cmd_expunge(IMAPSession *session)
3992
{
3993
        gint ok;
3994
3995
        imap_cmd_gen_send(session, "EXPUNGE");
3996
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3997
                log_warning(_("error while imap command: EXPUNGE\n"));
3998
                return ok;
3999
        }
4000
4001
        return IMAP_SUCCESS;
4002
}
4003
4004
static gint imap_cmd_close(IMAPSession *session)
4005
{
4006
        gint ok;
4007
4008
        imap_cmd_gen_send(session, "CLOSE");
4009
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
4010
                log_warning(_("error while imap command: CLOSE\n"));
4011
4012
        return ok;
4013
}
4014
4015
static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
4016
{
4017
        gint ok;
4018
        gchar *buf;
4019
        gint cmd_num;
4020
        gchar cmd_status[IMAPBUFSIZE + 1];
4021
        GString *str;
4022
        gchar *p;
4023
        gchar obuf[32];
4024
        gint len;
4025
        gchar *literal;
4026
4027
        str = g_string_sized_new(256);
4028
4029
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
4030
                g_string_append(str, buf);
4031
4032
                if ((p = strrchr_with_skip_quote(buf, '"', '{'))) {
4033
                        /* literal */
4034
                        p = strchr_cpy(p + 1, '}', obuf, sizeof(obuf));
4035
                        len = atoi(obuf);
4036
                        if (len < 0 || p == NULL || *p != '\0') {
4037
                                g_free(buf);
4038
                                ok = IMAP_ERROR;
4039
                                break;
4040
                        }
4041
4042
                        literal = recv_bytes(SESSION(session)->sock, len);
4043
                        if (!literal) {
4044
                                g_free(buf);
4045
                                ok = IMAP_SOCKET;
4046
                                break;
4047
                        }
4048
                        if (memchr(literal, '\n', len))
4049
                                log_print("IMAP4< (literal: %d bytes)\n", len);
4050
                        else
4051
                                log_print("IMAP4< %s\n", literal);
4052
4053
                        g_string_append(str, "\r\n");
4054
                        g_string_append_len(str, literal, len);
4055
                        g_free(literal);
4056
                        g_free(buf);
4057
                        continue;
4058
                }
4059
4060
                g_free(buf);
4061
4062
                if (str->str[0] == '*' && str->str[1] == ' ') {
4063
                        if (argbuf)
4064
                                g_ptr_array_add(argbuf, g_strdup(str->str + 2));
4065
4066
                        g_string_truncate(str, 0);
4067
                        continue;
4068
                } else if (sscanf(str->str, "%d %" Xstr(IMAPBUFSIZE) "s",
4069
                           &cmd_num, cmd_status) < 2) {
4070
                        ok = IMAP_ERROR;
4071
                } else if (cmd_num == session->cmd_count &&
4072
                           !strcmp(cmd_status, "OK")) {
4073
                        if (argbuf)
4074
                                g_ptr_array_add(argbuf, g_strdup(str->str));
4075
                } else {
4076
                        ok = IMAP_ERROR;
4077
                }
4078
4079
                break;
4080
        }
4081
4082
        g_string_free(str, TRUE);
4083
        return ok;
4084
}
4085
4086
static void imap_cmd_gen_send(IMAPSession *session, const gchar *format, ...)
4087
{
4088
        gchar buf[IMAPBUFSIZE];
4089
        gchar tmp[IMAPBUFSIZE];
4090
        gchar *p;
4091
        va_list args;
4092
4093
        va_start(args, format);
4094
        g_vsnprintf(tmp, sizeof(tmp), format, args);
4095
        va_end(args);
4096
4097
        session->cmd_count++;
4098
4099
        g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp);
4100
        if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) &&
4101
            (p = strchr(tmp + 6, ' '))) {
4102
                *p = '\0';
4103
                log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
4104
        } else
4105
                log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
4106
4107
        sock_write_all(SESSION(session)->sock, buf, strlen(buf));
4108
}
4109
4110
static gint imap_cmd_gen_recv(IMAPSession *session, gchar **ret)
4111
{
4112
        gint len;
4113
4114
        if ((len = sock_getline(SESSION(session)->sock, ret)) < 0)
4115
                return IMAP_SOCKET;
4116
4117
        strretchomp(*ret);
4118
4119
        if (len > 1000) {
4120
                gchar *str;
4121
4122
                str = trim_string(*ret, 1000);
4123
                log_print("IMAP4< %s\n", str);
4124
                g_free(str);
4125
        } else
4126
                log_print("IMAP4< %s\n", *ret);
4127
4128
        session_set_access_time(SESSION(session));
4129
4130
        return IMAP_SUCCESS;
4131
}
4132
4133
4134
/* misc utility functions */
4135
4136
static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
4137
{
4138
        gchar *tmp;
4139
4140
        dest[0] = '\0';
4141
        tmp = strchr(src, ch);
4142
        if (!tmp)
4143
                return NULL;
4144
4145
        memcpy(dest, src, MIN(tmp - src, len - 1));
4146
        dest[MIN(tmp - src, len - 1)] = '\0';
4147
4148
        return tmp + 1;
4149
}
4150
4151
static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
4152
{
4153
        const gchar *p = src;
4154
        gint n = 0;
4155
4156
        g_return_val_if_fail(*p == ch, NULL);
4157
4158
        *dest = '\0';
4159
        p++;
4160
4161
        while (*p != '\0' && *p != ch) {
4162
                if (n < len - 1) {
4163
                        if (*p == '\\' && *(p + 1) != '\0')
4164
                                p++;
4165
                        *dest++ = *p++;
4166
                } else
4167
                        p++;
4168
                n++;
4169
        }
4170
4171
        *dest = '\0';
4172
        return (gchar *)(*p == ch ? p + 1 : p);
4173
}
4174
4175
static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
4176
{
4177
        gint i;
4178
4179
        for (i = 0; i < array->len; i++) {
4180
                gchar *tmp;
4181
4182
                tmp = g_ptr_array_index(array, i);
4183
                if (strstr(tmp, str) != NULL)
4184
                        return tmp;
4185
        }
4186
4187
        return NULL;
4188
}
4189
4190
static gchar *search_array_str(GPtrArray *array, gchar *str)
4191
{
4192
        gint i;
4193
        gint len;
4194
4195
        len = strlen(str);
4196
4197
        for (i = 0; i < array->len; i++) {
4198
                gchar *tmp;
4199
4200
                tmp = g_ptr_array_index(array, i);
4201
                if (!strncmp(tmp, str, len))
4202
                        return tmp;
4203
        }
4204
4205
        return NULL;
4206
}
4207
4208
static void imap_path_separator_subst(gchar *str, gchar separator)
4209
{
4210
        gchar *p;
4211
        gboolean in_escape = FALSE;
4212
4213
        if (!separator || separator == '/') return;
4214
4215
        for (p = str; *p != '\0'; p++) {
4216
                if (*p == '/' && !in_escape)
4217
                        *p = separator;
4218
                else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4219
                        in_escape = TRUE;
4220
                else if (*p == '-' && in_escape)
4221
                        in_escape = FALSE;
4222
        }
4223
}
4224
4225
static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
4226
{
4227
        static iconv_t cd = (iconv_t)-1;
4228
        static gboolean iconv_ok = TRUE;
4229
        GString *norm_utf7;
4230
        gchar *norm_utf7_p;
4231
        size_t norm_utf7_len;
4232
        const gchar *p;
4233
        gchar *to_str, *to_p;
4234
        size_t to_len;
4235
        gboolean in_escape = FALSE;
4236
4237
        if (!iconv_ok) return g_strdup(mutf7_str);
4238
4239
        if (cd == (iconv_t)-1) {
4240
                cd = iconv_open(CS_INTERNAL, CS_UTF_7);
4241
                if (cd == (iconv_t)-1) {
4242
                        g_warning("iconv cannot convert UTF-7 to %s\n",
4243
                                  CS_INTERNAL);
4244
                        iconv_ok = FALSE;
4245
                        return g_strdup(mutf7_str);
4246
                }
4247
        }
4248
4249
        /* modified UTF-7 to normal UTF-7 conversion */
4250
        norm_utf7 = g_string_new(NULL);
4251
4252
        for (p = mutf7_str; *p != '\0'; p++) {
4253
                /* replace: '&'  -> '+',
4254
                            "&-" -> '&',
4255
                            "+"  -> "+-",
4256
                            escaped ','  -> '/' */
4257
                if (!in_escape && *p == '&') {
4258
                        if (*(p + 1) != '-') {
4259
                                g_string_append_c(norm_utf7, '+');
4260
                                in_escape = TRUE;
4261
                        } else {
4262
                                g_string_append_c(norm_utf7, '&');
4263
                                p++;
4264
                        }
4265
                } else if (!in_escape && *p == '+') {
4266
                        g_string_append(norm_utf7, "+-");
4267
                } else if (in_escape && *p == ',') {
4268
                        g_string_append_c(norm_utf7, '/');
4269
                } else if (in_escape && *p == '-') {
4270
                        g_string_append_c(norm_utf7, '-');
4271
                        in_escape = FALSE;
4272
                } else {
4273
                        g_string_append_c(norm_utf7, *p);
4274
                }
4275
        }
4276
4277
        /* somehow iconv() returns error when the last of the string is "+-" */
4278
        g_string_append_c(norm_utf7, '\n');
4279
        norm_utf7_p = norm_utf7->str;
4280
        norm_utf7_len = norm_utf7->len;
4281
        to_len = strlen(mutf7_str) * 5;
4282
        to_p = to_str = g_malloc(to_len + 1);
4283
4284
        if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
4285
                  &to_p, &to_len) == -1) {
4286
                g_warning(_("iconv cannot convert UTF-7 to %s\n"), CS_INTERNAL);
4287
                g_string_free(norm_utf7, TRUE);
4288
                g_free(to_str);
4289
                return g_strdup(mutf7_str);
4290
        }
4291
4292
        /* second iconv() call for flushing */
4293
        iconv(cd, NULL, NULL, &to_p, &to_len);
4294
        g_string_free(norm_utf7, TRUE);
4295
        *to_p = '\0';
4296
        strretchomp(to_str);
4297
4298
        return to_str;
4299
}
4300
4301
static gchar *imap_utf8_to_modified_utf7(const gchar *from)
4302
{
4303
        static iconv_t cd = (iconv_t)-1;
4304
        static gboolean iconv_ok = TRUE;
4305
        gchar *norm_utf7, *norm_utf7_p;
4306
        size_t from_len, norm_utf7_len;
4307
        GString *to_str;
4308
        gchar *from_tmp, *to, *p;
4309
        gboolean in_escape = FALSE;
4310
4311
        if (!iconv_ok) return g_strdup(from);
4312
4313
        if (cd == (iconv_t)-1) {
4314
                cd = iconv_open(CS_UTF_7, CS_INTERNAL);
4315
                if (cd == (iconv_t)-1) {
4316
                        g_warning(_("iconv cannot convert %s to UTF-7\n"),
4317
                                  CS_INTERNAL);
4318
                        iconv_ok = FALSE;
4319
                        return g_strdup(from);
4320
                }
4321
        }
4322
4323
        /* UTF-8 to normal UTF-7 conversion */
4324
        Xstrdup_a(from_tmp, from, return g_strdup(from));
4325
        from_len = strlen(from);
4326
        norm_utf7_len = from_len * 5;
4327
        Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
4328
        norm_utf7_p = norm_utf7;
4329
4330
        while (from_len > 0) {
4331
                if (*from_tmp == '+') {
4332
                        *norm_utf7_p++ = '+';
4333
                        *norm_utf7_p++ = '-';
4334
                        norm_utf7_len -= 2;
4335
                        from_tmp++;
4336
                        from_len--;
4337
                } else if (g_ascii_isprint(*from_tmp)) {
4338
                        /* printable ascii char */
4339
                        *norm_utf7_p = *from_tmp;
4340
                        norm_utf7_p++;
4341
                        norm_utf7_len--;
4342
                        from_tmp++;
4343
                        from_len--;
4344
                } else {
4345
                        size_t conv_len = 0;
4346
4347
                        /* unprintable char: convert to UTF-7 */
4348
                        p = from_tmp;
4349
                        while (!g_ascii_isprint(*p) && conv_len < from_len) {
4350
                                conv_len += g_utf8_skip[*(guchar *)p];
4351
                                p += g_utf8_skip[*(guchar *)p];
4352
                        }
4353
4354
                        from_len -= conv_len;
4355
                        if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
4356
                                  &conv_len,
4357
                                  &norm_utf7_p, &norm_utf7_len) == -1) {
4358
                                g_warning("iconv cannot convert %s to UTF-7\n",
4359
                                          CS_INTERNAL);
4360
                                return g_strdup(from);
4361
                        }
4362
4363
                        /* second iconv() call for flushing */
4364
                        iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
4365
                }
4366
        }
4367
4368
        *norm_utf7_p = '\0';
4369
        to_str = g_string_new(NULL);
4370
        for (p = norm_utf7; p < norm_utf7_p; p++) {
4371
                /* replace: '&' -> "&-",
4372
                            '+' -> '&',
4373
                            "+-" -> '+',
4374
                            BASE64 '/' -> ',' */
4375
                if (!in_escape && *p == '&') {
4376
                        g_string_append(to_str, "&-");
4377
                } else if (!in_escape && *p == '+') {
4378
                        if (*(p + 1) == '-') {
4379
                                g_string_append_c(to_str, '+');
4380
                                p++;
4381
                        } else {
4382
                                g_string_append_c(to_str, '&');
4383
                                in_escape = TRUE;
4384
                        }
4385
                } else if (in_escape && *p == '/') {
4386
                        g_string_append_c(to_str, ',');
4387
                } else if (in_escape && *p == '-') {
4388
                        g_string_append_c(to_str, '-');
4389
                        in_escape = FALSE;
4390
                } else {
4391
                        g_string_append_c(to_str, *p);
4392
                }
4393
        }
4394
4395
        if (in_escape) {
4396
                in_escape = FALSE;
4397
                g_string_append_c(to_str, '-');
4398
        }
4399
4400
        to = to_str->str;
4401
        g_string_free(to_str, FALSE);
4402
4403
        return to;
4404
}
4405
4406
static GSList *imap_get_seq_set_from_msglist(GSList *msglist, gint limit)
4407
{
4408
        GString *str;
4409
        GSList *sorted_list, *cur;
4410
        guint first, last, next;
4411
        gchar *ret_str;
4412
        GSList *ret_list = NULL;
4413
        gint count = 0;
4414
4415
        if (msglist == NULL)
4416
                return NULL;
4417
4418
        str = g_string_sized_new(256);
4419
4420
        sorted_list = g_slist_copy(msglist);
4421
        sorted_list = procmsg_sort_msg_list(sorted_list, SORT_BY_NUMBER,
4422
                                            SORT_ASCENDING);
4423
4424
        first = ((MsgInfo *)sorted_list->data)->msgnum;
4425
4426
        for (cur = sorted_list; cur != NULL; cur = cur->next) {
4427
                ++count;
4428
                last = ((MsgInfo *)cur->data)->msgnum;
4429
                if (cur->next)
4430
                        next = ((MsgInfo *)cur->next->data)->msgnum;
4431
                else
4432
                        next = 0;
4433
4434
                if (limit > 0 && count >= limit) {
4435
                        if (str->len > 0)
4436
                                g_string_append_c(str, ',');
4437
                        if (first == last)
4438
                                g_string_sprintfa(str, "%u", first);
4439
                        else
4440
                                g_string_sprintfa(str, "%u:%u", first, last);
4441
4442
                        first = next;
4443
4444
                        ret_str = g_strdup(str->str);
4445
                        ret_list = g_slist_append(ret_list, ret_str);
4446
                        g_string_truncate(str, 0);
4447
                        count = 0;
4448
                        continue;
4449
                }
4450
4451
                if (last + 1 != next || next == 0) {
4452
                        if (str->len > 0)
4453
                                g_string_append_c(str, ',');
4454
                        if (first == last)
4455
                                g_string_sprintfa(str, "%u", first);
4456
                        else
4457
                                g_string_sprintfa(str, "%u:%u", first, last);
4458
4459
                        first = next;
4460
4461
                        if (str->len > IMAP_CMD_LIMIT) {
4462
                                ret_str = g_strdup(str->str);
4463
                                ret_list = g_slist_append(ret_list, ret_str);
4464
                                g_string_truncate(str, 0);
4465
                        }
4466
                }
4467
        }
4468
4469
        if (str->len > 0) {
4470
                ret_str = g_strdup(str->str);
4471
                ret_list = g_slist_append(ret_list, ret_str);
4472
        }
4473
4474
        g_slist_free(sorted_list);
4475
        g_string_free(str, TRUE);
4476
4477
        return ret_list;
4478
}
4479
4480
static gint imap_seq_set_get_count(const gchar *seq_set)
4481
{
4482
        gint count = 0;
4483
        guint first, last;
4484
        gchar *tmp, *p, *q;
4485
4486
        p = q = tmp = g_strdup(seq_set);
4487
4488
        while (*p) {
4489
                if (*p == ',') {
4490
                        *p = '\0';
4491
                        if (sscanf(q, "%u:%u", &first, &last) == 2)
4492
                                count += last - first + 1;
4493
                        else if (sscanf(q, "%u", &first) == 1)
4494
                                count++;
4495
                        q = ++p;
4496
                } else
4497
                        ++p;
4498
        }
4499
        if (q != p) {
4500
                if (sscanf(q, "%u:%u", &first, &last) == 2)
4501
                        count += last - first + 1;
4502
                else if (sscanf(q, "%u", &first) == 1)
4503
                        count++;
4504
        }
4505
4506
        g_free(tmp);
4507
4508
        return count;
4509
}
4510
4511
static void imap_seq_set_free(GSList *seq_list)
4512
{
4513
        slist_free_strings(seq_list);
4514
        g_slist_free(seq_list);
4515
}
4516
4517
static GHashTable *imap_get_uid_table(GArray *array)
4518
{
4519
        GHashTable *table;
4520
        gint i;
4521
        guint32 uid;
4522
4523
        g_return_val_if_fail(array != NULL, NULL);
4524
4525
        table = g_hash_table_new(NULL, g_direct_equal);
4526
4527
        for (i = 0; i < array->len; i++) {
4528
                uid = g_array_index(array, guint32, i);
4529
                g_hash_table_insert(table, GUINT_TO_POINTER(uid),
4530
                                    GINT_TO_POINTER(i + 1));
4531
        }
4532
4533
        return table;
4534
}
4535
4536
static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4537
{
4538
        FolderItem *item = node->data;
4539
        gchar **paths = data;
4540
        const gchar *oldpath = paths[0];
4541
        const gchar *newpath = paths[1];
4542
        gchar *base;
4543
        gchar *new_itempath;
4544
        gint oldpathlen;
4545
4546
        oldpathlen = strlen(oldpath);
4547
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4548
                g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4549
                return TRUE;
4550
        }
4551
4552
        base = item->path + oldpathlen;
4553
        while (*base == '/') base++;
4554
        if (*base == '\0')
4555
                new_itempath = g_strdup(newpath);
4556
        else
4557
                new_itempath = g_strconcat(newpath, "/", base, NULL);
4558
        g_free(item->path);
4559
        item->path = new_itempath;
4560
4561
        return FALSE;
4562
}