Statistics
| Revision:

root / libsylph / imap.c @ 2164

History | View | Annotate | Download (109 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2008 Hiroyuki Yamamoto
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19

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

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <stdio.h>
29
#include <string.h>
30
#include <stdlib.h>
31
#include <dirent.h>
32
#include <unistd.h>
33
#include <ctype.h>
34
#include <time.h>
35
#if HAVE_ICONV
36
#  include <iconv.h>
37
#endif
38

    
39
#include "imap.h"
40
#include "socket.h"
41
#include "ssl.h"
42
#include "recv.h"
43
#include "procmsg.h"
44
#include "procheader.h"
45
#include "folder.h"
46
#include "prefs_account.h"
47
#include "codeconv.h"
48
#include "md5_hmac.h"
49
#include "base64.h"
50
#include "utils.h"
51
#include "prefs_common.h"
52
#include "virtual.h"
53

    
54
#define IMAP4_PORT        143
55
#if USE_SSL
56
#define IMAPS_PORT        993
57
#endif
58

    
59
#define IMAP_COPY_LIMIT        200
60
#define IMAP_CMD_LIMIT        1000
61

    
62
#define QUOTE_IF_REQUIRED(out, str)                                        \
63
{                                                                        \
64
        if (*str != '"' && strpbrk(str, " \t(){}[]%&*") != NULL) {        \
65
                gchar *__tmp;                                                \
66
                gint len;                                                \
67
                                                                        \
68
                len = strlen(str) + 3;                                        \
69
                Xalloca(__tmp, len, return IMAP_ERROR);                        \
70
                g_snprintf(__tmp, len, "\"%s\"", str);                        \
71
                out = __tmp;                                                \
72
        } else {                                                        \
73
                Xstrdup_a(out, str, return IMAP_ERROR);                        \
74
        }                                                                \
75
}
76

    
77
static GList *session_list = NULL;
78

    
79
static void imap_folder_init                (Folder                *folder,
80
                                         const gchar        *name,
81
                                         const gchar        *path);
82

    
83
static Folder        *imap_folder_new        (const gchar        *name,
84
                                         const gchar        *path);
85
static void         imap_folder_destroy        (Folder                *folder);
86

    
87
static Session *imap_session_new        (PrefsAccount        *account);
88
static gint imap_session_connect        (IMAPSession        *session);
89
static gint imap_session_reconnect        (IMAPSession        *session);
90
static void imap_session_destroy        (Session        *session);
91
/* static void imap_session_destroy_all        (void); */
92

    
93
static gint imap_search_flags                (IMAPSession        *session,
94
                                         GArray               **uids,
95
                                         GHashTable    **flags_table);
96
static gint imap_fetch_flags                (IMAPSession        *session,
97
                                         GArray               **uids,
98
                                         GHashTable    **flags_table);
99

    
100
static GSList *imap_get_msg_list        (Folder                *folder,
101
                                         FolderItem        *item,
102
                                         gboolean         use_cache);
103
static GSList *imap_get_uncached_msg_list
104
                                        (Folder                *folder,
105
                                         FolderItem        *item);
106
static gchar *imap_fetch_msg                (Folder                *folder,
107
                                         FolderItem        *item,
108
                                         gint                 uid);
109
static MsgInfo *imap_get_msginfo        (Folder                *folder,
110
                                         FolderItem        *item,
111
                                         gint                 uid);
112
static gint imap_add_msg                (Folder                *folder,
113
                                         FolderItem        *dest,
114
                                         const gchar        *file,
115
                                         MsgFlags        *flags,
116
                                         gboolean         remove_source);
117
static gint imap_add_msgs                (Folder                *folder,
118
                                         FolderItem        *dest,
119
                                         GSList                *file_list,
120
                                         gboolean         remove_source,
121
                                         gint                *first);
122

    
123
static gint imap_move_msg                (Folder                *folder,
124
                                         FolderItem        *dest,
125
                                         MsgInfo        *msginfo);
126
static gint imap_move_msgs                (Folder                *folder,
127
                                         FolderItem        *dest,
128
                                         GSList                *msglist);
129
static gint imap_copy_msg                (Folder                *folder,
130
                                         FolderItem        *dest,
131
                                         MsgInfo        *msginfo);
132
static gint imap_copy_msgs                (Folder                *folder,
133
                                         FolderItem        *dest,
134
                                         GSList                *msglist);
135

    
136
static gint imap_remove_msg                (Folder                *folder,
137
                                         FolderItem        *item,
138
                                         MsgInfo        *msginfo);
139
static gint imap_remove_msgs                (Folder                *folder,
140
                                         FolderItem        *item,
141
                                         GSList                *msglist);
142
static gint imap_remove_all_msg                (Folder                *folder,
143
                                         FolderItem        *item);
144

    
145
static gboolean imap_is_msg_changed        (Folder                *folder,
146
                                         FolderItem        *item,
147
                                         MsgInfo        *msginfo);
148

    
149
static gint imap_close                        (Folder                *folder,
150
                                         FolderItem        *item);
151

    
152
static gint imap_scan_folder                (Folder                *folder,
153
                                         FolderItem        *item);
154
static gint imap_scan_tree                (Folder                *folder);
155

    
156
static gint imap_create_tree                (Folder                *folder);
157

    
158
static FolderItem *imap_create_folder        (Folder                *folder,
159
                                         FolderItem        *parent,
160
                                         const gchar        *name);
161
static gint imap_rename_folder                (Folder                *folder,
162
                                         FolderItem        *item,
163
                                         const gchar        *name);
164
static gint imap_move_folder                (Folder                *folder,
165
                                         FolderItem        *item,
166
                                         FolderItem        *new_parent);
167
static gint imap_remove_folder                (Folder                *folder,
168
                                         FolderItem        *item);
169

    
170
static IMAPSession *imap_session_get        (Folder                *folder);
171

    
172
static gint imap_greeting                (IMAPSession        *session);
173
static gint imap_auth                        (IMAPSession        *session,
174
                                         const gchar        *user,
175
                                         const gchar        *pass,
176
                                         IMAPAuthType         type);
177

    
178
static gint imap_scan_tree_recursive        (IMAPSession        *session,
179
                                         FolderItem        *item,
180
                                         GSList                *item_list);
181
static GSList *imap_get_folder_list        (IMAPSession        *session,
182
                                         FolderItem        *item);
183
static GSList *imap_parse_list                (IMAPSession        *session,
184
                                         const gchar        *real_path,
185
                                         gchar                *separator);
186
static GSList *imap_add_inter_folders        (GSList                *item_list,
187
                                         const gchar        *root_path);
188
static GSList *imap_get_part_folder_list(GSList                *item_list,
189
                                         FolderItem        *item);
190

    
191
static void imap_create_missing_folders        (Folder                        *folder);
192
static FolderItem *imap_create_special_folder
193
                                        (Folder                        *folder,
194
                                         SpecialFolderItemType         stype,
195
                                         const gchar                *name);
196

    
197
static gint imap_do_copy_msgs                (Folder                *folder,
198
                                         FolderItem        *dest, 
199
                                         GSList                *msglist,
200
                                         gboolean         remove_source);
201
static gint imap_remove_msgs_by_seq_set        (Folder                *folder,
202
                                         FolderItem        *item,
203
                                         GSList                *seq_list);
204

    
205
static GSList *imap_get_uncached_messages        (IMAPSession        *session,
206
                                                 FolderItem        *item,
207
                                                 guint32         first_uid,
208
                                                 guint32         last_uid,
209
                                                 gint                 exists,
210
                                                 gboolean         update_count);
211
static void imap_delete_cached_message                (FolderItem        *item,
212
                                                 guint32         uid);
213
static GSList *imap_delete_cached_messages        (GSList                *mlist,
214
                                                 FolderItem        *item,
215
                                                 guint32         first_uid,
216
                                                 guint32         last_uid);
217
static void imap_delete_all_cached_messages        (FolderItem        *item);
218

    
219
#if USE_SSL
220
static SockInfo *imap_open                (const gchar        *server,
221
                                         gushort         port,
222
                                         SSLType         ssl_type);
223
#else
224
static SockInfo *imap_open                (const gchar        *server,
225
                                         gushort         port);
226
#endif
227

    
228
static gint imap_msg_list_change_perm_flags        (GSList                *msglist,
229
                                                 MsgPermFlags         flags,
230
                                                 gboolean         is_set);
231
static gchar *imap_get_flag_str                        (IMAPFlags         flags);
232
static gint imap_set_message_flags                (IMAPSession        *session,
233
                                                 const gchar        *seq_set,
234
                                                 IMAPFlags         flags,
235
                                                 gboolean         is_set);
236
static gint imap_select                                (IMAPSession        *session,
237
                                                 IMAPFolder        *folder,
238
                                                 const gchar        *path,
239
                                                 gint                *exists,
240
                                                 gint                *recent,
241
                                                 gint                *unseen,
242
                                                 guint32        *uid_validity);
243
static gint imap_status                                (IMAPSession        *session,
244
                                                 IMAPFolder        *folder,
245
                                                 const gchar        *path,
246
                                                 gint                *messages,
247
                                                 gint                *recent,
248
                                                 guint32        *uid_next,
249
                                                 guint32        *uid_validity,
250
                                                 gint                *unseen);
251

    
252
static void imap_parse_namespace                (IMAPSession        *session,
253
                                                 IMAPFolder        *folder);
254
static void imap_get_namespace_by_list                (IMAPSession        *session,
255
                                                 IMAPFolder        *folder);
256
static IMAPNameSpace *imap_find_namespace        (IMAPFolder        *folder,
257
                                                 const gchar        *path);
258
static gchar imap_get_path_separator                (IMAPFolder        *folder,
259
                                                 const gchar        *path);
260
static gchar *imap_get_real_path                (IMAPFolder        *folder,
261
                                                 const gchar        *path);
262

    
263
static gchar *imap_parse_atom                (IMAPSession        *session,
264
                                         gchar                *src,
265
                                         gchar                *dest,
266
                                         gint                 dest_len,
267
                                         GString        *str);
268
static MsgFlags imap_parse_flags        (const gchar        *flag_str);
269
static IMAPFlags imap_parse_imap_flags        (const gchar        *flag_str);
270
static MsgInfo *imap_parse_envelope        (IMAPSession        *session,
271
                                         FolderItem        *item,
272
                                         GString        *line_str);
273

    
274
static gboolean imap_has_capability        (IMAPSession        *session,
275
                                         const gchar        *capability);
276
static void imap_capability_free        (IMAPSession        *session);
277

    
278
/* low-level IMAP4rev1 commands */
279
static gint imap_cmd_capability        (IMAPSession        *session);
280
static gint imap_cmd_authenticate
281
                                (IMAPSession        *session,
282
                                 const gchar        *user,
283
                                 const gchar        *pass,
284
                                 IMAPAuthType         type);
285
static gint imap_cmd_login        (IMAPSession        *session,
286
                                 const gchar        *user,
287
                                 const gchar        *pass);
288
static gint imap_cmd_logout        (IMAPSession        *session);
289
static gint imap_cmd_noop        (IMAPSession        *session);
290
#if USE_SSL
291
static gint imap_cmd_starttls        (IMAPSession        *session);
292
#endif
293
static gint imap_cmd_namespace        (IMAPSession        *session,
294
                                 gchar               **ns_str);
295
static gint imap_cmd_list        (IMAPSession        *session,
296
                                 const gchar        *ref,
297
                                 const gchar        *mailbox,
298
                                 GPtrArray        *argbuf);
299
static gint imap_cmd_do_select        (IMAPSession        *session,
300
                                 const gchar        *folder,
301
                                 gboolean         examine,
302
                                 gint                *exists,
303
                                 gint                *recent,
304
                                 gint                *unseen,
305
                                 guint32        *uid_validity);
306
static gint imap_cmd_select        (IMAPSession        *session,
307
                                 const gchar        *folder,
308
                                 gint                *exists,
309
                                 gint                *recent,
310
                                 gint                *unseen,
311
                                 guint32        *uid_validity);
312
static gint imap_cmd_examine        (IMAPSession        *session,
313
                                 const gchar        *folder,
314
                                 gint                *exists,
315
                                 gint                *recent,
316
                                 gint                *unseen,
317
                                 guint32        *uid_validity);
318
static gint imap_cmd_create        (IMAPSession        *session,
319
                                 const gchar        *folder);
320
static gint imap_cmd_rename        (IMAPSession        *session,
321
                                 const gchar        *oldfolder,
322
                                 const gchar        *newfolder);
323
static gint imap_cmd_delete        (IMAPSession        *session,
324
                                 const gchar        *folder);
325
static gint imap_cmd_envelope        (IMAPSession        *session,
326
                                 const gchar        *seq_set);
327
static gint imap_cmd_search        (IMAPSession        *session,
328
                                 const gchar        *criteria,
329
                                 GArray        **result);
330
static gint imap_cmd_fetch        (IMAPSession        *session,
331
                                 guint32         uid,
332
                                 const gchar        *filename);
333
static gint imap_cmd_append        (IMAPSession        *session,
334
                                 const gchar        *destfolder,
335
                                 const gchar        *file,
336
                                 IMAPFlags         flags,
337
                                 guint32        *new_uid);
338
static gint imap_cmd_copy        (IMAPSession        *session,
339
                                 const gchar        *seq_set,
340
                                 const gchar        *destfolder);
341
static gint imap_cmd_store        (IMAPSession        *session,
342
                                 const gchar        *seq_set,
343
                                 const gchar        *sub_cmd);
344
static gint imap_cmd_expunge        (IMAPSession        *session);
345
static gint imap_cmd_close        (IMAPSession        *session);
346

    
347
static gint imap_cmd_ok                (IMAPSession        *session,
348
                                 GPtrArray        *argbuf);
349
static void imap_cmd_gen_send        (IMAPSession        *session,
350
                                 const gchar        *format, ...);
351
static gint imap_cmd_gen_recv        (IMAPSession        *session,
352
                                 gchar               **ret);
353

    
354
/* misc utility functions */
355
static gchar *strchr_cpy                        (const gchar        *src,
356
                                                 gchar                 ch,
357
                                                 gchar                *dest,
358
                                                 gint                 len);
359
static gchar *get_quoted                        (const gchar        *src,
360
                                                 gchar                 ch,
361
                                                 gchar                *dest,
362
                                                 gint                 len);
363
static gchar *search_array_contain_str                (GPtrArray        *array,
364
                                                 gchar                *str);
365
static gchar *search_array_str                        (GPtrArray        *array,
366
                                                 gchar                *str);
367
static void imap_path_separator_subst                (gchar                *str,
368
                                                 gchar                 separator);
369

    
370
static gchar *imap_modified_utf7_to_utf8        (const gchar        *mutf7_str);
371
static gchar *imap_utf8_to_modified_utf7        (const gchar        *from);
372

    
373
static GSList *imap_get_seq_set_from_msglist        (GSList                *msglist,
374
                                                 gint                 limit);
375
static gint imap_seq_set_get_count                (const gchar        *seq_set);
376
static void imap_seq_set_free                        (GSList                *seq_list);
377

    
378
static GHashTable *imap_get_uid_table                (GArray                *array);
379

    
380
static gboolean imap_rename_folder_func                (GNode                *node,
381
                                                 gpointer         data);
382

    
383
static FolderClass imap_class =
384
{
385
        F_IMAP,
386

    
387
        imap_folder_new,
388
        imap_folder_destroy,
389

    
390
        imap_scan_tree,
391
        imap_create_tree,
392

    
393
        imap_get_msg_list,
394
        imap_get_uncached_msg_list,
395
        imap_fetch_msg,
396
        imap_get_msginfo,
397
        imap_add_msg,
398
        imap_add_msgs,
399
        imap_move_msg,
400
        imap_move_msgs,
401
        imap_copy_msg,
402
        imap_copy_msgs,
403
        imap_remove_msg,
404
        imap_remove_msgs,
405
        imap_remove_all_msg,
406
        imap_is_msg_changed,
407
        imap_close,
408
        imap_scan_folder,
409

    
410
        imap_create_folder,
411
        imap_rename_folder,
412
        imap_move_folder,
413
        imap_remove_folder
414
};
415

    
416

    
417
FolderClass *imap_get_class(void)
418
{
419
        return &imap_class;
420
}
421

    
422
static Folder *imap_folder_new(const gchar *name, const gchar *path)
423
{
424
        Folder *folder;
425

    
426
        folder = (Folder *)g_new0(IMAPFolder, 1);
427
        imap_folder_init(folder, name, path);
428

    
429
        return folder;
430
}
431

    
432
static void imap_folder_destroy(Folder *folder)
433
{
434
        gchar *dir;
435

    
436
        g_return_if_fail(folder->account != NULL);
437

    
438
        dir = folder_get_path(folder);
439
        if (is_dir_exist(dir))
440
                remove_dir_recursive(dir);
441
        g_free(dir);
442

    
443
        dir = g_strconcat(get_imap_cache_dir(), G_DIR_SEPARATOR_S,
444
                          folder->account->recv_server, NULL);
445
        if (is_dir_exist(dir))
446
                g_rmdir(dir);
447
        g_free(dir);
448

    
449
        folder_remote_folder_destroy(REMOTE_FOLDER(folder));
450
}
451

    
452
static void imap_folder_init(Folder *folder, const gchar *name,
453
                             const gchar *path)
454
{
455
        folder->klass = imap_get_class();
456
        folder_remote_folder_init(folder, name, path);
457
}
458

    
459
static IMAPSession *imap_session_get(Folder *folder)
460
{
461
        RemoteFolder *rfolder = REMOTE_FOLDER(folder);
462

    
463
        g_return_val_if_fail(folder != NULL, NULL);
464
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
465
        g_return_val_if_fail(folder->account != NULL, NULL);
466

    
467
        if (!prefs_common.online_mode)
468
                return NULL;
469

    
470
        if (!rfolder->session) {
471
                rfolder->session = imap_session_new(folder->account);
472
                if (rfolder->session)
473
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
474
                                             IMAP_FOLDER(folder));
475
                return IMAP_SESSION(rfolder->session);
476
        }
477

    
478
        if (time(NULL) - rfolder->session->last_access_time <
479
                SESSION_TIMEOUT_INTERVAL) {
480
                return IMAP_SESSION(rfolder->session);
481
        }
482

    
483
        if (imap_cmd_noop(IMAP_SESSION(rfolder->session)) != IMAP_SUCCESS) {
484
                log_warning(_("IMAP4 connection to %s has been"
485
                              " disconnected. Reconnecting...\n"),
486
                            folder->account->recv_server);
487
                if (imap_session_reconnect(IMAP_SESSION(rfolder->session))
488
                    == IMAP_SUCCESS)
489
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
490
                                             IMAP_FOLDER(folder));
491
                else {
492
                        session_destroy(rfolder->session);
493
                        rfolder->session = NULL;
494
                }
495
        }
496

    
497
        return IMAP_SESSION(rfolder->session);
498
}
499

    
500
static gint imap_greeting(IMAPSession *session)
501
{
502
        gchar *greeting;
503
        gint ok;
504

    
505
        if ((ok = imap_cmd_gen_recv(session, &greeting)) != IMAP_SUCCESS)
506
                return ok;
507

    
508
        if (greeting[0] != '*' || greeting[1] != ' ')
509
                ok = IMAP_ERROR;
510
        else if (!strncmp(greeting + 2, "OK", 2))
511
                ok = IMAP_SUCCESS;
512
        else if (!strncmp(greeting + 2, "PREAUTH", 7)) {
513
                session->authenticated = TRUE;
514
                ok = IMAP_SUCCESS;
515
        } else
516
                ok = IMAP_ERROR;
517

    
518
        g_free(greeting);
519
        return ok;
520
}
521

    
522
static gint imap_auth(IMAPSession *session, const gchar *user,
523
                      const gchar *pass, IMAPAuthType type)
524
{
525
        gboolean nologin;
526
        gint ok = IMAP_AUTHFAIL;
527

    
528
        nologin = imap_has_capability(session, "LOGINDISABLED");
529

    
530
        switch (type) {
531
        case 0:
532
                if (imap_has_capability(session, "AUTH=CRAM-MD5"))
533
                        ok = imap_cmd_authenticate(session, user, pass,
534
                                                   IMAP_AUTH_CRAM_MD5);
535
                else if (imap_has_capability(session, "AUTH=PLAIN"))
536
                        ok = imap_cmd_authenticate(session, user, pass,
537
                                                   IMAP_AUTH_PLAIN);
538
                else if (nologin)
539
                        log_print(_("IMAP4 server disables LOGIN.\n"));
540
                else
541
                        ok = imap_cmd_login(session, user, pass);
542
                break;
543
        case IMAP_AUTH_LOGIN:
544
                if (nologin)
545
                        log_warning(_("IMAP4 server disables LOGIN.\n"));
546
                else
547
                        ok = imap_cmd_login(session, user, pass);
548
                break;
549
        case IMAP_AUTH_CRAM_MD5:
550
        case IMAP_AUTH_PLAIN:
551
                ok = imap_cmd_authenticate(session, user, pass, type);
552
                break;
553
        default:
554
                break;
555
        }
556

    
557
        if (ok == IMAP_SUCCESS)
558
                session->authenticated = TRUE;
559

    
560
        return ok;
561
}
562

    
563
static Session *imap_session_new(PrefsAccount *account)
564
{
565
        IMAPSession *session;
566
        gushort port;
567

    
568
        g_return_val_if_fail(account != NULL, NULL);
569
        g_return_val_if_fail(account->recv_server != NULL, NULL);
570
        g_return_val_if_fail(account->userid != NULL, NULL);
571

    
572
#if USE_SSL
573
        port = account->set_imapport ? account->imapport
574
                : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
575
#else
576
        port = account->set_imapport ? account->imapport : IMAP4_PORT;
577
#endif
578

    
579
        session = g_new0(IMAPSession, 1);
580

    
581
        session_init(SESSION(session));
582

    
583
        SESSION(session)->type             = SESSION_IMAP;
584
        SESSION(session)->sock             = NULL;
585
        SESSION(session)->server           = g_strdup(account->recv_server);
586
        SESSION(session)->port             = port;
587
#if USE_SSL
588
        SESSION(session)->ssl_type         = account->ssl_imap;
589
#endif
590
        SESSION(session)->last_access_time = time(NULL);
591
        SESSION(session)->data             = account;
592

    
593
        SESSION(session)->destroy          = imap_session_destroy;
594

    
595
        session->authenticated = FALSE;
596
        session->capability    = NULL;
597
        session->uidplus       = FALSE;
598
        session->mbox          = NULL;
599
        session->cmd_count     = 0;
600

    
601
        session_list = g_list_append(session_list, session);
602

    
603
        if (imap_session_connect(session) != IMAP_SUCCESS) {
604
                session_destroy(SESSION(session));
605
                return NULL;
606
        }
607

    
608
        return SESSION(session);
609
}
610

    
611
static gint imap_session_connect(IMAPSession *session)
612
{
613
        SockInfo *sock;
614
        PrefsAccount *account;
615
        const gchar *pass;
616

    
617
        g_return_val_if_fail(session != NULL, IMAP_ERROR);
618

    
619
        account = (PrefsAccount *)(SESSION(session)->data);
620

    
621
        log_message(_("creating IMAP4 connection to %s:%d ...\n"),
622
                    SESSION(session)->server, SESSION(session)->port);
623

    
624
        pass = account->passwd;
625
        if (!pass)
626
                pass = account->tmp_pass;
627
        if (!pass) {
628
                gchar *tmp_pass;
629

    
630
                tmp_pass = input_query_password(account->recv_server,
631
                                                account->userid);
632
                if (!tmp_pass)
633
                        return IMAP_ERROR;
634

    
635
                account->tmp_pass = tmp_pass;
636
                pass = account->tmp_pass;
637
        }
638

    
639
#if USE_SSL
640
        if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port,
641
                              SESSION(session)->ssl_type)) == NULL)
642
#else
643
        if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port))
644
            == NULL)
645
#endif
646
                return IMAP_ERROR;
647

    
648
        SESSION(session)->sock = sock;
649

    
650
        if (imap_greeting(session) != IMAP_SUCCESS)
651
                return IMAP_ERROR;
652
        if (imap_cmd_capability(session) != IMAP_SUCCESS)
653
                return IMAP_ERROR;
654

    
655
        if (imap_has_capability(session, "UIDPLUS"))
656
                session->uidplus = TRUE;
657

    
658
#if USE_SSL
659
        if (account->ssl_imap == SSL_STARTTLS &&
660
            imap_has_capability(session, "STARTTLS")) {
661
                gint ok;
662

    
663
                ok = imap_cmd_starttls(session);
664
                if (ok != IMAP_SUCCESS) {
665
                        log_warning(_("Can't start TLS session.\n"));
666
                        return IMAP_ERROR;
667
                }
668
                if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1))
669
                        return IMAP_SOCKET;
670

    
671
                /* capability can be changed after STARTTLS */
672
                if (imap_cmd_capability(session) != IMAP_SUCCESS)
673
                        return IMAP_ERROR;
674
        }
675
#endif
676

    
677
        if (!session->authenticated &&
678
            imap_auth(session, account->userid, pass, account->imap_auth_type)
679
            != IMAP_SUCCESS) {
680
                if (account->tmp_pass) {
681
                        g_free(account->tmp_pass);
682
                        account->tmp_pass = NULL;
683
                }
684
                imap_cmd_logout(session);
685
                return IMAP_AUTHFAIL;
686
        }
687

    
688
        return IMAP_SUCCESS;
689
}
690

    
691
static gint imap_session_reconnect(IMAPSession *session)
692
{
693
        g_return_val_if_fail(session != NULL, IMAP_ERROR);
694

    
695
        session_disconnect(SESSION(session));
696

    
697
        imap_capability_free(session);
698
        session->uidplus = FALSE;
699
        g_free(session->mbox);
700
        session->mbox = NULL;
701
        session->authenticated = FALSE;
702
        SESSION(session)->state = SESSION_READY;
703

    
704
        return imap_session_connect(session);
705
}
706

    
707
static void imap_session_destroy(Session *session)
708
{
709
        imap_capability_free(IMAP_SESSION(session));
710
        g_free(IMAP_SESSION(session)->mbox);
711
        session_list = g_list_remove(session_list, session);
712
}
713

    
714
#if 0
715
static void imap_session_destroy_all(void)
716
{
717
        while (session_list != NULL) {
718
                IMAPSession *session = (IMAPSession *)session_list->data;
719

720
                imap_cmd_logout(session);
721
                session_destroy(SESSION(session));
722
        }
723
}
724
#endif
725

    
726
#define THROW goto catch
727

    
728
static gint imap_search_flags(IMAPSession *session, GArray **uids,
729
                              GHashTable **flags_table)
730
{
731
        gint ok;
732
        gint i;
733
        GArray *flag_uids;
734
        GHashTable *unseen_table;
735
        GHashTable *flagged_table;
736
        GHashTable *answered_table;
737
        guint32 uid;
738
        IMAPFlags flags;
739

    
740
        ok = imap_cmd_search(session, "ALL", uids);
741
        if (ok != IMAP_SUCCESS) return ok;
742

    
743
        ok = imap_cmd_search(session, "UNSEEN", &flag_uids);
744
        if (ok != IMAP_SUCCESS) {
745
                g_array_free(*uids, TRUE);
746
                return ok;
747
        }
748
        unseen_table = imap_get_uid_table(flag_uids);
749
        g_array_free(flag_uids, TRUE);
750
        ok = imap_cmd_search(session, "FLAGGED", &flag_uids);
751
        if (ok != IMAP_SUCCESS) {
752
                g_hash_table_destroy(unseen_table);
753
                g_array_free(*uids, TRUE);
754
                return ok;
755
        }
756
        flagged_table = imap_get_uid_table(flag_uids);
757
        g_array_free(flag_uids, TRUE);
758
        ok = imap_cmd_search(session, "ANSWERED", &flag_uids);
759
        if (ok != IMAP_SUCCESS) {
760
                g_hash_table_destroy(flagged_table);
761
                g_hash_table_destroy(unseen_table);
762
                g_array_free(*uids, TRUE);
763
                return ok;
764
        }
765
        answered_table = imap_get_uid_table(flag_uids);
766
        g_array_free(flag_uids, TRUE);
767

    
768
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
769

    
770
        for (i = 0; i < (*uids)->len; i++) {
771
                uid = g_array_index(*uids, guint32, i);
772
                flags = IMAP_FLAG_DRAFT;
773
                if (!g_hash_table_lookup(unseen_table, GUINT_TO_POINTER(uid)))
774
                        flags |= IMAP_FLAG_SEEN;
775
                if (g_hash_table_lookup(flagged_table, GUINT_TO_POINTER(uid)))
776
                        flags |= IMAP_FLAG_FLAGGED;
777
                if (g_hash_table_lookup(answered_table, GUINT_TO_POINTER(uid)))
778
                        flags |= IMAP_FLAG_ANSWERED;
779
                g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
780
                                    GINT_TO_POINTER(flags));
781
        }
782

    
783
        g_hash_table_destroy(answered_table);
784
        g_hash_table_destroy(flagged_table);
785
        g_hash_table_destroy(unseen_table);
786

    
787
        return IMAP_SUCCESS;
788
}
789

    
790
static gint imap_fetch_flags(IMAPSession *session, GArray **uids,
791
                             GHashTable **flags_table)
792
{
793
        gint ok;
794
        gchar *tmp;
795
        gchar *cur_pos;
796
        gchar buf[IMAPBUFSIZE];
797
        guint32 uid;
798
        IMAPFlags flags;
799

    
800
        imap_cmd_gen_send(session, "UID FETCH 1:* (UID FLAGS)");
801

    
802
        *uids = g_array_new(FALSE, FALSE, sizeof(guint32));
803
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
804

    
805
        while ((ok = imap_cmd_gen_recv(session, &tmp)) == IMAP_SUCCESS) {
806
                if (tmp[0] != '*' || tmp[1] != ' ') {
807
                        g_free(tmp);
808
                        break;
809
                }
810
                cur_pos = tmp + 2;
811

    
812
#define PARSE_ONE_ELEMENT(ch)                                        \
813
{                                                                \
814
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
815
        if (cur_pos == NULL) {                                        \
816
                g_warning("cur_pos == NULL\n");                        \
817
                g_free(tmp);                                        \
818
                g_hash_table_destroy(*flags_table);                \
819
                g_array_free(*uids, TRUE);                        \
820
                return IMAP_ERROR;                                \
821
        }                                                        \
822
}
823

    
824
                PARSE_ONE_ELEMENT(' ');
825
                PARSE_ONE_ELEMENT(' ');
826
                if (strcmp(buf, "FETCH") != 0) {
827
                        g_free(tmp);
828
                        continue;
829
                }
830
                if (*cur_pos != '(') {
831
                        g_free(tmp);
832
                        continue;
833
                }
834
                cur_pos++;
835
                uid = 0;
836
                flags = 0;
837

    
838
                while (*cur_pos != '\0' && *cur_pos != ')') {
839
                        while (*cur_pos == ' ') cur_pos++;
840

    
841
                        if (!strncmp(cur_pos, "UID ", 4)) {
842
                                cur_pos += 4;
843
                                uid = strtoul(cur_pos, &cur_pos, 10);
844
                        } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
845
                                cur_pos += 6;
846
                                if (*cur_pos != '(') {
847
                                        g_warning("*cur_pos != '('\n");
848
                                        break;
849
                                }
850
                                cur_pos++;
851
                                PARSE_ONE_ELEMENT(')');
852
                                flags = imap_parse_imap_flags(buf);
853
                                flags |= IMAP_FLAG_DRAFT;
854
                        } else {
855
                                g_warning("invalid FETCH response: %s\n", cur_pos);
856
                                break;
857
                        }
858
                }
859

    
860
#undef PARSE_ONE_ELEMENT
861

    
862
                if (uid > 0) {
863
                        g_array_append_val(*uids, uid);
864
                        g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
865
                                            GINT_TO_POINTER(flags));
866
                }
867

    
868
                g_free(tmp);
869
        }
870

    
871
        if (ok != IMAP_SUCCESS) {
872
                g_hash_table_destroy(*flags_table);
873
                g_array_free(*uids, TRUE);
874
        }
875

    
876
        return ok;
877
}
878

    
879
static GSList *imap_get_msg_list_full(Folder *folder, FolderItem *item,
880
                                      gboolean use_cache,
881
                                      gboolean uncached_only)
882
{
883
        GSList *mlist = NULL;
884
        IMAPSession *session;
885
        gint ok, exists = 0, recent = 0, unseen = 0;
886
        guint32 uid_validity = 0;
887
        guint32 first_uid = 0, last_uid = 0;
888
        GSList *newlist = NULL;
889

    
890
        g_return_val_if_fail(folder != NULL, NULL);
891
        g_return_val_if_fail(item != NULL, NULL);
892
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
893
        g_return_val_if_fail(folder->account != NULL, NULL);
894

    
895
        item->new = item->unread = item->total = 0;
896

    
897
        session = imap_session_get(folder);
898

    
899
        if (!session) {
900
                if (uncached_only)
901
                        return NULL;
902
                mlist = procmsg_read_cache(item, FALSE);
903
                item->last_num = procmsg_get_last_num_in_msg_list(mlist);
904
                procmsg_set_flags(mlist, item);
905
                return mlist;
906
        }
907

    
908
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
909
                         &exists, &recent, &unseen, &uid_validity);
910
        if (ok != IMAP_SUCCESS) THROW;
911

    
912
        if (exists == 0) {
913
                imap_delete_all_cached_messages(item);
914
                return NULL;
915
        }
916

    
917
        /* invalidate current cache if UIDVALIDITY has been changed */
918
        if (item->mtime != uid_validity) {
919
                debug_print("imap_get_msg_list: "
920
                            "UIDVALIDITY has been changed.\n");
921
                use_cache = FALSE;
922
        }
923

    
924
        if (use_cache) {
925
                GArray *uids;
926
                GHashTable *msg_table;
927
                GHashTable *flags_table;
928
                guint32 cache_last;
929
                guint32 begin = 0;
930
                GSList *cur, *next = NULL;
931
                MsgInfo *msginfo;
932
                IMAPFlags imap_flags;
933

    
934
                /* get cache data */
935
                mlist = procmsg_read_cache(item, FALSE);
936
                procmsg_set_flags(mlist, item);
937
                cache_last = procmsg_get_last_num_in_msg_list(mlist);
938

    
939
                /* get all UID list and flags */
940
                ok = imap_search_flags(session, &uids, &flags_table);
941
                if (ok != IMAP_SUCCESS) {
942
                        if (ok == IMAP_SOCKET || ok == IMAP_IOERR) THROW;
943
                        ok = imap_fetch_flags(session, &uids, &flags_table);
944
                        if (ok != IMAP_SUCCESS) THROW;
945
                }
946

    
947
                if (uids->len > 0) {
948
                        first_uid = g_array_index(uids, guint32, 0);
949
                        last_uid = g_array_index(uids, guint32, uids->len - 1);
950
                } else {
951
                        g_array_free(uids, TRUE);
952
                        g_hash_table_destroy(flags_table);
953
                        THROW;
954
                }
955

    
956
                /* sync message flags with server */
957
                for (cur = mlist; cur != NULL; cur = next) {
958
                        msginfo = (MsgInfo *)cur->data;
959
                        next = cur->next;
960
                        imap_flags = GPOINTER_TO_INT(g_hash_table_lookup
961
                                (flags_table,
962
                                 GUINT_TO_POINTER(msginfo->msgnum)));
963

    
964
                        if (imap_flags == 0) {
965
                                debug_print("imap_get_msg_list: "
966
                                            "message %u has been deleted.\n",
967
                                            msginfo->msgnum);
968
                                imap_delete_cached_message
969
                                        (item, msginfo->msgnum);
970
                                if (MSG_IS_NEW(msginfo->flags))
971
                                        item->new--;
972
                                if (MSG_IS_UNREAD(msginfo->flags))
973
                                        item->unread--;
974
                                item->total--;
975
                                mlist = g_slist_remove(mlist, msginfo);
976
                                procmsg_msginfo_free(msginfo);
977
                                item->cache_dirty = TRUE;
978
                                item->mark_dirty = TRUE;
979
                                continue;
980
                        }
981

    
982
                        if (!IMAP_IS_SEEN(imap_flags)) {
983
                                if (!MSG_IS_UNREAD(msginfo->flags)) {
984
                                        item->unread++;
985
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
986
                                                           MSG_UNREAD);
987
                                        item->mark_dirty = TRUE;
988
                                }
989
                        } else {
990
                                if (MSG_IS_NEW(msginfo->flags)) {
991
                                        item->new--;
992
                                        item->mark_dirty = TRUE;
993
                                }
994
                                if (MSG_IS_UNREAD(msginfo->flags)) {
995
                                        item->unread--;
996
                                        item->mark_dirty = TRUE;
997
                                }
998
                                MSG_UNSET_PERM_FLAGS(msginfo->flags,
999
                                                     MSG_NEW|MSG_UNREAD);
1000
                        }
1001

    
1002
                        if (IMAP_IS_FLAGGED(imap_flags)) {
1003
                                if (!MSG_IS_MARKED(msginfo->flags)) {
1004
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
1005
                                                           MSG_MARKED);
1006
                                        item->mark_dirty = TRUE;
1007
                                }
1008
                        } else {
1009
                                if (MSG_IS_MARKED(msginfo->flags)) {
1010
                                        MSG_UNSET_PERM_FLAGS(msginfo->flags,
1011
                                                             MSG_MARKED);
1012
                                        item->mark_dirty = TRUE;
1013
                                }
1014
                        }
1015
                        if (IMAP_IS_ANSWERED(imap_flags)) {
1016
                                if (!MSG_IS_REPLIED(msginfo->flags)) {
1017
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
1018
                                                           MSG_REPLIED);
1019
                                        item->mark_dirty = TRUE;
1020
                                }
1021
                        } else {
1022
                                if (MSG_IS_REPLIED(msginfo->flags)) {
1023
                                        MSG_UNSET_PERM_FLAGS(msginfo->flags,
1024
                                                             MSG_REPLIED);
1025
                                        item->mark_dirty = TRUE;
1026
                                }
1027
                        }
1028
                }
1029

    
1030
                /* check for the first new message */
1031
                msg_table = procmsg_msg_hash_table_create(mlist);
1032
                if (msg_table == NULL)
1033
                        begin = first_uid;
1034
                else {
1035
                        gint i;
1036

    
1037
                        for (i = 0; i < uids->len; i++) {
1038
                                guint32 uid;
1039

    
1040
                                uid = g_array_index(uids, guint32, i);
1041
                                if (g_hash_table_lookup
1042
                                        (msg_table, GUINT_TO_POINTER(uid))
1043
                                        == NULL) {
1044
                                        debug_print("imap_get_msg_list: "
1045
                                                    "first new UID: %u\n", uid);
1046
                                        begin = uid;
1047
                                        break;
1048
                                }
1049
                        }
1050
                        g_hash_table_destroy(msg_table);
1051
                }
1052

    
1053
                g_array_free(uids, TRUE);
1054
                g_hash_table_destroy(flags_table);
1055

    
1056
                /* remove ununsed caches */
1057
                if (first_uid > 0 && last_uid > 0) {
1058
                        mlist = imap_delete_cached_messages
1059
                                (mlist, item, 0, first_uid - 1);
1060
                        mlist = imap_delete_cached_messages
1061
                                (mlist, item, begin > 0 ? begin : last_uid + 1,
1062
                                 UINT_MAX);
1063
                }
1064

    
1065
                if (begin > 0 && begin <= last_uid) {
1066
                        newlist = imap_get_uncached_messages
1067
                                (session, item, begin, last_uid,
1068
                                 exists - item->total, TRUE);
1069
                        if (newlist) {
1070
                                item->cache_dirty = TRUE;
1071
                                item->mark_dirty = TRUE;
1072
                        }
1073
                        mlist = g_slist_concat(mlist, newlist);
1074
                }
1075
        } else {
1076
                imap_delete_all_cached_messages(item);
1077
                mlist = imap_get_uncached_messages(session, item, 0, 0, exists,
1078
                                                   TRUE);
1079
                last_uid = procmsg_get_last_num_in_msg_list(mlist);
1080
                item->cache_dirty = TRUE;
1081
                item->mark_dirty = TRUE;
1082
                newlist = mlist;
1083
        }
1084

    
1085
        if (!uncached_only)
1086
                mlist = procmsg_sort_msg_list(mlist, item->sort_key,
1087
                                              item->sort_type);
1088

    
1089
        item->last_num = last_uid;
1090

    
1091
        if (item->mark_queue)
1092
                item->mark_dirty = TRUE;
1093

    
1094
        debug_print("cache_dirty: %d, mark_dirty: %d\n",
1095
                    item->cache_dirty, item->mark_dirty);
1096

    
1097
        if (!item->opened) {
1098
                item->mtime = uid_validity;
1099
                if (item->cache_dirty)
1100
                        procmsg_write_cache_list(item, mlist);
1101
                if (item->mark_dirty)
1102
                        procmsg_write_flags_list(item, mlist);
1103
        }
1104

    
1105
catch:
1106
        if (uncached_only) {
1107
                GSList *cur;
1108

    
1109
                if (newlist == NULL) {
1110
                        procmsg_msg_list_free(mlist);
1111
                        return NULL;
1112
                }
1113
                if (mlist == newlist)
1114
                        return newlist;
1115
                for (cur = mlist; cur != NULL; cur = cur->next) {
1116
                        if (cur->next == newlist) {
1117
                                cur->next = NULL;
1118
                                procmsg_msg_list_free(mlist);
1119
                                return newlist;
1120
                        }
1121
                }
1122
                procmsg_msg_list_free(mlist);
1123
                return NULL;
1124
        }
1125

    
1126
        return mlist;
1127
}
1128

    
1129
#undef THROW
1130

    
1131
static GSList *imap_get_msg_list(Folder *folder, FolderItem *item,
1132
                                 gboolean use_cache)
1133
{
1134
        return imap_get_msg_list_full(folder, item, use_cache, FALSE);
1135
}
1136

    
1137
static GSList *imap_get_uncached_msg_list(Folder *folder, FolderItem *item)
1138
{
1139
        return imap_get_msg_list_full(folder, item, TRUE, TRUE);
1140
}
1141

    
1142
static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1143
{
1144
        gchar *path, *filename;
1145
        IMAPSession *session;
1146
        gint ok;
1147

    
1148
        g_return_val_if_fail(folder != NULL, NULL);
1149
        g_return_val_if_fail(item != NULL, NULL);
1150

    
1151
        path = folder_item_get_path(item);
1152
        if (!is_dir_exist(path))
1153
                make_dir_hier(path);
1154
        filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1155
        g_free(path);
1156

    
1157
        if (is_file_exist(filename)) {
1158
                debug_print("message %d has been already cached.\n", uid);
1159
                return filename;
1160
        }
1161

    
1162
        session = imap_session_get(folder);
1163
        if (!session) {
1164
                g_free(filename);
1165
                return NULL;
1166
        }
1167

    
1168
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1169
                         NULL, NULL, NULL, NULL);
1170
        if (ok != IMAP_SUCCESS) {
1171
                g_warning("can't select mailbox %s\n", item->path);
1172
                g_free(filename);
1173
                return NULL;
1174
        }
1175

    
1176
        status_print(_("Getting message %d"), uid);
1177
        debug_print("getting message %d...\n", uid);
1178
        ok = imap_cmd_fetch(session, (guint32)uid, filename);
1179

    
1180
        if (ok != IMAP_SUCCESS) {
1181
                g_warning("can't fetch message %d\n", uid);
1182
                g_free(filename);
1183
                return NULL;
1184
        }
1185

    
1186
        return filename;
1187
}
1188

    
1189
static MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
1190
{
1191
        IMAPSession *session;
1192
        GSList *list;
1193
        MsgInfo *msginfo = NULL;
1194

    
1195
        g_return_val_if_fail(folder != NULL, NULL);
1196
        g_return_val_if_fail(item != NULL, NULL);
1197

    
1198
        session = imap_session_get(folder);
1199
        g_return_val_if_fail(session != NULL, NULL);
1200

    
1201
        list = imap_get_uncached_messages(session, item, uid, uid, 0, FALSE);
1202
        if (list) {
1203
                msginfo = (MsgInfo *)list->data;
1204
                list->data = NULL;
1205
        }
1206
        procmsg_msg_list_free(list);
1207

    
1208
        return msginfo;
1209
}
1210

    
1211
static gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1212
                         MsgFlags *flags, gboolean remove_source)
1213
{
1214
        GSList file_list;
1215
        MsgFileInfo fileinfo;
1216

    
1217
        g_return_val_if_fail(file != NULL, -1);
1218

    
1219
        fileinfo.file = (gchar *)file;
1220
        fileinfo.flags = flags;
1221
        file_list.data = &fileinfo;
1222
        file_list.next = NULL;
1223

    
1224
        return imap_add_msgs(folder, dest, &file_list, remove_source, NULL);
1225
}
1226

    
1227
static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1228
                          gboolean remove_source, gint *first)
1229
{
1230
        gchar *destdir;
1231
        IMAPSession *session;
1232
        gint messages, recent, unseen;
1233
        guint32 uid_next, uid_validity;
1234
        guint32 last_uid = 0;
1235
        GSList *cur;
1236
        MsgFileInfo *fileinfo;
1237
        gint count = 1;
1238
        gint total;
1239
        gint ok;
1240
        GTimeVal tv_prev, tv_cur;
1241

    
1242
        g_return_val_if_fail(folder != NULL, -1);
1243
        g_return_val_if_fail(dest != NULL, -1);
1244
        g_return_val_if_fail(file_list != NULL, -1);
1245

    
1246
        session = imap_session_get(folder);
1247
        if (!session) return -1;
1248

    
1249
        g_get_current_time(&tv_prev);
1250
        ui_update();
1251

    
1252
        ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
1253
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1254
        if (ok != IMAP_SUCCESS) {
1255
                g_warning("can't append messages\n");
1256
                return -1;
1257
        }
1258

    
1259
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1260

    
1261
        if (!session->uidplus)
1262
                last_uid = uid_next - 1;
1263
        if (first)
1264
                *first = uid_next;
1265

    
1266
        total = g_slist_length(file_list);
1267

    
1268
        for (cur = file_list; cur != NULL; cur = cur->next) {
1269
                IMAPFlags iflags = 0;
1270
                guint32 new_uid = 0;
1271

    
1272
                fileinfo = (MsgFileInfo *)cur->data;
1273

    
1274
                if (fileinfo->flags) {
1275
                        if (MSG_IS_MARKED(*fileinfo->flags))
1276
                                iflags |= IMAP_FLAG_FLAGGED;
1277
                        if (MSG_IS_REPLIED(*fileinfo->flags))
1278
                                iflags |= IMAP_FLAG_ANSWERED;
1279
                        if (!MSG_IS_UNREAD(*fileinfo->flags))
1280
                                iflags |= IMAP_FLAG_SEEN;
1281
                }
1282

    
1283
                if (dest->stype == F_OUTBOX ||
1284
                    dest->stype == F_QUEUE  ||
1285
                    dest->stype == F_DRAFT)
1286
                        iflags |= IMAP_FLAG_SEEN;
1287

    
1288
                g_get_current_time(&tv_cur);
1289
                if (tv_cur.tv_sec > tv_prev.tv_sec ||
1290
                    tv_cur.tv_usec - tv_prev.tv_usec >
1291
                    PROGRESS_UPDATE_INTERVAL * 1000) {
1292
                        status_print(_("Appending messages to %s (%d / %d)"),
1293
                                     dest->path, count, total);
1294
                        progress_show(count, total);
1295
                        ui_update();
1296
                        tv_prev = tv_cur;
1297
                }
1298
                ++count;
1299

    
1300
                ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1301
                                     &new_uid);
1302

    
1303
                if (ok != IMAP_SUCCESS) {
1304
                        g_warning("can't append message %s\n", fileinfo->file);
1305
                        g_free(destdir);
1306
                        progress_show(0, 0);
1307
                        return -1;
1308
                }
1309

    
1310
                if (!session->uidplus)
1311
                        last_uid++;
1312
                else if (last_uid < new_uid)
1313
                        last_uid = new_uid;
1314

    
1315
                dest->last_num = last_uid;
1316
                dest->total++;
1317
                dest->updated = TRUE;
1318

    
1319
                if (fileinfo->flags) {
1320
                        if (MSG_IS_UNREAD(*fileinfo->flags))
1321
                                dest->unread++;
1322
                } else
1323
                        dest->unread++;
1324
        }
1325

    
1326
        progress_show(0, 0);
1327
        g_free(destdir);
1328

    
1329
        if (remove_source) {
1330
                for (cur = file_list; cur != NULL; cur = cur->next) {
1331
                        fileinfo = (MsgFileInfo *)cur->data;
1332
                        if (g_unlink(fileinfo->file) < 0)
1333
                                 FILE_OP_ERROR(fileinfo->file, "unlink");
1334
                }
1335
        }
1336

    
1337
        return last_uid;
1338
}
1339

    
1340
static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist,
1341
                              gboolean remove_source)
1342
{
1343
        FolderItem *src;
1344
        gchar *destdir;
1345
        GSList *seq_list, *cur;
1346
        MsgInfo *msginfo;
1347
        IMAPSession *session;
1348
        gint count = 0, total;
1349
        gint ok = IMAP_SUCCESS;
1350

    
1351
        g_return_val_if_fail(folder != NULL, -1);
1352
        g_return_val_if_fail(dest != NULL, -1);
1353
        g_return_val_if_fail(msglist != NULL, -1);
1354

    
1355
        session = imap_session_get(folder);
1356
        if (!session) return -1;
1357

    
1358
        ui_update();
1359

    
1360
        msginfo = (MsgInfo *)msglist->data;
1361

    
1362
        src = msginfo->folder;
1363
        if (src == dest) {
1364
                g_warning("the src folder is identical to the dest.\n");
1365
                return -1;
1366
        }
1367

    
1368
        ok = imap_select(session, IMAP_FOLDER(folder), src->path,
1369
                         NULL, NULL, NULL, NULL);
1370
        if (ok != IMAP_SUCCESS)
1371
                return ok;
1372

    
1373
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1374

    
1375
        total = g_slist_length(msglist);
1376
        seq_list = imap_get_seq_set_from_msglist(msglist, IMAP_COPY_LIMIT);
1377

    
1378
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1379
                gchar *seq_set = (gchar *)cur->data;
1380

    
1381
                count += imap_seq_set_get_count(seq_set);
1382

    
1383
                if (remove_source) {
1384
                        status_print(_("Moving messages %s to %s ..."),
1385
                                     seq_set, dest->path);
1386
                        debug_print("Moving message %s/[%s] to %s ...\n",
1387
                                    src->path, seq_set, dest->path);
1388
                } else {
1389
                        status_print(_("Copying messages %s to %s ..."),
1390
                                     seq_set, dest->path);
1391
                        debug_print("Copying message %s/[%s] to %s ...\n",
1392
                                    src->path, seq_set, dest->path);
1393
                }
1394
                progress_show(count, total);
1395
                ui_update();
1396

    
1397
                ok = imap_cmd_copy(session, seq_set, destdir);
1398
                if (ok != IMAP_SUCCESS) {
1399
                        imap_seq_set_free(seq_list);
1400
                        progress_show(0, 0);
1401
                        return -1;
1402
                }
1403
        }
1404

    
1405
        progress_show(0, 0);
1406

    
1407
        dest->updated = TRUE;
1408

    
1409
        imap_seq_set_free(seq_list);
1410
        g_free(destdir);
1411

    
1412
        for (cur = msglist; cur != NULL; cur = cur->next) {
1413
                msginfo = (MsgInfo *)cur->data;
1414
                dest->total++;
1415
                if (MSG_IS_NEW(msginfo->flags))
1416
                        dest->new++;
1417
                if (MSG_IS_UNREAD(msginfo->flags))
1418
                        dest->unread++;
1419
        }
1420

    
1421
        if (remove_source) {
1422
                ok = imap_remove_msgs(folder, src, msglist);
1423
                if (ok != IMAP_SUCCESS)
1424
                        return ok;
1425
        }
1426

    
1427
        if (ok == IMAP_SUCCESS)
1428
                return 0;
1429
        else
1430
                return -1;
1431
}
1432

    
1433
static gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1434
{
1435
        GSList msglist;
1436

    
1437
        g_return_val_if_fail(msginfo != NULL, -1);
1438

    
1439
        msglist.data = msginfo;
1440
        msglist.next = NULL;
1441

    
1442
        return imap_move_msgs(folder, dest, &msglist);
1443
}
1444

    
1445
static gint imap_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1446
{
1447
        MsgInfo *msginfo;
1448
        GSList *file_list;
1449
        gint ret = 0;
1450

    
1451
        g_return_val_if_fail(folder != NULL, -1);
1452
        g_return_val_if_fail(dest != NULL, -1);
1453
        g_return_val_if_fail(msglist != NULL, -1);
1454

    
1455
        msginfo = (MsgInfo *)msglist->data;
1456
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1457

    
1458
        if (folder == msginfo->folder->folder)
1459
                return imap_do_copy_msgs(folder, dest, msglist, TRUE);
1460

    
1461
        file_list = procmsg_get_message_file_list(msglist);
1462
        g_return_val_if_fail(file_list != NULL, -1);
1463

    
1464
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1465

    
1466
        procmsg_message_file_list_free(file_list);
1467

    
1468
        if (ret != -1)
1469
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
1470

    
1471
        return ret;
1472
}
1473

    
1474
static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1475
{
1476
        GSList msglist;
1477

    
1478
        g_return_val_if_fail(msginfo != NULL, -1);
1479

    
1480
        msglist.data = msginfo;
1481
        msglist.next = NULL;
1482

    
1483
        return imap_copy_msgs(folder, dest, &msglist);
1484
}
1485

    
1486
static gint imap_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1487
{
1488
        MsgInfo *msginfo;
1489
        GSList *file_list;
1490
        gint ret;
1491

    
1492
        g_return_val_if_fail(folder != NULL, -1);
1493
        g_return_val_if_fail(dest != NULL, -1);
1494
        g_return_val_if_fail(msglist != NULL, -1);
1495

    
1496
        msginfo = (MsgInfo *)msglist->data;
1497
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1498

    
1499
        if (folder == msginfo->folder->folder)
1500
                return imap_do_copy_msgs(folder, dest, msglist, FALSE);
1501

    
1502
        file_list = procmsg_get_message_file_list(msglist);
1503
        g_return_val_if_fail(file_list != NULL, -1);
1504

    
1505
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1506

    
1507
        procmsg_message_file_list_free(file_list);
1508

    
1509
        return ret;
1510
}
1511

    
1512
static gint imap_remove_msgs_by_seq_set(Folder *folder, FolderItem *item,
1513
                                        GSList *seq_list)
1514
{
1515
        gint ok;
1516
        IMAPSession *session;
1517
        GSList *cur;
1518

    
1519
        g_return_val_if_fail(seq_list != NULL, -1);
1520

    
1521
        session = imap_session_get(folder);
1522
        if (!session) return -1;
1523

    
1524
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1525
                gchar *seq_set = (gchar *)cur->data;
1526

    
1527
                status_print(_("Removing messages %s"), seq_set);
1528
                ui_update();
1529

    
1530
                ok = imap_set_message_flags(session, seq_set, IMAP_FLAG_DELETED,
1531
                                            TRUE);
1532
                if (ok != IMAP_SUCCESS) {
1533
                        log_warning(_("can't set deleted flags: %s\n"),
1534
                                    seq_set);
1535
                        return ok;
1536
                }
1537
        }
1538

    
1539
        ok = imap_cmd_expunge(session);
1540
        if (ok != IMAP_SUCCESS)
1541
                log_warning(_("can't expunge\n"));
1542

    
1543
        item->updated = TRUE;
1544

    
1545
        return ok;
1546
}
1547

    
1548
static gint imap_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1549
{
1550
        GSList msglist;
1551

    
1552
        g_return_val_if_fail(msginfo != NULL, -1);
1553

    
1554
        msglist.data = msginfo;
1555
        msglist.next = NULL;
1556

    
1557
        return imap_remove_msgs(folder, item, &msglist);
1558
}
1559

    
1560
static gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
1561
{
1562
        gint ok;
1563
        IMAPSession *session;
1564
        GSList *seq_list, *cur;
1565
        gchar *dir;
1566
        gboolean dir_exist;
1567

    
1568
        g_return_val_if_fail(folder != NULL, -1);
1569
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
1570
        g_return_val_if_fail(item != NULL, -1);
1571
        g_return_val_if_fail(msglist != NULL, -1);
1572

    
1573
        session = imap_session_get(folder);
1574
        if (!session) return -1;
1575

    
1576
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1577
                         NULL, NULL, NULL, NULL);
1578
        if (ok != IMAP_SUCCESS)
1579
                return ok;
1580

    
1581
        seq_list = imap_get_seq_set_from_msglist(msglist, 0);
1582
        ok = imap_remove_msgs_by_seq_set(folder, item, seq_list);
1583
        imap_seq_set_free(seq_list);
1584
        if (ok != IMAP_SUCCESS)
1585
                return ok;
1586

    
1587
        dir = folder_item_get_path(item);
1588
        dir_exist = is_dir_exist(dir);
1589
        for (cur = msglist; cur != NULL; cur = cur->next) {
1590
                MsgInfo *msginfo = (MsgInfo *)cur->data;
1591
                guint32 uid = msginfo->msgnum;
1592

    
1593
                if (dir_exist)
1594
                        remove_numbered_files(dir, uid, uid);
1595
                item->total--;
1596
                if (MSG_IS_NEW(msginfo->flags))
1597
                        item->new--;
1598
                if (MSG_IS_UNREAD(msginfo->flags))
1599
                        item->unread--;
1600
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
1601
        }
1602
        g_free(dir);
1603

    
1604
        return IMAP_SUCCESS;
1605
}
1606

    
1607
static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1608
{
1609
        gint ok;
1610
        IMAPSession *session;
1611
        gchar *dir;
1612

    
1613
        g_return_val_if_fail(folder != NULL, -1);
1614
        g_return_val_if_fail(item != NULL, -1);
1615

    
1616
        session = imap_session_get(folder);
1617
        if (!session) return -1;
1618

    
1619
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1620
                         NULL, NULL, NULL, NULL);
1621
        if (ok != IMAP_SUCCESS)
1622
                return ok;
1623

    
1624
        status_print(_("Removing all messages in %s"), item->path);
1625
        ui_update();
1626

    
1627
        imap_cmd_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1628
        ok = imap_cmd_ok(session, NULL);
1629
        if (ok != IMAP_SUCCESS) {
1630
                log_warning(_("can't set deleted flags: 1:*\n"));
1631
                return ok;
1632
        }
1633

    
1634
        ok = imap_cmd_expunge(session);
1635
        if (ok != IMAP_SUCCESS) {
1636
                log_warning(_("can't expunge\n"));
1637
                return ok;
1638
        }
1639

    
1640
        item->new = item->unread = item->total = 0;
1641
        item->updated = TRUE;
1642

    
1643
        dir = folder_item_get_path(item);
1644
        if (is_dir_exist(dir))
1645
                remove_all_numbered_files(dir);
1646
        g_free(dir);
1647

    
1648
        return IMAP_SUCCESS;
1649
}
1650

    
1651
static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1652
                                    MsgInfo *msginfo)
1653
{
1654
        /* TODO: properly implement this method */
1655
        return FALSE;
1656
}
1657

    
1658
static gint imap_close(Folder *folder, FolderItem *item)
1659
{
1660
        gint ok;
1661
        IMAPSession *session;
1662

    
1663
        g_return_val_if_fail(folder != NULL, -1);
1664

    
1665
        if (!item->path) return 0;
1666

    
1667
        if (!REMOTE_FOLDER(folder)->session)
1668
                return 0;
1669

    
1670
        session = imap_session_get(folder);
1671
        if (!session) return -1;
1672

    
1673
        if (session->mbox) {
1674
                if (strcmp2(session->mbox, item->path) != 0) return -1;
1675

    
1676
                ok = imap_cmd_close(session);
1677
                if (ok != IMAP_SUCCESS)
1678
                        log_warning(_("can't close folder\n"));
1679

    
1680
                g_free(session->mbox);
1681
                session->mbox = NULL;
1682

    
1683
                return ok;
1684
        } else
1685
                return 0;
1686
}
1687

    
1688
static gint imap_scan_folder(Folder *folder, FolderItem *item)
1689
{
1690
        IMAPSession *session;
1691
        gint messages, recent, unseen;
1692
        guint32 uid_next, uid_validity;
1693
        gint ok;
1694

    
1695
        g_return_val_if_fail(folder != NULL, -1);
1696
        g_return_val_if_fail(item != NULL, -1);
1697

    
1698
        session = imap_session_get(folder);
1699
        if (!session) return -1;
1700

    
1701
        ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1702
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1703
        if (ok != IMAP_SUCCESS) return -1;
1704

    
1705
        item->new = unseen > 0 ? recent : 0;
1706
        item->unread = unseen;
1707
        item->total = messages;
1708
        item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1709
        /* item->mtime = uid_validity; */
1710
        item->updated = TRUE;
1711

    
1712
        return 0;
1713
}
1714

    
1715
static gint imap_scan_tree(Folder *folder)
1716
{
1717
        FolderItem *item = NULL;
1718
        IMAPSession *session;
1719
        gchar *root_folder = NULL;
1720
        GSList *item_list, *cur;
1721

    
1722
        g_return_val_if_fail(folder != NULL, -1);
1723
        g_return_val_if_fail(folder->account != NULL, -1);
1724

    
1725
        session = imap_session_get(folder);
1726
        if (!session) {
1727
                if (!folder->node) {
1728
                        folder_tree_destroy(folder);
1729
                        item = folder_item_new(folder->name, NULL);
1730
                        item->folder = folder;
1731
                        folder->node = item->node = g_node_new(item);
1732
                }
1733
                return -1;
1734
        }
1735

    
1736
        if (folder->account->imap_dir && *folder->account->imap_dir) {
1737
                gchar *real_path;
1738
                GPtrArray *argbuf;
1739
                gint ok;
1740

    
1741
                Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1742
                extract_quote(root_folder, '"');
1743
                subst_char(root_folder,
1744
                           imap_get_path_separator(IMAP_FOLDER(folder),
1745
                                                   root_folder),
1746
                           '/');
1747
                strtailchomp(root_folder, '/');
1748
                real_path = imap_get_real_path
1749
                        (IMAP_FOLDER(folder), root_folder);
1750
                debug_print("IMAP root directory: %s\n", real_path);
1751

    
1752
                /* check if root directory exist */
1753
                argbuf = g_ptr_array_new();
1754
                ok = imap_cmd_list(session, NULL, real_path, argbuf);
1755
                if (ok != IMAP_SUCCESS ||
1756
                    search_array_str(argbuf, "LIST ") == NULL) {
1757
                        log_warning(_("root folder %s not exist\n"), real_path);
1758
                        g_ptr_array_free(argbuf, TRUE);
1759
                        g_free(real_path);
1760
                        return -1;
1761
                }
1762
                g_ptr_array_free(argbuf, TRUE);
1763
                g_free(real_path);
1764
        }
1765

    
1766
        if (folder->node)
1767
                item = FOLDER_ITEM(folder->node->data);
1768
        if (!item || ((item->path || root_folder) &&
1769
                      strcmp2(item->path, root_folder) != 0)) {
1770
                folder_tree_destroy(folder);
1771
                item = folder_item_new(folder->name, root_folder);
1772
                item->folder = folder;
1773
                folder->node = item->node = g_node_new(item);
1774
        }
1775

    
1776
        item_list = imap_get_folder_list(session, item);
1777
        imap_scan_tree_recursive(session, item, item_list);
1778
        imap_create_missing_folders(folder);
1779

    
1780
        for (cur = item_list; cur != NULL; cur = cur->next)
1781
                folder_item_destroy(FOLDER_ITEM(cur->data));
1782
        g_slist_free(item_list);
1783

    
1784
        return 0;
1785
}
1786

    
1787
static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item,
1788
                                     GSList *item_list)
1789
{
1790
        Folder *folder;
1791
        FolderItem *new_item;
1792
        GSList *part_list, *cur;
1793
        GNode *node;
1794

    
1795
        g_return_val_if_fail(item != NULL, -1);
1796
        g_return_val_if_fail(item->folder != NULL, -1);
1797
        g_return_val_if_fail(item->no_sub == FALSE, -1);
1798

    
1799
        folder = item->folder;
1800

    
1801
        part_list = imap_get_part_folder_list(item_list, item);
1802

    
1803
        node = item->node->children;
1804
        while (node != NULL) {
1805
                FolderItem *old_item = FOLDER_ITEM(node->data);
1806
                GNode *next = node->next;
1807

    
1808
                new_item = NULL;
1809

    
1810
                for (cur = part_list; cur != NULL; cur = cur->next) {
1811
                        FolderItem *cur_item = FOLDER_ITEM(cur->data);
1812
                        if (!strcmp2(old_item->path, cur_item->path)) {
1813
                                new_item = cur_item;
1814
                                break;
1815
                        }
1816
                }
1817
                if (!new_item) {
1818
                        if (old_item->stype != F_VIRTUAL) {
1819
                                debug_print("folder '%s' not found. removing...\n", old_item->path);
1820
                                folder_item_remove(old_item);
1821
                        }
1822
                } else if (old_item->stype == F_VIRTUAL) {
1823
                                debug_print("IMAP4 folder found at the location of virtual folder '%s'. removing virtual folder...\n", old_item->path);
1824
                                virtual_get_class()->remove_folder
1825
                                        (folder, old_item);
1826
                } else {
1827
                        old_item->no_sub = new_item->no_sub;
1828
                        old_item->no_select = new_item->no_select;
1829
                        if (old_item->no_select == TRUE)
1830
                                old_item->new = old_item->unread =
1831
                                        old_item->total = 0;
1832
                        if (old_item->no_sub == TRUE && node->children) {
1833
                                debug_print("folder '%s' doesn't have "
1834
                                            "subfolders. removing...\n",
1835
                                            old_item->path);
1836
                                folder_item_remove_children(old_item);
1837
                        }
1838
                }
1839

    
1840
                node = next;
1841
        }
1842

    
1843
        for (cur = part_list; cur != NULL; cur = cur->next) {
1844
                FolderItem *cur_item = FOLDER_ITEM(cur->data);
1845
                new_item = NULL;
1846
                for (node = item->node->children; node != NULL;
1847
                     node = node->next) {
1848
                        if (!strcmp2(FOLDER_ITEM(node->data)->path,
1849
                                     cur_item->path)) {
1850
                                new_item = FOLDER_ITEM(node->data);
1851
                                cur_item = NULL;
1852
                                break;
1853
                        }
1854
                }
1855
                if (!new_item) {
1856
                        new_item = folder_item_copy(cur_item);
1857
                        debug_print("new folder '%s' found.\n", new_item->path);
1858
                        folder_item_append(item, new_item);
1859
                }
1860

    
1861
                if (!strcmp(new_item->path, "INBOX")) {
1862
                        new_item->stype = F_INBOX;
1863
                        folder->inbox = new_item;
1864
                } else if (!item->parent || item->stype == F_INBOX) {
1865
                        const gchar *base;
1866

    
1867
                        base = g_basename(new_item->path);
1868

    
1869
                        if (!folder->outbox &&
1870
                            !g_ascii_strcasecmp(base, "Sent")) {
1871
                                new_item->stype = F_OUTBOX;
1872
                                folder->outbox = new_item;
1873
                        } else if (!folder->draft &&
1874
                                   !g_ascii_strcasecmp(base, "Drafts")) {
1875
                                new_item->stype = F_DRAFT;
1876
                                folder->draft = new_item;
1877
                        } else if (!folder->queue &&
1878
                                   !g_ascii_strcasecmp(base, "Queue")) {
1879
                                new_item->stype = F_QUEUE;
1880
                                folder->queue = new_item;
1881
                        } else if (!folder->trash &&
1882
                                   !g_ascii_strcasecmp(base, "Trash")) {
1883
                                new_item->stype = F_TRASH;
1884
                                folder->trash = new_item;
1885
                        }
1886
                }
1887

    
1888
#if 0
1889
                if (new_item->no_select == FALSE)
1890
                        imap_scan_folder(folder, new_item);
1891
#endif
1892
                if (new_item->no_sub == FALSE)
1893
                        imap_scan_tree_recursive(session, new_item, item_list);
1894
        }
1895

    
1896
        g_slist_free(part_list);
1897

    
1898
        return IMAP_SUCCESS;
1899
}
1900

    
1901
static GSList *imap_get_folder_list(IMAPSession *session, FolderItem *item)
1902
{
1903
        Folder *folder;
1904
        IMAPFolder *imapfolder;
1905
        gchar *real_path;
1906
        gchar *wildcard_path;
1907
        gchar separator;
1908
        GSList *item_list;
1909

    
1910
        folder = item->folder;
1911
        imapfolder = IMAP_FOLDER(folder);
1912

    
1913
        separator = imap_get_path_separator(imapfolder, item->path);
1914

    
1915
        if (folder->ui_func)
1916
                folder->ui_func(folder, item, folder->ui_func_data);
1917

    
1918
        if (item->path) {
1919
                real_path = imap_get_real_path(imapfolder, item->path);
1920
                strtailchomp(real_path, separator);
1921
                wildcard_path = g_strdup_printf("%s%c*", real_path, separator);
1922
        } else {
1923
                real_path = g_strdup("");
1924
                wildcard_path = g_strdup("*");
1925
        }
1926

    
1927
        imap_cmd_gen_send(session, "LIST \"\" \"%s\"", wildcard_path);
1928

    
1929
        item_list = imap_parse_list(session, real_path, NULL);
1930
        item_list = imap_add_inter_folders(item_list, item->path);
1931
        g_free(real_path);
1932
        g_free(wildcard_path);
1933

    
1934
        return item_list;
1935
}
1936

    
1937
static GSList *imap_parse_list(IMAPSession *session, const gchar *real_path,
1938
                               gchar *separator)
1939
{
1940
        gchar buf[IMAPBUFSIZE];
1941
        gchar flags[256];
1942
        gchar separator_str[16];
1943
        gchar *p;
1944
        const gchar *name;
1945
        gchar *loc_name, *loc_path;
1946
        GSList *item_list = NULL;
1947
        GString *str;
1948
        FolderItem *new_item;
1949

    
1950
        debug_print("getting list of %s ...\n",
1951
                    *real_path ? real_path : "\"\"");
1952

    
1953
        str = g_string_new(NULL);
1954

    
1955
        for (;;) {
1956
                if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1957
                        log_warning(_("error occurred while getting LIST.\n"));
1958
                        break;
1959
                }
1960
                strretchomp(buf);
1961
                if (buf[0] != '*' || buf[1] != ' ') {
1962
                        log_print("IMAP4< %s\n", buf);
1963
                        if (sscanf(buf, "%*d %16s", buf) < 1 ||
1964
                            strcmp(buf, "OK") != 0)
1965
                                log_warning(_("error occurred while getting LIST.\n"));
1966
                                
1967
                        break;
1968
                }
1969
                debug_print("IMAP4< %s\n", buf);
1970

    
1971
                g_string_assign(str, buf);
1972
                p = str->str + 2;
1973
                if (strncmp(p, "LIST ", 5) != 0) continue;
1974
                p += 5;
1975

    
1976
                if (*p != '(') continue;
1977
                p++;
1978
                p = strchr_cpy(p, ')', flags, sizeof(flags));
1979
                if (!p) continue;
1980
                while (*p == ' ') p++;
1981

    
1982
                p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1983
                if (!p) continue;
1984
                extract_quote(separator_str, '"');
1985
                if (!strcmp(separator_str, "NIL"))
1986
                        separator_str[0] = '\0';
1987
                if (separator)
1988
                        *separator = separator_str[0];
1989

    
1990
                buf[0] = '\0';
1991
                while (*p == ' ') p++;
1992
                if ((*p == '~' && *(p + 1) == '{') || *p == '{' || *p == '"')
1993
                        p = imap_parse_atom(session, p, buf, sizeof(buf), str);
1994
                else
1995
                        strncpy2(buf, p, sizeof(buf));
1996
                strtailchomp(buf, separator_str[0]);
1997
                if (buf[0] == '\0') continue;
1998
                if (!strcmp(buf, real_path)) continue;
1999

    
2000
                if (separator_str[0] != '\0')
2001
                        subst_char(buf, separator_str[0], '/');
2002
                name = g_basename(buf);
2003
                if (name[0] == '.') continue;
2004

    
2005
                loc_name = imap_modified_utf7_to_utf8(name);
2006
                loc_path = imap_modified_utf7_to_utf8(buf);
2007
                new_item = folder_item_new(loc_name, loc_path);
2008
                if (strcasestr(flags, "\\Noinferiors") != NULL)
2009
                        new_item->no_sub = TRUE;
2010
                if (strcmp(buf, "INBOX") != 0 &&
2011
                    strcasestr(flags, "\\Noselect") != NULL)
2012
                        new_item->no_select = TRUE;
2013

    
2014
                item_list = g_slist_prepend(item_list, new_item);
2015

    
2016
                debug_print("folder '%s' found.\n", loc_path);
2017
                g_free(loc_path);
2018
                g_free(loc_name);
2019
        }
2020

    
2021
        g_string_free(str, TRUE);
2022

    
2023
        item_list = g_slist_reverse(item_list);
2024
        return item_list;
2025
}
2026

    
2027
static GSList *imap_add_inter_folders(GSList *item_list, const gchar *root_path)
2028
{
2029
        FolderItem *item;
2030
        GSList *cur;
2031
        GSList *add_list = NULL;
2032
        GHashTable *exist;
2033
        const gchar *p;
2034
        gint root_path_len = 0;
2035

    
2036
        if (root_path)
2037
                root_path_len = strlen(root_path);
2038

    
2039
        exist = g_hash_table_new(g_str_hash, g_str_equal);
2040

    
2041
        for (cur = item_list; cur != NULL; cur = cur->next) {
2042
                item = FOLDER_ITEM(cur->data);
2043

    
2044
                if (root_path_len > 0 &&
2045
                    strncmp(root_path, item->path, root_path_len) != 0)
2046
                        continue;
2047
                p = item->path + root_path_len;
2048
                if (root_path_len > 0 && *p != '/') continue;
2049
                while (*p == '/') p++;
2050
                if (*p == '\0') continue;
2051
                g_hash_table_insert(exist, (gpointer)p, GINT_TO_POINTER(1));
2052
        }
2053

    
2054
        for (cur = item_list; cur != NULL; cur = cur->next) {
2055
                const gchar *q, *r;
2056
                gchar *parent, *full_parent;
2057
                FolderItem *new_item;
2058

    
2059
                item = FOLDER_ITEM(cur->data);
2060

    
2061
                if (root_path_len > 0 &&
2062
                    strncmp(root_path, item->path, root_path_len) != 0)
2063
                        continue;
2064
                p = item->path + root_path_len;
2065
                if (root_path_len > 0 && *p != '/') continue;
2066
                while (*p == '/') p++;
2067
                if (*p == '\0') continue;
2068

    
2069
                q = p;
2070
                while ((q = strchr(q, '/')) != NULL) {
2071
                        parent = g_strndup(p, q - p);
2072
                        if (!g_hash_table_lookup(exist, parent)) {
2073
                                if (root_path_len > 0)
2074
                                        full_parent = g_strconcat
2075
                                                (root_path, "/", parent, NULL);
2076
                                else
2077
                                        full_parent = g_strdup(parent);
2078
                                new_item = folder_item_new(g_basename(parent),
2079
                                                           full_parent);
2080
                                new_item->no_select = TRUE;
2081
                                add_list = g_slist_prepend(add_list, new_item);
2082
                                r = new_item->path + root_path_len;
2083
                                while (*r == '/') r++;
2084
                                g_hash_table_insert(exist, (gpointer)r,
2085
                                                    GINT_TO_POINTER(1));
2086
                                debug_print("intermediate folder '%s' added\n",
2087
                                            full_parent);
2088
                                g_free(full_parent);
2089
                        }
2090
                        g_free(parent);
2091
                        while (*q == '/') q++;
2092
                }
2093
        }
2094

    
2095
        g_hash_table_destroy(exist);
2096

    
2097
        add_list = g_slist_reverse(add_list);
2098
        item_list = g_slist_concat(item_list, add_list);
2099

    
2100
        return item_list;
2101
}
2102

    
2103
static GSList *imap_get_part_folder_list(GSList *item_list, FolderItem *item)
2104
{
2105
        FolderItem *cur_item;
2106
        GSList *part_list = NULL, *cur;
2107
        gint len;
2108

    
2109
        if (!item->path) {
2110
                debug_print("imap_get_part_folder_list(): get root folders\n");
2111
                for (cur = item_list; cur != NULL; cur = cur->next) {
2112
                        cur_item = FOLDER_ITEM(cur->data);
2113

    
2114
                        if (!strchr(cur_item->path, '/')) {
2115
                                part_list = g_slist_prepend(part_list,
2116
                                                            cur_item);
2117
                                debug_print("append '%s'\n", cur_item->path);
2118
                        }
2119
                }
2120
                part_list = g_slist_reverse(part_list);
2121
                return part_list;
2122
        }
2123

    
2124
        len = strlen(item->path);
2125
        debug_print("imap_get_part_folder_list(): get folders under '%s'\n",
2126
                    item->path);
2127

    
2128
        for (cur = item_list; cur != NULL; cur = cur->next) {
2129
                cur_item = FOLDER_ITEM(cur->data);
2130

    
2131
                if (!strncmp(cur_item->path, item->path, len) &&
2132
                    cur_item->path[len] == '/' &&
2133
                    !strchr(cur_item->path + len + 1, '/')) {
2134
                        part_list = g_slist_prepend(part_list, cur_item);
2135
                        debug_print("append '%s'\n", cur_item->path);
2136
                }
2137
        }
2138

    
2139
        part_list = g_slist_reverse(part_list);
2140
        return part_list;
2141
}
2142

    
2143
static gint imap_create_tree(Folder *folder)
2144
{
2145
        g_return_val_if_fail(folder != NULL, -1);
2146
        g_return_val_if_fail(folder->node != NULL, -1);
2147
        g_return_val_if_fail(folder->node->data != NULL, -1);
2148
        g_return_val_if_fail(folder->account != NULL, -1);
2149

    
2150
        imap_scan_tree(folder);
2151
        imap_create_missing_folders(folder);
2152

    
2153
        return 0;
2154
}
2155

    
2156
static void imap_create_missing_folders(Folder *folder)
2157
{
2158
        g_return_if_fail(folder != NULL);
2159

    
2160
        if (!folder->inbox)
2161
                folder->inbox = imap_create_special_folder
2162
                        (folder, F_INBOX, "INBOX");
2163
#if 0
2164
        if (!folder->outbox)
2165
                folder->outbox = imap_create_special_folder
2166
                        (folder, F_OUTBOX, "Sent");
2167
        if (!folder->draft)
2168
                folder->draft = imap_create_special_folder
2169
                        (folder, F_DRAFT, "Drafts");
2170
        if (!folder->queue)
2171
                folder->queue = imap_create_special_folder
2172
                        (folder, F_QUEUE, "Queue");
2173
#endif
2174
        if (!folder->trash)
2175
                folder->trash = imap_create_special_folder
2176
                        (folder, F_TRASH, "Trash");
2177
}
2178

    
2179
static FolderItem *imap_create_special_folder(Folder *folder,
2180
                                              SpecialFolderItemType stype,
2181
                                              const gchar *name)
2182
{
2183
        FolderItem *item;
2184
        FolderItem *new_item;
2185

    
2186
        g_return_val_if_fail(folder != NULL, NULL);
2187
        g_return_val_if_fail(folder->node != NULL, NULL);
2188
        g_return_val_if_fail(folder->node->data != NULL, NULL);
2189
        g_return_val_if_fail(folder->account != NULL, NULL);
2190
        g_return_val_if_fail(name != NULL, NULL);
2191

    
2192
        item = FOLDER_ITEM(folder->node->data);
2193
        new_item = imap_create_folder(folder, item, name);
2194

    
2195
        if (!new_item) {
2196
                g_warning(_("Can't create '%s'\n"), name);
2197
                if (!folder->inbox) return NULL;
2198

    
2199
                new_item = imap_create_folder(folder, folder->inbox, name);
2200
                if (!new_item)
2201
                        g_warning(_("Can't create '%s' under INBOX\n"), name);
2202
                else
2203
                        new_item->stype = stype;
2204
        } else
2205
                new_item->stype = stype;
2206

    
2207
        return new_item;
2208
}
2209

    
2210
static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2211
                                      const gchar *name)
2212
{
2213
        gchar *dirpath, *imap_path;
2214
        IMAPSession *session;
2215
        FolderItem *new_item;
2216
        gchar separator;
2217
        gchar *new_name;
2218
        const gchar *p;
2219
        gboolean no_sub = FALSE, no_select = FALSE;
2220
        gint ok;
2221

    
2222
        g_return_val_if_fail(folder != NULL, NULL);
2223
        g_return_val_if_fail(folder->account != NULL, NULL);
2224
        g_return_val_if_fail(parent != NULL, NULL);
2225
        g_return_val_if_fail(name != NULL, NULL);
2226

    
2227
        session = imap_session_get(folder);
2228
        if (!session) return NULL;
2229

    
2230
        if (!parent->parent && strcmp(name, "INBOX") == 0)
2231
                dirpath = g_strdup(name);
2232
        else if (parent->path)
2233
                dirpath = g_strconcat(parent->path, "/", name, NULL);
2234
        else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2235
                dirpath = g_strdup(name);
2236
        else if (folder->account->imap_dir && *folder->account->imap_dir) {
2237
                gchar *imap_dir;
2238

    
2239
                Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
2240
                strtailchomp(imap_dir, '/');
2241
                dirpath = g_strconcat(imap_dir, "/", name, NULL);
2242
        } else
2243
                dirpath = g_strdup(name);
2244

    
2245
        /* keep trailing directory separator to create a folder that contains
2246
           sub folder */
2247
        imap_path = imap_utf8_to_modified_utf7(dirpath);
2248
        strtailchomp(dirpath, '/');
2249
        Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
2250
        strtailchomp(new_name, '/');
2251
        separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
2252
        imap_path_separator_subst(imap_path, separator);
2253
        subst_char(new_name, '/', separator);
2254

    
2255
        if (strcmp(name, "INBOX") != 0) {
2256
                GPtrArray *argbuf;
2257
                gint i;
2258
                gboolean exist = FALSE;
2259

    
2260
                argbuf = g_ptr_array_new();
2261
                ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2262
                if (ok != IMAP_SUCCESS) {
2263
                        log_warning(_("can't create mailbox: LIST failed\n"));
2264
                        g_free(imap_path);
2265
                        g_free(dirpath);
2266
                        g_ptr_array_free(argbuf, TRUE);
2267
                        return NULL;
2268
                }
2269

    
2270
                for (i = 0; i < argbuf->len; i++) {
2271
                        gchar *str;
2272
                        str = g_ptr_array_index(argbuf, i);
2273
                        if (!strncmp(str, "LIST ", 5)) {
2274
                                exist = TRUE;
2275
                                if (strcasestr(str + 5, "\\Noinferiors"))
2276
                                        no_sub = TRUE;
2277
                                if (strcasestr(str + 5, "\\Noselect"))
2278
                                        no_select = TRUE;
2279
                                break;
2280
                        }
2281
                }
2282

    
2283
                if (!exist) {
2284
                        ok = imap_cmd_create(session, imap_path);
2285
                        if (ok != IMAP_SUCCESS) {
2286
                                log_warning(_("can't create mailbox\n"));
2287
                                g_free(imap_path);
2288
                                g_free(dirpath);
2289
                                return NULL;
2290
                        }
2291

    
2292
                        g_ptr_array_free(argbuf, TRUE);
2293
                        argbuf = g_ptr_array_new();
2294
                        ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2295
                        if (ok != IMAP_SUCCESS) {
2296
                                log_warning("LIST failed\n");
2297
                                g_free(imap_path);
2298
                                g_free(dirpath);
2299
                                g_ptr_array_free(argbuf, TRUE);
2300
                                return NULL;
2301
                        }
2302

    
2303
                        for (i = 0; i < argbuf->len; i++) {
2304
                                gchar *str;
2305
                                str = g_ptr_array_index(argbuf, i);
2306
                                if (!strncmp(str, "LIST ", 5)) {
2307
                                        if (strcasestr(str + 5, "\\Noinferiors"))
2308
                                                no_sub = TRUE;
2309
                                        if (strcasestr(str + 5, "\\Noselect"))
2310
                                                no_select = TRUE;
2311
                                        break;
2312
                                }
2313
                        }
2314
                }
2315

    
2316
                g_ptr_array_free(argbuf, TRUE);
2317
        }
2318

    
2319
        new_item = folder_item_new(new_name, dirpath);
2320
        new_item->no_sub = no_sub;
2321
        new_item->no_select = no_select;
2322
        folder_item_append(parent, new_item);
2323
        g_free(imap_path);
2324
        g_free(dirpath);
2325

    
2326
        dirpath = folder_item_get_path(new_item);
2327
        if (!is_dir_exist(dirpath))
2328
                make_dir_hier(dirpath);
2329
        g_free(dirpath);
2330

    
2331
        return new_item;
2332
}
2333

    
2334
static gint imap_rename_folder_real(Folder *folder, FolderItem *item,
2335
                                    FolderItem *new_parent, const gchar *name)
2336
{
2337
        gchar *newpath;
2338
        gchar *real_oldpath;
2339
        gchar *real_newpath;
2340
        gchar *paths[2];
2341
        gchar *old_cache_dir;
2342
        gchar *new_cache_dir;
2343
        IMAPSession *session;
2344
        gchar separator;
2345
        gint ok;
2346
        gint exists, recent, unseen;
2347
        guint32 uid_validity;
2348

    
2349
        g_return_val_if_fail(folder != NULL, -1);
2350
        g_return_val_if_fail(item != NULL, -1);
2351
        g_return_val_if_fail(folder == item->folder, -1);
2352
        g_return_val_if_fail(item->path != NULL, -1);
2353
        g_return_val_if_fail(new_parent != NULL || name != NULL, -1);
2354
        if (new_parent) {
2355
                g_return_val_if_fail(item != new_parent, -1);
2356
                g_return_val_if_fail(item->parent != new_parent, -1);
2357
                g_return_val_if_fail(item->folder == new_parent->folder, -1);
2358
                if (g_node_is_ancestor(item->node, new_parent->node)) {
2359
                        g_warning("folder to be moved is ancestor of new parent\n");
2360
                        return -1;
2361
                }
2362
        }
2363

    
2364
        session = imap_session_get(folder);
2365
        if (!session) return -1;
2366

    
2367
        real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2368

    
2369
        g_free(session->mbox);
2370
        session->mbox = NULL;
2371
        ok = imap_cmd_examine(session, "INBOX",
2372
                              &exists, &recent, &unseen, &uid_validity);
2373
        if (ok != IMAP_SUCCESS) {
2374
                g_free(real_oldpath);
2375
                return -1;
2376
        }
2377

    
2378
        separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
2379
        if (new_parent) {
2380
                if (name) {
2381
                        if (new_parent->path)
2382
                                newpath = g_strconcat(new_parent->path,
2383
                                                      "/", name, NULL);
2384
                        else
2385
                                newpath = g_strdup(name);
2386
                } else {
2387
                        gchar *name_;
2388

    
2389
                        name_ = g_path_get_basename(item->path);
2390
                        if (new_parent->path)
2391
                                newpath = g_strconcat(new_parent->path,
2392
                                                      "/", name_, NULL);
2393
                        else
2394
                                newpath = g_strdup(name_);
2395
                        AUTORELEASE_STR(name_, );
2396
                        name = name_;
2397
                }
2398
        } else {
2399
                if (strchr(item->path, '/')) {
2400
                        gchar *dirpath;
2401

    
2402
                        dirpath = g_dirname(item->path);
2403
                        newpath = g_strconcat(dirpath, "/", name, NULL);
2404
                        g_free(dirpath);
2405
                } else
2406
                        newpath = g_strdup(name);
2407
        }
2408

    
2409
        real_newpath = imap_utf8_to_modified_utf7(newpath);
2410
        imap_path_separator_subst(real_newpath, separator);
2411

    
2412
        ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2413
        if (ok != IMAP_SUCCESS) {
2414
                log_warning(_("can't rename mailbox: %s to %s\n"),
2415
                            real_oldpath, real_newpath);
2416
                g_free(real_oldpath);
2417
                g_free(newpath);
2418
                g_free(real_newpath);
2419
                return -1;
2420
        }
2421

    
2422
        if (new_parent) {
2423
                g_node_unlink(item->node);
2424
                g_node_append(new_parent->node, item->node);
2425
                item->parent = new_parent;
2426
        }
2427

    
2428
        g_free(item->name);
2429
        item->name = g_strdup(name);
2430

    
2431
        old_cache_dir = folder_item_get_path(item);
2432

    
2433
        paths[0] = g_strdup(item->path);
2434
        paths[1] = newpath;
2435
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2436
                        imap_rename_folder_func, paths);
2437

    
2438
        if (is_dir_exist(old_cache_dir)) {
2439
                new_cache_dir = folder_item_get_path(item);
2440
                if (g_rename(old_cache_dir, new_cache_dir) < 0) {
2441
                        FILE_OP_ERROR(old_cache_dir, "rename");
2442
                }
2443
                g_free(new_cache_dir);
2444
        }
2445

    
2446
        g_free(old_cache_dir);
2447
        g_free(paths[0]);
2448
        g_free(newpath);
2449
        g_free(real_oldpath);
2450
        g_free(real_newpath);
2451

    
2452
        return 0;
2453
}
2454

    
2455
static gint imap_rename_folder(Folder *folder, FolderItem *item,
2456
                               const gchar *name)
2457
{
2458
        return imap_rename_folder_real(folder, item, NULL, name);
2459
}
2460

    
2461
static gint imap_move_folder(Folder *folder, FolderItem *item,
2462
                             FolderItem *new_parent)
2463
{
2464
        return imap_rename_folder_real(folder, item, new_parent, NULL);
2465
}
2466

    
2467
static gint imap_remove_folder(Folder *folder, FolderItem *item)
2468
{
2469
        gint ok;
2470
        IMAPSession *session;
2471
        gchar *path;
2472
        gchar *cache_dir;
2473
        gint exists, recent, unseen;
2474
        guint32 uid_validity;
2475

    
2476
        g_return_val_if_fail(folder != NULL, -1);
2477
        g_return_val_if_fail(item != NULL, -1);
2478
        g_return_val_if_fail(item->path != NULL, -1);
2479

    
2480
        session = imap_session_get(folder);
2481
        if (!session) return -1;
2482

    
2483
        path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2484

    
2485
        ok = imap_cmd_examine(session, "INBOX",
2486
                              &exists, &recent, &unseen, &uid_validity);
2487
        if (ok != IMAP_SUCCESS) {
2488
                g_free(path);
2489
                return -1;
2490
        }
2491

    
2492
        ok = imap_cmd_delete(session, path);
2493
        if (ok != IMAP_SUCCESS) {
2494
                log_warning(_("can't delete mailbox\n"));
2495
                g_free(path);
2496
                return -1;
2497
        }
2498

    
2499
        g_free(path);
2500
        cache_dir = folder_item_get_path(item);
2501
        if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2502
                g_warning("can't remove directory '%s'\n", cache_dir);
2503
        g_free(cache_dir);
2504
        folder_item_remove(item);
2505

    
2506
        return 0;
2507
}
2508

    
2509
static GSList *imap_get_uncached_messages(IMAPSession *session,
2510
                                          FolderItem *item,
2511
                                          guint32 first_uid, guint32 last_uid,
2512
                                          gint exists, gboolean update_count)
2513
{
2514
        gchar *tmp;
2515
        GSList *newlist = NULL;
2516
        GSList *llast = NULL;
2517
        GString *str;
2518
        MsgInfo *msginfo;
2519
        gchar seq_set[22];
2520
        gint count = 1;
2521
        GTimeVal tv_prev, tv_cur;
2522

    
2523
        g_return_val_if_fail(session != NULL, NULL);
2524
        g_return_val_if_fail(item != NULL, NULL);
2525
        g_return_val_if_fail(item->folder != NULL, NULL);
2526
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, NULL);
2527
        g_return_val_if_fail(first_uid <= last_uid, NULL);
2528

    
2529
        g_get_current_time(&tv_prev);
2530
        ui_update();
2531

    
2532
        if (first_uid == 0 && last_uid == 0)
2533
                strcpy(seq_set, "1:*");
2534
        else
2535
                g_snprintf(seq_set, sizeof(seq_set), "%u:%u",
2536
                           first_uid, last_uid);
2537
        if (imap_cmd_envelope(session, seq_set) != IMAP_SUCCESS) {
2538
                log_warning(_("can't get envelope\n"));
2539
                return NULL;
2540
        }
2541

    
2542
        str = g_string_new(NULL);
2543

    
2544
        for (;;) {
2545
                if (exists > 0 && count <= exists) {
2546
                        g_get_current_time(&tv_cur);
2547
                        if (tv_cur.tv_sec > tv_prev.tv_sec ||
2548
                            tv_cur.tv_usec - tv_prev.tv_usec >
2549
                            PROGRESS_UPDATE_INTERVAL * 1000) {
2550
                                status_print
2551
                                        (_("Getting message headers (%d / %d)"),
2552
                                         count, exists);
2553
                                progress_show(count, exists);
2554
                                ui_update();
2555
                                tv_prev = tv_cur;
2556
                        }
2557
                }
2558
                ++count;
2559

    
2560
                if (sock_getline(SESSION(session)->sock, &tmp) < 0) {
2561
                        log_warning(_("error occurred while getting envelope.\n"));
2562
                        g_string_free(str, TRUE);
2563
                        progress_show(0, 0);
2564
                        return newlist;
2565
                }
2566
                strretchomp(tmp);
2567
                if (tmp[0] != '*' || tmp[1] != ' ') {
2568
                        log_print("IMAP4< %s\n", tmp);
2569
                        g_free(tmp);
2570
                        break;
2571
                }
2572
                if (strstr(tmp, "FETCH") == NULL) {
2573
                        log_print("IMAP4< %s\n", tmp);
2574
                        g_free(tmp);
2575
                        continue;
2576
                }
2577
                log_print("IMAP4< %s\n", tmp);
2578
                g_string_assign(str, tmp);
2579
                g_free(tmp);
2580

    
2581
                msginfo = imap_parse_envelope(session, item, str);
2582
                if (!msginfo) {
2583
                        log_warning(_("can't parse envelope: %s\n"), str->str);
2584
                        continue;
2585
                }
2586
                if (update_count) {
2587
                        if (MSG_IS_NEW(msginfo->flags))
2588
                                item->new++;
2589
                        if (MSG_IS_UNREAD(msginfo->flags))
2590
                                item->unread++;
2591
                }
2592
                if (item->stype == F_QUEUE) {
2593
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2594
                } else if (item->stype == F_DRAFT) {
2595
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2596
                }
2597

    
2598
                msginfo->folder = item;
2599

    
2600
                if (!newlist)
2601
                        llast = newlist = g_slist_append(newlist, msginfo);
2602
                else {
2603
                        llast = g_slist_append(llast, msginfo);
2604
                        llast = llast->next;
2605
                }
2606

    
2607
                if (update_count)
2608
                        item->total++;
2609
        }
2610

    
2611
        progress_show(0, 0);
2612
        g_string_free(str, TRUE);
2613

    
2614
        session_set_access_time(SESSION(session));
2615

    
2616
        return newlist;
2617
}
2618

    
2619
static void imap_delete_cached_message(FolderItem *item, guint32 uid)
2620
{
2621
        gchar *dir;
2622
        gchar *file;
2623

    
2624
        g_return_if_fail(item != NULL);
2625
        g_return_if_fail(item->folder != NULL);
2626
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2627

    
2628
        dir = folder_item_get_path(item);
2629
        file = g_strdup_printf("%s%c%u", dir, G_DIR_SEPARATOR, uid);
2630

    
2631
        debug_print("Deleting cached message: %s\n", file);
2632

    
2633
        g_unlink(file);
2634

    
2635
        g_free(file);
2636
        g_free(dir);
2637
}
2638

    
2639
static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
2640
                                           guint32 first_uid, guint32 last_uid)
2641
{
2642
        GSList *cur, *next;
2643
        MsgInfo *msginfo;
2644
        gchar *dir;
2645

    
2646
        g_return_val_if_fail(item != NULL, mlist);
2647
        g_return_val_if_fail(item->folder != NULL, mlist);
2648
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, mlist);
2649

    
2650
        if (first_uid == 0 && last_uid == 0)
2651
                return mlist;
2652

    
2653
        debug_print("Deleting cached messages %u - %u ... ",
2654
                    first_uid, last_uid);
2655

    
2656
        dir = folder_item_get_path(item);
2657
        if (is_dir_exist(dir))
2658
                remove_numbered_files(dir, first_uid, last_uid);
2659
        g_free(dir);
2660

    
2661
        for (cur = mlist; cur != NULL; ) {
2662
                next = cur->next;
2663

    
2664
                msginfo = (MsgInfo *)cur->data;
2665
                if (msginfo != NULL && first_uid <= msginfo->msgnum &&
2666
                    msginfo->msgnum <= last_uid) {
2667
                        procmsg_msginfo_free(msginfo);
2668
                        mlist = g_slist_remove(mlist, msginfo);
2669
                }
2670

    
2671
                cur = next;
2672
        }
2673

    
2674
        debug_print("done.\n");
2675

    
2676
        return mlist;
2677
}
2678

    
2679
static void imap_delete_all_cached_messages(FolderItem *item)
2680
{
2681
        gchar *dir;
2682

    
2683
        g_return_if_fail(item != NULL);
2684
        g_return_if_fail(item->folder != NULL);
2685
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2686

    
2687
        debug_print("Deleting all cached messages... ");
2688

    
2689
        dir = folder_item_get_path(item);
2690
        if (is_dir_exist(dir))
2691
                remove_all_numbered_files(dir);
2692
        g_free(dir);
2693

    
2694
        debug_print("done.\n");
2695
}
2696

    
2697
#if USE_SSL
2698
static SockInfo *imap_open(const gchar *server, gushort port,
2699
                           SSLType ssl_type)
2700
#else
2701
static SockInfo *imap_open(const gchar *server, gushort port)
2702
#endif
2703
{
2704
        SockInfo *sock;
2705

    
2706
        if ((sock = sock_connect(server, port)) == NULL) {
2707
                log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2708
                            server, port);
2709
                return NULL;
2710
        }
2711

    
2712
#if USE_SSL
2713
        if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2714
                log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2715
                            server, port);
2716
                sock_close(sock);
2717
                return NULL;
2718
        }
2719
#endif
2720

    
2721
        return sock;
2722
}
2723

    
2724
static GList *imap_parse_namespace_str(gchar *str)
2725
{
2726
        gchar *p = str;
2727
        gchar *name;
2728
        gchar *separator;
2729
        IMAPNameSpace *namespace;
2730
        GList *ns_list = NULL;
2731

    
2732
        while (*p != '\0') {
2733
                /* parse ("#foo" "/") */
2734

    
2735
                while (*p && *p != '(') p++;
2736
                if (*p == '\0') break;
2737
                p++;
2738

    
2739
                while (*p && *p != '"') p++;
2740
                if (*p == '\0') break;
2741
                p++;
2742
                name = p;
2743

    
2744
                while (*p && *p != '"') p++;
2745
                if (*p == '\0') break;
2746
                *p = '\0';
2747
                p++;
2748

    
2749
                while (*p && g_ascii_isspace(*p)) p++;
2750
                if (*p == '\0') break;
2751
                if (strncmp(p, "NIL", 3) == 0)
2752
                        separator = NULL;
2753
                else if (*p == '"') {
2754
                        p++;
2755
                        separator = p;
2756
                        while (*p && *p != '"') p++;
2757
                        if (*p == '\0') break;
2758
                        *p = '\0';
2759
                        p++;
2760
                } else break;
2761

    
2762
                while (*p && *p != ')') p++;
2763
                if (*p == '\0') break;
2764
                p++;
2765

    
2766
                namespace = g_new(IMAPNameSpace, 1);
2767
                namespace->name = g_strdup(name);
2768
                namespace->separator = separator ? separator[0] : '\0';
2769
                ns_list = g_list_append(ns_list, namespace);
2770
        }
2771

    
2772
        return ns_list;
2773
}
2774

    
2775
static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2776
{
2777
        gchar *ns_str = NULL;
2778
        gchar **str_array;
2779

    
2780
        g_return_if_fail(session != NULL);
2781
        g_return_if_fail(folder != NULL);
2782

    
2783
        if (folder->ns_personal != NULL ||
2784
            folder->ns_others   != NULL ||
2785
            folder->ns_shared   != NULL)
2786
                return;
2787

    
2788
        if (imap_cmd_namespace(session, &ns_str) != IMAP_SUCCESS) {
2789
                log_warning(_("can't get namespace\n"));
2790
                imap_get_namespace_by_list(session, folder);
2791
                return;
2792
        }
2793

    
2794
        str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2795
        if (str_array[0])
2796
                folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2797
        if (str_array[0] && str_array[1])
2798
                folder->ns_others = imap_parse_namespace_str(str_array[1]);
2799
        if (str_array[0] && str_array[1] && str_array[2])
2800
                folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2801
        g_strfreev(str_array);
2802
        g_free(ns_str);
2803
}
2804

    
2805
static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2806
{
2807
        GSList *item_list, *cur;
2808
        gchar separator = '\0';
2809
        IMAPNameSpace *namespace;
2810

    
2811
        g_return_if_fail(session != NULL);
2812
        g_return_if_fail(folder != NULL);
2813

    
2814
        if (folder->ns_personal != NULL ||
2815
            folder->ns_others   != NULL ||
2816
            folder->ns_shared   != NULL)
2817
                return;
2818

    
2819
        imap_cmd_gen_send(session, "LIST \"\" \"\"");
2820
        item_list = imap_parse_list(session, "", &separator);
2821
        for (cur = item_list; cur != NULL; cur = cur->next)
2822
                folder_item_destroy(FOLDER_ITEM(cur->data));
2823
        g_slist_free(item_list);
2824

    
2825
        namespace = g_new(IMAPNameSpace, 1);
2826
        namespace->name = g_strdup("");
2827
        namespace->separator = separator;
2828
        folder->ns_personal = g_list_append(NULL, namespace);
2829
}
2830

    
2831
static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2832
                                                    const gchar *path)
2833
{
2834
        IMAPNameSpace *namespace = NULL;
2835
        gchar *tmp_path, *name;
2836

    
2837
        if (!path) path = "";
2838

    
2839
        for (; ns_list != NULL; ns_list = ns_list->next) {
2840
                IMAPNameSpace *tmp_ns = ns_list->data;
2841

    
2842
                Xstrcat_a(tmp_path, path, "/", return namespace);
2843
                Xstrdup_a(name, tmp_ns->name, return namespace);
2844
                if (tmp_ns->separator && tmp_ns->separator != '/') {
2845
                        subst_char(tmp_path, tmp_ns->separator, '/');
2846
                        subst_char(name, tmp_ns->separator, '/');
2847
                }
2848
                if (strncmp(tmp_path, name, strlen(name)) == 0)
2849
                        namespace = tmp_ns;
2850
        }
2851

    
2852
        return namespace;
2853
}
2854

    
2855
static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2856
                                          const gchar *path)
2857
{
2858
        IMAPNameSpace *namespace;
2859

    
2860
        g_return_val_if_fail(folder != NULL, NULL);
2861

    
2862
        namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2863
        if (namespace) return namespace;
2864
        namespace = imap_find_namespace_from_list(folder->ns_others, path);
2865
        if (namespace) return namespace;
2866
        namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2867
        if (namespace) return namespace;
2868

    
2869
        return NULL;
2870
}
2871

    
2872
static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2873
{
2874
        IMAPNameSpace *namespace;
2875
        gchar separator = '/';
2876

    
2877
        namespace = imap_find_namespace(folder, path);
2878
        if (namespace && namespace->separator)
2879
                separator = namespace->separator;
2880

    
2881
        return separator;
2882
}
2883

    
2884
static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2885
{
2886
        gchar *real_path;
2887
        gchar separator;
2888

    
2889
        g_return_val_if_fail(folder != NULL, NULL);
2890
        g_return_val_if_fail(path != NULL, NULL);
2891

    
2892
        real_path = imap_utf8_to_modified_utf7(path);
2893
        separator = imap_get_path_separator(folder, path);
2894
        imap_path_separator_subst(real_path, separator);
2895

    
2896
        return real_path;
2897
}
2898

    
2899
static gchar *imap_parse_atom(IMAPSession *session, gchar *src,
2900
                              gchar *dest, gint dest_len, GString *str)
2901
{
2902
        gchar *cur_pos = src;
2903
        gchar *nextline;
2904

    
2905
        g_return_val_if_fail(str != NULL, cur_pos);
2906

    
2907
        /* read the next line if the current response buffer is empty */
2908
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
2909
        while (*cur_pos == '\0') {
2910
                if (sock_getline(SESSION(session)->sock, &nextline) < 0)
2911
                        return cur_pos;
2912
                g_string_assign(str, nextline);
2913
                cur_pos = str->str;
2914
                strretchomp(nextline);
2915
                /* log_print("IMAP4< %s\n", nextline); */
2916
                debug_print("IMAP4< %s\n", nextline);
2917
                g_free(nextline);
2918

    
2919
                while (g_ascii_isspace(*cur_pos)) cur_pos++;
2920
        }
2921

    
2922
        if (*cur_pos == '~' && *(cur_pos + 1) == '{')
2923
                cur_pos++;
2924

    
2925
        if (!strncmp(cur_pos, "NIL", 3)) {
2926
                *dest = '\0';
2927
                cur_pos += 3;
2928
        } else if (*cur_pos == '\"') {
2929
                gchar *p;
2930

    
2931
                p = get_quoted(cur_pos, '\"', dest, dest_len);
2932
                cur_pos = p ? p : cur_pos + 2;
2933
        } else if (*cur_pos == '{') {
2934
                gchar buf[32];
2935
                gint len;
2936
                gint block_len = 0;
2937

    
2938
                cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2939
                len = atoi(buf);
2940
                g_return_val_if_fail(len >= 0, cur_pos);
2941

    
2942
                g_string_truncate(str, 0);
2943
                cur_pos = str->str;
2944

    
2945
                do {
2946
                        gint cur_len;
2947

    
2948
                        cur_len = sock_getline(SESSION(session)->sock,
2949
                                               &nextline);
2950
                        if (cur_len < 0)
2951
                                return cur_pos;
2952
                        block_len += cur_len;
2953
                        subst_null(nextline, cur_len, ' ');
2954
                        g_string_append(str, nextline);
2955
                        cur_pos = str->str;
2956
                        strretchomp(nextline);
2957
                        /* log_print("IMAP4< %s\n", nextline); */
2958
                        debug_print("IMAP4< %s\n", nextline);
2959
                        g_free(nextline);
2960
                } while (block_len < len);
2961

    
2962
                memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2963
                dest[MIN(len, dest_len - 1)] = '\0';
2964
                cur_pos += len;
2965
        }
2966

    
2967
        return cur_pos;
2968
}
2969

    
2970
static gchar *imap_get_header(IMAPSession *session, gchar *cur_pos,
2971
                              gchar **headers, GString *str)
2972
{
2973
        gchar *nextline;
2974
        gchar buf[32];
2975
        gint len;
2976
        gint block_len = 0;
2977

    
2978
        *headers = NULL;
2979

    
2980
        g_return_val_if_fail(str != NULL, cur_pos);
2981

    
2982
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
2983
        if (*cur_pos == '~' && *(cur_pos + 1) == '{')
2984
                cur_pos++;
2985

    
2986
        g_return_val_if_fail(*cur_pos == '{', cur_pos);
2987

    
2988
        cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2989
        len = atoi(buf);
2990
        g_return_val_if_fail(len >= 0, cur_pos);
2991

    
2992
        g_string_truncate(str, 0);
2993
        cur_pos = str->str;
2994

    
2995
        do {
2996
                gint cur_len;
2997

    
2998
                cur_len = sock_getline(SESSION(session)->sock, &nextline);
2999
                if (cur_len < 0)
3000
                        return cur_pos;
3001
                block_len += cur_len;
3002
                subst_null(nextline, cur_len, ' ');
3003
                g_string_append(str, nextline);
3004
                cur_pos = str->str;
3005
                /* strretchomp(nextline); */
3006
                /* debug_print("IMAP4< %s\n", nextline); */
3007
                g_free(nextline);
3008
        } while (block_len < len);
3009

    
3010
        debug_print("IMAP4< [contents of RFC822.HEADER]\n");
3011

    
3012
        *headers = g_strndup(cur_pos, len);
3013
        cur_pos += len;
3014

    
3015
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
3016
        while (*cur_pos == '\0') {
3017
                if (sock_getline(SESSION(session)->sock, &nextline) < 0)
3018
                        return cur_pos;
3019
                g_string_assign(str, nextline);
3020
                cur_pos = str->str;
3021
                strretchomp(nextline);
3022
                debug_print("IMAP4< %s\n", nextline);
3023
                g_free(nextline);
3024

    
3025
                while (g_ascii_isspace(*cur_pos)) cur_pos++;
3026
        }
3027

    
3028
        return cur_pos;
3029
}
3030

    
3031
static MsgFlags imap_parse_flags(const gchar *flag_str)  
3032
{
3033
        const gchar *p = flag_str;
3034
        MsgFlags flags = {0, 0};
3035

    
3036
        flags.perm_flags = MSG_UNREAD;
3037

    
3038
        while ((p = strchr(p, '\\')) != NULL) {
3039
                p++;
3040

    
3041
                if (g_ascii_strncasecmp(p, "Recent", 6) == 0 &&
3042
                    MSG_IS_UNREAD(flags)) {
3043
                        MSG_SET_PERM_FLAGS(flags, MSG_NEW);
3044
                } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
3045
                        MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
3046
                } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
3047
                        MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
3048
                } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
3049
                        MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
3050
                } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
3051
                        MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
3052
                }
3053
        }
3054

    
3055
        return flags;
3056
}
3057

    
3058
static IMAPFlags imap_parse_imap_flags(const gchar *flag_str)  
3059
{
3060
        const gchar *p = flag_str;
3061
        IMAPFlags flags = 0;
3062

    
3063
        while ((p = strchr(p, '\\')) != NULL) {
3064
                p++;
3065

    
3066
                if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
3067
                        flags |= IMAP_FLAG_SEEN;
3068
                } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
3069
                        flags |= IMAP_FLAG_DELETED;
3070
                } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
3071
                        flags |= IMAP_FLAG_FLAGGED;
3072
                } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
3073
                        flags |= IMAP_FLAG_ANSWERED;
3074
                }
3075
        }
3076

    
3077
        return flags;
3078
}
3079

    
3080
static MsgInfo *imap_parse_envelope(IMAPSession *session, FolderItem *item,
3081
                                    GString *line_str)
3082
{
3083
        gchar buf[IMAPBUFSIZE];
3084
        MsgInfo *msginfo = NULL;
3085
        gchar *cur_pos;
3086
        gint msgnum;
3087
        guint32 uid = 0;
3088
        size_t size = 0;
3089
        MsgFlags flags = {0, 0}, imap_flags = {0, 0};
3090

    
3091
        g_return_val_if_fail(line_str != NULL, NULL);
3092
        g_return_val_if_fail(line_str->str[0] == '*' &&
3093
                             line_str->str[1] == ' ', NULL);
3094

    
3095
        MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3096
        if (item->stype == F_QUEUE) {
3097
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3098
        } else if (item->stype == F_DRAFT) {
3099
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3100
        }
3101

    
3102
        cur_pos = line_str->str + 2;
3103

    
3104
#define PARSE_ONE_ELEMENT(ch)                                        \
3105
{                                                                \
3106
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
3107
        if (cur_pos == NULL) {                                        \
3108
                g_warning("cur_pos == NULL\n");                        \
3109
                procmsg_msginfo_free(msginfo);                        \
3110
                return NULL;                                        \
3111
        }                                                        \
3112
}
3113

    
3114
        PARSE_ONE_ELEMENT(' ');
3115
        msgnum = atoi(buf);
3116

    
3117
        PARSE_ONE_ELEMENT(' ');
3118
        g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
3119

    
3120
        g_return_val_if_fail(*cur_pos == '(', NULL);
3121
        cur_pos++;
3122

    
3123
        while (*cur_pos != '\0' && *cur_pos != ')') {
3124
                while (*cur_pos == ' ') cur_pos++;
3125

    
3126
                if (!strncmp(cur_pos, "UID ", 4)) {
3127
                        cur_pos += 4;
3128
                        uid = strtoul(cur_pos, &cur_pos, 10);
3129
                } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
3130
                        cur_pos += 6;
3131
                        if (*cur_pos != '(') {
3132
                                g_warning("*cur_pos != '('\n");
3133
                                procmsg_msginfo_free(msginfo);
3134
                                return NULL;
3135
                        }
3136
                        cur_pos++;
3137
                        PARSE_ONE_ELEMENT(')');
3138
                        imap_flags = imap_parse_flags(buf);
3139
                } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
3140
                        cur_pos += 12;
3141
                        size = strtol(cur_pos, &cur_pos, 10);
3142
                } else if (!strncmp(cur_pos, "RFC822.HEADER", 13)) {
3143
                        gchar *headers;
3144

    
3145
                        cur_pos += 13;
3146
                        cur_pos = imap_get_header(session, cur_pos, &headers,
3147
                                                  line_str);
3148
                        msginfo = procheader_parse_str(headers, flags, FALSE);
3149
                        g_free(headers);
3150
                } else {
3151
                        g_warning("invalid FETCH response: %s\n", cur_pos);
3152
                        break;
3153
                }
3154
        }
3155

    
3156
#undef PARSE_ONE_ELEMENT
3157

    
3158
        if (msginfo) {
3159
                msginfo->msgnum = uid;
3160
                msginfo->size = size;
3161
                msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
3162
                msginfo->flags.perm_flags = imap_flags.perm_flags;
3163
        }
3164

    
3165
        return msginfo;
3166
}
3167

    
3168
static gint imap_msg_list_change_perm_flags(GSList *msglist, MsgPermFlags flags,
3169
                                            gboolean is_set)
3170
{
3171
        Folder *folder;
3172
        IMAPSession *session;
3173
        IMAPFlags iflags = 0;
3174
        MsgInfo *msginfo;
3175
        GSList *seq_list, *cur;
3176
        gint ok = IMAP_SUCCESS;
3177

    
3178
        if (msglist == NULL) return IMAP_SUCCESS;
3179

    
3180
        msginfo = (MsgInfo *)msglist->data;
3181
        g_return_val_if_fail(msginfo != NULL, -1);
3182

    
3183
        g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
3184
        g_return_val_if_fail(msginfo->folder != NULL, -1);
3185
        g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
3186

    
3187
        folder = msginfo->folder->folder;
3188
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
3189

    
3190
        session = imap_session_get(folder);
3191
        if (!session) return -1;
3192

    
3193
        ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3194
                         NULL, NULL, NULL, NULL);
3195
        if (ok != IMAP_SUCCESS)
3196
                return ok;
3197

    
3198
        seq_list = imap_get_seq_set_from_msglist(msglist, 0);
3199

    
3200
        if (flags & MSG_MARKED)  iflags |= IMAP_FLAG_FLAGGED;
3201
        if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
3202

    
3203
        for (cur = seq_list; cur != NULL; cur = cur->next) {
3204
                gchar *seq_set = (gchar *)cur->data;
3205

    
3206
                if (iflags) {
3207
                        ok = imap_set_message_flags(session, seq_set, iflags,
3208
                                                    is_set);
3209
                        if (ok != IMAP_SUCCESS) break;
3210
                }
3211

    
3212
                if (flags & MSG_UNREAD) {
3213
                        ok = imap_set_message_flags(session, seq_set,
3214
                                                    IMAP_FLAG_SEEN, !is_set);
3215
                        if (ok != IMAP_SUCCESS) break;
3216
                }
3217
        }
3218

    
3219
        imap_seq_set_free(seq_list);
3220

    
3221
        return ok;
3222
}
3223

    
3224
gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3225
{
3226
        GSList msglist;
3227

    
3228
        msglist.data = msginfo;
3229
        msglist.next = NULL;
3230

    
3231
        return imap_msg_list_change_perm_flags(&msglist, flags, TRUE);
3232
}
3233

    
3234
gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3235
{
3236
        GSList msglist;
3237

    
3238
        msglist.data = msginfo;
3239
        msglist.next = NULL;
3240

    
3241
        return imap_msg_list_change_perm_flags(&msglist, flags, FALSE);
3242
}
3243

    
3244
gint imap_msg_list_set_perm_flags(GSList *msglist, MsgPermFlags flags)
3245
{
3246
        return imap_msg_list_change_perm_flags(msglist, flags, TRUE);
3247
}
3248

    
3249
gint imap_msg_list_unset_perm_flags(GSList *msglist, MsgPermFlags flags)
3250
{
3251
        return imap_msg_list_change_perm_flags(msglist, flags, FALSE);
3252
}
3253

    
3254
static gchar *imap_get_flag_str(IMAPFlags flags)
3255
{
3256
        GString *str;
3257
        gchar *ret;
3258

    
3259
        str = g_string_new(NULL);
3260

    
3261
        if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
3262
        if (IMAP_IS_ANSWERED(flags))        g_string_append(str, "\\Answered ");
3263
        if (IMAP_IS_FLAGGED(flags))        g_string_append(str, "\\Flagged ");
3264
        if (IMAP_IS_DELETED(flags))        g_string_append(str, "\\Deleted ");
3265
        if (IMAP_IS_DRAFT(flags))        g_string_append(str, "\\Draft");
3266

    
3267
        if (str->len > 0 && str->str[str->len - 1] == ' ')
3268
                g_string_truncate(str, str->len - 1);
3269

    
3270
        ret = str->str;
3271
        g_string_free(str, FALSE);
3272

    
3273
        return ret;
3274
}
3275

    
3276
static gint imap_set_message_flags(IMAPSession *session,
3277
                                   const gchar *seq_set,
3278
                                   IMAPFlags flags,
3279
                                   gboolean is_set)
3280
{
3281
        gchar *cmd;
3282
        gchar *flag_str;
3283
        gint ok;
3284

    
3285
        flag_str = imap_get_flag_str(flags);
3286
        cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
3287
                          flag_str, ")", NULL);
3288
        g_free(flag_str);
3289

    
3290
        ok = imap_cmd_store(session, seq_set, cmd);
3291
        g_free(cmd);
3292

    
3293
        return ok;
3294
}
3295

    
3296
static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3297
                        const gchar *path,
3298
                        gint *exists, gint *recent, gint *unseen,
3299
                        guint32 *uid_validity)
3300
{
3301
        gchar *real_path;
3302
        gint ok;
3303
        gint exists_, recent_, unseen_;
3304
        guint32 uid_validity_;
3305

    
3306
        if (!exists || !recent || !unseen || !uid_validity) {
3307
                if (session->mbox && strcmp(session->mbox, path) == 0)
3308
                        return IMAP_SUCCESS;
3309
                exists = &exists_;
3310
                recent = &recent_;
3311
                unseen = &unseen_;
3312
                uid_validity = &uid_validity_;
3313
        }
3314

    
3315
        g_free(session->mbox);
3316
        session->mbox = NULL;
3317

    
3318
        real_path = imap_get_real_path(folder, path);
3319
        ok = imap_cmd_select(session, real_path,
3320
                             exists, recent, unseen, uid_validity);
3321
        if (ok != IMAP_SUCCESS)
3322
                log_warning(_("can't select folder: %s\n"), real_path);
3323
        else
3324
                session->mbox = g_strdup(path);
3325
        g_free(real_path);
3326

    
3327
        return ok;
3328
}
3329

    
3330
#define THROW(err) { ok = err; goto catch; }
3331

    
3332
static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3333
                        const gchar *path,
3334
                        gint *messages, gint *recent,
3335
                        guint32 *uid_next, guint32 *uid_validity,
3336
                        gint *unseen)
3337
{
3338
        gchar *real_path;
3339
        gchar *real_path_;
3340
        gint ok;
3341
        GPtrArray *argbuf = NULL;
3342
        gchar *str;
3343

    
3344
        if (messages && recent && uid_next && uid_validity && unseen) {
3345
                *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
3346
                argbuf = g_ptr_array_new();
3347
        }
3348

    
3349
        real_path = imap_get_real_path(folder, path);
3350
        QUOTE_IF_REQUIRED(real_path_, real_path);
3351
        imap_cmd_gen_send(session, "STATUS %s "
3352
                          "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
3353
                          real_path_);
3354

    
3355
        ok = imap_cmd_ok(session, argbuf);
3356
        if (ok != IMAP_SUCCESS)
3357
                log_warning(_("error on imap command: STATUS\n"));
3358
        if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
3359

    
3360
        str = search_array_str(argbuf, "STATUS");
3361
        if (!str) THROW(IMAP_ERROR);
3362

    
3363
        str = strchr(str, '(');
3364
        if (!str) THROW(IMAP_ERROR);
3365
        str++;
3366
        while (*str != '\0' && *str != ')') {
3367
                while (*str == ' ') str++;
3368

    
3369
                if (!strncmp(str, "MESSAGES ", 9)) {
3370
                        str += 9;
3371
                        *messages = strtol(str, &str, 10);
3372
                } else if (!strncmp(str, "RECENT ", 7)) {
3373
                        str += 7;
3374
                        *recent = strtol(str, &str, 10);
3375
                } else if (!strncmp(str, "UIDNEXT ", 8)) {
3376
                        str += 8;
3377
                        *uid_next = strtoul(str, &str, 10);
3378
                } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
3379
                        str += 12;
3380
                        *uid_validity = strtoul(str, &str, 10);
3381
                } else if (!strncmp(str, "UNSEEN ", 7)) {
3382
                        str += 7;
3383
                        *unseen = strtol(str, &str, 10);
3384
                } else {
3385
                        g_warning("invalid STATUS response: %s\n", str);
3386
                        break;
3387
                }
3388
        }
3389

    
3390
catch:
3391
        g_free(real_path);
3392
        if (argbuf) {
3393
                ptr_array_free_strings(argbuf);
3394
                g_ptr_array_free(argbuf, TRUE);
3395
        }
3396

    
3397
        return ok;
3398
}
3399

    
3400
#undef THROW
3401

    
3402
static gboolean imap_has_capability(IMAPSession        *session,
3403
                                    const gchar *capability)
3404
{
3405
        gchar **p;
3406

    
3407
        for (p = session->capability; *p != NULL; ++p) {
3408
                if (!g_ascii_strcasecmp(*p, capability))
3409
                        return TRUE;
3410
        }
3411

    
3412
        return FALSE;
3413
}
3414

    
3415
static void imap_capability_free(IMAPSession *session)
3416
{
3417
        if (session->capability) {
3418
                g_strfreev(session->capability);
3419
                session->capability = NULL;
3420
        }
3421
}
3422

    
3423

    
3424
/* low-level IMAP4rev1 commands */
3425

    
3426
#define THROW(err) { ok = err; goto catch; }
3427

    
3428
static gint imap_cmd_capability(IMAPSession *session)
3429
{
3430
        gint ok;
3431
        GPtrArray *argbuf;
3432
        gchar *capability;
3433

    
3434
        argbuf = g_ptr_array_new();
3435

    
3436
        imap_cmd_gen_send(session, "CAPABILITY");
3437
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3438

    
3439
        capability = search_array_str(argbuf, "CAPABILITY ");
3440
        if (!capability) THROW(IMAP_ERROR);
3441

    
3442
        capability += strlen("CAPABILITY ");
3443

    
3444
        imap_capability_free(session);
3445
        session->capability = g_strsplit(capability, " ", -1);
3446

    
3447
catch:
3448
        ptr_array_free_strings(argbuf);
3449
        g_ptr_array_free(argbuf, TRUE);
3450

    
3451
        return ok;
3452
}
3453

    
3454
#undef THROW
3455

    
3456
static gint imap_cmd_auth_plain(IMAPSession *session, const gchar *user,
3457
                                const gchar *pass)
3458
{
3459
        gchar *p;
3460
        gchar *response;
3461
        gchar *response64;
3462
        gint ok;
3463

    
3464
        p = response = g_malloc(strlen(user) * 2 + 2 + strlen(pass) + 1);
3465
        strcpy(p, user);
3466
        p += strlen(user) + 1;
3467
        strcpy(p, user);
3468
        p += strlen(user) + 1;
3469
        strcpy(p, pass);
3470
        p += strlen(pass);
3471

    
3472
        response64 = g_malloc((p - response) * 2 + 1);
3473
        base64_encode(response64, (guchar *)response, p - response);
3474
        g_free(response);
3475

    
3476
        log_print("IMAP4> ****************\n");
3477
        sock_puts(SESSION(session)->sock, response64);
3478
        ok = imap_cmd_ok(session, NULL);
3479
        if (ok != IMAP_SUCCESS)
3480
                log_warning(_("IMAP4 authentication failed.\n"));
3481
        g_free(response64);
3482

    
3483
        return ok;
3484
}
3485

    
3486
static gint imap_cmd_auth_cram_md5(IMAPSession *session, const gchar *user,
3487
                                   const gchar *pass, const gchar *challenge64)
3488
{
3489
        gchar *challenge;
3490
        gint challenge_len;
3491
        gchar hexdigest[33];
3492
        gchar *response;
3493
        gchar *response64;
3494
        gint ok;
3495

    
3496
        challenge = g_malloc(strlen(challenge64 + 2) + 1);
3497
        challenge_len = base64_decode((guchar *)challenge, challenge64 + 2, -1);
3498
        challenge[challenge_len] = '\0';
3499
        log_print("IMAP< [Decoded: %s]\n", challenge);
3500

    
3501
        md5_hex_hmac(hexdigest, (guchar *)challenge, challenge_len,
3502
                     (guchar *)pass, strlen(pass));
3503
        g_free(challenge);
3504

    
3505
        response = g_strdup_printf("%s %s", user, hexdigest);
3506
        log_print("IMAP> [Encoded: %s]\n", response);
3507
        response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3508
        base64_encode(response64, (guchar *)response, strlen(response));
3509
        g_free(response);
3510

    
3511
        log_print("IMAP> %s\n", response64);
3512
        sock_puts(SESSION(session)->sock, response64);
3513
        ok = imap_cmd_ok(session, NULL);
3514
        if (ok != IMAP_SUCCESS)
3515
                log_warning(_("IMAP4 authentication failed.\n"));
3516

    
3517
        return ok;
3518
}
3519

    
3520
static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
3521
                                  const gchar *pass, IMAPAuthType type)
3522
{
3523
        gchar *auth_type;
3524
        gint ok;
3525
        gchar *buf = NULL;
3526

    
3527
        g_return_val_if_fail((type == 0 || type == IMAP_AUTH_CRAM_MD5 ||
3528
                              type == IMAP_AUTH_PLAIN), IMAP_ERROR);
3529

    
3530
        if (type == IMAP_AUTH_PLAIN)
3531
                auth_type = "PLAIN";
3532
        else
3533
                auth_type = "CRAM-MD5";
3534

    
3535
        imap_cmd_gen_send(session, "AUTHENTICATE %s", auth_type);
3536
        ok = imap_cmd_gen_recv(session, &buf);
3537
        if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
3538
                g_free(buf);
3539
                return IMAP_ERROR;
3540
        }
3541

    
3542
        if (type == IMAP_AUTH_PLAIN)
3543
                ok = imap_cmd_auth_plain(session, user, pass);
3544
        else
3545
                ok = imap_cmd_auth_cram_md5(session, user, pass, buf);
3546

    
3547
        g_free(buf);
3548

    
3549
        return ok;
3550
}
3551

    
3552
static gint imap_cmd_login(IMAPSession *session,
3553
                           const gchar *user, const gchar *pass)
3554
{
3555
        gchar *user_, *pass_;
3556
        gint ok;
3557

    
3558
        QUOTE_IF_REQUIRED(user_, user);
3559
        QUOTE_IF_REQUIRED(pass_, pass);
3560
        imap_cmd_gen_send(session, "LOGIN %s %s", user_, pass_);
3561

    
3562
        ok = imap_cmd_ok(session, NULL);
3563
        if (ok != IMAP_SUCCESS)
3564
                log_warning(_("IMAP4 login failed.\n"));
3565

    
3566
        return ok;
3567
}
3568

    
3569
static gint imap_cmd_logout(IMAPSession *session)
3570
{
3571
        imap_cmd_gen_send(session, "LOGOUT");
3572
        return imap_cmd_ok(session, NULL);
3573
}
3574

    
3575
static gint imap_cmd_noop(IMAPSession *session)
3576
{
3577
        imap_cmd_gen_send(session, "NOOP");
3578
        return imap_cmd_ok(session, NULL);
3579
}
3580

    
3581
#if USE_SSL
3582
static gint imap_cmd_starttls(IMAPSession *session)
3583
{
3584
        imap_cmd_gen_send(session, "STARTTLS");
3585
        return imap_cmd_ok(session, NULL);
3586
}
3587
#endif
3588

    
3589
#define THROW(err) { ok = err; goto catch; }
3590

    
3591
static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3592
{
3593
        gint ok;
3594
        GPtrArray *argbuf;
3595
        gchar *str;
3596

    
3597
        argbuf = g_ptr_array_new();
3598

    
3599
        imap_cmd_gen_send(session, "NAMESPACE");
3600
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3601

    
3602
        str = search_array_str(argbuf, "NAMESPACE");
3603
        if (!str) THROW(IMAP_ERROR);
3604

    
3605
        *ns_str = g_strdup(str);
3606

    
3607
catch:
3608
        ptr_array_free_strings(argbuf);
3609
        g_ptr_array_free(argbuf, TRUE);
3610

    
3611
        return ok;
3612
}
3613

    
3614
#undef THROW
3615

    
3616
static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3617
                          const gchar *mailbox, GPtrArray *argbuf)
3618
{
3619
        gchar *ref_, *mailbox_;
3620

    
3621
        if (!ref) ref = "\"\"";
3622
        if (!mailbox) mailbox = "\"\"";
3623

    
3624
        QUOTE_IF_REQUIRED(ref_, ref);
3625
        QUOTE_IF_REQUIRED(mailbox_, mailbox);
3626
        imap_cmd_gen_send(session, "LIST %s %s", ref_, mailbox_);
3627

    
3628
        return imap_cmd_ok(session, argbuf);
3629
}
3630

    
3631
#define THROW goto catch
3632

    
3633
static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3634
                               gboolean examine,
3635
                               gint *exists, gint *recent, gint *unseen,
3636
                               guint32 *uid_validity)
3637
{
3638
        gint ok;
3639
        gchar *resp_str;
3640
        GPtrArray *argbuf;
3641
        gchar *select_cmd;
3642
        gchar *folder_;
3643
        guint uid_validity_;
3644

    
3645
        *exists = *recent = *unseen = *uid_validity = 0;
3646
        argbuf = g_ptr_array_new();
3647

    
3648
        if (examine)
3649
                select_cmd = "EXAMINE";
3650
        else
3651
                select_cmd = "SELECT";
3652

    
3653
        QUOTE_IF_REQUIRED(folder_, folder);
3654
        imap_cmd_gen_send(session, "%s %s", select_cmd, folder_);
3655

    
3656
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
3657

    
3658
        resp_str = search_array_contain_str(argbuf, "EXISTS");
3659
        if (resp_str) {
3660
                if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3661
                        g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3662
                        THROW;
3663
                }
3664
        }
3665

    
3666
        resp_str = search_array_contain_str(argbuf, "RECENT");
3667
        if (resp_str) {
3668
                if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3669
                        g_warning("imap_cmd_select(): invalid RECENT line.\n");
3670
                        THROW;
3671
                }
3672
        }
3673

    
3674
        resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3675
        if (resp_str) {
3676
                if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3677
                    != 1) {
3678
                        g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3679
                        THROW;
3680
                }
3681
                *uid_validity = uid_validity_;
3682
        }
3683

    
3684
        resp_str = search_array_contain_str(argbuf, "UNSEEN");
3685
        if (resp_str) {
3686
                if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3687
                        g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3688
                        THROW;
3689
                }
3690
        }
3691

    
3692
catch:
3693
        ptr_array_free_strings(argbuf);
3694
        g_ptr_array_free(argbuf, TRUE);
3695

    
3696
        return ok;
3697
}
3698

    
3699
static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3700
                            gint *exists, gint *recent, gint *unseen,
3701
                            guint32 *uid_validity)
3702
{
3703
        return imap_cmd_do_select(session, folder, FALSE,
3704
                                  exists, recent, unseen, uid_validity);
3705
}
3706

    
3707
static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3708
                             gint *exists, gint *recent, gint *unseen,
3709
                             guint32 *uid_validity)
3710
{
3711
        return imap_cmd_do_select(session, folder, TRUE,
3712
                                  exists, recent, unseen, uid_validity);
3713
}
3714

    
3715
#undef THROW
3716

    
3717
static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3718
{
3719
        gchar *folder_;
3720

    
3721
        QUOTE_IF_REQUIRED(folder_, folder);
3722
        imap_cmd_gen_send(session, "CREATE %s", folder_);
3723

    
3724
        return imap_cmd_ok(session, NULL);
3725
}
3726

    
3727
static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3728
                            const gchar *new_folder)
3729
{
3730
        gchar *old_folder_, *new_folder_;
3731

    
3732
        QUOTE_IF_REQUIRED(old_folder_, old_folder);
3733
        QUOTE_IF_REQUIRED(new_folder_, new_folder);
3734
        imap_cmd_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3735

    
3736
        return imap_cmd_ok(session, NULL);
3737
}
3738

    
3739
static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3740
{
3741
        gchar *folder_;
3742

    
3743
        QUOTE_IF_REQUIRED(folder_, folder);
3744
        imap_cmd_gen_send(session, "DELETE %s", folder_);
3745

    
3746
        return imap_cmd_ok(session, NULL);
3747
}
3748

    
3749
#define THROW(err) { ok = err; goto catch; }
3750

    
3751
static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3752
                            GArray **result)
3753
{
3754
        gint ok;
3755
        GPtrArray *argbuf;
3756
        GArray *array;
3757
        gchar *str;
3758
        gchar *p, *ep;
3759
        gint i;
3760
        guint32 uid;
3761

    
3762
        g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3763
        g_return_val_if_fail(result != NULL, IMAP_ERROR);
3764

    
3765
        argbuf = g_ptr_array_new();
3766

    
3767
        imap_cmd_gen_send(session, "UID SEARCH %s", criteria);
3768
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3769

    
3770
        array = g_array_new(FALSE, FALSE, sizeof(guint32));
3771

    
3772
        for (i = 0; i < argbuf->len; i++) {
3773
                str = g_ptr_array_index(argbuf, i);
3774
                if (strncmp(str, "SEARCH", 6) != 0)
3775
                        continue;
3776

    
3777
                p = str + 6;
3778
                while (*p != '\0') {
3779
                        uid = strtoul(p, &ep, 10);
3780
                        if (p < ep && uid > 0) {
3781
                                g_array_append_val(array, uid);
3782
                                p = ep;
3783
                        } else
3784
                                break;
3785
                }
3786
        }
3787

    
3788
        *result = array;
3789

    
3790
catch:
3791
        ptr_array_free_strings(argbuf);
3792
        g_ptr_array_free(argbuf, TRUE);
3793

    
3794
        return ok;
3795
}
3796

    
3797
static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3798
                           const gchar *filename)
3799
{
3800
        gint ok;
3801
        gchar *buf;
3802
        gchar *cur_pos;
3803
        gchar size_str[32];
3804
        glong size_num;
3805
        gint ret;
3806

    
3807
        g_return_val_if_fail(filename != NULL, IMAP_ERROR);
3808

    
3809
        imap_cmd_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3810

    
3811
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
3812
                if (buf[0] != '*' || buf[1] != ' ') {
3813
                        g_free(buf);
3814
                        return IMAP_ERROR;
3815
                }
3816
                if (strstr(buf, "FETCH") != NULL) break;
3817
                g_free(buf);
3818
        }
3819
        if (ok != IMAP_SUCCESS)
3820
                return ok;
3821

    
3822
#define RETURN_ERROR_IF_FAIL(cond)                 \
3823
        if (!(cond)) {                                 \
3824
                g_free(buf);                         \
3825
                ok = imap_cmd_ok(session, NULL); \
3826
                return IMAP_ERROR;                 \
3827
        }
3828

    
3829
        cur_pos = strchr(buf, '{');
3830
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3831
        cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3832
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3833
        size_num = atol(size_str);
3834
        RETURN_ERROR_IF_FAIL(size_num >= 0);
3835

    
3836
        RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3837

    
3838
#undef RETURN_ERROR_IF_FAIL
3839

    
3840
        g_free(buf);
3841

    
3842
        if ((ret = recv_bytes_write_to_file(SESSION(session)->sock,
3843
                                            size_num, filename)) != 0) {
3844
                if (ret == -2)
3845
                        return IMAP_SOCKET;
3846
        }
3847

    
3848
        if (imap_cmd_gen_recv(session, &buf) != IMAP_SUCCESS)
3849
                return IMAP_ERROR;
3850

    
3851
        if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3852
                g_free(buf);
3853
                return IMAP_ERROR;
3854
        }
3855
        g_free(buf);
3856

    
3857
        ok = imap_cmd_ok(session, NULL);
3858

    
3859
        if (ret != 0)
3860
                return IMAP_ERROR;
3861

    
3862
        return ok;
3863
}
3864

    
3865
static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3866
                            const gchar *file, IMAPFlags flags,
3867
                            guint32 *new_uid)
3868
{
3869
        gint ok;
3870
        gint size;
3871
        gchar *destfolder_;
3872
        gchar *flag_str;
3873
        guint new_uid_;
3874
        gchar *ret = NULL;
3875
        gchar buf[BUFFSIZE];
3876
        FILE *fp;
3877
        FILE *tmp;
3878
        size_t read_len;
3879
        GPtrArray *argbuf;
3880
        gchar *resp_str;
3881

    
3882
        g_return_val_if_fail(file != NULL, IMAP_ERROR);
3883

    
3884
        if ((fp = g_fopen(file, "rb")) == NULL) {
3885
                FILE_OP_ERROR(file, "fopen");
3886
                return -1;
3887
        }
3888
        tmp = canonicalize_file_stream(fp, &size);
3889
        fclose(fp);
3890
        if (!tmp)
3891
                return -1;
3892

    
3893
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
3894
        flag_str = imap_get_flag_str(flags);
3895
        imap_cmd_gen_send(session, "APPEND %s (%s) {%d}",
3896
                          destfolder_, flag_str, size);
3897
        g_free(flag_str);
3898

    
3899
        ok = imap_cmd_gen_recv(session, &ret);
3900
        if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3901
                log_warning(_("can't append %s to %s\n"), file, destfolder_);
3902
                g_free(ret);
3903
                fclose(tmp);
3904
                return IMAP_ERROR;
3905
        }
3906
        g_free(ret);
3907

    
3908
        log_print("IMAP4> %s\n", _("(sending file...)"));
3909

    
3910
        while ((read_len = fread(buf, 1, sizeof(buf), tmp)) > 0) {
3911
                if (read_len < sizeof(buf) && ferror(tmp))
3912
                        break;
3913
                if (sock_write_all(SESSION(session)->sock, buf, read_len) < 0) {
3914
                        fclose(tmp);
3915
                        return -1;
3916
                }
3917
        }
3918

    
3919
        if (ferror(tmp)) {
3920
                FILE_OP_ERROR(file, "fread");
3921
                fclose(tmp);
3922
                return -1;
3923
        }
3924

    
3925
        sock_puts(SESSION(session)->sock, "");
3926

    
3927
        fclose(tmp);
3928

    
3929
        if (new_uid != NULL)
3930
                *new_uid = 0;
3931

    
3932
        if (new_uid != NULL && session->uidplus) {
3933
                argbuf = g_ptr_array_new();
3934

    
3935
                ok = imap_cmd_ok(session, argbuf);
3936
                if (ok != IMAP_SUCCESS)
3937
                        log_warning(_("can't append message to %s\n"),
3938
                                    destfolder_);
3939
                else if (argbuf->len > 0) {
3940
                        resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3941
                        if (resp_str &&
3942
                            sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3943
                                   &new_uid_) == 1) {
3944
                                *new_uid = new_uid_;
3945
                        }
3946
                }
3947

    
3948
                ptr_array_free_strings(argbuf);
3949
                g_ptr_array_free(argbuf, TRUE);
3950
        } else
3951
                ok = imap_cmd_ok(session, NULL);
3952

    
3953
        return ok;
3954
}
3955

    
3956
static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3957
                          const gchar *destfolder)
3958
{
3959
        gint ok;
3960
        gchar *destfolder_;
3961

    
3962
        g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3963

    
3964
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
3965
        imap_cmd_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3966

    
3967
        ok = imap_cmd_ok(session, NULL);
3968
        if (ok != IMAP_SUCCESS) {
3969
                log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3970
                return -1;
3971
        }
3972

    
3973
        return ok;
3974
}
3975

    
3976
gint imap_cmd_envelope(IMAPSession *session, const gchar *seq_set)
3977
{
3978
        imap_cmd_gen_send
3979
                (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
3980
                 seq_set);
3981

    
3982
        return IMAP_SUCCESS;
3983
}
3984

    
3985
static gint imap_cmd_store(IMAPSession *session, const gchar *seq_set,
3986
                           const gchar *sub_cmd)
3987
{
3988
        gint ok;
3989

    
3990
        imap_cmd_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3991

    
3992
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3993
                log_warning(_("error while imap command: STORE %s %s\n"),
3994
                            seq_set, sub_cmd);
3995
                return ok;
3996
        }
3997

    
3998
        return IMAP_SUCCESS;
3999
}
4000

    
4001
static gint imap_cmd_expunge(IMAPSession *session)
4002
{
4003
        gint ok;
4004

    
4005
        imap_cmd_gen_send(session, "EXPUNGE");
4006
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
4007
                log_warning(_("error while imap command: EXPUNGE\n"));
4008
                return ok;
4009
        }
4010

    
4011
        return IMAP_SUCCESS;
4012
}
4013

    
4014
static gint imap_cmd_close(IMAPSession *session)
4015
{
4016
        gint ok;
4017

    
4018
        imap_cmd_gen_send(session, "CLOSE");
4019
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
4020
                log_warning(_("error while imap command: CLOSE\n"));
4021

    
4022
        return ok;
4023
}
4024

    
4025
static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
4026
{
4027
        gint ok;
4028
        gchar *buf;
4029
        gint cmd_num;
4030
        gchar cmd_status[IMAPBUFSIZE + 1];
4031
        GString *str;
4032
        gchar *p;
4033
        gchar obuf[32];
4034
        gint len;
4035
        gchar *literal;
4036

    
4037
        str = g_string_sized_new(256);
4038

    
4039
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
4040
                g_string_append(str, buf);
4041

    
4042
                if ((p = strrchr_with_skip_quote(buf, '"', '{'))) {
4043
                        /* literal */
4044
                        p = strchr_cpy(p + 1, '}', obuf, sizeof(obuf));
4045
                        len = atoi(obuf);
4046
                        if (len < 0 || p == NULL || *p != '\0') {
4047
                                g_free(buf);
4048
                                ok = IMAP_ERROR;
4049
                                break;
4050
                        }
4051

    
4052
                        literal = recv_bytes(SESSION(session)->sock, len);
4053
                        if (!literal) {
4054
                                g_free(buf);
4055
                                ok = IMAP_SOCKET;
4056
                                break;
4057
                        }
4058
                        if (memchr(literal, '\n', len))
4059
                                log_print("IMAP4< (literal: %d bytes)\n", len);
4060
                        else
4061
                                log_print("IMAP4< %s\n", literal);
4062

    
4063
                        g_string_append(str, "\r\n");
4064
                        g_string_append_len(str, literal, len);
4065
                        g_free(literal);
4066
                        g_free(buf);
4067
                        continue;
4068
                }
4069

    
4070
                g_free(buf);
4071

    
4072
                if (str->str[0] == '*' && str->str[1] == ' ') {
4073
                        if (argbuf)
4074
                                g_ptr_array_add(argbuf, g_strdup(str->str + 2));
4075

    
4076
                        g_string_truncate(str, 0);
4077
                        continue;
4078
                } else if (sscanf(str->str, "%d %" Xstr(IMAPBUFSIZE) "s",
4079
                           &cmd_num, cmd_status) < 2) {
4080
                        ok = IMAP_ERROR;
4081
                } else if (cmd_num == session->cmd_count &&
4082
                           !strcmp(cmd_status, "OK")) {
4083
                        if (argbuf)
4084
                                g_ptr_array_add(argbuf, g_strdup(str->str));
4085
                } else {
4086
                        ok = IMAP_ERROR;
4087
                }
4088

    
4089
                break;
4090
        }
4091

    
4092
        g_string_free(str, TRUE);
4093
        return ok;
4094
}
4095

    
4096
static void imap_cmd_gen_send(IMAPSession *session, const gchar *format, ...)
4097
{
4098
        gchar buf[IMAPBUFSIZE];
4099
        gchar tmp[IMAPBUFSIZE];
4100
        gchar *p;
4101
        va_list args;
4102

    
4103
        va_start(args, format);
4104
        g_vsnprintf(tmp, sizeof(tmp), format, args);
4105
        va_end(args);
4106

    
4107
        session->cmd_count++;
4108

    
4109
        g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp);
4110
        if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) &&
4111
            (p = strchr(tmp + 6, ' '))) {
4112
                *p = '\0';
4113
                log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
4114
        } else
4115
                log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
4116

    
4117
        sock_write_all(SESSION(session)->sock, buf, strlen(buf));
4118
}
4119

    
4120
static gint imap_cmd_gen_recv(IMAPSession *session, gchar **ret)
4121
{
4122
        gint len;
4123

    
4124
        if ((len = sock_getline(SESSION(session)->sock, ret)) < 0)
4125
                return IMAP_SOCKET;
4126

    
4127
        strretchomp(*ret);
4128

    
4129
        if (len > 1000) {
4130
                gchar *str;
4131

    
4132
                str = trim_string(*ret, 1000);
4133
                log_print("IMAP4< %s\n", str);
4134
                g_free(str);
4135
        } else
4136
                log_print("IMAP4< %s\n", *ret);
4137

    
4138
        session_set_access_time(SESSION(session));
4139

    
4140
        return IMAP_SUCCESS;
4141
}
4142

    
4143

    
4144
/* misc utility functions */
4145

    
4146
static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
4147
{
4148
        gchar *tmp;
4149

    
4150
        dest[0] = '\0';
4151
        tmp = strchr(src, ch);
4152
        if (!tmp)
4153
                return NULL;
4154

    
4155
        memcpy(dest, src, MIN(tmp - src, len - 1));
4156
        dest[MIN(tmp - src, len - 1)] = '\0';
4157

    
4158
        return tmp + 1;
4159
}
4160

    
4161
static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
4162
{
4163
        const gchar *p = src;
4164
        gint n = 0;
4165

    
4166
        g_return_val_if_fail(*p == ch, NULL);
4167

    
4168
        *dest = '\0';
4169
        p++;
4170

    
4171
        while (*p != '\0' && *p != ch) {
4172
                if (n < len - 1) {
4173
                        if (*p == '\\' && *(p + 1) != '\0')
4174
                                p++;
4175
                        *dest++ = *p++;
4176
                } else
4177
                        p++;
4178
                n++;
4179
        }
4180

    
4181
        *dest = '\0';
4182
        return (gchar *)(*p == ch ? p + 1 : p);
4183
}
4184

    
4185
static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
4186
{
4187
        gint i;
4188

    
4189
        for (i = 0; i < array->len; i++) {
4190
                gchar *tmp;
4191

    
4192
                tmp = g_ptr_array_index(array, i);
4193
                if (strstr(tmp, str) != NULL)
4194
                        return tmp;
4195
        }
4196

    
4197
        return NULL;
4198
}
4199

    
4200
static gchar *search_array_str(GPtrArray *array, gchar *str)
4201
{
4202
        gint i;
4203
        gint len;
4204

    
4205
        len = strlen(str);
4206

    
4207
        for (i = 0; i < array->len; i++) {
4208
                gchar *tmp;
4209

    
4210
                tmp = g_ptr_array_index(array, i);
4211
                if (!strncmp(tmp, str, len))
4212
                        return tmp;
4213
        }
4214

    
4215
        return NULL;
4216
}
4217

    
4218
static void imap_path_separator_subst(gchar *str, gchar separator)
4219
{
4220
        gchar *p;
4221
        gboolean in_escape = FALSE;
4222

    
4223
        if (!separator || separator == '/') return;
4224

    
4225
        for (p = str; *p != '\0'; p++) {
4226
                if (*p == '/' && !in_escape)
4227
                        *p = separator;
4228
                else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4229
                        in_escape = TRUE;
4230
                else if (*p == '-' && in_escape)
4231
                        in_escape = FALSE;
4232
        }
4233
}
4234

    
4235
static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
4236
{
4237
        static iconv_t cd = (iconv_t)-1;
4238
        static gboolean iconv_ok = TRUE;
4239
        GString *norm_utf7;
4240
        gchar *norm_utf7_p;
4241
        size_t norm_utf7_len;
4242
        const gchar *p;
4243
        gchar *to_str, *to_p;
4244
        size_t to_len;
4245
        gboolean in_escape = FALSE;
4246

    
4247
        if (!iconv_ok) return g_strdup(mutf7_str);
4248

    
4249
        if (cd == (iconv_t)-1) {
4250
                cd = iconv_open(CS_INTERNAL, CS_UTF_7);
4251
                if (cd == (iconv_t)-1) {
4252
                        g_warning("iconv cannot convert UTF-7 to %s\n",
4253
                                  CS_INTERNAL);
4254
                        iconv_ok = FALSE;
4255
                        return g_strdup(mutf7_str);
4256
                }
4257
        }
4258

    
4259
        /* modified UTF-7 to normal UTF-7 conversion */
4260
        norm_utf7 = g_string_new(NULL);
4261

    
4262
        for (p = mutf7_str; *p != '\0'; p++) {
4263
                /* replace: '&'  -> '+',
4264
                            "&-" -> '&',
4265
                            "+"  -> "+-",
4266
                            escaped ','  -> '/' */
4267
                if (!in_escape && *p == '&') {
4268
                        if (*(p + 1) != '-') {
4269
                                g_string_append_c(norm_utf7, '+');
4270
                                in_escape = TRUE;
4271
                        } else {
4272
                                g_string_append_c(norm_utf7, '&');
4273
                                p++;
4274
                        }
4275
                } else if (!in_escape && *p == '+') {
4276
                        g_string_append(norm_utf7, "+-");
4277
                } else if (in_escape && *p == ',') {
4278
                        g_string_append_c(norm_utf7, '/');
4279
                } else if (in_escape && *p == '-') {
4280
                        g_string_append_c(norm_utf7, '-');
4281
                        in_escape = FALSE;
4282
                } else {
4283
                        g_string_append_c(norm_utf7, *p);
4284
                }
4285
        }
4286

    
4287
        /* somehow iconv() returns error when the last of the string is "+-" */
4288
        g_string_append_c(norm_utf7, '\n');
4289
        norm_utf7_p = norm_utf7->str;
4290
        norm_utf7_len = norm_utf7->len;
4291
        to_len = strlen(mutf7_str) * 5;
4292
        to_p = to_str = g_malloc(to_len + 1);
4293

    
4294
        if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
4295
                  &to_p, &to_len) == -1) {
4296
                g_warning(_("iconv cannot convert UTF-7 to %s\n"), CS_INTERNAL);
4297
                g_string_free(norm_utf7, TRUE);
4298
                g_free(to_str);
4299
                return g_strdup(mutf7_str);
4300
        }
4301

    
4302
        /* second iconv() call for flushing */
4303
        iconv(cd, NULL, NULL, &to_p, &to_len);
4304
        g_string_free(norm_utf7, TRUE);
4305
        *to_p = '\0';
4306
        strretchomp(to_str);
4307

    
4308
        return to_str;
4309
}
4310

    
4311
static gchar *imap_utf8_to_modified_utf7(const gchar *from)
4312
{
4313
        static iconv_t cd = (iconv_t)-1;
4314
        static gboolean iconv_ok = TRUE;
4315
        gchar *norm_utf7, *norm_utf7_p;
4316
        size_t from_len, norm_utf7_len;
4317
        GString *to_str;
4318
        gchar *from_tmp, *to, *p;
4319
        gboolean in_escape = FALSE;
4320

    
4321
        if (!iconv_ok) return g_strdup(from);
4322

    
4323
        if (cd == (iconv_t)-1) {
4324
                cd = iconv_open(CS_UTF_7, CS_INTERNAL);
4325
                if (cd == (iconv_t)-1) {
4326
                        g_warning(_("iconv cannot convert %s to UTF-7\n"),
4327
                                  CS_INTERNAL);
4328
                        iconv_ok = FALSE;
4329
                        return g_strdup(from);
4330
                }
4331
        }
4332

    
4333
        /* UTF-8 to normal UTF-7 conversion */
4334
        Xstrdup_a(from_tmp, from, return g_strdup(from));
4335
        from_len = strlen(from);
4336
        norm_utf7_len = from_len * 5;
4337
        Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
4338
        norm_utf7_p = norm_utf7;
4339

    
4340
        while (from_len > 0) {
4341
                if (*from_tmp == '+') {
4342
                        *norm_utf7_p++ = '+';
4343
                        *norm_utf7_p++ = '-';
4344
                        norm_utf7_len -= 2;
4345
                        from_tmp++;
4346
                        from_len--;
4347
                } else if (g_ascii_isprint(*from_tmp)) {
4348
                        /* printable ascii char */
4349
                        *norm_utf7_p = *from_tmp;
4350
                        norm_utf7_p++;
4351
                        norm_utf7_len--;
4352
                        from_tmp++;
4353
                        from_len--;
4354
                } else {
4355
                        size_t conv_len = 0;
4356

    
4357
                        /* unprintable char: convert to UTF-7 */
4358
                        p = from_tmp;
4359
                        while (!g_ascii_isprint(*p) && conv_len < from_len) {
4360
                                conv_len += g_utf8_skip[*(guchar *)p];
4361
                                p += g_utf8_skip[*(guchar *)p];
4362
                        }
4363

    
4364
                        from_len -= conv_len;
4365
                        if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
4366
                                  &conv_len,
4367
                                  &norm_utf7_p, &norm_utf7_len) == -1) {
4368
                                g_warning("iconv cannot convert %s to UTF-7\n",
4369
                                          CS_INTERNAL);
4370
                                return g_strdup(from);
4371
                        }
4372

    
4373
                        /* second iconv() call for flushing */
4374
                        iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
4375
                }
4376
        }
4377

    
4378
        *norm_utf7_p = '\0';
4379
        to_str = g_string_new(NULL);
4380
        for (p = norm_utf7; p < norm_utf7_p; p++) {
4381
                /* replace: '&' -> "&-",
4382
                            '+' -> '&',
4383
                            "+-" -> '+',
4384
                            BASE64 '/' -> ',' */
4385
                if (!in_escape && *p == '&') {
4386
                        g_string_append(to_str, "&-");
4387
                } else if (!in_escape && *p == '+') {
4388
                        if (*(p + 1) == '-') {
4389
                                g_string_append_c(to_str, '+');
4390
                                p++;
4391
                        } else {
4392
                                g_string_append_c(to_str, '&');
4393
                                in_escape = TRUE;
4394
                        }
4395
                } else if (in_escape && *p == '/') {
4396
                        g_string_append_c(to_str, ',');
4397
                } else if (in_escape && *p == '-') {
4398
                        g_string_append_c(to_str, '-');
4399
                        in_escape = FALSE;
4400
                } else {
4401
                        g_string_append_c(to_str, *p);
4402
                }
4403
        }
4404

    
4405
        if (in_escape) {
4406
                in_escape = FALSE;
4407
                g_string_append_c(to_str, '-');
4408
        }
4409

    
4410
        to = to_str->str;
4411
        g_string_free(to_str, FALSE);
4412

    
4413
        return to;
4414
}
4415

    
4416
static GSList *imap_get_seq_set_from_msglist(GSList *msglist, gint limit)
4417
{
4418
        GString *str;
4419
        GSList *sorted_list, *cur;
4420
        guint first, last, next;
4421
        gchar *ret_str;
4422
        GSList *ret_list = NULL;
4423
        gint count = 0;
4424

    
4425
        if (msglist == NULL)
4426
                return NULL;
4427

    
4428
        str = g_string_sized_new(256);
4429

    
4430
        sorted_list = g_slist_copy(msglist);
4431
        sorted_list = procmsg_sort_msg_list(sorted_list, SORT_BY_NUMBER,
4432
                                            SORT_ASCENDING);
4433

    
4434
        first = ((MsgInfo *)sorted_list->data)->msgnum;
4435

    
4436
        for (cur = sorted_list; cur != NULL; cur = cur->next) {
4437
                ++count;
4438
                last = ((MsgInfo *)cur->data)->msgnum;
4439
                if (cur->next)
4440
                        next = ((MsgInfo *)cur->next->data)->msgnum;
4441
                else
4442
                        next = 0;
4443

    
4444
                if (limit > 0 && count >= limit) {
4445
                        if (str->len > 0)
4446
                                g_string_append_c(str, ',');
4447
                        if (first == last)
4448
                                g_string_sprintfa(str, "%u", first);
4449
                        else
4450
                                g_string_sprintfa(str, "%u:%u", first, last);
4451

    
4452
                        first = next;
4453

    
4454
                        ret_str = g_strdup(str->str);
4455
                        ret_list = g_slist_append(ret_list, ret_str);
4456
                        g_string_truncate(str, 0);
4457
                        count = 0;
4458
                        continue;
4459
                }
4460

    
4461
                if (last + 1 != next || next == 0) {
4462
                        if (str->len > 0)
4463
                                g_string_append_c(str, ',');
4464
                        if (first == last)
4465
                                g_string_sprintfa(str, "%u", first);
4466
                        else
4467
                                g_string_sprintfa(str, "%u:%u", first, last);
4468

    
4469
                        first = next;
4470

    
4471
                        if (str->len > IMAP_CMD_LIMIT) {
4472
                                ret_str = g_strdup(str->str);
4473
                                ret_list = g_slist_append(ret_list, ret_str);
4474
                                g_string_truncate(str, 0);
4475
                        }
4476
                }
4477
        }
4478

    
4479
        if (str->len > 0) {
4480
                ret_str = g_strdup(str->str);
4481
                ret_list = g_slist_append(ret_list, ret_str);
4482
        }
4483

    
4484
        g_slist_free(sorted_list);
4485
        g_string_free(str, TRUE);
4486

    
4487
        return ret_list;
4488
}
4489

    
4490
static gint imap_seq_set_get_count(const gchar *seq_set)
4491
{
4492
        gint count = 0;
4493
        guint first, last;
4494
        gchar *tmp, *p, *q;
4495

    
4496
        p = q = tmp = g_strdup(seq_set);
4497

    
4498
        while (*p) {
4499
                if (*p == ',') {
4500
                        *p = '\0';
4501
                        if (sscanf(q, "%u:%u", &first, &last) == 2)
4502
                                count += last - first + 1;
4503
                        else if (sscanf(q, "%u", &first) == 1)
4504
                                count++;
4505
                        q = ++p;
4506
                } else
4507
                        ++p;
4508
        }
4509
        if (q != p) {
4510
                if (sscanf(q, "%u:%u", &first, &last) == 2)
4511
                        count += last - first + 1;
4512
                else if (sscanf(q, "%u", &first) == 1)
4513
                        count++;
4514
        }
4515

    
4516
        g_free(tmp);
4517

    
4518
        return count;
4519
}
4520

    
4521
static void imap_seq_set_free(GSList *seq_list)
4522
{
4523
        slist_free_strings(seq_list);
4524
        g_slist_free(seq_list);
4525
}
4526

    
4527
static GHashTable *imap_get_uid_table(GArray *array)
4528
{
4529
        GHashTable *table;
4530
        gint i;
4531
        guint32 uid;
4532

    
4533
        g_return_val_if_fail(array != NULL, NULL);
4534

    
4535
        table = g_hash_table_new(NULL, g_direct_equal);
4536

    
4537
        for (i = 0; i < array->len; i++) {
4538
                uid = g_array_index(array, guint32, i);
4539
                g_hash_table_insert(table, GUINT_TO_POINTER(uid),
4540
                                    GINT_TO_POINTER(i + 1));
4541
        }
4542

    
4543
        return table;
4544
}
4545

    
4546
static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4547
{
4548
        FolderItem *item = node->data;
4549
        gchar **paths = data;
4550
        const gchar *oldpath = paths[0];
4551
        const gchar *newpath = paths[1];
4552
        gchar *base;
4553
        gchar *new_itempath;
4554
        gint oldpathlen;
4555

    
4556
        oldpathlen = strlen(oldpath);
4557
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4558
                g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4559
                return TRUE;
4560
        }
4561

    
4562
        base = item->path + oldpathlen;
4563
        while (*base == '/') base++;
4564
        if (*base == '\0')
4565
                new_itempath = g_strdup(newpath);
4566
        else
4567
                new_itempath = g_strconcat(newpath, "/", base, NULL);
4568
        g_free(item->path);
4569
        item->path = new_itempath;
4570

    
4571
        return FALSE;
4572
}