Statistics
| Revision:

root / src / imap.c @ 1

History | View | Annotate | Download (94.3 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19

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

    
24
#include "defs.h"
25

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

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

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

    
59
#define IMAP_CMD_LIMIT        1000
60

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

    
76
static GList *session_list = NULL;
77

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

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

    
86
static Session *imap_session_new        (PrefsAccount        *account);
87
static void imap_session_destroy        (Session        *session);
88
/* static void imap_session_destroy_all        (void); */
89

    
90
static gint imap_search_flags                (IMAPSession        *session,
91
                                         GArray               **uids,
92
                                         GHashTable    **flags_table);
93
static gint imap_fetch_flags                (IMAPSession        *session,
94
                                         GArray               **uids,
95
                                         GHashTable    **flags_table);
96

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

    
117
static gint imap_move_msg                (Folder                *folder,
118
                                         FolderItem        *dest,
119
                                         MsgInfo        *msginfo);
120
static gint imap_move_msgs                (Folder                *folder,
121
                                         FolderItem        *dest,
122
                                         GSList                *msglist);
123
static gint imap_copy_msg                (Folder                *folder,
124
                                         FolderItem        *dest,
125
                                         MsgInfo        *msginfo);
126
static gint imap_copy_msgs                (Folder                *folder,
127
                                         FolderItem        *dest,
128
                                         GSList                *msglist);
129

    
130
static gint imap_remove_msg                (Folder                *folder,
131
                                         FolderItem        *item,
132
                                         MsgInfo        *msginfo);
133
static gint imap_remove_msgs                (Folder                *folder,
134
                                         FolderItem        *item,
135
                                         GSList                *msglist);
136
static gint imap_remove_all_msg                (Folder                *folder,
137
                                         FolderItem        *item);
138

    
139
static gboolean imap_is_msg_changed        (Folder                *folder,
140
                                         FolderItem        *item,
141
                                         MsgInfo        *msginfo);
142

    
143
static gint imap_close                        (Folder                *folder,
144
                                         FolderItem        *item);
145

    
146
static gint imap_scan_folder                (Folder                *folder,
147
                                         FolderItem        *item);
148
static gint imap_scan_tree                (Folder                *folder);
149

    
150
static gint imap_create_tree                (Folder                *folder);
151

    
152
static FolderItem *imap_create_folder        (Folder                *folder,
153
                                         FolderItem        *parent,
154
                                         const gchar        *name);
155
static gint imap_rename_folder                (Folder                *folder,
156
                                         FolderItem        *item,
157
                                         const gchar        *name);
158
static gint imap_remove_folder                (Folder                *folder,
159
                                         FolderItem        *item);
160

    
161
static IMAPSession *imap_session_get        (Folder                *folder);
162

    
163
static gint imap_greeting                (IMAPSession        *session);
164
static gint imap_auth                        (IMAPSession        *session,
165
                                         const gchar        *user,
166
                                         const gchar        *pass,
167
                                         IMAPAuthType         type);
168

    
169
static gint imap_scan_tree_recursive        (IMAPSession        *session,
170
                                         FolderItem        *item);
171
static GSList *imap_parse_list                (IMAPSession        *session,
172
                                         const gchar        *real_path,
173
                                         gchar                *separator);
174

    
175
static void imap_create_missing_folders        (Folder                        *folder);
176
static FolderItem *imap_create_special_folder
177
                                        (Folder                        *folder,
178
                                         SpecialFolderItemType         stype,
179
                                         const gchar                *name);
180

    
181
static gint imap_do_copy_msgs                (Folder                *folder,
182
                                         FolderItem        *dest, 
183
                                         GSList                *msglist,
184
                                         gboolean         remove_source);
185
static gint imap_remove_msgs_by_seq_set        (Folder                *folder,
186
                                         FolderItem        *item,
187
                                         GSList                *seq_list);
188

    
189
static GSList *imap_get_uncached_messages        (IMAPSession        *session,
190
                                                 FolderItem        *item,
191
                                                 guint32         first_uid,
192
                                                 guint32         last_uid,
193
                                                 gboolean         update_count);
194
static void imap_delete_cached_message                (FolderItem        *item,
195
                                                 guint32         uid);
196
static GSList *imap_delete_cached_messages        (GSList                *mlist,
197
                                                 FolderItem        *item,
198
                                                 guint32         first_uid,
199
                                                 guint32         last_uid);
200
static void imap_delete_all_cached_messages        (FolderItem        *item);
201

    
202
#if USE_SSL
203
static SockInfo *imap_open                (const gchar        *server,
204
                                         gushort         port,
205
                                         SSLType         ssl_type);
206
#else
207
static SockInfo *imap_open                (const gchar        *server,
208
                                         gushort         port);
209
#endif
210

    
211
static gint imap_msg_list_change_perm_flags        (GSList                *msglist,
212
                                                 MsgPermFlags         flags,
213
                                                 gboolean         is_set);
214
static gchar *imap_get_flag_str                        (IMAPFlags         flags);
215
static gint imap_set_message_flags                (IMAPSession        *session,
216
                                                 const gchar        *seq_set,
217
                                                 IMAPFlags         flags,
218
                                                 gboolean         is_set);
219
static gint imap_select                                (IMAPSession        *session,
220
                                                 IMAPFolder        *folder,
221
                                                 const gchar        *path,
222
                                                 gint                *exists,
223
                                                 gint                *recent,
224
                                                 gint                *unseen,
225
                                                 guint32        *uid_validity);
226
static gint imap_status                                (IMAPSession        *session,
227
                                                 IMAPFolder        *folder,
228
                                                 const gchar        *path,
229
                                                 gint                *messages,
230
                                                 gint                *recent,
231
                                                 guint32        *uid_next,
232
                                                 guint32        *uid_validity,
233
                                                 gint                *unseen);
234

    
235
static void imap_parse_namespace                (IMAPSession        *session,
236
                                                 IMAPFolder        *folder);
237
static void imap_get_namespace_by_list                (IMAPSession        *session,
238
                                                 IMAPFolder        *folder);
239
static IMAPNameSpace *imap_find_namespace        (IMAPFolder        *folder,
240
                                                 const gchar        *path);
241
static gchar imap_get_path_separator                (IMAPFolder        *folder,
242
                                                 const gchar        *path);
243
static gchar *imap_get_real_path                (IMAPFolder        *folder,
244
                                                 const gchar        *path);
245

    
246
static gchar *imap_parse_atom                (IMAPSession        *session,
247
                                         gchar                *src,
248
                                         gchar                *dest,
249
                                         gint                 dest_len,
250
                                         GString        *str);
251
static MsgFlags imap_parse_flags        (const gchar        *flag_str);
252
static IMAPFlags imap_parse_imap_flags        (const gchar        *flag_str);
253
static MsgInfo *imap_parse_envelope        (IMAPSession        *session,
254
                                         FolderItem        *item,
255
                                         GString        *line_str);
256

    
257
static gboolean imap_has_capability        (IMAPSession        *session,
258
                                         const gchar        *capability);
259
static void imap_capability_free        (IMAPSession        *session);
260

    
261
/* low-level IMAP4rev1 commands */
262
static gint imap_cmd_capability        (IMAPSession        *session);
263
static gint imap_cmd_authenticate
264
                                (IMAPSession        *session,
265
                                 const gchar        *user,
266
                                 const gchar        *pass,
267
                                 IMAPAuthType         type);
268
static gint imap_cmd_login        (IMAPSession        *session,
269
                                 const gchar        *user,
270
                                 const gchar        *pass);
271
static gint imap_cmd_logout        (IMAPSession        *session);
272
static gint imap_cmd_noop        (IMAPSession        *session);
273
#if USE_SSL
274
static gint imap_cmd_starttls        (IMAPSession        *session);
275
#endif
276
static gint imap_cmd_namespace        (IMAPSession        *session,
277
                                 gchar               **ns_str);
278
static gint imap_cmd_list        (IMAPSession        *session,
279
                                 const gchar        *ref,
280
                                 const gchar        *mailbox,
281
                                 GPtrArray        *argbuf);
282
static gint imap_cmd_do_select        (IMAPSession        *session,
283
                                 const gchar        *folder,
284
                                 gboolean         examine,
285
                                 gint                *exists,
286
                                 gint                *recent,
287
                                 gint                *unseen,
288
                                 guint32        *uid_validity);
289
static gint imap_cmd_select        (IMAPSession        *session,
290
                                 const gchar        *folder,
291
                                 gint                *exists,
292
                                 gint                *recent,
293
                                 gint                *unseen,
294
                                 guint32        *uid_validity);
295
static gint imap_cmd_examine        (IMAPSession        *session,
296
                                 const gchar        *folder,
297
                                 gint                *exists,
298
                                 gint                *recent,
299
                                 gint                *unseen,
300
                                 guint32        *uid_validity);
301
static gint imap_cmd_create        (IMAPSession        *session,
302
                                 const gchar        *folder);
303
static gint imap_cmd_rename        (IMAPSession        *session,
304
                                 const gchar        *oldfolder,
305
                                 const gchar        *newfolder);
306
static gint imap_cmd_delete        (IMAPSession        *session,
307
                                 const gchar        *folder);
308
static gint imap_cmd_envelope        (IMAPSession        *session,
309
                                 const gchar        *seq_set);
310
static gint imap_cmd_search        (IMAPSession        *session,
311
                                 const gchar        *criteria,
312
                                 GArray        **result);
313
static gint imap_cmd_fetch        (IMAPSession        *session,
314
                                 guint32         uid,
315
                                 const gchar        *filename);
316
static gint imap_cmd_append        (IMAPSession        *session,
317
                                 const gchar        *destfolder,
318
                                 const gchar        *file,
319
                                 IMAPFlags         flags,
320
                                 guint32        *new_uid);
321
static gint imap_cmd_copy        (IMAPSession        *session,
322
                                 const gchar        *seq_set,
323
                                 const gchar        *destfolder);
324
static gint imap_cmd_store        (IMAPSession        *session,
325
                                 const gchar        *seq_set,
326
                                 const gchar        *sub_cmd);
327
static gint imap_cmd_expunge        (IMAPSession        *session);
328
static gint imap_cmd_close        (IMAPSession        *session);
329

    
330
static gint imap_cmd_ok                (IMAPSession        *session,
331
                                 GPtrArray        *argbuf);
332
static void imap_cmd_gen_send        (IMAPSession        *session,
333
                                 const gchar        *format, ...);
334
static gint imap_cmd_gen_recv        (IMAPSession        *session,
335
                                 gchar               **ret);
336

    
337
/* misc utility functions */
338
static gchar *strchr_cpy                        (const gchar        *src,
339
                                                 gchar                 ch,
340
                                                 gchar                *dest,
341
                                                 gint                 len);
342
static gchar *get_quoted                        (const gchar        *src,
343
                                                 gchar                 ch,
344
                                                 gchar                *dest,
345
                                                 gint                 len);
346
static gchar *search_array_contain_str                (GPtrArray        *array,
347
                                                 gchar                *str);
348
static gchar *search_array_str                        (GPtrArray        *array,
349
                                                 gchar                *str);
350
static void imap_path_separator_subst                (gchar                *str,
351
                                                 gchar                 separator);
352

    
353
static gchar *imap_modified_utf7_to_locale        (const gchar        *mutf7_str);
354
static gchar *imap_locale_to_modified_utf7        (const gchar        *from);
355

    
356
static GSList *imap_get_seq_set_from_msglist        (GSList                *msglist);
357
static void imap_seq_set_free                        (GSList                *seq_list);
358

    
359
static GHashTable *imap_get_uid_table                (GArray                *array);
360

    
361
static gboolean imap_rename_folder_func                (GNode                *node,
362
                                                 gpointer         data);
363

    
364
static FolderClass imap_class =
365
{
366
        F_IMAP,
367

    
368
        imap_folder_new,
369
        imap_folder_destroy,
370

    
371
        imap_scan_tree,
372
        imap_create_tree,
373

    
374
        imap_get_msg_list,
375
        imap_fetch_msg,
376
        imap_get_msginfo,
377
        imap_add_msg,
378
        imap_add_msgs,
379
        imap_move_msg,
380
        imap_move_msgs,
381
        imap_copy_msg,
382
        imap_copy_msgs,
383
        imap_remove_msg,
384
        imap_remove_msgs,
385
        imap_remove_all_msg,
386
        imap_is_msg_changed,
387
        imap_close,
388
        imap_scan_folder,
389

    
390
        imap_create_folder,
391
        imap_rename_folder,
392
        imap_remove_folder
393
};
394

    
395

    
396
FolderClass *imap_get_class(void)
397
{
398
        return &imap_class;
399
}
400

    
401
static Folder *imap_folder_new(const gchar *name, const gchar *path)
402
{
403
        Folder *folder;
404

    
405
        folder = (Folder *)g_new0(IMAPFolder, 1);
406
        imap_folder_init(folder, name, path);
407

    
408
        return folder;
409
}
410

    
411
static void imap_folder_destroy(Folder *folder)
412
{
413
        gchar *dir;
414

    
415
        dir = folder_get_path(folder);
416
        if (is_dir_exist(dir))
417
                remove_dir_recursive(dir);
418
        g_free(dir);
419

    
420
        folder_remote_folder_destroy(REMOTE_FOLDER(folder));
421
}
422

    
423
static void imap_folder_init(Folder *folder, const gchar *name,
424
                             const gchar *path)
425
{
426
        folder->klass = imap_get_class();
427
        folder_remote_folder_init(folder, name, path);
428
}
429

    
430
static IMAPSession *imap_session_get(Folder *folder)
431
{
432
        RemoteFolder *rfolder = REMOTE_FOLDER(folder);
433

    
434
        g_return_val_if_fail(folder != NULL, NULL);
435
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
436
        g_return_val_if_fail(folder->account != NULL, NULL);
437

    
438
        if (!prefs_common.online_mode)
439
                return NULL;
440

    
441
        if (!rfolder->session) {
442
                rfolder->session = imap_session_new(folder->account);
443
                if (rfolder->session)
444
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
445
                                             IMAP_FOLDER(folder));
446
                return IMAP_SESSION(rfolder->session);
447
        }
448

    
449
        if (time(NULL) - rfolder->session->last_access_time <
450
                SESSION_TIMEOUT_INTERVAL) {
451
                return IMAP_SESSION(rfolder->session);
452
        }
453

    
454
        if (imap_cmd_noop(IMAP_SESSION(rfolder->session)) != IMAP_SUCCESS) {
455
                log_warning(_("IMAP4 connection to %s has been"
456
                              " disconnected. Reconnecting...\n"),
457
                            folder->account->recv_server);
458
                session_destroy(rfolder->session);
459
                rfolder->session = imap_session_new(folder->account);
460
                if (rfolder->session)
461
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
462
                                             IMAP_FOLDER(folder));
463
        }
464

    
465
        return IMAP_SESSION(rfolder->session);
466
}
467

    
468
static gint imap_greeting(IMAPSession *session)
469
{
470
        gchar *greeting;
471
        gint ok;
472

    
473
        if ((ok = imap_cmd_gen_recv(session, &greeting)) != IMAP_SUCCESS)
474
                return ok;
475

    
476
        if (greeting[0] != '*' || greeting[1] != ' ')
477
                ok = IMAP_ERROR;
478
        else if (!strncmp(greeting + 2, "OK", 2))
479
                ok = IMAP_SUCCESS;
480
        else if (!strncmp(greeting + 2, "PREAUTH", 7)) {
481
                session->authenticated = TRUE;
482
                ok = IMAP_SUCCESS;
483
        } else
484
                ok = IMAP_ERROR;
485

    
486
        g_free(greeting);
487
        return ok;
488
}
489

    
490
static gint imap_auth(IMAPSession *session, const gchar *user,
491
                      const gchar *pass, IMAPAuthType type)
492
{
493
        gint ok;
494

    
495
        if (type == 0 || type == IMAP_AUTH_LOGIN)
496
                ok = imap_cmd_login(session, user, pass);
497
        else
498
                ok = imap_cmd_authenticate(session, user, pass, type);
499

    
500
        if (ok == IMAP_SUCCESS)
501
                session->authenticated = TRUE;
502

    
503
        return ok;
504
}
505

    
506
static Session *imap_session_new(PrefsAccount *account)
507
{
508
        IMAPSession *session;
509
        SockInfo *sock;
510
        gchar *pass;
511
        gushort port;
512

    
513
        g_return_val_if_fail(account != NULL, NULL);
514
        g_return_val_if_fail(account->recv_server != NULL, NULL);
515
        g_return_val_if_fail(account->userid != NULL, NULL);
516

    
517
        pass = account->passwd;
518
        if (!pass) {
519
                gchar *tmp_pass;
520
                tmp_pass = input_dialog_query_password(account->recv_server,
521
                                                       account->userid);
522
                if (!tmp_pass)
523
                        return NULL;
524
                Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return NULL;});
525
                g_free(tmp_pass);
526
        }
527

    
528
#if USE_SSL
529
        port = account->set_imapport ? account->imapport
530
                : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
531
#else
532
        port = account->set_imapport ? account->imapport : IMAP4_PORT;
533
#endif
534

    
535
        log_message(_("creating IMAP4 connection to %s:%d ...\n"),
536
                    account->recv_server, port);
537

    
538
#if USE_SSL
539
        if ((sock = imap_open(account->recv_server, port,
540
                                   account->ssl_imap)) == NULL)
541
#else
542
        if ((sock = imap_open(account->recv_server, port)) == NULL)
543
#endif
544
                return NULL;
545

    
546
        session = g_new0(IMAPSession, 1);
547

    
548
        session_init(SESSION(session));
549

    
550
        SESSION(session)->type             = SESSION_IMAP;
551
        SESSION(session)->server           = g_strdup(account->recv_server);
552
        SESSION(session)->sock             = sock;
553
        SESSION(session)->last_access_time = time(NULL);
554
        SESSION(session)->data             = NULL;
555

    
556
        SESSION(session)->destroy          = imap_session_destroy;
557

    
558
        session->authenticated = FALSE;
559
        session->mbox      = NULL;
560
        session->cmd_count = 0;
561

    
562
        session_list = g_list_append(session_list, session);
563

    
564
        if (imap_greeting(session) != IMAP_SUCCESS) {
565
                session_destroy(SESSION(session));
566
                return NULL;
567
        }
568

    
569
        if (imap_cmd_capability(session) != IMAP_SUCCESS) {
570
                session_destroy(SESSION(session));
571
                return NULL;
572
        }
573
        if (imap_has_capability(session, "UIDPLUS"))
574
                session->uidplus = TRUE;
575

    
576
#if USE_SSL
577
        if (account->ssl_imap == SSL_STARTTLS &&
578
            imap_has_capability(session, "STARTTLS")) {
579
                gint ok;
580

    
581
                ok = imap_cmd_starttls(session);
582
                if (ok != IMAP_SUCCESS) {
583
                        log_warning(_("Can't start TLS session.\n"));
584
                        session_destroy(SESSION(session));
585
                        return NULL;
586
                }
587
                if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
588
                        session_destroy(SESSION(session));
589
                        return NULL;
590
                }
591
        }
592
#endif
593

    
594
        if (!session->authenticated &&
595
            imap_auth(session, account->userid, pass, account->imap_auth_type)
596
            != IMAP_SUCCESS) {
597
                imap_cmd_logout(session);
598
                session_destroy(SESSION(session));
599
                return NULL;
600
        }
601

    
602
        return SESSION(session);
603
}
604

    
605
static void imap_session_destroy(Session *session)
606
{
607
        imap_capability_free(IMAP_SESSION(session));
608
        g_free(IMAP_SESSION(session)->mbox);
609
        session_list = g_list_remove(session_list, session);
610
}
611

    
612
#if 0
613
static void imap_session_destroy_all(void)
614
{
615
        while (session_list != NULL) {
616
                IMAPSession *session = (IMAPSession *)session_list->data;
617

618
                imap_cmd_logout(session);
619
                session_destroy(SESSION(session));
620
        }
621
}
622
#endif
623

    
624
#define THROW goto catch
625

    
626
static gint imap_search_flags(IMAPSession *session, GArray **uids,
627
                              GHashTable **flags_table)
628
{
629
        gint ok;
630
        gint i;
631
        GArray *flag_uids;
632
        GHashTable *unseen_table;
633
        GHashTable *flagged_table;
634
        GHashTable *answered_table;
635
        guint32 uid;
636
        IMAPFlags flags;
637

    
638
        ok = imap_cmd_search(session, "ALL", uids);
639
        if (ok != IMAP_SUCCESS) return ok;
640

    
641
        ok = imap_cmd_search(session, "UNSEEN", &flag_uids);
642
        if (ok != IMAP_SUCCESS) {
643
                g_array_free(*uids, TRUE);
644
                return ok;
645
        }
646
        unseen_table = imap_get_uid_table(flag_uids);
647
        g_array_free(flag_uids, TRUE);
648
        ok = imap_cmd_search(session, "FLAGGED", &flag_uids);
649
        if (ok != IMAP_SUCCESS) {
650
                g_hash_table_destroy(unseen_table);
651
                g_array_free(*uids, TRUE);
652
                return ok;
653
        }
654
        flagged_table = imap_get_uid_table(flag_uids);
655
        g_array_free(flag_uids, TRUE);
656
        ok = imap_cmd_search(session, "ANSWERED", &flag_uids);
657
        if (ok != IMAP_SUCCESS) {
658
                g_hash_table_destroy(flagged_table);
659
                g_hash_table_destroy(unseen_table);
660
                g_array_free(*uids, TRUE);
661
                return ok;
662
        }
663
        answered_table = imap_get_uid_table(flag_uids);
664
        g_array_free(flag_uids, TRUE);
665

    
666
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
667

    
668
        for (i = 0; i < (*uids)->len; i++) {
669
                uid = g_array_index(*uids, guint32, i);
670
                flags = IMAP_FLAG_DRAFT;
671
                if (!g_hash_table_lookup(unseen_table, GUINT_TO_POINTER(uid)))
672
                        flags |= IMAP_FLAG_SEEN;
673
                if (g_hash_table_lookup(flagged_table, GUINT_TO_POINTER(uid)))
674
                        flags |= IMAP_FLAG_FLAGGED;
675
                if (g_hash_table_lookup(answered_table, GUINT_TO_POINTER(uid)))
676
                        flags |= IMAP_FLAG_ANSWERED;
677
                g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
678
                                    GINT_TO_POINTER(flags));
679
        }
680

    
681
        g_hash_table_destroy(answered_table);
682
        g_hash_table_destroy(flagged_table);
683
        g_hash_table_destroy(unseen_table);
684

    
685
        return IMAP_SUCCESS;
686
}
687

    
688
static gint imap_fetch_flags(IMAPSession *session, GArray **uids,
689
                             GHashTable **flags_table)
690
{
691
        gint ok;
692
        gchar *tmp;
693
        gchar *cur_pos;
694
        gchar buf[IMAPBUFSIZE];
695
        guint32 uid;
696
        IMAPFlags flags;
697

    
698
        imap_cmd_gen_send(session, "UID FETCH 1:* (UID FLAGS)");
699

    
700
        *uids = g_array_new(FALSE, FALSE, sizeof(guint32));
701
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
702

    
703
        while ((ok = imap_cmd_gen_recv(session, &tmp)) == IMAP_SUCCESS) {
704
                if (tmp[0] != '*' || tmp[1] != ' ') {
705
                        g_free(tmp);
706
                        break;
707
                }
708
                cur_pos = tmp + 2;
709

    
710
#define PARSE_ONE_ELEMENT(ch)                                        \
711
{                                                                \
712
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
713
        if (cur_pos == NULL) {                                        \
714
                g_warning("cur_pos == NULL\n");                        \
715
                g_free(tmp);                                        \
716
                g_hash_table_destroy(*flags_table);                \
717
                g_array_free(*uids, TRUE);                        \
718
                return IMAP_ERROR;                                \
719
        }                                                        \
720
}
721

    
722
                PARSE_ONE_ELEMENT(' ');
723
                PARSE_ONE_ELEMENT(' ');
724
                if (strcmp(buf, "FETCH") != 0) {
725
                        g_free(tmp);
726
                        continue;
727
                }
728
                if (*cur_pos != '(') {
729
                        g_free(tmp);
730
                        continue;
731
                }
732
                cur_pos++;
733
                uid = 0;
734
                flags = 0;
735

    
736
                while (*cur_pos != '\0' && *cur_pos != ')') {
737
                        while (*cur_pos == ' ') cur_pos++;
738

    
739
                        if (!strncmp(cur_pos, "UID ", 4)) {
740
                                cur_pos += 4;
741
                                uid = strtoul(cur_pos, &cur_pos, 10);
742
                        } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
743
                                cur_pos += 6;
744
                                if (*cur_pos != '(') {
745
                                        g_warning("*cur_pos != '('\n");
746
                                        break;
747
                                }
748
                                cur_pos++;
749
                                PARSE_ONE_ELEMENT(')');
750
                                flags = imap_parse_imap_flags(buf);
751
                                flags |= IMAP_FLAG_DRAFT;
752
                        } else {
753
                                g_warning("invalid FETCH response: %s\n", cur_pos);
754
                                break;
755
                        }
756
                }
757

    
758
#undef PARSE_ONE_ELEMENT
759

    
760
                if (uid > 0) {
761
                        g_array_append_val(*uids, uid);
762
                        g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
763
                                            GINT_TO_POINTER(flags));
764
                }
765

    
766
                g_free(tmp);
767
        }
768

    
769
        if (ok != IMAP_SUCCESS) {
770
                g_hash_table_destroy(*flags_table);
771
                g_array_free(*uids, TRUE);
772
        }
773

    
774
        return ok;
775
}
776

    
777
static GSList *imap_get_msg_list(Folder *folder, FolderItem *item,
778
                                 gboolean use_cache)
779
{
780
        GSList *mlist = NULL;
781
        IMAPSession *session;
782
        gint ok, exists = 0, recent = 0, unseen = 0;
783
        guint32 uid_validity = 0;
784
        guint32 first_uid = 0, last_uid = 0;
785

    
786
        g_return_val_if_fail(folder != NULL, NULL);
787
        g_return_val_if_fail(item != NULL, NULL);
788
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
789
        g_return_val_if_fail(folder->account != NULL, NULL);
790

    
791
        item->new = item->unread = item->total = 0;
792

    
793
        session = imap_session_get(folder);
794

    
795
        if (!session) {
796
                mlist = procmsg_read_cache(item, FALSE);
797
                item->last_num = procmsg_get_last_num_in_msg_list(mlist);
798
                procmsg_set_flags(mlist, item);
799
                return mlist;
800
        }
801

    
802
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
803
                         &exists, &recent, &unseen, &uid_validity);
804
        if (ok != IMAP_SUCCESS) THROW;
805

    
806
        if (exists == 0) {
807
                imap_delete_all_cached_messages(item);
808
                return NULL;
809
        }
810

    
811
        /* invalidate current cache if UIDVALIDITY has been changed */
812
        if (item->mtime != uid_validity) {
813
                debug_print("imap_get_msg_list: "
814
                            "UIDVALIDITY has been changed.\n");
815
                use_cache = FALSE;
816
        }
817

    
818
        if (use_cache) {
819
                GArray *uids;
820
                GHashTable *msg_table;
821
                GHashTable *flags_table;
822
                guint32 cache_last;
823
                guint32 begin = 0;
824
                GSList *cur, *next = NULL;
825
                MsgInfo *msginfo;
826
                IMAPFlags imap_flags;
827

    
828
                /* get cache data */
829
                mlist = procmsg_read_cache(item, FALSE);
830
                procmsg_set_flags(mlist, item);
831
                cache_last = procmsg_get_last_num_in_msg_list(mlist);
832

    
833
                /* get all UID list and flags */
834
                ok = imap_search_flags(session, &uids, &flags_table);
835
                if (ok != IMAP_SUCCESS) {
836
                        if (ok == IMAP_SOCKET || ok == IMAP_IOERR) THROW;
837
                        ok = imap_fetch_flags(session, &uids, &flags_table);
838
                        if (ok != IMAP_SUCCESS) THROW;
839
                }
840

    
841
                if (uids->len > 0) {
842
                        first_uid = g_array_index(uids, guint32, 0);
843
                        last_uid = g_array_index(uids, guint32, uids->len - 1);
844
                } else {
845
                        g_array_free(uids, TRUE);
846
                        g_hash_table_destroy(flags_table);
847
                        THROW;
848
                }
849

    
850
                /* sync message flags with server */
851
                for (cur = mlist; cur != NULL; cur = next) {
852
                        msginfo = (MsgInfo *)cur->data;
853
                        next = cur->next;
854
                        imap_flags = GPOINTER_TO_INT(g_hash_table_lookup
855
                                (flags_table,
856
                                 GUINT_TO_POINTER(msginfo->msgnum)));
857

    
858
                        if (imap_flags == 0) {
859
                                debug_print("imap_get_msg_list: "
860
                                            "message %u has been deleted.\n",
861
                                            msginfo->msgnum);
862
                                imap_delete_cached_message
863
                                        (item, msginfo->msgnum);
864
                                if (MSG_IS_NEW(msginfo->flags))
865
                                        item->new--;
866
                                if (MSG_IS_UNREAD(msginfo->flags))
867
                                        item->unread--;
868
                                item->total--;
869
                                mlist = g_slist_remove(mlist, msginfo);
870
                                procmsg_msginfo_free(msginfo);
871
                                continue;
872
                        }
873

    
874
                        if (!IMAP_IS_SEEN(imap_flags)) {
875
                                if (!MSG_IS_UNREAD(msginfo->flags)) {
876
                                        item->unread++;
877
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
878
                                                           MSG_UNREAD);
879
                                }
880
                        } else {
881
                                if (MSG_IS_NEW(msginfo->flags))
882
                                        item->new--;
883
                                if (MSG_IS_UNREAD(msginfo->flags))
884
                                        item->unread--;
885
                                MSG_UNSET_PERM_FLAGS(msginfo->flags,
886
                                                     MSG_NEW|MSG_UNREAD);
887
                        }
888

    
889
                        if (IMAP_IS_FLAGGED(imap_flags)) {
890
                                MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
891
                        } else {
892
                                MSG_UNSET_PERM_FLAGS(msginfo->flags,
893
                                                     MSG_MARKED);
894
                        }
895
                        if (IMAP_IS_ANSWERED(imap_flags)) {
896
                                MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
897
                        } else {
898
                                MSG_UNSET_PERM_FLAGS(msginfo->flags,
899
                                                     MSG_REPLIED);
900
                        }
901
                }
902

    
903
                /* check for the first new message */
904
                msg_table = procmsg_msg_hash_table_create(mlist);
905
                if (msg_table == NULL)
906
                        begin = first_uid;
907
                else {
908
                        gint i;
909

    
910
                        for (i = 0; i < uids->len; i++) {
911
                                guint32 uid;
912

    
913
                                uid = g_array_index(uids, guint32, i);
914
                                if (g_hash_table_lookup
915
                                        (msg_table, GUINT_TO_POINTER(uid))
916
                                        == NULL) {
917
                                        debug_print("imap_get_msg_list: "
918
                                                    "first new UID: %u\n", uid);
919
                                        begin = uid;
920
                                        break;
921
                                }
922
                        }
923
                        g_hash_table_destroy(msg_table);
924
                }
925

    
926
                g_array_free(uids, TRUE);
927
                g_hash_table_destroy(flags_table);
928

    
929
                /* remove ununsed caches */
930
                if (first_uid > 0 && last_uid > 0) {
931
                        mlist = imap_delete_cached_messages
932
                                (mlist, item, 0, first_uid - 1);
933
                        mlist = imap_delete_cached_messages
934
                                (mlist, item, begin > 0 ? begin : last_uid + 1,
935
                                 UINT_MAX);
936
                }
937

    
938
                if (begin > 0 && begin <= last_uid) {
939
                        GSList *newlist;
940
                        newlist = imap_get_uncached_messages(session, item,
941
                                                             begin, last_uid,
942
                                                             TRUE);
943
                        mlist = g_slist_concat(mlist, newlist);
944
                }
945
        } else {
946
                imap_delete_all_cached_messages(item);
947
                mlist = imap_get_uncached_messages(session, item, 0, 0, TRUE);
948
                last_uid = procmsg_get_last_num_in_msg_list(mlist);
949
        }
950

    
951
        item->mtime = uid_validity;
952

    
953
        mlist = procmsg_sort_msg_list(mlist, item->sort_key, item->sort_type);
954

    
955
        item->last_num = last_uid;
956

    
957
catch:
958
        return mlist;
959
}
960

    
961
#undef THROW
962

    
963
static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
964
{
965
        gchar *path, *filename;
966
        IMAPSession *session;
967
        gint ok;
968

    
969
        g_return_val_if_fail(folder != NULL, NULL);
970
        g_return_val_if_fail(item != NULL, NULL);
971

    
972
        path = folder_item_get_path(item);
973
        if (!is_dir_exist(path))
974
                make_dir_hier(path);
975
        filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
976
        g_free(path);
977

    
978
        if (is_file_exist(filename)) {
979
                debug_print("message %d has been already cached.\n", uid);
980
                return filename;
981
        }
982

    
983
        session = imap_session_get(folder);
984
        if (!session) {
985
                g_free(filename);
986
                return NULL;
987
        }
988

    
989
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
990
                         NULL, NULL, NULL, NULL);
991
        if (ok != IMAP_SUCCESS) {
992
                g_warning("can't select mailbox %s\n", item->path);
993
                g_free(filename);
994
                return NULL;
995
        }
996

    
997
        debug_print("getting message %d...\n", uid);
998
        ok = imap_cmd_fetch(session, (guint32)uid, filename);
999

    
1000
        if (ok != IMAP_SUCCESS) {
1001
                g_warning("can't fetch message %d\n", uid);
1002
                g_free(filename);
1003
                return NULL;
1004
        }
1005

    
1006
        return filename;
1007
}
1008

    
1009
static MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
1010
{
1011
        IMAPSession *session;
1012
        GSList *list;
1013
        MsgInfo *msginfo = NULL;
1014

    
1015
        g_return_val_if_fail(folder != NULL, NULL);
1016
        g_return_val_if_fail(item != NULL, NULL);
1017

    
1018
        session = imap_session_get(folder);
1019
        g_return_val_if_fail(session != NULL, NULL);
1020

    
1021
        list = imap_get_uncached_messages(session, item, uid, uid, FALSE);
1022
        if (list) {
1023
                msginfo = (MsgInfo *)list->data;
1024
                list->data = NULL;
1025
        }
1026
        procmsg_msg_list_free(list);
1027

    
1028
        return msginfo;
1029
}
1030

    
1031
static gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1032
                         MsgFlags *flags, gboolean remove_source)
1033
{
1034
        GSList file_list;
1035
        MsgFileInfo fileinfo;
1036

    
1037
        g_return_val_if_fail(file != NULL, -1);
1038

    
1039
        fileinfo.file = (gchar *)file;
1040
        fileinfo.flags = flags;
1041
        file_list.data = &fileinfo;
1042
        file_list.next = NULL;
1043

    
1044
        return imap_add_msgs(folder, dest, &file_list, remove_source, NULL);
1045
}
1046

    
1047
static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1048
                          gboolean remove_source, gint *first)
1049
{
1050
        gchar *destdir;
1051
        IMAPSession *session;
1052
        gint messages, recent, unseen;
1053
        guint32 uid_next, uid_validity;
1054
        guint32 last_uid = 0;
1055
        GSList *cur;
1056
        MsgFileInfo *fileinfo;
1057
        gint ok;
1058

    
1059
        g_return_val_if_fail(folder != NULL, -1);
1060
        g_return_val_if_fail(dest != NULL, -1);
1061
        g_return_val_if_fail(file_list != NULL, -1);
1062

    
1063
        session = imap_session_get(folder);
1064
        if (!session) return -1;
1065

    
1066
        ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
1067
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1068
        if (ok != IMAP_SUCCESS) {
1069
                g_warning("can't append messages\n");
1070
                return -1;
1071
        }
1072

    
1073
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1074

    
1075
        if (!session->uidplus)
1076
                last_uid = uid_next - 1;
1077
        if (first)
1078
                *first = uid_next;
1079

    
1080
        for (cur = file_list; cur != NULL; cur = cur->next) {
1081
                IMAPFlags iflags = 0;
1082
                guint32 new_uid = 0;
1083

    
1084
                fileinfo = (MsgFileInfo *)cur->data;
1085

    
1086
                if (fileinfo->flags) {
1087
                        if (MSG_IS_MARKED(*fileinfo->flags))
1088
                                iflags |= IMAP_FLAG_FLAGGED;
1089
                        if (MSG_IS_REPLIED(*fileinfo->flags))
1090
                                iflags |= IMAP_FLAG_ANSWERED;
1091
                        if (!MSG_IS_UNREAD(*fileinfo->flags))
1092
                                iflags |= IMAP_FLAG_SEEN;
1093
                }
1094

    
1095
                if (dest->stype == F_OUTBOX ||
1096
                    dest->stype == F_QUEUE  ||
1097
                    dest->stype == F_DRAFT  ||
1098
                    dest->stype == F_TRASH)
1099
                        iflags |= IMAP_FLAG_SEEN;
1100

    
1101
                ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1102
                                     &new_uid);
1103

    
1104
                if (ok != IMAP_SUCCESS) {
1105
                        g_warning("can't append message %s\n", fileinfo->file);
1106
                        g_free(destdir);
1107
                        return -1;
1108
                }
1109

    
1110
                if (!session->uidplus)
1111
                        last_uid++;
1112
                else if (last_uid < new_uid)
1113
                        last_uid = new_uid;
1114

    
1115
                dest->last_num = last_uid;
1116
                dest->total++;
1117
                dest->updated = TRUE;
1118

    
1119
                if (fileinfo->flags) {
1120
                        if (MSG_IS_UNREAD(*fileinfo->flags))
1121
                                dest->unread++;
1122
                } else
1123
                        dest->unread++;
1124
        }
1125

    
1126
        g_free(destdir);
1127

    
1128
        if (remove_source) {
1129
                for (cur = file_list; cur != NULL; cur = cur->next) {
1130
                        fileinfo = (MsgFileInfo *)cur->data;
1131
                        if (unlink(fileinfo->file) < 0)
1132
                                 FILE_OP_ERROR(fileinfo->file, "unlink");
1133
                }
1134
        }
1135

    
1136
        return last_uid;
1137
}
1138

    
1139
static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist,
1140
                              gboolean remove_source)
1141
{
1142
        FolderItem *src;
1143
        gchar *destdir;
1144
        GSList *seq_list, *cur;
1145
        MsgInfo *msginfo;
1146
        IMAPSession *session;
1147
        gint ok = IMAP_SUCCESS;
1148

    
1149
        g_return_val_if_fail(folder != NULL, -1);
1150
        g_return_val_if_fail(dest != NULL, -1);
1151
        g_return_val_if_fail(msglist != NULL, -1);
1152

    
1153
        session = imap_session_get(folder);
1154
        if (!session) return -1;
1155

    
1156
        msginfo = (MsgInfo *)msglist->data;
1157

    
1158
        src = msginfo->folder;
1159
        if (src == dest) {
1160
                g_warning("the src folder is identical to the dest.\n");
1161
                return -1;
1162
        }
1163

    
1164
        ok = imap_select(session, IMAP_FOLDER(folder), src->path,
1165
                         NULL, NULL, NULL, NULL);
1166
        if (ok != IMAP_SUCCESS)
1167
                return ok;
1168

    
1169
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1170

    
1171
        seq_list = imap_get_seq_set_from_msglist(msglist);
1172

    
1173
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1174
                gchar *seq_set = (gchar *)cur->data;
1175

    
1176
                if (remove_source)
1177
                        debug_print("Moving message %s%c[%s] to %s ...\n",
1178
                                    src->path, G_DIR_SEPARATOR,
1179
                                    seq_set, destdir);
1180
                else
1181
                        debug_print("Copying message %s%c[%s] to %s ...\n",
1182
                                    src->path, G_DIR_SEPARATOR,
1183
                                    seq_set, destdir);
1184

    
1185
                ok = imap_cmd_copy(session, seq_set, destdir);
1186
                if (ok != IMAP_SUCCESS) {
1187
                        imap_seq_set_free(seq_list);
1188
                        return -1;
1189
                }
1190
        }
1191

    
1192
        dest->updated = TRUE;
1193

    
1194
        if (remove_source) {
1195
                imap_remove_msgs_by_seq_set(folder, src, seq_list);
1196
                if (ok != IMAP_SUCCESS) {
1197
                        imap_seq_set_free(seq_list);
1198
                        return ok;
1199
                }
1200
        }
1201

    
1202
        imap_seq_set_free(seq_list);
1203

    
1204
        for (cur = msglist; cur != NULL; cur = cur->next) {
1205
                msginfo = (MsgInfo *)cur->data;
1206
                dest->total++;
1207
                if (MSG_IS_NEW(msginfo->flags))
1208
                        dest->new++;
1209
                if (MSG_IS_UNREAD(msginfo->flags))
1210
                        dest->unread++;
1211

    
1212
                if (remove_source) {
1213
                        src->total--;
1214
                        if (MSG_IS_NEW(msginfo->flags))
1215
                                src->new--;
1216
                        if (MSG_IS_UNREAD(msginfo->flags))
1217
                                src->unread--;
1218
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
1219
                }
1220
        }
1221

    
1222
        g_free(destdir);
1223

    
1224
        if (ok == IMAP_SUCCESS)
1225
                return 0;
1226
        else
1227
                return -1;
1228
}
1229

    
1230
static gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1231
{
1232
        GSList msglist;
1233

    
1234
        g_return_val_if_fail(msginfo != NULL, -1);
1235

    
1236
        msglist.data = msginfo;
1237
        msglist.next = NULL;
1238

    
1239
        return imap_move_msgs(folder, dest, &msglist);
1240
}
1241

    
1242
static gint imap_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1243
{
1244
        MsgInfo *msginfo;
1245
        GSList *file_list;
1246
        gint ret = 0;
1247

    
1248
        g_return_val_if_fail(folder != NULL, -1);
1249
        g_return_val_if_fail(dest != NULL, -1);
1250
        g_return_val_if_fail(msglist != NULL, -1);
1251

    
1252
        msginfo = (MsgInfo *)msglist->data;
1253
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1254

    
1255
        if (folder == msginfo->folder->folder)
1256
                return imap_do_copy_msgs(folder, dest, msglist, TRUE);
1257

    
1258
        file_list = procmsg_get_message_file_list(msglist);
1259
        g_return_val_if_fail(file_list != NULL, -1);
1260

    
1261
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1262

    
1263
        procmsg_message_file_list_free(file_list);
1264

    
1265
        if (ret != -1)
1266
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
1267

    
1268
        return ret;
1269
}
1270

    
1271
static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1272
{
1273
        GSList msglist;
1274

    
1275
        g_return_val_if_fail(msginfo != NULL, -1);
1276

    
1277
        msglist.data = msginfo;
1278
        msglist.next = NULL;
1279

    
1280
        return imap_copy_msgs(folder, dest, &msglist);
1281
}
1282

    
1283
static gint imap_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1284
{
1285
        MsgInfo *msginfo;
1286
        GSList *file_list;
1287
        gint ret;
1288

    
1289
        g_return_val_if_fail(folder != NULL, -1);
1290
        g_return_val_if_fail(dest != NULL, -1);
1291
        g_return_val_if_fail(msglist != NULL, -1);
1292

    
1293
        msginfo = (MsgInfo *)msglist->data;
1294
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1295

    
1296
        if (folder == msginfo->folder->folder)
1297
                return imap_do_copy_msgs(folder, dest, msglist, FALSE);
1298

    
1299
        file_list = procmsg_get_message_file_list(msglist);
1300
        g_return_val_if_fail(file_list != NULL, -1);
1301

    
1302
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1303

    
1304
        procmsg_message_file_list_free(file_list);
1305

    
1306
        return ret;
1307
}
1308

    
1309
static gint imap_remove_msgs_by_seq_set(Folder *folder, FolderItem *item,
1310
                                        GSList *seq_list)
1311
{
1312
        gint ok;
1313
        IMAPSession *session;
1314
        GSList *cur;
1315

    
1316
        g_return_val_if_fail(seq_list != NULL, -1);
1317

    
1318
        session = imap_session_get(folder);
1319
        if (!session) return -1;
1320

    
1321
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1322
                gchar *seq_set = (gchar *)cur->data;
1323

    
1324
                ok = imap_set_message_flags(session, seq_set, IMAP_FLAG_DELETED,
1325
                                            TRUE);
1326
                if (ok != IMAP_SUCCESS) {
1327
                        log_warning(_("can't set deleted flags: %s\n"),
1328
                                    seq_set);
1329
                        return ok;
1330
                }
1331
        }
1332

    
1333
        ok = imap_cmd_expunge(session);
1334
        if (ok != IMAP_SUCCESS)
1335
                log_warning(_("can't expunge\n"));
1336

    
1337
        item->updated = TRUE;
1338

    
1339
        return ok;
1340
}
1341

    
1342
static gint imap_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1343
{
1344
        GSList msglist;
1345

    
1346
        g_return_val_if_fail(msginfo != NULL, -1);
1347

    
1348
        msglist.data = msginfo;
1349
        msglist.next = NULL;
1350

    
1351
        return imap_remove_msgs(folder, item, &msglist);
1352
}
1353

    
1354
static gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
1355
{
1356
        gint ok;
1357
        IMAPSession *session;
1358
        GSList *seq_list, *cur;
1359
        gchar *dir;
1360
        gboolean dir_exist;
1361

    
1362
        g_return_val_if_fail(folder != NULL, -1);
1363
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
1364
        g_return_val_if_fail(item != NULL, -1);
1365
        g_return_val_if_fail(msglist != NULL, -1);
1366

    
1367
        session = imap_session_get(folder);
1368
        if (!session) return -1;
1369

    
1370
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1371
                         NULL, NULL, NULL, NULL);
1372
        if (ok != IMAP_SUCCESS)
1373
                return ok;
1374

    
1375
        seq_list = imap_get_seq_set_from_msglist(msglist);
1376
        ok = imap_remove_msgs_by_seq_set(folder, item, seq_list);
1377
        imap_seq_set_free(seq_list);
1378
        if (ok != IMAP_SUCCESS)
1379
                return ok;
1380

    
1381
        dir = folder_item_get_path(item);
1382
        dir_exist = is_dir_exist(dir);
1383
        for (cur = msglist; cur != NULL; cur = cur->next) {
1384
                MsgInfo *msginfo = (MsgInfo *)cur->data;
1385
                guint32 uid = msginfo->msgnum;
1386

    
1387
                if (dir_exist)
1388
                        remove_numbered_files(dir, uid, uid);
1389
                item->total--;
1390
                if (MSG_IS_NEW(msginfo->flags))
1391
                        item->new--;
1392
                if (MSG_IS_UNREAD(msginfo->flags))
1393
                        item->unread--;
1394
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
1395
        }
1396
        g_free(dir);
1397

    
1398
        return IMAP_SUCCESS;
1399
}
1400

    
1401
static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1402
{
1403
        gint ok;
1404
        IMAPSession *session;
1405
        gchar *dir;
1406

    
1407
        g_return_val_if_fail(folder != NULL, -1);
1408
        g_return_val_if_fail(item != NULL, -1);
1409

    
1410
        session = imap_session_get(folder);
1411
        if (!session) return -1;
1412

    
1413
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1414
                         NULL, NULL, NULL, NULL);
1415
        if (ok != IMAP_SUCCESS)
1416
                return ok;
1417

    
1418
        imap_cmd_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1419
        ok = imap_cmd_ok(session, NULL);
1420
        if (ok != IMAP_SUCCESS) {
1421
                log_warning(_("can't set deleted flags: 1:*\n"));
1422
                return ok;
1423
        }
1424

    
1425
        ok = imap_cmd_expunge(session);
1426
        if (ok != IMAP_SUCCESS) {
1427
                log_warning(_("can't expunge\n"));
1428
                return ok;
1429
        }
1430

    
1431
        item->new = item->unread = item->total = 0;
1432
        item->updated = TRUE;
1433

    
1434
        dir = folder_item_get_path(item);
1435
        if (is_dir_exist(dir))
1436
                remove_all_numbered_files(dir);
1437
        g_free(dir);
1438

    
1439
        return IMAP_SUCCESS;
1440
}
1441

    
1442
static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1443
                                    MsgInfo *msginfo)
1444
{
1445
        /* TODO: properly implement this method */
1446
        return FALSE;
1447
}
1448

    
1449
static gint imap_close(Folder *folder, FolderItem *item)
1450
{
1451
        gint ok;
1452
        IMAPSession *session;
1453

    
1454
        g_return_val_if_fail(folder != NULL, -1);
1455

    
1456
        session = imap_session_get(folder);
1457
        if (!session) return -1;
1458

    
1459
        if (session->mbox) {
1460
                if (strcmp2(session->mbox, item->path) != 0) return -1;
1461

    
1462
                ok = imap_cmd_close(session);
1463
                if (ok != IMAP_SUCCESS)
1464
                        log_warning(_("can't close folder\n"));
1465

    
1466
                g_free(session->mbox);
1467
                session->mbox = NULL;
1468

    
1469
                return ok;
1470
        } else
1471
                return 0;
1472
}
1473

    
1474
static gint imap_scan_folder(Folder *folder, FolderItem *item)
1475
{
1476
        IMAPSession *session;
1477
        gint messages, recent, unseen;
1478
        guint32 uid_next, uid_validity;
1479
        gint ok;
1480

    
1481
        g_return_val_if_fail(folder != NULL, -1);
1482
        g_return_val_if_fail(item != NULL, -1);
1483

    
1484
        session = imap_session_get(folder);
1485
        if (!session) return -1;
1486

    
1487
        ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1488
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1489
        if (ok != IMAP_SUCCESS) return -1;
1490

    
1491
        item->new = unseen > 0 ? recent : 0;
1492
        item->unread = unseen;
1493
        item->total = messages;
1494
        item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1495
        /* item->mtime = uid_validity; */
1496
        item->updated = TRUE;
1497

    
1498
        return 0;
1499
}
1500

    
1501
static gint imap_scan_tree(Folder *folder)
1502
{
1503
        FolderItem *item = NULL;
1504
        IMAPSession *session;
1505
        gchar *root_folder = NULL;
1506

    
1507
        g_return_val_if_fail(folder != NULL, -1);
1508
        g_return_val_if_fail(folder->account != NULL, -1);
1509

    
1510
        session = imap_session_get(folder);
1511
        if (!session) {
1512
                if (!folder->node) {
1513
                        folder_tree_destroy(folder);
1514
                        item = folder_item_new(folder->name, NULL);
1515
                        item->folder = folder;
1516
                        folder->node = item->node = g_node_new(item);
1517
                }
1518
                return -1;
1519
        }
1520

    
1521
        if (folder->account->imap_dir && *folder->account->imap_dir) {
1522
                gchar *real_path;
1523
                GPtrArray *argbuf;
1524
                gint ok;
1525

    
1526
                Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1527
                extract_quote(root_folder, '"');
1528
                subst_char(root_folder,
1529
                           imap_get_path_separator(IMAP_FOLDER(folder),
1530
                                                   root_folder),
1531
                           '/');
1532
                strtailchomp(root_folder, '/');
1533
                real_path = imap_get_real_path
1534
                        (IMAP_FOLDER(folder), root_folder);
1535
                debug_print("IMAP root directory: %s\n", real_path);
1536

    
1537
                /* check if root directory exist */
1538
                argbuf = g_ptr_array_new();
1539
                ok = imap_cmd_list(session, NULL, real_path, argbuf);
1540
                if (ok != IMAP_SUCCESS ||
1541
                    search_array_str(argbuf, "LIST ") == NULL) {
1542
                        log_warning(_("root folder %s not exist\n"), real_path);
1543
                        g_ptr_array_free(argbuf, TRUE);
1544
                        g_free(real_path);
1545
                        return -1;
1546
                }
1547
                g_ptr_array_free(argbuf, TRUE);
1548
                g_free(real_path);
1549
        }
1550

    
1551
        if (folder->node)
1552
                item = FOLDER_ITEM(folder->node->data);
1553
        if (!item || ((item->path || root_folder) &&
1554
                      strcmp2(item->path, root_folder) != 0)) {
1555
                folder_tree_destroy(folder);
1556
                item = folder_item_new(folder->name, root_folder);
1557
                item->folder = folder;
1558
                folder->node = item->node = g_node_new(item);
1559
        }
1560

    
1561
        imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1562
        imap_create_missing_folders(folder);
1563

    
1564
        return 0;
1565
}
1566

    
1567
static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1568
{
1569
        Folder *folder;
1570
        IMAPFolder *imapfolder;
1571
        FolderItem *new_item;
1572
        GSList *item_list, *cur;
1573
        GNode *node;
1574
        gchar *real_path;
1575
        gchar *wildcard_path;
1576
        gchar separator;
1577
        gchar wildcard[3];
1578

    
1579
        g_return_val_if_fail(item != NULL, -1);
1580
        g_return_val_if_fail(item->folder != NULL, -1);
1581
        g_return_val_if_fail(item->no_sub == FALSE, -1);
1582

    
1583
        folder = item->folder;
1584
        imapfolder = IMAP_FOLDER(folder);
1585

    
1586
        separator = imap_get_path_separator(imapfolder, item->path);
1587

    
1588
        if (folder->ui_func)
1589
                folder->ui_func(folder, item, folder->ui_func_data);
1590

    
1591
        if (item->path) {
1592
                wildcard[0] = separator;
1593
                wildcard[1] = '%';
1594
                wildcard[2] = '\0';
1595
                real_path = imap_get_real_path(imapfolder, item->path);
1596
        } else {
1597
                wildcard[0] = '%';
1598
                wildcard[1] = '\0';
1599
                real_path = g_strdup("");
1600
        }
1601

    
1602
        Xstrcat_a(wildcard_path, real_path, wildcard,
1603
                  {g_free(real_path); return IMAP_ERROR;});
1604
        QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1605

    
1606
        imap_cmd_gen_send(session, "LIST \"\" %s", wildcard_path);
1607

    
1608
        strtailchomp(real_path, separator);
1609
        item_list = imap_parse_list(session, real_path, NULL);
1610
        g_free(real_path);
1611

    
1612
        node = item->node->children;
1613
        while (node != NULL) {
1614
                FolderItem *old_item = FOLDER_ITEM(node->data);
1615
                GNode *next = node->next;
1616

    
1617
                new_item = NULL;
1618

    
1619
                for (cur = item_list; cur != NULL; cur = cur->next) {
1620
                        FolderItem *cur_item = FOLDER_ITEM(cur->data);
1621
                        if (!strcmp2(old_item->path, cur_item->path)) {
1622
                                new_item = cur_item;
1623
                                break;
1624
                        }
1625
                }
1626
                if (!new_item) {
1627
                        debug_print("folder '%s' not found. removing...\n",
1628
                                    old_item->path);
1629
                        folder_item_remove(old_item);
1630
                } else {
1631
                        old_item->no_sub = new_item->no_sub;
1632
                        old_item->no_select = new_item->no_select;
1633
                        if (old_item->no_select == TRUE)
1634
                                old_item->new = old_item->unread =
1635
                                        old_item->total = 0;
1636
                        if (old_item->no_sub == TRUE && node->children) {
1637
                                debug_print("folder '%s' doesn't have "
1638
                                            "subfolders. removing...\n",
1639
                                            old_item->path);
1640
                                folder_item_remove_children(old_item);
1641
                        }
1642
                }
1643

    
1644
                node = next;
1645
        }
1646

    
1647
        for (cur = item_list; cur != NULL; cur = cur->next) {
1648
                FolderItem *cur_item = FOLDER_ITEM(cur->data);
1649
                new_item = NULL;
1650
                for (node = item->node->children; node != NULL;
1651
                     node = node->next) {
1652
                        if (!strcmp2(FOLDER_ITEM(node->data)->path,
1653
                                     cur_item->path)) {
1654
                                new_item = FOLDER_ITEM(node->data);
1655
                                folder_item_destroy(cur_item);
1656
                                cur_item = NULL;
1657
                                break;
1658
                        }
1659
                }
1660
                if (!new_item) {
1661
                        new_item = cur_item;
1662
                        debug_print("new folder '%s' found.\n", new_item->path);
1663
                        folder_item_append(item, new_item);
1664
                }
1665

    
1666
                if (!strcmp(new_item->path, "INBOX")) {
1667
                        new_item->stype = F_INBOX;
1668
                        folder->inbox = new_item;
1669
                } else if (!item->parent || item->stype == F_INBOX) {
1670
                        const gchar *base;
1671

    
1672
                        base = g_basename(new_item->path);
1673

    
1674
                        if (!folder->outbox && !strcasecmp(base, "Sent")) {
1675
                                new_item->stype = F_OUTBOX;
1676
                                folder->outbox = new_item;
1677
                        } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1678
                                new_item->stype = F_DRAFT;
1679
                                folder->draft = new_item;
1680
                        } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1681
                                new_item->stype = F_QUEUE;
1682
                                folder->queue = new_item;
1683
                        } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1684
                                new_item->stype = F_TRASH;
1685
                                folder->trash = new_item;
1686
                        }
1687
                }
1688

    
1689
                if (new_item->no_select == FALSE)
1690
                        imap_scan_folder(folder, new_item);
1691
                if (new_item->no_sub == FALSE)
1692
                        imap_scan_tree_recursive(session, new_item);
1693
        }
1694

    
1695
        g_slist_free(item_list);
1696

    
1697
        return IMAP_SUCCESS;
1698
}
1699

    
1700
static GSList *imap_parse_list(IMAPSession *session, const gchar *real_path,
1701
                               gchar *separator)
1702
{
1703
        gchar buf[IMAPBUFSIZE];
1704
        gchar flags[256];
1705
        gchar separator_str[16];
1706
        gchar *p;
1707
        const gchar *name;
1708
        gchar *loc_name, *loc_path;
1709
        GSList *item_list = NULL;
1710
        GString *str;
1711
        FolderItem *new_item;
1712

    
1713
        debug_print("getting list of %s ...\n",
1714
                    *real_path ? real_path : "\"\"");
1715

    
1716
        str = g_string_new(NULL);
1717

    
1718
        for (;;) {
1719
                if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1720
                        log_warning(_("error occurred while getting LIST.\n"));
1721
                        break;
1722
                }
1723
                strretchomp(buf);
1724
                if (buf[0] != '*' || buf[1] != ' ') {
1725
                        log_print("IMAP4< %s\n", buf);
1726
                        if (sscanf(buf, "%*d %16s", buf) < 1 ||
1727
                            strcmp(buf, "OK") != 0)
1728
                                log_warning(_("error occurred while getting LIST.\n"));
1729
                                
1730
                        break;
1731
                }
1732
                debug_print("IMAP4< %s\n", buf);
1733

    
1734
                g_string_assign(str, buf);
1735
                p = str->str + 2;
1736
                if (strncmp(p, "LIST ", 5) != 0) continue;
1737
                p += 5;
1738

    
1739
                if (*p != '(') continue;
1740
                p++;
1741
                p = strchr_cpy(p, ')', flags, sizeof(flags));
1742
                if (!p) continue;
1743
                while (*p == ' ') p++;
1744

    
1745
                p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1746
                if (!p) continue;
1747
                extract_quote(separator_str, '"');
1748
                if (!strcmp(separator_str, "NIL"))
1749
                        separator_str[0] = '\0';
1750
                if (separator)
1751
                        *separator = separator_str[0];
1752

    
1753
                buf[0] = '\0';
1754
                while (*p == ' ') p++;
1755
                if (*p == '{' || *p == '"')
1756
                        p = imap_parse_atom(session, p, buf, sizeof(buf), str);
1757
                else
1758
                        strncpy2(buf, p, sizeof(buf));
1759
                strtailchomp(buf, separator_str[0]);
1760
                if (buf[0] == '\0') continue;
1761
                if (!strcmp(buf, real_path)) continue;
1762

    
1763
                if (separator_str[0] != '\0')
1764
                        subst_char(buf, separator_str[0], '/');
1765
                name = g_basename(buf);
1766
                if (name[0] == '.') continue;
1767

    
1768
                loc_name = imap_modified_utf7_to_locale(name);
1769
                loc_path = imap_modified_utf7_to_locale(buf);
1770
                new_item = folder_item_new(loc_name, loc_path);
1771
                if (strcasestr(flags, "\\Noinferiors") != NULL)
1772
                        new_item->no_sub = TRUE;
1773
                if (strcmp(buf, "INBOX") != 0 &&
1774
                    strcasestr(flags, "\\Noselect") != NULL)
1775
                        new_item->no_select = TRUE;
1776

    
1777
                item_list = g_slist_append(item_list, new_item);
1778

    
1779
                debug_print("folder '%s' found.\n", loc_path);
1780
                g_free(loc_path);
1781
                g_free(loc_name);
1782
        }
1783

    
1784
        g_string_free(str, TRUE);
1785

    
1786
        return item_list;
1787
}
1788

    
1789
static gint imap_create_tree(Folder *folder)
1790
{
1791
        g_return_val_if_fail(folder != NULL, -1);
1792
        g_return_val_if_fail(folder->node != NULL, -1);
1793
        g_return_val_if_fail(folder->node->data != NULL, -1);
1794
        g_return_val_if_fail(folder->account != NULL, -1);
1795

    
1796
        imap_scan_tree(folder);
1797
        imap_create_missing_folders(folder);
1798

    
1799
        return 0;
1800
}
1801

    
1802
static void imap_create_missing_folders(Folder *folder)
1803
{
1804
        g_return_if_fail(folder != NULL);
1805

    
1806
        if (!folder->inbox)
1807
                folder->inbox = imap_create_special_folder
1808
                        (folder, F_INBOX, "INBOX");
1809
#if 0
1810
        if (!folder->outbox)
1811
                folder->outbox = imap_create_special_folder
1812
                        (folder, F_OUTBOX, "Sent");
1813
        if (!folder->draft)
1814
                folder->draft = imap_create_special_folder
1815
                        (folder, F_DRAFT, "Drafts");
1816
        if (!folder->queue)
1817
                folder->queue = imap_create_special_folder
1818
                        (folder, F_QUEUE, "Queue");
1819
#endif
1820
        if (!folder->trash)
1821
                folder->trash = imap_create_special_folder
1822
                        (folder, F_TRASH, "Trash");
1823
}
1824

    
1825
static FolderItem *imap_create_special_folder(Folder *folder,
1826
                                              SpecialFolderItemType stype,
1827
                                              const gchar *name)
1828
{
1829
        FolderItem *item;
1830
        FolderItem *new_item;
1831

    
1832
        g_return_val_if_fail(folder != NULL, NULL);
1833
        g_return_val_if_fail(folder->node != NULL, NULL);
1834
        g_return_val_if_fail(folder->node->data != NULL, NULL);
1835
        g_return_val_if_fail(folder->account != NULL, NULL);
1836
        g_return_val_if_fail(name != NULL, NULL);
1837

    
1838
        item = FOLDER_ITEM(folder->node->data);
1839
        new_item = imap_create_folder(folder, item, name);
1840

    
1841
        if (!new_item) {
1842
                g_warning(_("Can't create '%s'\n"), name);
1843
                if (!folder->inbox) return NULL;
1844

    
1845
                new_item = imap_create_folder(folder, folder->inbox, name);
1846
                if (!new_item)
1847
                        g_warning(_("Can't create '%s' under INBOX\n"), name);
1848
                else
1849
                        new_item->stype = stype;
1850
        } else
1851
                new_item->stype = stype;
1852

    
1853
        return new_item;
1854
}
1855

    
1856
static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1857
                                      const gchar *name)
1858
{
1859
        gchar *dirpath, *imap_path;
1860
        IMAPSession *session;
1861
        FolderItem *new_item;
1862
        gchar separator;
1863
        gchar *new_name;
1864
        const gchar *p;
1865
        gint ok;
1866

    
1867
        g_return_val_if_fail(folder != NULL, NULL);
1868
        g_return_val_if_fail(folder->account != NULL, NULL);
1869
        g_return_val_if_fail(parent != NULL, NULL);
1870
        g_return_val_if_fail(name != NULL, NULL);
1871

    
1872
        session = imap_session_get(folder);
1873
        if (!session) return NULL;
1874

    
1875
        if (!parent->parent && strcmp(name, "INBOX") == 0)
1876
                dirpath = g_strdup(name);
1877
        else if (parent->path)
1878
                dirpath = g_strconcat(parent->path, "/", name, NULL);
1879
        else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1880
                dirpath = g_strdup(name);
1881
        else if (folder->account->imap_dir && *folder->account->imap_dir) {
1882
                gchar *imap_dir;
1883

    
1884
                Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1885
                strtailchomp(imap_dir, '/');
1886
                dirpath = g_strconcat(imap_dir, "/", name, NULL);
1887
        } else
1888
                dirpath = g_strdup(name);
1889

    
1890
        /* keep trailing directory separator to create a folder that contains
1891
           sub folder */
1892
        imap_path = imap_locale_to_modified_utf7(dirpath);
1893
        strtailchomp(dirpath, '/');
1894
        Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1895
        strtailchomp(new_name, '/');
1896
        separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1897
        imap_path_separator_subst(imap_path, separator);
1898
        subst_char(new_name, '/', separator);
1899

    
1900
        if (strcmp(name, "INBOX") != 0) {
1901
                GPtrArray *argbuf;
1902
                gint i;
1903
                gboolean exist = FALSE;
1904

    
1905
                argbuf = g_ptr_array_new();
1906
                ok = imap_cmd_list(session, NULL, imap_path, argbuf);
1907
                if (ok != IMAP_SUCCESS) {
1908
                        log_warning(_("can't create mailbox: LIST failed\n"));
1909
                        g_free(imap_path);
1910
                        g_free(dirpath);
1911
                        g_ptr_array_free(argbuf, TRUE);
1912
                        return NULL;
1913
                }
1914

    
1915
                for (i = 0; i < argbuf->len; i++) {
1916
                        gchar *str;
1917
                        str = g_ptr_array_index(argbuf, i);
1918
                        if (!strncmp(str, "LIST ", 5)) {
1919
                                exist = TRUE;
1920
                                break;
1921
                        }
1922
                }
1923
                g_ptr_array_free(argbuf, TRUE);
1924

    
1925
                if (!exist) {
1926
                        ok = imap_cmd_create(session, imap_path);
1927
                        if (ok != IMAP_SUCCESS) {
1928
                                log_warning(_("can't create mailbox\n"));
1929
                                g_free(imap_path);
1930
                                g_free(dirpath);
1931
                                return NULL;
1932
                        }
1933
                }
1934
        }
1935

    
1936
        new_item = folder_item_new(new_name, dirpath);
1937
        folder_item_append(parent, new_item);
1938
        g_free(imap_path);
1939
        g_free(dirpath);
1940

    
1941
        dirpath = folder_item_get_path(new_item);
1942
        if (!is_dir_exist(dirpath))
1943
                make_dir_hier(dirpath);
1944
        g_free(dirpath);
1945

    
1946
        return new_item;
1947
}
1948

    
1949
static gint imap_rename_folder(Folder *folder, FolderItem *item,
1950
                               const gchar *name)
1951
{
1952
        gchar *dirpath;
1953
        gchar *newpath;
1954
        gchar *real_oldpath;
1955
        gchar *real_newpath;
1956
        gchar *paths[2];
1957
        gchar *old_cache_dir;
1958
        gchar *new_cache_dir;
1959
        IMAPSession *session;
1960
        gchar separator;
1961
        gint ok;
1962
        gint exists, recent, unseen;
1963
        guint32 uid_validity;
1964

    
1965
        g_return_val_if_fail(folder != NULL, -1);
1966
        g_return_val_if_fail(item != NULL, -1);
1967
        g_return_val_if_fail(item->path != NULL, -1);
1968
        g_return_val_if_fail(name != NULL, -1);
1969

    
1970
        session = imap_session_get(folder);
1971
        if (!session) return -1;
1972

    
1973
        real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1974

    
1975
        g_free(session->mbox);
1976
        session->mbox = NULL;
1977
        ok = imap_cmd_examine(session, "INBOX",
1978
                              &exists, &recent, &unseen, &uid_validity);
1979
        if (ok != IMAP_SUCCESS) {
1980
                g_free(real_oldpath);
1981
                return -1;
1982
        }
1983

    
1984
        separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1985
        if (strchr(item->path, G_DIR_SEPARATOR)) {
1986
                dirpath = g_dirname(item->path);
1987
                newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1988
                g_free(dirpath);
1989
        } else
1990
                newpath = g_strdup(name);
1991

    
1992
        real_newpath = imap_locale_to_modified_utf7(newpath);
1993
        imap_path_separator_subst(real_newpath, separator);
1994

    
1995
        ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1996
        if (ok != IMAP_SUCCESS) {
1997
                log_warning(_("can't rename mailbox: %s to %s\n"),
1998
                            real_oldpath, real_newpath);
1999
                g_free(real_oldpath);
2000
                g_free(newpath);
2001
                g_free(real_newpath);
2002
                return -1;
2003
        }
2004

    
2005
        g_free(item->name);
2006
        item->name = g_strdup(name);
2007

    
2008
        old_cache_dir = folder_item_get_path(item);
2009

    
2010
        paths[0] = g_strdup(item->path);
2011
        paths[1] = newpath;
2012
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2013
                        imap_rename_folder_func, paths);
2014

    
2015
        if (is_dir_exist(old_cache_dir)) {
2016
                new_cache_dir = folder_item_get_path(item);
2017
                if (rename(old_cache_dir, new_cache_dir) < 0) {
2018
                        FILE_OP_ERROR(old_cache_dir, "rename");
2019
                }
2020
                g_free(new_cache_dir);
2021
        }
2022

    
2023
        g_free(old_cache_dir);
2024
        g_free(paths[0]);
2025
        g_free(newpath);
2026
        g_free(real_oldpath);
2027
        g_free(real_newpath);
2028

    
2029
        return 0;
2030
}
2031

    
2032
static gint imap_remove_folder(Folder *folder, FolderItem *item)
2033
{
2034
        gint ok;
2035
        IMAPSession *session;
2036
        gchar *path;
2037
        gchar *cache_dir;
2038
        gint exists, recent, unseen;
2039
        guint32 uid_validity;
2040

    
2041
        g_return_val_if_fail(folder != NULL, -1);
2042
        g_return_val_if_fail(item != NULL, -1);
2043
        g_return_val_if_fail(item->path != NULL, -1);
2044

    
2045
        session = imap_session_get(folder);
2046
        if (!session) return -1;
2047

    
2048
        path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2049

    
2050
        ok = imap_cmd_examine(session, "INBOX",
2051
                              &exists, &recent, &unseen, &uid_validity);
2052
        if (ok != IMAP_SUCCESS) {
2053
                g_free(path);
2054
                return -1;
2055
        }
2056

    
2057
        ok = imap_cmd_delete(session, path);
2058
        if (ok != IMAP_SUCCESS) {
2059
                log_warning(_("can't delete mailbox\n"));
2060
                g_free(path);
2061
                return -1;
2062
        }
2063

    
2064
        g_free(path);
2065
        cache_dir = folder_item_get_path(item);
2066
        if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2067
                g_warning("can't remove directory '%s'\n", cache_dir);
2068
        g_free(cache_dir);
2069
        folder_item_remove(item);
2070

    
2071
        return 0;
2072
}
2073

    
2074
static GSList *imap_get_uncached_messages(IMAPSession *session,
2075
                                          FolderItem *item,
2076
                                          guint32 first_uid, guint32 last_uid,
2077
                                          gboolean update_count)
2078
{
2079
        gchar *tmp;
2080
        GSList *newlist = NULL;
2081
        GSList *llast = NULL;
2082
        GString *str;
2083
        MsgInfo *msginfo;
2084
        gchar seq_set[22];
2085

    
2086
        g_return_val_if_fail(session != NULL, NULL);
2087
        g_return_val_if_fail(item != NULL, NULL);
2088
        g_return_val_if_fail(item->folder != NULL, NULL);
2089
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, NULL);
2090
        g_return_val_if_fail(first_uid <= last_uid, NULL);
2091

    
2092
        if (first_uid == 0 && last_uid == 0)
2093
                strcpy(seq_set, "1:*");
2094
        else
2095
                g_snprintf(seq_set, sizeof(seq_set), "%u:%u",
2096
                           first_uid, last_uid);
2097
        if (imap_cmd_envelope(session, seq_set) != IMAP_SUCCESS) {
2098
                log_warning(_("can't get envelope\n"));
2099
                return NULL;
2100
        }
2101

    
2102
        str = g_string_new(NULL);
2103

    
2104
        for (;;) {
2105
                if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
2106
                        log_warning(_("error occurred while getting envelope.\n"));
2107
                        g_string_free(str, TRUE);
2108
                        return newlist;
2109
                }
2110
                strretchomp(tmp);
2111
                if (tmp[0] != '*' || tmp[1] != ' ') {
2112
                        log_print("IMAP4< %s\n", tmp);
2113
                        g_free(tmp);
2114
                        break;
2115
                }
2116
                if (strstr(tmp, "FETCH") == NULL) {
2117
                        log_print("IMAP4< %s\n", tmp);
2118
                        g_free(tmp);
2119
                        continue;
2120
                }
2121
                log_print("IMAP4< %s\n", tmp);
2122
                g_string_assign(str, tmp);
2123
                g_free(tmp);
2124

    
2125
                msginfo = imap_parse_envelope(session, item, str);
2126
                if (!msginfo) {
2127
                        log_warning(_("can't parse envelope: %s\n"), str->str);
2128
                        continue;
2129
                }
2130
                if (update_count) {
2131
                        if (MSG_IS_NEW(msginfo->flags))
2132
                                item->new++;
2133
                        if (MSG_IS_UNREAD(msginfo->flags))
2134
                                item->unread++;
2135
                }
2136
                if (item->stype == F_QUEUE) {
2137
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2138
                } else if (item->stype == F_DRAFT) {
2139
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2140
                }
2141

    
2142
                msginfo->folder = item;
2143

    
2144
                if (!newlist)
2145
                        llast = newlist = g_slist_append(newlist, msginfo);
2146
                else {
2147
                        llast = g_slist_append(llast, msginfo);
2148
                        llast = llast->next;
2149
                }
2150

    
2151
                if (update_count)
2152
                        item->total++;
2153
        }
2154

    
2155
        g_string_free(str, TRUE);
2156

    
2157
        session_set_access_time(SESSION(session));
2158

    
2159
        return newlist;
2160
}
2161

    
2162
static void imap_delete_cached_message(FolderItem *item, guint32 uid)
2163
{
2164
        gchar *dir;
2165
        gchar *file;
2166

    
2167
        g_return_if_fail(item != NULL);
2168
        g_return_if_fail(item->folder != NULL);
2169
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2170

    
2171
        dir = folder_item_get_path(item);
2172
        file = g_strdup_printf("%s%c%u", dir, G_DIR_SEPARATOR, uid);
2173

    
2174
        debug_print("Deleting cached message: %s\n", file);
2175

    
2176
        unlink(file);
2177

    
2178
        g_free(file);
2179
        g_free(dir);
2180
}
2181

    
2182
static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
2183
                                           guint32 first_uid, guint32 last_uid)
2184
{
2185
        GSList *cur, *next;
2186
        MsgInfo *msginfo;
2187
        gchar *dir;
2188

    
2189
        g_return_val_if_fail(item != NULL, mlist);
2190
        g_return_val_if_fail(item->folder != NULL, mlist);
2191
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, mlist);
2192

    
2193
        if (first_uid == 0 && last_uid == 0)
2194
                return mlist;
2195

    
2196
        debug_print("Deleting cached messages %u - %u ... ",
2197
                    first_uid, last_uid);
2198

    
2199
        dir = folder_item_get_path(item);
2200
        if (is_dir_exist(dir))
2201
                remove_numbered_files(dir, first_uid, last_uid);
2202
        g_free(dir);
2203

    
2204
        for (cur = mlist; cur != NULL; ) {
2205
                next = cur->next;
2206

    
2207
                msginfo = (MsgInfo *)cur->data;
2208
                if (msginfo != NULL && first_uid <= msginfo->msgnum &&
2209
                    msginfo->msgnum <= last_uid) {
2210
                        procmsg_msginfo_free(msginfo);
2211
                        mlist = g_slist_remove(mlist, msginfo);
2212
                }
2213

    
2214
                cur = next;
2215
        }
2216

    
2217
        debug_print("done.\n");
2218

    
2219
        return mlist;
2220
}
2221

    
2222
static void imap_delete_all_cached_messages(FolderItem *item)
2223
{
2224
        gchar *dir;
2225

    
2226
        g_return_if_fail(item != NULL);
2227
        g_return_if_fail(item->folder != NULL);
2228
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2229

    
2230
        debug_print("Deleting all cached messages... ");
2231

    
2232
        dir = folder_item_get_path(item);
2233
        if (is_dir_exist(dir))
2234
                remove_all_numbered_files(dir);
2235
        g_free(dir);
2236

    
2237
        debug_print("done.\n");
2238
}
2239

    
2240
#if USE_SSL
2241
static SockInfo *imap_open(const gchar *server, gushort port,
2242
                           SSLType ssl_type)
2243
#else
2244
static SockInfo *imap_open(const gchar *server, gushort port)
2245
#endif
2246
{
2247
        SockInfo *sock;
2248

    
2249
        if ((sock = sock_connect(server, port)) == NULL) {
2250
                log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2251
                            server, port);
2252
                return NULL;
2253
        }
2254

    
2255
#if USE_SSL
2256
        if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2257
                log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2258
                            server, port);
2259
                sock_close(sock);
2260
                return NULL;
2261
        }
2262
#endif
2263

    
2264
        return sock;
2265
}
2266

    
2267
static GList *imap_parse_namespace_str(gchar *str)
2268
{
2269
        guchar *p = str;
2270
        gchar *name;
2271
        gchar *separator;
2272
        IMAPNameSpace *namespace;
2273
        GList *ns_list = NULL;
2274

    
2275
        while (*p != '\0') {
2276
                /* parse ("#foo" "/") */
2277

    
2278
                while (*p && *p != '(') p++;
2279
                if (*p == '\0') break;
2280
                p++;
2281

    
2282
                while (*p && *p != '"') p++;
2283
                if (*p == '\0') break;
2284
                p++;
2285
                name = p;
2286

    
2287
                while (*p && *p != '"') p++;
2288
                if (*p == '\0') break;
2289
                *p = '\0';
2290
                p++;
2291

    
2292
                while (*p && isspace(*p)) p++;
2293
                if (*p == '\0') break;
2294
                if (strncmp(p, "NIL", 3) == 0)
2295
                        separator = NULL;
2296
                else if (*p == '"') {
2297
                        p++;
2298
                        separator = p;
2299
                        while (*p && *p != '"') p++;
2300
                        if (*p == '\0') break;
2301
                        *p = '\0';
2302
                        p++;
2303
                } else break;
2304

    
2305
                while (*p && *p != ')') p++;
2306
                if (*p == '\0') break;
2307
                p++;
2308

    
2309
                namespace = g_new(IMAPNameSpace, 1);
2310
                namespace->name = g_strdup(name);
2311
                namespace->separator = separator ? separator[0] : '\0';
2312
                ns_list = g_list_append(ns_list, namespace);
2313
        }
2314

    
2315
        return ns_list;
2316
}
2317

    
2318
static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2319
{
2320
        gchar *ns_str;
2321
        gchar **str_array;
2322

    
2323
        g_return_if_fail(session != NULL);
2324
        g_return_if_fail(folder != NULL);
2325

    
2326
        if (folder->ns_personal != NULL ||
2327
            folder->ns_others   != NULL ||
2328
            folder->ns_shared   != NULL)
2329
                return;
2330

    
2331
        if (imap_cmd_namespace(session, &ns_str) != IMAP_SUCCESS) {
2332
                log_warning(_("can't get namespace\n"));
2333
                imap_get_namespace_by_list(session, folder);
2334
                return;
2335
        }
2336

    
2337
        str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2338
        if (str_array[0])
2339
                folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2340
        if (str_array[0] && str_array[1])
2341
                folder->ns_others = imap_parse_namespace_str(str_array[1]);
2342
        if (str_array[0] && str_array[1] && str_array[2])
2343
                folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2344
        g_strfreev(str_array);
2345
        g_free(ns_str);
2346
}
2347

    
2348
static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2349
{
2350
        GSList *item_list, *cur;
2351
        gchar separator = '\0';
2352
        IMAPNameSpace *namespace;
2353

    
2354
        g_return_if_fail(session != NULL);
2355
        g_return_if_fail(folder != NULL);
2356

    
2357
        if (folder->ns_personal != NULL ||
2358
            folder->ns_others   != NULL ||
2359
            folder->ns_shared   != NULL)
2360
                return;
2361

    
2362
        imap_cmd_gen_send(session, "LIST \"\" \"\"");
2363
        item_list = imap_parse_list(session, "", &separator);
2364
        for (cur = item_list; cur != NULL; cur = cur->next)
2365
                folder_item_destroy(FOLDER_ITEM(cur->data));
2366
        g_slist_free(item_list);
2367

    
2368
        namespace = g_new(IMAPNameSpace, 1);
2369
        namespace->name = g_strdup("");
2370
        namespace->separator = separator;
2371
        folder->ns_personal = g_list_append(NULL, namespace);
2372
}
2373

    
2374
static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2375
                                                    const gchar *path)
2376
{
2377
        IMAPNameSpace *namespace = NULL;
2378
        gchar *tmp_path, *name;
2379

    
2380
        if (!path) path = "";
2381

    
2382
        for (; ns_list != NULL; ns_list = ns_list->next) {
2383
                IMAPNameSpace *tmp_ns = ns_list->data;
2384

    
2385
                Xstrcat_a(tmp_path, path, "/", return namespace);
2386
                Xstrdup_a(name, tmp_ns->name, return namespace);
2387
                if (tmp_ns->separator && tmp_ns->separator != '/') {
2388
                        subst_char(tmp_path, tmp_ns->separator, '/');
2389
                        subst_char(name, tmp_ns->separator, '/');
2390
                }
2391
                if (strncmp(tmp_path, name, strlen(name)) == 0)
2392
                        namespace = tmp_ns;
2393
        }
2394

    
2395
        return namespace;
2396
}
2397

    
2398
static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2399
                                          const gchar *path)
2400
{
2401
        IMAPNameSpace *namespace;
2402

    
2403
        g_return_val_if_fail(folder != NULL, NULL);
2404

    
2405
        namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2406
        if (namespace) return namespace;
2407
        namespace = imap_find_namespace_from_list(folder->ns_others, path);
2408
        if (namespace) return namespace;
2409
        namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2410
        if (namespace) return namespace;
2411

    
2412
        return NULL;
2413
}
2414

    
2415
static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2416
{
2417
        IMAPNameSpace *namespace;
2418
        gchar separator = '/';
2419

    
2420
        namespace = imap_find_namespace(folder, path);
2421
        if (namespace && namespace->separator)
2422
                separator = namespace->separator;
2423

    
2424
        return separator;
2425
}
2426

    
2427
static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2428
{
2429
        gchar *real_path;
2430
        gchar separator;
2431

    
2432
        g_return_val_if_fail(folder != NULL, NULL);
2433
        g_return_val_if_fail(path != NULL, NULL);
2434

    
2435
        real_path = imap_locale_to_modified_utf7(path);
2436
        separator = imap_get_path_separator(folder, path);
2437
        imap_path_separator_subst(real_path, separator);
2438

    
2439
        return real_path;
2440
}
2441

    
2442
static gchar *imap_parse_atom(IMAPSession *session, gchar *src,
2443
                              gchar *dest, gint dest_len, GString *str)
2444
{
2445
        gchar *cur_pos = src;
2446
        gchar *nextline;
2447

    
2448
        g_return_val_if_fail(str != NULL, cur_pos);
2449

    
2450
        /* read the next line if the current response buffer is empty */
2451
        while (isspace(*(guchar *)cur_pos)) cur_pos++;
2452
        while (*cur_pos == '\0') {
2453
                if ((nextline = sock_getline(SESSION(session)->sock)) == NULL)
2454
                        return cur_pos;
2455
                g_string_assign(str, nextline);
2456
                cur_pos = str->str;
2457
                strretchomp(nextline);
2458
                /* log_print("IMAP4< %s\n", nextline); */
2459
                debug_print("IMAP4< %s\n", nextline);
2460
                g_free(nextline);
2461

    
2462
                while (isspace(*(guchar *)cur_pos)) cur_pos++;
2463
        }
2464

    
2465
        if (!strncmp(cur_pos, "NIL", 3)) {
2466
                *dest = '\0';
2467
                cur_pos += 3;
2468
        } else if (*cur_pos == '\"') {
2469
                gchar *p;
2470

    
2471
                p = get_quoted(cur_pos, '\"', dest, dest_len);
2472
                cur_pos = p ? p : cur_pos + 2;
2473
        } else if (*cur_pos == '{') {
2474
                gchar buf[32];
2475
                gint len;
2476
                gint line_len = 0;
2477

    
2478
                cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2479
                len = atoi(buf);
2480
                g_return_val_if_fail(len >= 0, cur_pos);
2481

    
2482
                g_string_truncate(str, 0);
2483
                cur_pos = str->str;
2484

    
2485
                do {
2486
                        if ((nextline = sock_getline(SESSION(session)->sock))
2487
                                == NULL)
2488
                                return cur_pos;
2489
                        line_len += strlen(nextline);
2490
                        g_string_append(str, nextline);
2491
                        cur_pos = str->str;
2492
                        strretchomp(nextline);
2493
                        /* log_print("IMAP4< %s\n", nextline); */
2494
                        debug_print("IMAP4< %s\n", nextline);
2495
                        g_free(nextline);
2496
                } while (line_len < len);
2497

    
2498
                memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2499
                dest[MIN(len, dest_len - 1)] = '\0';
2500
                cur_pos += len;
2501
        }
2502

    
2503
        return cur_pos;
2504
}
2505

    
2506
static gchar *imap_get_header(IMAPSession *session, gchar *cur_pos,
2507
                              gchar **headers, GString *str)
2508
{
2509
        gchar *nextline;
2510
        gchar buf[32];
2511
        gint len;
2512
        gint block_len = 0;
2513

    
2514
        *headers = NULL;
2515

    
2516
        g_return_val_if_fail(str != NULL, cur_pos);
2517

    
2518
        while (isspace(*(guchar *)cur_pos)) cur_pos++;
2519

    
2520
        g_return_val_if_fail(*cur_pos == '{', cur_pos);
2521

    
2522
        cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2523
        len = atoi(buf);
2524
        g_return_val_if_fail(len >= 0, cur_pos);
2525

    
2526
        g_string_truncate(str, 0);
2527
        cur_pos = str->str;
2528

    
2529
        do {
2530
                if ((nextline = sock_getline(SESSION(session)->sock)) == NULL)
2531
                        return cur_pos;
2532
                block_len += strlen(nextline);
2533
                g_string_append(str, nextline);
2534
                cur_pos = str->str;
2535
                strretchomp(nextline);
2536
                /* debug_print("IMAP4< %s\n", nextline); */
2537
                g_free(nextline);
2538
        } while (block_len < len);
2539

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

    
2542
        *headers = g_strndup(cur_pos, len);
2543
        cur_pos += len;
2544

    
2545
        while (isspace(*(guchar *)cur_pos)) cur_pos++;
2546
        while (*cur_pos == '\0') {
2547
                if ((nextline = sock_getline(SESSION(session)->sock)) == NULL)
2548
                        return cur_pos;
2549
                g_string_assign(str, nextline);
2550
                cur_pos = str->str;
2551
                strretchomp(nextline);
2552
                debug_print("IMAP4< %s\n", nextline);
2553
                g_free(nextline);
2554

    
2555
                while (isspace(*(guchar *)cur_pos)) cur_pos++;
2556
        }
2557

    
2558
        return cur_pos;
2559
}
2560

    
2561
static MsgFlags imap_parse_flags(const gchar *flag_str)  
2562
{
2563
        const gchar *p = flag_str;
2564
        MsgFlags flags = {0, 0};
2565

    
2566
        flags.perm_flags = MSG_UNREAD;
2567

    
2568
        while ((p = strchr(p, '\\')) != NULL) {
2569
                p++;
2570

    
2571
                if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2572
                        MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2573
                } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2574
                        MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2575
                } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2576
                        MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2577
                } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2578
                        MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2579
                } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2580
                        MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2581
                }
2582
        }
2583

    
2584
        return flags;
2585
}
2586

    
2587
static IMAPFlags imap_parse_imap_flags(const gchar *flag_str)  
2588
{
2589
        const gchar *p = flag_str;
2590
        IMAPFlags flags = 0;
2591

    
2592
        while ((p = strchr(p, '\\')) != NULL) {
2593
                p++;
2594

    
2595
                if (g_strncasecmp(p, "Seen", 4) == 0) {
2596
                        flags |= IMAP_FLAG_SEEN;
2597
                } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2598
                        flags |= IMAP_FLAG_DELETED;
2599
                } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2600
                        flags |= IMAP_FLAG_FLAGGED;
2601
                } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2602
                        flags |= IMAP_FLAG_ANSWERED;
2603
                }
2604
        }
2605

    
2606
        return flags;
2607
}
2608

    
2609
static MsgInfo *imap_parse_envelope(IMAPSession *session, FolderItem *item,
2610
                                    GString *line_str)
2611
{
2612
        gchar buf[IMAPBUFSIZE];
2613
        MsgInfo *msginfo = NULL;
2614
        gchar *cur_pos;
2615
        gint msgnum;
2616
        guint32 uid = 0;
2617
        size_t size = 0;
2618
        MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2619

    
2620
        g_return_val_if_fail(line_str != NULL, NULL);
2621
        g_return_val_if_fail(line_str->str[0] == '*' &&
2622
                             line_str->str[1] == ' ', NULL);
2623

    
2624
        MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2625
        if (item->stype == F_QUEUE) {
2626
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2627
        } else if (item->stype == F_DRAFT) {
2628
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2629
        }
2630

    
2631
        cur_pos = line_str->str + 2;
2632

    
2633
#define PARSE_ONE_ELEMENT(ch)                                        \
2634
{                                                                \
2635
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
2636
        if (cur_pos == NULL) {                                        \
2637
                g_warning("cur_pos == NULL\n");                        \
2638
                procmsg_msginfo_free(msginfo);                        \
2639
                return NULL;                                        \
2640
        }                                                        \
2641
}
2642

    
2643
        PARSE_ONE_ELEMENT(' ');
2644
        msgnum = atoi(buf);
2645

    
2646
        PARSE_ONE_ELEMENT(' ');
2647
        g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2648

    
2649
        g_return_val_if_fail(*cur_pos == '(', NULL);
2650
        cur_pos++;
2651

    
2652
        while (*cur_pos != '\0' && *cur_pos != ')') {
2653
                while (*cur_pos == ' ') cur_pos++;
2654

    
2655
                if (!strncmp(cur_pos, "UID ", 4)) {
2656
                        cur_pos += 4;
2657
                        uid = strtoul(cur_pos, &cur_pos, 10);
2658
                } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2659
                        cur_pos += 6;
2660
                        if (*cur_pos != '(') {
2661
                                g_warning("*cur_pos != '('\n");
2662
                                procmsg_msginfo_free(msginfo);
2663
                                return NULL;
2664
                        }
2665
                        cur_pos++;
2666
                        PARSE_ONE_ELEMENT(')');
2667
                        imap_flags = imap_parse_flags(buf);
2668
                } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2669
                        cur_pos += 12;
2670
                        size = strtol(cur_pos, &cur_pos, 10);
2671
                } else if (!strncmp(cur_pos, "RFC822.HEADER ", 14)) {
2672
                        gchar *headers;
2673

    
2674
                        cur_pos += 14;
2675
                        cur_pos = imap_get_header(session, cur_pos, &headers,
2676
                                                  line_str);
2677
                        msginfo = procheader_parse_str(headers, flags, FALSE);
2678
                        g_free(headers);
2679
                } else {
2680
                        g_warning("invalid FETCH response: %s\n", cur_pos);
2681
                        break;
2682
                }
2683
        }
2684

    
2685
#undef PARSE_ONE_ELEMENT
2686

    
2687
        if (msginfo) {
2688
                msginfo->msgnum = uid;
2689
                msginfo->size = size;
2690
                msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2691
                msginfo->flags.perm_flags = imap_flags.perm_flags;
2692
        }
2693

    
2694
        return msginfo;
2695
}
2696

    
2697
static gint imap_msg_list_change_perm_flags(GSList *msglist, MsgPermFlags flags,
2698
                                            gboolean is_set)
2699
{
2700
        Folder *folder;
2701
        IMAPSession *session;
2702
        IMAPFlags iflags = 0;
2703
        MsgInfo *msginfo;
2704
        GSList *seq_list, *cur;
2705
        gint ok = IMAP_SUCCESS;
2706

    
2707
        if (msglist == NULL) return IMAP_SUCCESS;
2708

    
2709
        msginfo = (MsgInfo *)msglist->data;
2710
        g_return_val_if_fail(msginfo != NULL, -1);
2711

    
2712
        g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
2713
        g_return_val_if_fail(msginfo->folder != NULL, -1);
2714
        g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2715

    
2716
        folder = msginfo->folder->folder;
2717
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
2718

    
2719
        session = imap_session_get(folder);
2720
        if (!session) return -1;
2721

    
2722
        ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
2723
                         NULL, NULL, NULL, NULL);
2724
        if (ok != IMAP_SUCCESS)
2725
                return ok;
2726

    
2727
        seq_list = imap_get_seq_set_from_msglist(msglist);
2728

    
2729
        if (flags & MSG_MARKED)  iflags |= IMAP_FLAG_FLAGGED;
2730
        if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2731

    
2732
        for (cur = seq_list; cur != NULL; cur = cur->next) {
2733
                gchar *seq_set = (gchar *)cur->data;
2734

    
2735
                if (iflags) {
2736
                        ok = imap_set_message_flags(session, seq_set, iflags,
2737
                                                    is_set);
2738
                        if (ok != IMAP_SUCCESS) break;
2739
                }
2740

    
2741
                if (flags & MSG_UNREAD) {
2742
                        ok = imap_set_message_flags(session, seq_set,
2743
                                                    IMAP_FLAG_SEEN, !is_set);
2744
                        if (ok != IMAP_SUCCESS) break;
2745
                }
2746
        }
2747

    
2748
        imap_seq_set_free(seq_list);
2749

    
2750
        return ok;
2751
}
2752

    
2753
gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2754
{
2755
        GSList msglist;
2756

    
2757
        msglist.data = msginfo;
2758
        msglist.next = NULL;
2759

    
2760
        return imap_msg_list_change_perm_flags(&msglist, flags, TRUE);
2761
}
2762

    
2763
gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2764
{
2765
        GSList msglist;
2766

    
2767
        msglist.data = msginfo;
2768
        msglist.next = NULL;
2769

    
2770
        return imap_msg_list_change_perm_flags(&msglist, flags, FALSE);
2771
}
2772

    
2773
gint imap_msg_list_set_perm_flags(GSList *msglist, MsgPermFlags flags)
2774
{
2775
        return imap_msg_list_change_perm_flags(msglist, flags, TRUE);
2776
}
2777

    
2778
gint imap_msg_list_unset_perm_flags(GSList *msglist, MsgPermFlags flags)
2779
{
2780
        return imap_msg_list_change_perm_flags(msglist, flags, FALSE);
2781
}
2782

    
2783
static gchar *imap_get_flag_str(IMAPFlags flags)
2784
{
2785
        GString *str;
2786
        gchar *ret;
2787

    
2788
        str = g_string_new(NULL);
2789

    
2790
        if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
2791
        if (IMAP_IS_ANSWERED(flags))        g_string_append(str, "\\Answered ");
2792
        if (IMAP_IS_FLAGGED(flags))        g_string_append(str, "\\Flagged ");
2793
        if (IMAP_IS_DELETED(flags))        g_string_append(str, "\\Deleted ");
2794
        if (IMAP_IS_DRAFT(flags))        g_string_append(str, "\\Draft");
2795

    
2796
        if (str->len > 0 && str->str[str->len - 1] == ' ')
2797
                g_string_truncate(str, str->len - 1);
2798

    
2799
        ret = str->str;
2800
        g_string_free(str, FALSE);
2801

    
2802
        return ret;
2803
}
2804

    
2805
static gint imap_set_message_flags(IMAPSession *session,
2806
                                   const gchar *seq_set,
2807
                                   IMAPFlags flags,
2808
                                   gboolean is_set)
2809
{
2810
        gchar *cmd;
2811
        gchar *flag_str;
2812
        gint ok;
2813

    
2814
        flag_str = imap_get_flag_str(flags);
2815
        cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2816
                          flag_str, ")", NULL);
2817
        g_free(flag_str);
2818

    
2819
        ok = imap_cmd_store(session, seq_set, cmd);
2820
        g_free(cmd);
2821

    
2822
        return ok;
2823
}
2824

    
2825
static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2826
                        const gchar *path,
2827
                        gint *exists, gint *recent, gint *unseen,
2828
                        guint32 *uid_validity)
2829
{
2830
        gchar *real_path;
2831
        gint ok;
2832
        gint exists_, recent_, unseen_, uid_validity_;
2833

    
2834
        if (!exists || !recent || !unseen || !uid_validity) {
2835
                if (session->mbox && strcmp(session->mbox, path) == 0)
2836
                        return IMAP_SUCCESS;
2837
                exists = &exists_;
2838
                recent = &recent_;
2839
                unseen = &unseen_;
2840
                uid_validity = &uid_validity_;
2841
        }
2842

    
2843
        g_free(session->mbox);
2844
        session->mbox = NULL;
2845

    
2846
        real_path = imap_get_real_path(folder, path);
2847
        ok = imap_cmd_select(session, real_path,
2848
                             exists, recent, unseen, uid_validity);
2849
        if (ok != IMAP_SUCCESS)
2850
                log_warning(_("can't select folder: %s\n"), real_path);
2851
        else
2852
                session->mbox = g_strdup(path);
2853
        g_free(real_path);
2854

    
2855
        return ok;
2856
}
2857

    
2858
#define THROW(err) { ok = err; goto catch; }
2859

    
2860
static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2861
                        const gchar *path,
2862
                        gint *messages, gint *recent,
2863
                        guint32 *uid_next, guint32 *uid_validity,
2864
                        gint *unseen)
2865
{
2866
        gchar *real_path;
2867
        gchar *real_path_;
2868
        gint ok;
2869
        GPtrArray *argbuf = NULL;
2870
        gchar *str;
2871

    
2872
        if (messages && recent && uid_next && uid_validity && unseen) {
2873
                *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2874
                argbuf = g_ptr_array_new();
2875
        }
2876

    
2877
        real_path = imap_get_real_path(folder, path);
2878
        QUOTE_IF_REQUIRED(real_path_, real_path);
2879
        imap_cmd_gen_send(session, "STATUS %s "
2880
                          "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2881
                          real_path_);
2882

    
2883
        ok = imap_cmd_ok(session, argbuf);
2884
        if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2885

    
2886
        str = search_array_str(argbuf, "STATUS");
2887
        if (!str) THROW(IMAP_ERROR);
2888

    
2889
        str = strchr(str, '(');
2890
        if (!str) THROW(IMAP_ERROR);
2891
        str++;
2892
        while (*str != '\0' && *str != ')') {
2893
                while (*str == ' ') str++;
2894

    
2895
                if (!strncmp(str, "MESSAGES ", 9)) {
2896
                        str += 9;
2897
                        *messages = strtol(str, &str, 10);
2898
                } else if (!strncmp(str, "RECENT ", 7)) {
2899
                        str += 7;
2900
                        *recent = strtol(str, &str, 10);
2901
                } else if (!strncmp(str, "UIDNEXT ", 8)) {
2902
                        str += 8;
2903
                        *uid_next = strtoul(str, &str, 10);
2904
                } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2905
                        str += 12;
2906
                        *uid_validity = strtoul(str, &str, 10);
2907
                } else if (!strncmp(str, "UNSEEN ", 7)) {
2908
                        str += 7;
2909
                        *unseen = strtol(str, &str, 10);
2910
                } else {
2911
                        g_warning("invalid STATUS response: %s\n", str);
2912
                        break;
2913
                }
2914
        }
2915

    
2916
catch:
2917
        g_free(real_path);
2918
        if (argbuf) {
2919
                ptr_array_free_strings(argbuf);
2920
                g_ptr_array_free(argbuf, TRUE);
2921
        }
2922

    
2923
        return ok;
2924
}
2925

    
2926
#undef THROW
2927

    
2928
static gboolean imap_has_capability(IMAPSession        *session,
2929
                                    const gchar *capability)
2930
{
2931
        gchar **p;
2932

    
2933
        for (p = session->capability; *p != NULL; ++p) {
2934
                if (!g_strcasecmp(*p, capability))
2935
                        return TRUE;
2936
        }
2937

    
2938
        return FALSE;
2939
}
2940

    
2941
static void imap_capability_free(IMAPSession *session)
2942
{
2943
        g_strfreev(session->capability);
2944
        session->capability = NULL;
2945
}
2946

    
2947

    
2948
/* low-level IMAP4rev1 commands */
2949

    
2950
#define THROW(err) { ok = err; goto catch; }
2951

    
2952
static gint imap_cmd_capability(IMAPSession *session)
2953
{
2954
        gint ok;
2955
        GPtrArray *argbuf;
2956
        gchar *capability;
2957

    
2958
        argbuf = g_ptr_array_new();
2959

    
2960
        imap_cmd_gen_send(session, "CAPABILITY");
2961
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2962

    
2963
        capability = search_array_str(argbuf, "CAPABILITY ");
2964
        if (!capability) THROW(IMAP_ERROR);
2965

    
2966
        capability += strlen("CAPABILITY ");
2967

    
2968
        IMAP_SESSION(session)->capability = g_strsplit(capability, " ", -1);
2969

    
2970
catch:
2971
        ptr_array_free_strings(argbuf);
2972
        g_ptr_array_free(argbuf, TRUE);
2973

    
2974
        return ok;
2975
}
2976

    
2977
#undef THROW
2978

    
2979
static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2980
                                  const gchar *pass, IMAPAuthType type)
2981
{
2982
        gchar *auth_type;
2983
        gint ok;
2984
        gchar *buf = NULL;
2985
        gchar *challenge;
2986
        gint challenge_len;
2987
        gchar hexdigest[33];
2988
        gchar *response;
2989
        gchar *response64;
2990

    
2991
        g_return_val_if_fail(type == IMAP_AUTH_CRAM_MD5, IMAP_ERROR);
2992

    
2993
        auth_type = "CRAM-MD5";
2994

    
2995
        imap_cmd_gen_send(session, "AUTHENTICATE %s", auth_type);
2996
        ok = imap_cmd_gen_recv(session, &buf);
2997
        if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2998
                g_free(buf);
2999
                return IMAP_ERROR;
3000
        }
3001

    
3002
        challenge = g_malloc(strlen(buf + 2) + 1);
3003
        challenge_len = base64_decode(challenge, buf + 2, -1);
3004
        challenge[challenge_len] = '\0';
3005
        g_free(buf);
3006
        log_print("IMAP< [Decoded: %s]\n", challenge);
3007

    
3008
        md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
3009
        g_free(challenge);
3010

    
3011
        response = g_strdup_printf("%s %s", user, hexdigest);
3012
        log_print("IMAP> [Encoded: %s]\n", response);
3013
        response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3014
        base64_encode(response64, response, strlen(response));
3015
        g_free(response);
3016

    
3017
        log_print("IMAP> %s\n", response64);
3018
        sock_puts(SESSION(session)->sock, response64);
3019
        ok = imap_cmd_ok(session, NULL);
3020
        if (ok != IMAP_SUCCESS)
3021
                log_warning(_("IMAP4 authentication failed.\n"));
3022

    
3023
        return ok;
3024
}
3025

    
3026
static gint imap_cmd_login(IMAPSession *session,
3027
                           const gchar *user, const gchar *pass)
3028
{
3029
        gchar *user_, *pass_;
3030
        gint ok;
3031

    
3032
        QUOTE_IF_REQUIRED(user_, user);
3033
        QUOTE_IF_REQUIRED(pass_, pass);
3034
        imap_cmd_gen_send(session, "LOGIN %s %s", user_, pass_);
3035

    
3036
        ok = imap_cmd_ok(session, NULL);
3037
        if (ok != IMAP_SUCCESS)
3038
                log_warning(_("IMAP4 login failed.\n"));
3039

    
3040
        return ok;
3041
}
3042

    
3043
static gint imap_cmd_logout(IMAPSession *session)
3044
{
3045
        imap_cmd_gen_send(session, "LOGOUT");
3046
        return imap_cmd_ok(session, NULL);
3047
}
3048

    
3049
static gint imap_cmd_noop(IMAPSession *session)
3050
{
3051
        imap_cmd_gen_send(session, "NOOP");
3052
        return imap_cmd_ok(session, NULL);
3053
}
3054

    
3055
#if USE_SSL
3056
static gint imap_cmd_starttls(IMAPSession *session)
3057
{
3058
        imap_cmd_gen_send(session, "STARTTLS");
3059
        return imap_cmd_ok(session, NULL);
3060
}
3061
#endif
3062

    
3063
#define THROW(err) { ok = err; goto catch; }
3064

    
3065
static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3066
{
3067
        gint ok;
3068
        GPtrArray *argbuf;
3069
        gchar *str;
3070

    
3071
        argbuf = g_ptr_array_new();
3072

    
3073
        imap_cmd_gen_send(session, "NAMESPACE");
3074
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3075

    
3076
        str = search_array_str(argbuf, "NAMESPACE");
3077
        if (!str) THROW(IMAP_ERROR);
3078

    
3079
        *ns_str = g_strdup(str);
3080

    
3081
catch:
3082
        ptr_array_free_strings(argbuf);
3083
        g_ptr_array_free(argbuf, TRUE);
3084

    
3085
        return ok;
3086
}
3087

    
3088
#undef THROW
3089

    
3090
static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3091
                          const gchar *mailbox, GPtrArray *argbuf)
3092
{
3093
        gchar *ref_, *mailbox_;
3094

    
3095
        if (!ref) ref = "\"\"";
3096
        if (!mailbox) mailbox = "\"\"";
3097

    
3098
        QUOTE_IF_REQUIRED(ref_, ref);
3099
        QUOTE_IF_REQUIRED(mailbox_, mailbox);
3100
        imap_cmd_gen_send(session, "LIST %s %s", ref_, mailbox_);
3101

    
3102
        return imap_cmd_ok(session, argbuf);
3103
}
3104

    
3105
#define THROW goto catch
3106

    
3107
static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3108
                               gboolean examine,
3109
                               gint *exists, gint *recent, gint *unseen,
3110
                               guint32 *uid_validity)
3111
{
3112
        gint ok;
3113
        gchar *resp_str;
3114
        GPtrArray *argbuf;
3115
        gchar *select_cmd;
3116
        gchar *folder_;
3117
        guint uid_validity_;
3118

    
3119
        *exists = *recent = *unseen = *uid_validity = 0;
3120
        argbuf = g_ptr_array_new();
3121

    
3122
        if (examine)
3123
                select_cmd = "EXAMINE";
3124
        else
3125
                select_cmd = "SELECT";
3126

    
3127
        QUOTE_IF_REQUIRED(folder_, folder);
3128
        imap_cmd_gen_send(session, "%s %s", select_cmd, folder_);
3129

    
3130
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
3131

    
3132
        resp_str = search_array_contain_str(argbuf, "EXISTS");
3133
        if (resp_str) {
3134
                if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3135
                        g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3136
                        THROW;
3137
                }
3138
        }
3139

    
3140
        resp_str = search_array_contain_str(argbuf, "RECENT");
3141
        if (resp_str) {
3142
                if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3143
                        g_warning("imap_cmd_select(): invalid RECENT line.\n");
3144
                        THROW;
3145
                }
3146
        }
3147

    
3148
        resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3149
        if (resp_str) {
3150
                if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3151
                    != 1) {
3152
                        g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3153
                        THROW;
3154
                }
3155
                *uid_validity = uid_validity_;
3156
        }
3157

    
3158
        resp_str = search_array_contain_str(argbuf, "UNSEEN");
3159
        if (resp_str) {
3160
                if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3161
                        g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3162
                        THROW;
3163
                }
3164
        }
3165

    
3166
catch:
3167
        ptr_array_free_strings(argbuf);
3168
        g_ptr_array_free(argbuf, TRUE);
3169

    
3170
        return ok;
3171
}
3172

    
3173
static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3174
                            gint *exists, gint *recent, gint *unseen,
3175
                            guint32 *uid_validity)
3176
{
3177
        return imap_cmd_do_select(session, folder, FALSE,
3178
                                  exists, recent, unseen, uid_validity);
3179
}
3180

    
3181
static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3182
                             gint *exists, gint *recent, gint *unseen,
3183
                             guint32 *uid_validity)
3184
{
3185
        return imap_cmd_do_select(session, folder, TRUE,
3186
                                  exists, recent, unseen, uid_validity);
3187
}
3188

    
3189
#undef THROW
3190

    
3191
static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3192
{
3193
        gchar *folder_;
3194

    
3195
        QUOTE_IF_REQUIRED(folder_, folder);
3196
        imap_cmd_gen_send(session, "CREATE %s", folder_);
3197

    
3198
        return imap_cmd_ok(session, NULL);
3199
}
3200

    
3201
static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3202
                            const gchar *new_folder)
3203
{
3204
        gchar *old_folder_, *new_folder_;
3205

    
3206
        QUOTE_IF_REQUIRED(old_folder_, old_folder);
3207
        QUOTE_IF_REQUIRED(new_folder_, new_folder);
3208
        imap_cmd_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3209

    
3210
        return imap_cmd_ok(session, NULL);
3211
}
3212

    
3213
static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3214
{
3215
        gchar *folder_;
3216

    
3217
        QUOTE_IF_REQUIRED(folder_, folder);
3218
        imap_cmd_gen_send(session, "DELETE %s", folder_);
3219

    
3220
        return imap_cmd_ok(session, NULL);
3221
}
3222

    
3223
#define THROW(err) { ok = err; goto catch; }
3224

    
3225
static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3226
                            GArray **result)
3227
{
3228
        gint ok;
3229
        GPtrArray *argbuf;
3230
        GArray *array;
3231
        gchar *str;
3232
        gchar *p, *ep;
3233
        guint32 uid;
3234

    
3235
        g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3236
        g_return_val_if_fail(result != NULL, IMAP_ERROR);
3237

    
3238
        argbuf = g_ptr_array_new();
3239

    
3240
        imap_cmd_gen_send(session, "UID SEARCH %s", criteria);
3241
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3242

    
3243
        str = search_array_str(argbuf, "SEARCH");
3244
        if (!str) THROW(IMAP_ERROR);
3245

    
3246
        array = g_array_new(FALSE, FALSE, sizeof(guint32));
3247

    
3248
        p = str + strlen("SEARCH");
3249

    
3250
        while (*p != '\0') {
3251
                uid = strtoul(p, &ep, 10);
3252
                if (p < ep && uid > 0) {
3253
                        g_array_append_val(array, uid);
3254
                        p = ep;
3255
                } else
3256
                        break;
3257
        }
3258

    
3259
        *result = array;
3260

    
3261
catch:
3262
        ptr_array_free_strings(argbuf);
3263
        g_ptr_array_free(argbuf, TRUE);
3264

    
3265
        return ok;
3266
}
3267

    
3268
static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3269
                           const gchar *filename)
3270
{
3271
        gint ok;
3272
        gchar *buf;
3273
        gchar *cur_pos;
3274
        gchar size_str[32];
3275
        glong size_num;
3276

    
3277
        g_return_val_if_fail(filename != NULL, IMAP_ERROR);
3278

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

    
3281
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
3282
                if (buf[0] != '*' || buf[1] != ' ') {
3283
                        g_free(buf);
3284
                        return IMAP_ERROR;
3285
                }
3286
                if (strstr(buf, "FETCH") != NULL) break;
3287
                g_free(buf);
3288
        }
3289
        if (ok != IMAP_SUCCESS)
3290
                return ok;
3291

    
3292
#define RETURN_ERROR_IF_FAIL(cond)        \
3293
        if (!(cond)) {                        \
3294
                g_free(buf);                \
3295
                return IMAP_ERROR;        \
3296
        }
3297

    
3298
        cur_pos = strchr(buf, '{');
3299
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3300
        cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3301
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3302
        size_num = atol(size_str);
3303
        RETURN_ERROR_IF_FAIL(size_num >= 0);
3304

    
3305
        RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3306

    
3307
#undef RETURN_ERROR_IF_FAIL
3308

    
3309
        g_free(buf);
3310

    
3311
        if (recv_bytes_write_to_file(SESSION(session)->sock,
3312
                                     size_num, filename) != 0)
3313
                return IMAP_ERROR;
3314

    
3315
        if (imap_cmd_gen_recv(session, &buf) != IMAP_SUCCESS)
3316
                return IMAP_ERROR;
3317

    
3318
        if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3319
                g_free(buf);
3320
                return IMAP_ERROR;
3321
        }
3322
        g_free(buf);
3323

    
3324
        ok = imap_cmd_ok(session, NULL);
3325

    
3326
        return ok;
3327
}
3328

    
3329
static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3330
                            const gchar *file, IMAPFlags flags,
3331
                            guint32 *new_uid)
3332
{
3333
        gint ok;
3334
        gint size;
3335
        gchar *destfolder_;
3336
        gchar *flag_str;
3337
        guint new_uid_;
3338
        gchar *ret = NULL;
3339
        gchar buf[BUFFSIZE];
3340
        FILE *fp;
3341
        GPtrArray *argbuf;
3342
        gchar *resp_str;
3343

    
3344
        g_return_val_if_fail(file != NULL, IMAP_ERROR);
3345

    
3346
        size = get_file_size_as_crlf(file);
3347
        if ((fp = fopen(file, "rb")) == NULL) {
3348
                FILE_OP_ERROR(file, "fopen");
3349
                return -1;
3350
        }
3351
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
3352
        flag_str = imap_get_flag_str(flags);
3353
        imap_cmd_gen_send(session, "APPEND %s (%s) {%d}",
3354
                          destfolder_, flag_str, size);
3355
        g_free(flag_str);
3356

    
3357
        ok = imap_cmd_gen_recv(session, &ret);
3358
        if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3359
                log_warning(_("can't append %s to %s\n"), file, destfolder_);
3360
                g_free(ret);
3361
                fclose(fp);
3362
                return IMAP_ERROR;
3363
        }
3364
        g_free(ret);
3365

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

    
3368
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3369
                strretchomp(buf);
3370
                if (sock_puts(SESSION(session)->sock, buf) < 0) {
3371
                        fclose(fp);
3372
                        return -1;
3373
                }
3374
        }
3375

    
3376
        if (ferror(fp)) {
3377
                FILE_OP_ERROR(file, "fgets");
3378
                fclose(fp);
3379
                return -1;
3380
        }
3381

    
3382
        sock_puts(SESSION(session)->sock, "");
3383

    
3384
        fclose(fp);
3385

    
3386
        if (new_uid != NULL)
3387
                *new_uid = 0;
3388

    
3389
        if (new_uid != NULL && session->uidplus) {
3390
                argbuf = g_ptr_array_new();
3391

    
3392
                ok = imap_cmd_ok(session, argbuf);
3393
                if (ok != IMAP_SUCCESS)
3394
                        log_warning(_("can't append message to %s\n"),
3395
                                    destfolder_);
3396
                else if (argbuf->len > 0) {
3397
                        resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3398
                        if (resp_str &&
3399
                            sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3400
                                   &new_uid_) == 1) {
3401
                                *new_uid = new_uid_;
3402
                        }
3403
                }
3404

    
3405
                ptr_array_free_strings(argbuf);
3406
                g_ptr_array_free(argbuf, TRUE);
3407
        } else
3408
                ok = imap_cmd_ok(session, NULL);
3409

    
3410
        return ok;
3411
}
3412

    
3413
static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3414
                          const gchar *destfolder)
3415
{
3416
        gint ok;
3417
        gchar *destfolder_;
3418

    
3419
        g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3420

    
3421
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
3422
        imap_cmd_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3423

    
3424
        ok = imap_cmd_ok(session, NULL);
3425
        if (ok != IMAP_SUCCESS) {
3426
                log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3427
                return -1;
3428
        }
3429

    
3430
        return ok;
3431
}
3432

    
3433
gint imap_cmd_envelope(IMAPSession *session, const gchar *seq_set)
3434
{
3435
        imap_cmd_gen_send
3436
                (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
3437
                 seq_set);
3438

    
3439
        return IMAP_SUCCESS;
3440
}
3441

    
3442
static gint imap_cmd_store(IMAPSession *session, const gchar *seq_set,
3443
                           const gchar *sub_cmd)
3444
{
3445
        gint ok;
3446

    
3447
        imap_cmd_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3448

    
3449
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3450
                log_warning(_("error while imap command: STORE %s %s\n"),
3451
                            seq_set, sub_cmd);
3452
                return ok;
3453
        }
3454

    
3455
        return IMAP_SUCCESS;
3456
}
3457

    
3458
static gint imap_cmd_expunge(IMAPSession *session)
3459
{
3460
        gint ok;
3461

    
3462
        imap_cmd_gen_send(session, "EXPUNGE");
3463
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3464
                log_warning(_("error while imap command: EXPUNGE\n"));
3465
                return ok;
3466
        }
3467

    
3468
        return IMAP_SUCCESS;
3469
}
3470

    
3471
static gint imap_cmd_close(IMAPSession *session)
3472
{
3473
        gint ok;
3474

    
3475
        imap_cmd_gen_send(session, "CLOSE");
3476
        if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3477
                log_warning(_("error while imap command: CLOSE\n"));
3478

    
3479
        return ok;
3480
}
3481

    
3482
static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3483
{
3484
        gint ok;
3485
        gchar *buf;
3486
        gint cmd_num;
3487
        gchar cmd_status[IMAPBUFSIZE + 1];
3488

    
3489
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
3490
                if (buf[0] == '*' && buf[1] == ' ') {
3491
                        if (argbuf) {
3492
                                g_memmove(buf, buf + 2, strlen(buf + 2) + 1);
3493
                                g_ptr_array_add(argbuf, buf);
3494
                        } else
3495
                                g_free(buf);
3496
                        continue;
3497
                }
3498

    
3499
                if (sscanf(buf, "%d %" Xstr(IMAPBUFSIZE) "s",
3500
                           &cmd_num, cmd_status) < 2) {
3501
                        g_free(buf);
3502
                        return IMAP_ERROR;
3503
                } else if (cmd_num == session->cmd_count &&
3504
                         !strcmp(cmd_status, "OK")) {
3505
                        if (argbuf)
3506
                                g_ptr_array_add(argbuf, buf);
3507
                        else
3508
                                g_free(buf);
3509
                        return IMAP_SUCCESS;
3510
                } else {
3511
                        g_free(buf);
3512
                        return IMAP_ERROR;
3513
                }
3514
        }
3515

    
3516
        return ok;
3517
}
3518

    
3519
static void imap_cmd_gen_send(IMAPSession *session, const gchar *format, ...)
3520
{
3521
        gchar buf[IMAPBUFSIZE];
3522
        gchar tmp[IMAPBUFSIZE];
3523
        gchar *p;
3524
        va_list args;
3525

    
3526
        va_start(args, format);
3527
        g_vsnprintf(tmp, sizeof(tmp), format, args);
3528
        va_end(args);
3529

    
3530
        session->cmd_count++;
3531

    
3532
        g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp);
3533
        if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3534
                *p = '\0';
3535
                log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3536
        } else
3537
                log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3538

    
3539
        sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3540
}
3541

    
3542
static gint imap_cmd_gen_recv(IMAPSession *session, gchar **ret)
3543
{
3544
        if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3545
                return IMAP_SOCKET;
3546

    
3547
        strretchomp(*ret);
3548

    
3549
        log_print("IMAP4< %s\n", *ret);
3550

    
3551
        session_set_access_time(SESSION(session));
3552

    
3553
        return IMAP_SUCCESS;
3554
}
3555

    
3556

    
3557
/* misc utility functions */
3558

    
3559
static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3560
{
3561
        gchar *tmp;
3562

    
3563
        dest[0] = '\0';
3564
        tmp = strchr(src, ch);
3565
        if (!tmp)
3566
                return NULL;
3567

    
3568
        memcpy(dest, src, MIN(tmp - src, len - 1));
3569
        dest[MIN(tmp - src, len - 1)] = '\0';
3570

    
3571
        return tmp + 1;
3572
}
3573

    
3574
static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3575
{
3576
        const gchar *p = src;
3577
        gint n = 0;
3578

    
3579
        g_return_val_if_fail(*p == ch, NULL);
3580

    
3581
        *dest = '\0';
3582
        p++;
3583

    
3584
        while (*p != '\0' && *p != ch) {
3585
                if (n < len - 1) {
3586
                        if (*p == '\\' && *(p + 1) != '\0')
3587
                                p++;
3588
                        *dest++ = *p++;
3589
                } else
3590
                        p++;
3591
                n++;
3592
        }
3593

    
3594
        *dest = '\0';
3595
        return (gchar *)(*p == ch ? p + 1 : p);
3596
}
3597

    
3598
static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
3599
{
3600
        gint i;
3601

    
3602
        for (i = 0; i < array->len; i++) {
3603
                gchar *tmp;
3604

    
3605
                tmp = g_ptr_array_index(array, i);
3606
                if (strstr(tmp, str) != NULL)
3607
                        return tmp;
3608
        }
3609

    
3610
        return NULL;
3611
}
3612

    
3613
static gchar *search_array_str(GPtrArray *array, gchar *str)
3614
{
3615
        gint i;
3616
        gint len;
3617

    
3618
        len = strlen(str);
3619

    
3620
        for (i = 0; i < array->len; i++) {
3621
                gchar *tmp;
3622

    
3623
                tmp = g_ptr_array_index(array, i);
3624
                if (!strncmp(tmp, str, len))
3625
                        return tmp;
3626
        }
3627

    
3628
        return NULL;
3629
}
3630

    
3631
static void imap_path_separator_subst(gchar *str, gchar separator)
3632
{
3633
        gchar *p;
3634
        gboolean in_escape = FALSE;
3635

    
3636
        if (!separator || separator == '/') return;
3637

    
3638
        for (p = str; *p != '\0'; p++) {
3639
                if (*p == '/' && !in_escape)
3640
                        *p = separator;
3641
                else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3642
                        in_escape = TRUE;
3643
                else if (*p == '-' && in_escape)
3644
                        in_escape = FALSE;
3645
        }
3646
}
3647

    
3648
static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3649
{
3650
#if !HAVE_ICONV
3651
        const gchar *from_p;
3652
        gchar *to, *to_p;
3653

    
3654
        to = g_malloc(strlen(mutf7_str) + 1);
3655
        to_p = to;
3656

    
3657
        for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3658
                if (*from_p == '&' && *(from_p + 1) == '-') {
3659
                        *to_p++ = '&';
3660
                        from_p++;
3661
                } else
3662
                        *to_p++ = *from_p;
3663
        }
3664
        *to_p = '\0';
3665

    
3666
        return to;
3667
#else
3668
        static iconv_t cd = (iconv_t)-1;
3669
        static gboolean iconv_ok = TRUE;
3670
        GString *norm_utf7;
3671
        gchar *norm_utf7_p;
3672
        size_t norm_utf7_len;
3673
        const gchar *p;
3674
        gchar *to_str, *to_p;
3675
        size_t to_len;
3676
        gboolean in_escape = FALSE;
3677

    
3678
        if (!iconv_ok) return g_strdup(mutf7_str);
3679

    
3680
        if (cd == (iconv_t)-1) {
3681
                cd = iconv_open(conv_get_locale_charset_str(), "UTF-7");
3682
                if (cd == (iconv_t)-1) {
3683
                        g_warning("iconv cannot convert UTF-7 to %s\n",
3684
                                  conv_get_locale_charset_str());
3685
                        iconv_ok = FALSE;
3686
                        return g_strdup(mutf7_str);
3687
                }
3688
        }
3689

    
3690
        norm_utf7 = g_string_new(NULL);
3691

    
3692
        for (p = mutf7_str; *p != '\0'; p++) {
3693
                /* replace: '&'  -> '+',
3694
                            "&-" -> '&',
3695
                            escaped ','  -> '/' */
3696
                if (!in_escape && *p == '&') {
3697
                        if (*(p + 1) != '-') {
3698
                                g_string_append_c(norm_utf7, '+');
3699
                                in_escape = TRUE;
3700
                        } else {
3701
                                g_string_append_c(norm_utf7, '&');
3702
                                p++;
3703
                        }
3704
                } else if (in_escape && *p == ',') {
3705
                        g_string_append_c(norm_utf7, '/');
3706
                } else if (in_escape && *p == '-') {
3707
                        g_string_append_c(norm_utf7, '-');
3708
                        in_escape = FALSE;
3709
                } else {
3710
                        g_string_append_c(norm_utf7, *p);
3711
                }
3712
        }
3713

    
3714
        norm_utf7_p = norm_utf7->str;
3715
        norm_utf7_len = norm_utf7->len;
3716
        to_len = strlen(mutf7_str) * 5;
3717
        to_p = to_str = g_malloc(to_len + 1);
3718

    
3719
        if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3720
                  &to_p, &to_len) == -1) {
3721
                g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3722
                          conv_get_locale_charset_str());
3723
                g_string_free(norm_utf7, TRUE);
3724
                g_free(to_str);
3725
                return g_strdup(mutf7_str);
3726
        }
3727

    
3728
        /* second iconv() call for flushing */
3729
        iconv(cd, NULL, NULL, &to_p, &to_len);
3730
        g_string_free(norm_utf7, TRUE);
3731
        *to_p = '\0';
3732

    
3733
        return to_str;
3734
#endif /* !HAVE_ICONV */
3735
}
3736

    
3737
static gchar *imap_locale_to_modified_utf7(const gchar *from)
3738
{
3739
#if !HAVE_ICONV
3740
        const gchar *from_p;
3741
        gchar *to, *to_p;
3742

    
3743
        to = g_malloc(strlen(from) * 2 + 1);
3744
        to_p = to;
3745

    
3746
        for (from_p = from; *from_p != '\0'; from_p++) {
3747
                if (*from_p == '&') {
3748
                        *to_p++ = '&';
3749
                        *to_p++ = '-';
3750
                } else
3751
                        *to_p++ = *from_p;
3752
        }
3753
        *to_p = '\0';
3754

    
3755
        return to;
3756
#else
3757
        static iconv_t cd = (iconv_t)-1;
3758
        static gboolean iconv_ok = TRUE;
3759
        gchar *norm_utf7, *norm_utf7_p;
3760
        size_t from_len, norm_utf7_len;
3761
        GString *to_str;
3762
        gchar *from_tmp, *to, *p;
3763
        gboolean in_escape = FALSE;
3764

    
3765
        if (!iconv_ok) return g_strdup(from);
3766

    
3767
        if (cd == (iconv_t)-1) {
3768
                cd = iconv_open("UTF-7", conv_get_locale_charset_str());
3769
                if (cd == (iconv_t)-1) {
3770
                        g_warning(_("iconv cannot convert %s to UTF-7\n"),
3771
                                  conv_get_locale_charset_str());
3772
                        iconv_ok = FALSE;
3773
                        return g_strdup(from);
3774
                }
3775
        }
3776

    
3777
        Xstrdup_a(from_tmp, from, return g_strdup(from));
3778
        from_len = strlen(from);
3779
        norm_utf7_len = from_len * 5;
3780
        Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3781
        norm_utf7_p = norm_utf7;
3782

    
3783
#define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3784

    
3785
        while (from_len > 0) {
3786
                if (*from_tmp == '+') {
3787
                        *norm_utf7_p++ = '+';
3788
                        *norm_utf7_p++ = '-';
3789
                        norm_utf7_len -= 2;
3790
                        from_tmp++;
3791
                        from_len--;
3792
                } else if (IS_PRINT(*(guchar *)from_tmp)) {
3793
                        /* printable ascii char */
3794
                        *norm_utf7_p = *from_tmp;
3795
                        norm_utf7_p++;
3796
                        norm_utf7_len--;
3797
                        from_tmp++;
3798
                        from_len--;
3799
                } else {
3800
                        size_t mb_len = 0, conv_len = 0;
3801

    
3802
                        /* unprintable char: convert to UTF-7 */
3803
                        p = from_tmp;
3804
                        while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3805
                                mb_len = mblen(p, MB_LEN_MAX);
3806
                                if (mb_len <= 0) {
3807
                                        g_warning("wrong multibyte sequence\n");
3808
                                        return g_strdup(from);
3809
                                }
3810
                                conv_len += mb_len;
3811
                                p += mb_len;
3812
                        }
3813

    
3814
                        from_len -= conv_len;
3815
                        if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3816
                                  &conv_len,
3817
                                  &norm_utf7_p, &norm_utf7_len) == -1) {
3818
                                g_warning("iconv cannot convert %s to UTF-7\n",
3819
                                          conv_get_locale_charset_str());
3820
                                return g_strdup(from);
3821
                        }
3822

    
3823
                        /* second iconv() call for flushing */
3824
                        iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3825
                }
3826
        }
3827

    
3828
#undef IS_PRINT
3829

    
3830
        *norm_utf7_p = '\0';
3831
        to_str = g_string_new(NULL);
3832
        for (p = norm_utf7; p < norm_utf7_p; p++) {
3833
                /* replace: '&' -> "&-",
3834
                            '+' -> '&',
3835
                            "+-" -> '+',
3836
                            BASE64 '/' -> ',' */
3837
                if (!in_escape && *p == '&') {
3838
                        g_string_append(to_str, "&-");
3839
                } else if (!in_escape && *p == '+') {
3840
                        if (*(p + 1) == '-') {
3841
                                g_string_append_c(to_str, '+');
3842
                                p++;
3843
                        } else {
3844
                                g_string_append_c(to_str, '&');
3845
                                in_escape = TRUE;
3846
                        }
3847
                } else if (in_escape && *p == '/') {
3848
                        g_string_append_c(to_str, ',');
3849
                } else if (in_escape && *p == '-') {
3850
                        g_string_append_c(to_str, '-');
3851
                        in_escape = FALSE;
3852
                } else {
3853
                        g_string_append_c(to_str, *p);
3854
                }
3855
        }
3856

    
3857
        if (in_escape) {
3858
                in_escape = FALSE;
3859
                g_string_append_c(to_str, '-');
3860
        }
3861

    
3862
        to = to_str->str;
3863
        g_string_free(to_str, FALSE);
3864

    
3865
        return to;
3866
#endif /* !HAVE_ICONV */
3867
}
3868

    
3869
static GSList *imap_get_seq_set_from_msglist(GSList *msglist)
3870
{
3871
        GString *str;
3872
        GSList *sorted_list, *cur;
3873
        guint first, last, next;
3874
        gchar *ret_str;
3875
        GSList *ret_list = NULL;
3876

    
3877
        if (msglist == NULL)
3878
                return NULL;
3879

    
3880
        str = g_string_sized_new(256);
3881

    
3882
        sorted_list = g_slist_copy(msglist);
3883
        sorted_list = procmsg_sort_msg_list(sorted_list, SORT_BY_NUMBER,
3884
                                            SORT_ASCENDING);
3885

    
3886
        first = ((MsgInfo *)sorted_list->data)->msgnum;
3887

    
3888
        for (cur = sorted_list; cur != NULL; cur = cur->next) {
3889
                last = ((MsgInfo *)cur->data)->msgnum;
3890
                if (cur->next)
3891
                        next = ((MsgInfo *)cur->next->data)->msgnum;
3892
                else
3893
                        next = 0;
3894

    
3895
                if (last + 1 != next || next == 0) {
3896
                        if (str->len > 0)
3897
                                g_string_append_c(str, ',');
3898
                        if (first == last)
3899
                                g_string_sprintfa(str, "%u", first);
3900
                        else
3901
                                g_string_sprintfa(str, "%u:%u", first, last);
3902

    
3903
                        first = next;
3904

    
3905
                        if (str->len > IMAP_CMD_LIMIT) {
3906
                                ret_str = g_strdup(str->str);
3907
                                ret_list = g_slist_append(ret_list, ret_str);
3908
                                g_string_truncate(str, 0);
3909
                        }
3910
                }
3911
        }
3912

    
3913
        if (str->len > 0) {
3914
                ret_str = g_strdup(str->str);
3915
                ret_list = g_slist_append(ret_list, ret_str);
3916
        }
3917

    
3918
        g_slist_free(sorted_list);
3919
        g_string_free(str, TRUE);
3920

    
3921
        return ret_list;
3922
}
3923

    
3924
static void imap_seq_set_free(GSList *seq_list)
3925
{
3926
        slist_free_strings(seq_list);
3927
        g_slist_free(seq_list);
3928
}
3929

    
3930
static GHashTable *imap_get_uid_table(GArray *array)
3931
{
3932
        GHashTable *table;
3933
        gint i;
3934
        guint32 uid;
3935

    
3936
        g_return_val_if_fail(array != NULL, NULL);
3937

    
3938
        table = g_hash_table_new(NULL, g_direct_equal);
3939

    
3940
        for (i = 0; i < array->len; i++) {
3941
                uid = g_array_index(array, guint32, i);
3942
                g_hash_table_insert(table, GUINT_TO_POINTER(uid),
3943
                                    GINT_TO_POINTER(i + 1));
3944
        }
3945

    
3946
        return table;
3947
}
3948

    
3949
static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3950
{
3951
        FolderItem *item = node->data;
3952
        gchar **paths = data;
3953
        const gchar *oldpath = paths[0];
3954
        const gchar *newpath = paths[1];
3955
        gchar *base;
3956
        gchar *new_itempath;
3957
        gint oldpathlen;
3958

    
3959
        oldpathlen = strlen(oldpath);
3960
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3961
                g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3962
                return TRUE;
3963
        }
3964

    
3965
        base = item->path + oldpathlen;
3966
        while (*base == G_DIR_SEPARATOR) base++;
3967
        if (*base == '\0')
3968
                new_itempath = g_strdup(newpath);
3969
        else
3970
                new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3971
                                           NULL);
3972
        g_free(item->path);
3973
        item->path = new_itempath;
3974

    
3975
        return FALSE;
3976
}