Statistics
| Revision:

root / src / imap.c @ 1

History | View | Annotate | Download (94.3 kB)

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