Statistics
| Revision:

root / libsylph / imap.c @ 3272

History | View | Annotate | Download (124 KB)

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

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

    
24
#include "defs.h"
25

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

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

    
56
#define IMAP4_PORT        143
57
#if USE_SSL
58
#define IMAPS_PORT        993
59
#endif
60

    
61
#define IMAP_COPY_LIMIT        200
62
#define IMAP_CMD_LIMIT        1000
63

    
64
#define QUOTE_IF_REQUIRED(out, str)                                        \
65
{                                                                        \
66
        if (!str || *str == '\0') {                                        \
67
                Xstrdup_a(out, "\"\"", return IMAP_ERROR);                \
68
        } else if (strpbrk(str, " \t(){}[]%&*\"\\") != NULL) {                \
69
                gchar *__tmp;                                                \
70
                gint len;                                                \
71
                const gchar *p;                                                \
72
                gchar *tp;                                                \
73
                                                                        \
74
                len = strlen(str) * 2 + 3;                                \
75
                Xalloca(__tmp, len, return IMAP_ERROR);                        \
76
                tp = __tmp;                                                \
77
                *tp++ = '\"';                                                \
78
                for (p = str; *p != '\0'; p++) {                        \
79
                        if (*p == '\"' || *p == '\\')                        \
80
                                *tp++ = '\\';                                \
81
                        *tp++ = *p;                                        \
82
                }                                                        \
83
                *tp++ = '\"';                                                \
84
                *tp = '\0';                                                \
85
                out = __tmp;                                                \
86
        } else {                                                        \
87
                Xstrdup_a(out, str, return IMAP_ERROR);                        \
88
        }                                                                \
89
}
90

    
91
typedef gint (*IMAPThreadFunc)                (IMAPSession        *session,
92
                                         gpointer         data);
93
typedef gint (*IMAPProgressFunc)        (IMAPSession        *session,
94
                                         gint                 count,
95
                                         gint                 total,
96
                                         gpointer         data);
97

    
98
typedef struct _IMAPRealSession
99
{
100
        IMAPSession imap_session;
101
#if USE_THREADS
102
        GThreadPool *pool;
103
        IMAPThreadFunc thread_func;
104
        gpointer thread_data;
105
        gboolean is_running;
106
        gint prog_count;
107
        gint prog_total;
108
        gint flag;
109
        gint retval;
110
#endif
111
} IMAPRealSession;
112

    
113
static GList *session_list = NULL;
114

    
115
static void imap_folder_init                (Folder                *folder,
116
                                         const gchar        *name,
117
                                         const gchar        *path);
118

    
119
static Folder        *imap_folder_new        (const gchar        *name,
120
                                         const gchar        *path);
121
static void         imap_folder_destroy        (Folder                *folder);
122

    
123
static Session *imap_session_new        (PrefsAccount        *account);
124
static gint imap_session_connect        (IMAPSession        *session);
125
static gint imap_session_reconnect        (IMAPSession        *session);
126
static void imap_session_destroy        (Session        *session);
127
/* static void imap_session_destroy_all        (void); */
128

    
129
static gint imap_search_flags                (IMAPSession        *session,
130
                                         GArray               **uids,
131
                                         GHashTable    **flags_table);
132
static gint imap_fetch_flags                (IMAPSession        *session,
133
                                         GArray               **uids,
134
                                         GHashTable    **flags_table);
135

    
136
static GSList *imap_get_msg_list        (Folder                *folder,
137
                                         FolderItem        *item,
138
                                         gboolean         use_cache);
139
static GSList *imap_get_uncached_msg_list
140
                                        (Folder                *folder,
141
                                         FolderItem        *item);
142
static gchar *imap_fetch_msg                (Folder                *folder,
143
                                         FolderItem        *item,
144
                                         gint                 uid);
145
static MsgInfo *imap_get_msginfo        (Folder                *folder,
146
                                         FolderItem        *item,
147
                                         gint                 uid);
148
static gint imap_add_msg                (Folder                *folder,
149
                                         FolderItem        *dest,
150
                                         const gchar        *file,
151
                                         MsgFlags        *flags,
152
                                         gboolean         remove_source);
153
static gint imap_add_msgs                (Folder                *folder,
154
                                         FolderItem        *dest,
155
                                         GSList                *file_list,
156
                                         gboolean         remove_source,
157
                                         gint                *first);
158
static gint imap_add_msg_msginfo        (Folder                *folder,
159
                                         FolderItem        *dest,
160
                                         MsgInfo        *msginfo,
161
                                         gboolean         remove_source);
162
static gint imap_add_msgs_msginfo        (Folder                *folder,
163
                                         FolderItem        *dest,
164
                                         GSList                *msglist,
165
                                         gboolean         remove_source,
166
                                         gint                *first);
167

    
168
static gint imap_move_msg                (Folder                *folder,
169
                                         FolderItem        *dest,
170
                                         MsgInfo        *msginfo);
171
static gint imap_move_msgs                (Folder                *folder,
172
                                         FolderItem        *dest,
173
                                         GSList                *msglist);
174
static gint imap_copy_msg                (Folder                *folder,
175
                                         FolderItem        *dest,
176
                                         MsgInfo        *msginfo);
177
static gint imap_copy_msgs                (Folder                *folder,
178
                                         FolderItem        *dest,
179
                                         GSList                *msglist);
180

    
181
static gint imap_remove_msg                (Folder                *folder,
182
                                         FolderItem        *item,
183
                                         MsgInfo        *msginfo);
184
static gint imap_remove_msgs                (Folder                *folder,
185
                                         FolderItem        *item,
186
                                         GSList                *msglist);
187
static gint imap_remove_all_msg                (Folder                *folder,
188
                                         FolderItem        *item);
189

    
190
static gboolean imap_is_msg_changed        (Folder                *folder,
191
                                         FolderItem        *item,
192
                                         MsgInfo        *msginfo);
193

    
194
static gint imap_close                        (Folder                *folder,
195
                                         FolderItem        *item);
196

    
197
static gint imap_scan_folder                (Folder                *folder,
198
                                         FolderItem        *item);
199
static gint imap_scan_tree                (Folder                *folder);
200

    
201
static gint imap_create_tree                (Folder                *folder);
202

    
203
static FolderItem *imap_create_folder        (Folder                *folder,
204
                                         FolderItem        *parent,
205
                                         const gchar        *name);
206
static gint imap_rename_folder                (Folder                *folder,
207
                                         FolderItem        *item,
208
                                         const gchar        *name);
209
static gint imap_move_folder                (Folder                *folder,
210
                                         FolderItem        *item,
211
                                         FolderItem        *new_parent);
212
static gint imap_remove_folder                (Folder                *folder,
213
                                         FolderItem        *item);
214

    
215
static IMAPSession *imap_session_get        (Folder                *folder);
216

    
217
static gint imap_greeting                (IMAPSession        *session);
218
static gint imap_auth                        (IMAPSession        *session,
219
                                         const gchar        *user,
220
                                         const gchar        *pass,
221
                                         IMAPAuthType         type);
222

    
223
static gint imap_scan_tree_recursive        (IMAPSession        *session,
224
                                         FolderItem        *item,
225
                                         GSList                *item_list);
226
static GSList *imap_get_folder_list        (IMAPSession        *session,
227
                                         FolderItem        *item);
228
static GSList *imap_parse_list                (IMAPSession        *session,
229
                                         const gchar        *real_path,
230
                                         gchar                *separator);
231
static GSList *imap_add_inter_folders        (GSList                *item_list,
232
                                         const gchar        *root_path);
233
static GSList *imap_get_part_folder_list(GSList                *item_list,
234
                                         FolderItem        *item);
235

    
236
static void imap_create_missing_folders        (Folder                        *folder);
237
static FolderItem *imap_create_special_folder
238
                                        (Folder                        *folder,
239
                                         SpecialFolderItemType         stype,
240
                                         const gchar                *name);
241

    
242
static gint imap_do_copy_msgs                (Folder                *folder,
243
                                         FolderItem        *dest, 
244
                                         GSList                *msglist,
245
                                         gboolean         remove_source);
246
static gint imap_remove_msgs_by_seq_set        (Folder                *folder,
247
                                         FolderItem        *item,
248
                                         GSList                *seq_list);
249

    
250
static GSList *imap_get_uncached_messages        (IMAPSession        *session,
251
                                                 FolderItem        *item,
252
                                                 guint32         first_uid,
253
                                                 guint32         last_uid,
254
                                                 gint                 exists,
255
                                                 gboolean         update_count);
256
static void imap_delete_cached_message                (FolderItem        *item,
257
                                                 guint32         uid);
258
static GSList *imap_delete_cached_messages        (GSList                *mlist,
259
                                                 FolderItem        *item,
260
                                                 guint32         first_uid,
261
                                                 guint32         last_uid);
262
static void imap_delete_all_cached_messages        (FolderItem        *item);
263

    
264
#if USE_SSL
265
static SockInfo *imap_open                (const gchar        *server,
266
                                         gushort         port,
267
                                         SocksInfo        *socks_info,
268
                                         SSLType         ssl_type);
269
#else
270
static SockInfo *imap_open                (const gchar        *server,
271
                                         gushort         port,
272
                                         SocksInfo        *socks_info);
273
#endif
274

    
275
static gint imap_msg_list_change_perm_flags        (GSList                *msglist,
276
                                                 MsgPermFlags         flags,
277
                                                 gboolean         is_set);
278
static gchar *imap_get_flag_str                        (IMAPFlags         flags);
279
static gint imap_set_message_flags                (IMAPSession        *session,
280
                                                 const gchar        *seq_set,
281
                                                 IMAPFlags         flags,
282
                                                 gboolean         is_set);
283
static gint imap_select                                (IMAPSession        *session,
284
                                                 IMAPFolder        *folder,
285
                                                 const gchar        *path,
286
                                                 gint                *exists,
287
                                                 gint                *recent,
288
                                                 gint                *unseen,
289
                                                 guint32        *uid_validity);
290
static gint imap_status                                (IMAPSession        *session,
291
                                                 IMAPFolder        *folder,
292
                                                 const gchar        *path,
293
                                                 gint                *messages,
294
                                                 gint                *recent,
295
                                                 guint32        *uid_next,
296
                                                 guint32        *uid_validity,
297
                                                 gint                *unseen);
298

    
299
static void imap_parse_namespace                (IMAPSession        *session,
300
                                                 IMAPFolder        *folder);
301
static void imap_get_namespace_by_list                (IMAPSession        *session,
302
                                                 IMAPFolder        *folder);
303
static IMAPNameSpace *imap_find_namespace        (IMAPFolder        *folder,
304
                                                 const gchar        *path);
305
static gchar imap_get_path_separator                (IMAPFolder        *folder,
306
                                                 const gchar        *path);
307
static gchar *imap_get_real_path                (IMAPFolder        *folder,
308
                                                 const gchar        *path);
309

    
310
static gchar *imap_parse_atom                (IMAPSession        *session,
311
                                         gchar                *src,
312
                                         gchar                *dest,
313
                                         gint                 dest_len,
314
                                         GString        *str);
315
static MsgFlags imap_parse_flags        (const gchar        *flag_str);
316
static IMAPFlags imap_parse_imap_flags        (const gchar        *flag_str);
317
static MsgInfo *imap_parse_envelope        (IMAPSession        *session,
318
                                         FolderItem        *item,
319
                                         GString        *line_str);
320

    
321
static gboolean imap_has_capability        (IMAPSession        *session,
322
                                         const gchar        *capability);
323
static void imap_capability_free        (IMAPSession        *session);
324

    
325
/* low-level IMAP4rev1 commands */
326
static gint imap_cmd_capability        (IMAPSession        *session);
327
static gint imap_cmd_authenticate
328
                                (IMAPSession        *session,
329
                                 const gchar        *user,
330
                                 const gchar        *pass,
331
                                 IMAPAuthType         type);
332
static gint imap_cmd_login        (IMAPSession        *session,
333
                                 const gchar        *user,
334
                                 const gchar        *pass);
335
static gint imap_cmd_logout        (IMAPSession        *session);
336
static gint imap_cmd_noop        (IMAPSession        *session);
337
#if USE_SSL
338
static gint imap_cmd_starttls        (IMAPSession        *session);
339
#endif
340
static gint imap_cmd_namespace        (IMAPSession        *session,
341
                                 gchar               **ns_str);
342
static gint imap_cmd_list        (IMAPSession        *session,
343
                                 const gchar        *ref,
344
                                 const gchar        *mailbox,
345
                                 GPtrArray        *argbuf);
346
static gint imap_cmd_do_select        (IMAPSession        *session,
347
                                 const gchar        *folder,
348
                                 gboolean         examine,
349
                                 gint                *exists,
350
                                 gint                *recent,
351
                                 gint                *unseen,
352
                                 guint32        *uid_validity);
353
static gint imap_cmd_select        (IMAPSession        *session,
354
                                 const gchar        *folder,
355
                                 gint                *exists,
356
                                 gint                *recent,
357
                                 gint                *unseen,
358
                                 guint32        *uid_validity);
359
static gint imap_cmd_examine        (IMAPSession        *session,
360
                                 const gchar        *folder,
361
                                 gint                *exists,
362
                                 gint                *recent,
363
                                 gint                *unseen,
364
                                 guint32        *uid_validity);
365
static gint imap_cmd_create        (IMAPSession        *session,
366
                                 const gchar        *folder);
367
static gint imap_cmd_rename        (IMAPSession        *session,
368
                                 const gchar        *oldfolder,
369
                                 const gchar        *newfolder);
370
static gint imap_cmd_delete        (IMAPSession        *session,
371
                                 const gchar        *folder);
372
static gint imap_cmd_envelope        (IMAPSession        *session,
373
                                 const gchar        *seq_set);
374
static gint imap_cmd_search        (IMAPSession        *session,
375
                                 const gchar        *criteria,
376
                                 GArray        **result);
377
static gint imap_cmd_fetch        (IMAPSession        *session,
378
                                 guint32         uid,
379
                                 const gchar        *filename);
380
static gint imap_cmd_append        (IMAPSession        *session,
381
                                 const gchar        *destfolder,
382
                                 const gchar        *file,
383
                                 IMAPFlags         flags,
384
                                 guint32        *new_uid);
385
static gint imap_cmd_copy        (IMAPSession        *session,
386
                                 const gchar        *seq_set,
387
                                 const gchar        *destfolder);
388
static gint imap_cmd_store        (IMAPSession        *session,
389
                                 const gchar        *seq_set,
390
                                 const gchar        *sub_cmd);
391
static gint imap_cmd_expunge        (IMAPSession        *session);
392
static gint imap_cmd_close        (IMAPSession        *session);
393

    
394
static gint imap_cmd_ok                (IMAPSession        *session,
395
                                 GPtrArray        *argbuf);
396
static gint imap_cmd_ok_real        (IMAPSession        *session,
397
                                 GPtrArray        *argbuf);
398
static gint imap_cmd_gen_send        (IMAPSession        *session,
399
                                 const gchar        *format, ...);
400
static gint imap_cmd_gen_recv        (IMAPSession        *session,
401
                                 gchar               **ret);
402

    
403
static gint imap_cmd_gen_recv_silent        (IMAPSession        *session,
404
                                         gchar               **ret);
405

    
406
/* misc utility functions */
407
static gchar *strchr_cpy                        (const gchar        *src,
408
                                                 gchar                 ch,
409
                                                 gchar                *dest,
410
                                                 gint                 len);
411
static gchar *get_quoted                        (const gchar        *src,
412
                                                 gchar                 ch,
413
                                                 gchar                *dest,
414
                                                 gint                 len);
415
static gchar *search_array_contain_str                (GPtrArray        *array,
416
                                                 gchar                *str);
417
static gchar *search_array_str                        (GPtrArray        *array,
418
                                                 gchar                *str);
419
static void imap_path_separator_subst                (gchar                *str,
420
                                                 gchar                 separator);
421

    
422
static gchar *imap_modified_utf7_to_utf8        (const gchar        *mutf7_str);
423
static gchar *imap_utf8_to_modified_utf7        (const gchar        *from);
424

    
425
static GSList *imap_get_seq_set_from_msglist        (GSList                *msglist,
426
                                                 gint                 limit);
427
static gint imap_seq_set_get_count                (const gchar        *seq_set);
428
static void imap_seq_set_free                        (GSList                *seq_list);
429

    
430
static GHashTable *imap_get_uid_table                (GArray                *array);
431

    
432
static gboolean imap_rename_folder_func                (GNode                *node,
433
                                                 gpointer         data);
434

    
435
#if USE_THREADS
436
static gint imap_thread_run                (IMAPSession                *session,
437
                                         IMAPThreadFunc                 func,
438
                                         gpointer                 data);
439
static gint imap_thread_run_progress        (IMAPSession                *session,
440
                                         IMAPThreadFunc                 func,
441
                                         IMAPProgressFunc         progress_func,
442
                                         gpointer                 data);
443
#endif
444

    
445
static FolderClass imap_class =
446
{
447
        F_IMAP,
448

    
449
        imap_folder_new,
450
        imap_folder_destroy,
451

    
452
        imap_scan_tree,
453
        imap_create_tree,
454

    
455
        imap_get_msg_list,
456
        imap_get_uncached_msg_list,
457
        imap_fetch_msg,
458
        imap_get_msginfo,
459
        imap_add_msg,
460
        imap_add_msgs,
461
        imap_add_msg_msginfo,
462
        imap_add_msgs_msginfo,
463
        imap_move_msg,
464
        imap_move_msgs,
465
        imap_copy_msg,
466
        imap_copy_msgs,
467
        imap_remove_msg,
468
        imap_remove_msgs,
469
        imap_remove_all_msg,
470
        imap_is_msg_changed,
471
        imap_close,
472
        imap_scan_folder,
473

    
474
        imap_create_folder,
475
        imap_rename_folder,
476
        imap_move_folder,
477
        imap_remove_folder
478
};
479

    
480

    
481
FolderClass *imap_get_class(void)
482
{
483
        return &imap_class;
484
}
485

    
486
static Folder *imap_folder_new(const gchar *name, const gchar *path)
487
{
488
        Folder *folder;
489

    
490
        folder = (Folder *)g_new0(IMAPFolder, 1);
491
        imap_folder_init(folder, name, path);
492

    
493
        return folder;
494
}
495

    
496
static void imap_folder_destroy(Folder *folder)
497
{
498
        g_return_if_fail(folder->account != NULL);
499

    
500
        if (REMOTE_FOLDER(folder)->remove_cache_on_destroy) {
501
                gchar *dir;
502
                gchar *server;
503

    
504
                dir = folder_get_path(folder);
505
                if (is_dir_exist(dir))
506
                        remove_dir_recursive(dir);
507
                g_free(dir);
508

    
509
                server = uriencode_for_filename(folder->account->recv_server);
510
                dir = g_strconcat(get_imap_cache_dir(), G_DIR_SEPARATOR_S,
511
                                  server, NULL);
512
                if (is_dir_exist(dir))
513
                        g_rmdir(dir);
514
                g_free(dir);
515
                g_free(server);
516
        }
517

    
518
        folder_remote_folder_destroy(REMOTE_FOLDER(folder));
519
}
520

    
521
static void imap_folder_init(Folder *folder, const gchar *name,
522
                             const gchar *path)
523
{
524
        folder->klass = imap_get_class();
525
        folder_remote_folder_init(folder, name, path);
526
}
527

    
528
static IMAPSession *imap_session_get(Folder *folder)
529
{
530
        RemoteFolder *rfolder = REMOTE_FOLDER(folder);
531
        gint ret;
532

    
533
        g_return_val_if_fail(folder != NULL, NULL);
534
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
535
        g_return_val_if_fail(folder->account != NULL, NULL);
536

    
537
        if (!prefs_common.online_mode)
538
                return NULL;
539

    
540
        if (!rfolder->session) {
541
                rfolder->session = imap_session_new(folder->account);
542
                if (rfolder->session)
543
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
544
                                             IMAP_FOLDER(folder));
545
                return IMAP_SESSION(rfolder->session);
546
        }
547

    
548
        if (imap_is_session_active(IMAP_FOLDER(folder))) {
549
                g_warning("imap_session_get: session is busy.");
550
                return NULL;
551
        }
552

    
553
        if (time(NULL) - rfolder->session->last_access_time <
554
                SESSION_TIMEOUT_INTERVAL) {
555
                return IMAP_SESSION(rfolder->session);
556
        }
557

    
558
        if ((ret = imap_cmd_noop(IMAP_SESSION(rfolder->session))) != IMAP_SUCCESS) {
559
                if (ret == IMAP_EAGAIN) {
560
                        g_warning("imap_session_get: session is busy.");
561
                        return NULL;
562
                }
563

    
564
                log_warning(_("IMAP4 connection to %s has been"
565
                              " disconnected. Reconnecting...\n"),
566
                            folder->account->recv_server);
567
                if (imap_session_reconnect(IMAP_SESSION(rfolder->session))
568
                    == IMAP_SUCCESS)
569
                        imap_parse_namespace(IMAP_SESSION(rfolder->session),
570
                                             IMAP_FOLDER(folder));
571
                else {
572
                        session_destroy(rfolder->session);
573
                        rfolder->session = NULL;
574
                }
575
        }
576

    
577
        return IMAP_SESSION(rfolder->session);
578
}
579

    
580
static gint imap_greeting(IMAPSession *session)
581
{
582
        gchar *greeting;
583
        gint ok;
584

    
585
        if ((ok = imap_cmd_gen_recv(session, &greeting)) != IMAP_SUCCESS) {
586
                log_warning("Cannot get greeting message (%d)\n", ok);
587
                return ok;
588
        }
589

    
590
        if (greeting[0] != '*' || greeting[1] != ' ')
591
                ok = IMAP_ERROR;
592
        else if (!strncmp(greeting + 2, "OK", 2))
593
                ok = IMAP_SUCCESS;
594
        else if (!strncmp(greeting + 2, "PREAUTH", 7)) {
595
                session->authenticated = TRUE;
596
                ok = IMAP_SUCCESS;
597
        } else
598
                ok = IMAP_ERROR;
599

    
600
        g_free(greeting);
601
        return ok;
602
}
603

    
604
static gint imap_auth(IMAPSession *session, const gchar *user,
605
                      const gchar *pass, IMAPAuthType type)
606
{
607
        gboolean nologin;
608
        gint ok = IMAP_AUTHFAIL;
609

    
610
        nologin = imap_has_capability(session, "LOGINDISABLED");
611

    
612
        switch (type) {
613
        case 0:
614
                if (imap_has_capability(session, "AUTH=CRAM-MD5"))
615
                        ok = imap_cmd_authenticate(session, user, pass,
616
                                                   IMAP_AUTH_CRAM_MD5);
617
                else if (imap_has_capability(session, "AUTH=PLAIN"))
618
                        ok = imap_cmd_authenticate(session, user, pass,
619
                                                   IMAP_AUTH_PLAIN);
620
                else if (nologin)
621
                        log_print(_("IMAP4 server disables LOGIN.\n"));
622
                else
623
                        ok = imap_cmd_login(session, user, pass);
624
                break;
625
        case IMAP_AUTH_LOGIN:
626
                if (nologin)
627
                        log_warning(_("IMAP4 server disables LOGIN.\n"));
628
                else
629
                        ok = imap_cmd_login(session, user, pass);
630
                break;
631
        case IMAP_AUTH_CRAM_MD5:
632
        case IMAP_AUTH_PLAIN:
633
                ok = imap_cmd_authenticate(session, user, pass, type);
634
                break;
635
        default:
636
                break;
637
        }
638

    
639
        if (ok == IMAP_SUCCESS)
640
                session->authenticated = TRUE;
641

    
642
        return ok;
643
}
644

    
645
static Session *imap_session_new(PrefsAccount *account)
646
{
647
        IMAPSession *session;
648
        gushort port;
649

    
650
        g_return_val_if_fail(account != NULL, NULL);
651
        g_return_val_if_fail(account->recv_server != NULL, NULL);
652
        g_return_val_if_fail(account->userid != NULL, NULL);
653

    
654
#if USE_SSL
655
        port = account->set_imapport ? account->imapport
656
                : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
657
#else
658
        port = account->set_imapport ? account->imapport : IMAP4_PORT;
659
#endif
660

    
661
        session = IMAP_SESSION(g_new0(IMAPRealSession, 1));
662

    
663
        session_init(SESSION(session));
664

    
665
        SESSION(session)->type             = SESSION_IMAP;
666
        SESSION(session)->sock             = NULL;
667
        SESSION(session)->server           = g_strdup(account->recv_server);
668
        SESSION(session)->port             = port;
669
#if USE_SSL
670
        SESSION(session)->ssl_type         = account->ssl_imap;
671
#endif
672
        SESSION(session)->last_access_time = time(NULL);
673
        SESSION(session)->data             = account;
674

    
675
        SESSION(session)->destroy          = imap_session_destroy;
676

    
677
        session->authenticated = FALSE;
678
        session->capability    = NULL;
679
        session->uidplus       = FALSE;
680
        session->mbox          = NULL;
681
        session->cmd_count     = 0;
682

    
683
        session_list = g_list_append(session_list, session);
684

    
685
        if (imap_session_connect(session) != IMAP_SUCCESS) {
686
                log_warning(_("Could not establish IMAP connection.\n"));
687
                session_destroy(SESSION(session));
688
                return NULL;
689
        }
690

    
691
        return SESSION(session);
692
}
693

    
694
static gint imap_session_connect(IMAPSession *session)
695
{
696
        SockInfo *sock;
697
        SocksInfo *socks_info = NULL;
698
        PrefsAccount *account;
699
        const gchar *pass;
700

    
701
        g_return_val_if_fail(session != NULL, IMAP_ERROR);
702

    
703
        account = (PrefsAccount *)(SESSION(session)->data);
704

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

    
708
        pass = account->passwd;
709
        if (!pass)
710
                pass = account->tmp_pass;
711
        if (!pass) {
712
                gchar *tmp_pass;
713

    
714
                tmp_pass = input_query_password(account->recv_server,
715
                                                account->userid);
716
                if (!tmp_pass)
717
                        return IMAP_ERROR;
718

    
719
                account->tmp_pass = tmp_pass;
720
                pass = account->tmp_pass;
721
        }
722

    
723
        if (account->use_socks && account->use_socks_for_recv &&
724
            account->proxy_host) {
725
                socks_info = socks_info_new(account->socks_type, account->proxy_host, account->proxy_port, account->use_proxy_auth ? account->proxy_name : NULL, account->use_proxy_auth ? account->proxy_pass : NULL);
726
        }
727

    
728
#if USE_SSL
729
        if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port,
730
                              socks_info, SESSION(session)->ssl_type)) == NULL)
731
#else
732
        if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port,
733
                              socks_info))
734
            == NULL)
735
#endif
736
                return IMAP_ERROR;
737

    
738
        if (socks_info)
739
                socks_info_free(socks_info);
740

    
741
        SESSION(session)->sock = sock;
742

    
743
        if (imap_greeting(session) != IMAP_SUCCESS)
744
                return IMAP_ERROR;
745
        if (imap_cmd_capability(session) != IMAP_SUCCESS)
746
                return IMAP_ERROR;
747

    
748
        if (imap_has_capability(session, "UIDPLUS"))
749
                session->uidplus = TRUE;
750

    
751
#if USE_SSL
752
        if (account->ssl_imap == SSL_STARTTLS &&
753
            imap_has_capability(session, "STARTTLS")) {
754
                gint ok;
755

    
756
                ok = imap_cmd_starttls(session);
757
                if (ok != IMAP_SUCCESS) {
758
                        log_warning(_("Can't start TLS session.\n"));
759
                        return IMAP_ERROR;
760
                }
761
                if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1))
762
                        return IMAP_SOCKET;
763

    
764
                /* capability can be changed after STARTTLS */
765
                if (imap_cmd_capability(session) != IMAP_SUCCESS)
766
                        return IMAP_ERROR;
767
        }
768
#endif
769

    
770
        if (!session->authenticated &&
771
            imap_auth(session, account->userid, pass, account->imap_auth_type)
772
            != IMAP_SUCCESS) {
773
                if (account->tmp_pass) {
774
                        g_free(account->tmp_pass);
775
                        account->tmp_pass = NULL;
776
                }
777
                imap_cmd_logout(session);
778
                return IMAP_AUTHFAIL;
779
        }
780

    
781
        return IMAP_SUCCESS;
782
}
783

    
784
static gint imap_session_reconnect(IMAPSession *session)
785
{
786
        g_return_val_if_fail(session != NULL, IMAP_ERROR);
787

    
788
        session_disconnect(SESSION(session));
789

    
790
        imap_capability_free(session);
791
        session->uidplus = FALSE;
792
        g_free(session->mbox);
793
        session->mbox = NULL;
794
        session->authenticated = FALSE;
795
        SESSION(session)->state = SESSION_READY;
796

    
797
        return imap_session_connect(session);
798
}
799

    
800
static void imap_session_destroy(Session *session)
801
{
802
#if USE_THREADS
803
        IMAPRealSession *real = (IMAPRealSession *)session;
804

    
805
        if (real->pool)
806
                g_thread_pool_free(real->pool, TRUE, TRUE);
807
#endif
808
        imap_capability_free(IMAP_SESSION(session));
809
        g_free(IMAP_SESSION(session)->mbox);
810
        session_list = g_list_remove(session_list, session);
811
}
812

    
813
#if 0
814
static void imap_session_destroy_all(void)
815
{
816
        while (session_list != NULL) {
817
                IMAPSession *session = (IMAPSession *)session_list->data;
818

819
                imap_cmd_logout(session);
820
                session_destroy(SESSION(session));
821
        }
822
}
823
#endif
824

    
825
#define THROW goto catch
826

    
827
static gint imap_search_flags(IMAPSession *session, GArray **uids,
828
                              GHashTable **flags_table)
829
{
830
        gint ok;
831
        gint i;
832
        GArray *flag_uids;
833
        GHashTable *unseen_table;
834
        GHashTable *flagged_table;
835
        GHashTable *answered_table;
836
        guint32 uid;
837
        IMAPFlags flags;
838

    
839
        ok = imap_cmd_search(session, "ALL", uids);
840
        if (ok != IMAP_SUCCESS) return ok;
841

    
842
        ok = imap_cmd_search(session, "UNSEEN", &flag_uids);
843
        if (ok != IMAP_SUCCESS) {
844
                g_array_free(*uids, TRUE);
845
                return ok;
846
        }
847
        unseen_table = imap_get_uid_table(flag_uids);
848
        g_array_free(flag_uids, TRUE);
849
        ok = imap_cmd_search(session, "FLAGGED", &flag_uids);
850
        if (ok != IMAP_SUCCESS) {
851
                g_hash_table_destroy(unseen_table);
852
                g_array_free(*uids, TRUE);
853
                return ok;
854
        }
855
        flagged_table = imap_get_uid_table(flag_uids);
856
        g_array_free(flag_uids, TRUE);
857
        ok = imap_cmd_search(session, "ANSWERED", &flag_uids);
858
        if (ok != IMAP_SUCCESS) {
859
                g_hash_table_destroy(flagged_table);
860
                g_hash_table_destroy(unseen_table);
861
                g_array_free(*uids, TRUE);
862
                return ok;
863
        }
864
        answered_table = imap_get_uid_table(flag_uids);
865
        g_array_free(flag_uids, TRUE);
866

    
867
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
868

    
869
        for (i = 0; i < (*uids)->len; i++) {
870
                uid = g_array_index(*uids, guint32, i);
871
                flags = IMAP_FLAG_DRAFT;
872
                if (!g_hash_table_lookup(unseen_table, GUINT_TO_POINTER(uid)))
873
                        flags |= IMAP_FLAG_SEEN;
874
                if (g_hash_table_lookup(flagged_table, GUINT_TO_POINTER(uid)))
875
                        flags |= IMAP_FLAG_FLAGGED;
876
                if (g_hash_table_lookup(answered_table, GUINT_TO_POINTER(uid)))
877
                        flags |= IMAP_FLAG_ANSWERED;
878
                g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
879
                                    GINT_TO_POINTER(flags));
880
        }
881

    
882
        g_hash_table_destroy(answered_table);
883
        g_hash_table_destroy(flagged_table);
884
        g_hash_table_destroy(unseen_table);
885

    
886
        return IMAP_SUCCESS;
887
}
888

    
889
static gint imap_fetch_flags(IMAPSession *session, GArray **uids,
890
                             GHashTable **flags_table)
891
{
892
        gint ok;
893
        gchar *tmp;
894
        gchar *cur_pos;
895
        gchar buf[IMAPBUFSIZE];
896
        guint32 uid;
897
        IMAPFlags flags;
898

    
899
        if (imap_cmd_gen_send(session, "UID FETCH 1:* (UID FLAGS)") != IMAP_SUCCESS)
900
                return IMAP_ERROR;
901

    
902
        *uids = g_array_new(FALSE, FALSE, sizeof(guint32));
903
        *flags_table = g_hash_table_new(NULL, g_direct_equal);
904

    
905
        log_print("IMAP4< %s\n", _("(retrieving FLAGS...)"));
906

    
907
        while ((ok = imap_cmd_gen_recv_silent(session, &tmp)) == IMAP_SUCCESS) {
908
                if (tmp[0] != '*' || tmp[1] != ' ') {
909
                        log_print("IMAP4< %s\n", tmp);
910
                        g_free(tmp);
911
                        break;
912
                }
913
                cur_pos = tmp + 2;
914

    
915
#define PARSE_ONE_ELEMENT(ch)                                        \
916
{                                                                \
917
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
918
        if (cur_pos == NULL) {                                        \
919
                g_warning("cur_pos == NULL\n");                        \
920
                g_free(tmp);                                        \
921
                g_hash_table_destroy(*flags_table);                \
922
                g_array_free(*uids, TRUE);                        \
923
                return IMAP_ERROR;                                \
924
        }                                                        \
925
}
926

    
927
                PARSE_ONE_ELEMENT(' ');
928
                PARSE_ONE_ELEMENT(' ');
929
                if (strcmp(buf, "FETCH") != 0) {
930
                        g_free(tmp);
931
                        continue;
932
                }
933
                if (*cur_pos != '(') {
934
                        g_free(tmp);
935
                        continue;
936
                }
937
                cur_pos++;
938
                uid = 0;
939
                flags = 0;
940

    
941
                while (*cur_pos != '\0' && *cur_pos != ')') {
942
                        while (*cur_pos == ' ') cur_pos++;
943

    
944
                        if (!strncmp(cur_pos, "UID ", 4)) {
945
                                cur_pos += 4;
946
                                uid = strtoul(cur_pos, &cur_pos, 10);
947
                        } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
948
                                cur_pos += 6;
949
                                if (*cur_pos != '(') {
950
                                        g_warning("*cur_pos != '('\n");
951
                                        break;
952
                                }
953
                                cur_pos++;
954
                                PARSE_ONE_ELEMENT(')');
955
                                flags = imap_parse_imap_flags(buf);
956
                                flags |= IMAP_FLAG_DRAFT;
957
                        } else {
958
                                g_warning("invalid FETCH response: %s\n", cur_pos);
959
                                break;
960
                        }
961
                }
962

    
963
#undef PARSE_ONE_ELEMENT
964

    
965
                if (uid > 0) {
966
                        g_array_append_val(*uids, uid);
967
                        g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
968
                                            GINT_TO_POINTER(flags));
969
                }
970

    
971
                g_free(tmp);
972
        }
973

    
974
        if (ok != IMAP_SUCCESS) {
975
                g_hash_table_destroy(*flags_table);
976
                g_array_free(*uids, TRUE);
977
        }
978

    
979
        return ok;
980
}
981

    
982
static GSList *imap_get_msg_list_full(Folder *folder, FolderItem *item,
983
                                      gboolean use_cache,
984
                                      gboolean uncached_only)
985
{
986
        GSList *mlist = NULL;
987
        IMAPSession *session;
988
        gint ok, exists = 0, recent = 0, unseen = 0;
989
        guint32 uid_validity = 0;
990
        guint32 first_uid = 0, last_uid = 0;
991
        GSList *newlist = NULL;
992

    
993
        g_return_val_if_fail(folder != NULL, NULL);
994
        g_return_val_if_fail(item != NULL, NULL);
995
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
996
        g_return_val_if_fail(folder->account != NULL, NULL);
997

    
998
        item->new = item->unread = item->total = 0;
999

    
1000
        session = imap_session_get(folder);
1001

    
1002
        if (!session) {
1003
                if (uncached_only)
1004
                        return NULL;
1005
                mlist = procmsg_read_cache(item, FALSE);
1006
                item->last_num = procmsg_get_last_num_in_msg_list(mlist);
1007
                procmsg_set_flags(mlist, item);
1008
                return mlist;
1009
        }
1010

    
1011
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1012
                         &exists, &recent, &unseen, &uid_validity);
1013
        if (ok != IMAP_SUCCESS) THROW;
1014

    
1015
        if (exists == 0) {
1016
                imap_delete_all_cached_messages(item);
1017
                return NULL;
1018
        }
1019

    
1020
        /* invalidate current cache if UIDVALIDITY has been changed */
1021
        if (item->mtime != uid_validity) {
1022
                debug_print("imap_get_msg_list: "
1023
                            "UIDVALIDITY has been changed.\n");
1024
                use_cache = FALSE;
1025
        }
1026

    
1027
        if (use_cache) {
1028
                GArray *uids;
1029
                GHashTable *msg_table;
1030
                GHashTable *flags_table;
1031
                guint32 cache_last;
1032
                guint32 begin = 0;
1033
                GSList *cur, *next = NULL;
1034
                MsgInfo *msginfo;
1035
                IMAPFlags imap_flags;
1036
                guint color;
1037

    
1038
                /* get cache data */
1039
                mlist = procmsg_read_cache(item, FALSE);
1040
                procmsg_set_flags(mlist, item);
1041
                cache_last = procmsg_get_last_num_in_msg_list(mlist);
1042

    
1043
                /* get all UID list and flags */
1044
#if 0
1045
                ok = imap_search_flags(session, &uids, &flags_table);
1046
                if (ok != IMAP_SUCCESS) {
1047
                        if (ok == IMAP_SOCKET || ok == IMAP_IOERR) THROW;
1048
                        ok = imap_fetch_flags(session, &uids, &flags_table);
1049
                        if (ok != IMAP_SUCCESS) THROW;
1050
                }
1051
#else
1052
                ok = imap_fetch_flags(session, &uids, &flags_table);
1053
                if (ok != IMAP_SUCCESS) THROW;
1054
#endif
1055

    
1056
                if (uids->len > 0) {
1057
                        first_uid = g_array_index(uids, guint32, 0);
1058
                        last_uid = g_array_index(uids, guint32, uids->len - 1);
1059
                } else {
1060
                        g_array_free(uids, TRUE);
1061
                        g_hash_table_destroy(flags_table);
1062
                        THROW;
1063
                }
1064

    
1065
                /* sync message flags with server */
1066
                for (cur = mlist; cur != NULL; cur = next) {
1067
                        msginfo = (MsgInfo *)cur->data;
1068
                        next = cur->next;
1069
                        imap_flags = GPOINTER_TO_INT(g_hash_table_lookup
1070
                                (flags_table,
1071
                                 GUINT_TO_POINTER(msginfo->msgnum)));
1072

    
1073
                        if (imap_flags == 0) {
1074
                                debug_print("imap_get_msg_list: "
1075
                                            "message %u has been deleted.\n",
1076
                                            msginfo->msgnum);
1077
                                imap_delete_cached_message
1078
                                        (item, msginfo->msgnum);
1079
                                if (MSG_IS_NEW(msginfo->flags))
1080
                                        item->new--;
1081
                                if (MSG_IS_UNREAD(msginfo->flags))
1082
                                        item->unread--;
1083
                                item->total--;
1084
                                mlist = g_slist_remove(mlist, msginfo);
1085
                                procmsg_msginfo_free(msginfo);
1086
                                item->cache_dirty = TRUE;
1087
                                item->mark_dirty = TRUE;
1088
                                continue;
1089
                        }
1090

    
1091
                        if (!IMAP_IS_SEEN(imap_flags)) {
1092
                                if (!MSG_IS_UNREAD(msginfo->flags)) {
1093
                                        item->unread++;
1094
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
1095
                                                           MSG_UNREAD);
1096
                                        item->mark_dirty = TRUE;
1097
                                }
1098
                        } else {
1099
                                if (MSG_IS_NEW(msginfo->flags)) {
1100
                                        item->new--;
1101
                                        item->mark_dirty = TRUE;
1102
                                }
1103
                                if (MSG_IS_UNREAD(msginfo->flags)) {
1104
                                        item->unread--;
1105
                                        item->mark_dirty = TRUE;
1106
                                }
1107
                                MSG_UNSET_PERM_FLAGS(msginfo->flags,
1108
                                                     MSG_NEW|MSG_UNREAD);
1109
                        }
1110

    
1111
                        if (IMAP_IS_FLAGGED(imap_flags)) {
1112
                                if (!MSG_IS_MARKED(msginfo->flags)) {
1113
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
1114
                                                           MSG_MARKED);
1115
                                        item->mark_dirty = TRUE;
1116
                                }
1117
                        } else {
1118
                                if (MSG_IS_MARKED(msginfo->flags)) {
1119
                                        MSG_UNSET_PERM_FLAGS(msginfo->flags,
1120
                                                             MSG_MARKED);
1121
                                        item->mark_dirty = TRUE;
1122
                                }
1123
                        }
1124
                        if (IMAP_IS_ANSWERED(imap_flags)) {
1125
                                if (!MSG_IS_REPLIED(msginfo->flags)) {
1126
                                        MSG_SET_PERM_FLAGS(msginfo->flags,
1127
                                                           MSG_REPLIED);
1128
                                        item->mark_dirty = TRUE;
1129
                                }
1130
                        } else {
1131
                                if (MSG_IS_REPLIED(msginfo->flags)) {
1132
                                        MSG_UNSET_PERM_FLAGS(msginfo->flags,
1133
                                                             MSG_REPLIED);
1134
                                        item->mark_dirty = TRUE;
1135
                                }
1136
                        }
1137

    
1138
                        color = IMAP_GET_COLORLABEL_VALUE(imap_flags);
1139
                        if (MSG_GET_COLORLABEL_VALUE(msginfo->flags) != color) {
1140
                                MSG_UNSET_PERM_FLAGS(msginfo->flags,
1141
                                                     MSG_CLABEL_FLAG_MASK);
1142
                                MSG_SET_COLORLABEL_VALUE(msginfo->flags, color);
1143
                                item->mark_dirty = TRUE;
1144
                        }
1145
                }
1146

    
1147
                /* check for the first new message */
1148
                msg_table = procmsg_msg_hash_table_create(mlist);
1149
                if (msg_table == NULL)
1150
                        begin = first_uid;
1151
                else {
1152
                        gint i;
1153

    
1154
                        for (i = 0; i < uids->len; i++) {
1155
                                guint32 uid;
1156

    
1157
                                uid = g_array_index(uids, guint32, i);
1158
                                if (g_hash_table_lookup
1159
                                        (msg_table, GUINT_TO_POINTER(uid))
1160
                                        == NULL) {
1161
                                        debug_print("imap_get_msg_list: "
1162
                                                    "first new UID: %u\n", uid);
1163
                                        begin = uid;
1164
                                        break;
1165
                                }
1166
                        }
1167
                        g_hash_table_destroy(msg_table);
1168
                }
1169

    
1170
                g_array_free(uids, TRUE);
1171
                g_hash_table_destroy(flags_table);
1172

    
1173
                /* remove ununsed caches */
1174
                if (first_uid > 0 && last_uid > 0) {
1175
                        mlist = imap_delete_cached_messages
1176
                                (mlist, item, 0, first_uid - 1);
1177
                        mlist = imap_delete_cached_messages
1178
                                (mlist, item, begin > 0 ? begin : last_uid + 1,
1179
                                 UINT_MAX);
1180
                }
1181

    
1182
                if (begin > 0 && begin <= last_uid) {
1183
                        newlist = imap_get_uncached_messages
1184
                                (session, item, begin, last_uid,
1185
                                 exists - item->total, TRUE);
1186
                        if (newlist) {
1187
                                item->cache_dirty = TRUE;
1188
                                item->mark_dirty = TRUE;
1189
                        }
1190
                        mlist = g_slist_concat(mlist, newlist);
1191
                }
1192
        } else {
1193
                imap_delete_all_cached_messages(item);
1194
                mlist = imap_get_uncached_messages(session, item, 0, 0, exists,
1195
                                                   TRUE);
1196
                last_uid = procmsg_get_last_num_in_msg_list(mlist);
1197
                item->cache_dirty = TRUE;
1198
                item->mark_dirty = TRUE;
1199
                newlist = mlist;
1200
        }
1201

    
1202
        if (!uncached_only)
1203
                mlist = procmsg_sort_msg_list(mlist, item->sort_key,
1204
                                              item->sort_type);
1205

    
1206
        item->last_num = last_uid;
1207

    
1208
        if (item->mark_queue)
1209
                item->mark_dirty = TRUE;
1210

    
1211
        debug_print("cache_dirty: %d, mark_dirty: %d\n",
1212
                    item->cache_dirty, item->mark_dirty);
1213

    
1214
        if (!item->opened) {
1215
                item->mtime = uid_validity;
1216
                if (item->cache_dirty)
1217
                        procmsg_write_cache_list(item, mlist);
1218
                if (item->mark_dirty)
1219
                        procmsg_write_flags_list(item, mlist);
1220
        }
1221

    
1222
catch:
1223
        if (uncached_only) {
1224
                GSList *cur;
1225

    
1226
                if (newlist == NULL) {
1227
                        procmsg_msg_list_free(mlist);
1228
                        return NULL;
1229
                }
1230
                if (mlist == newlist)
1231
                        return newlist;
1232
                for (cur = mlist; cur != NULL; cur = cur->next) {
1233
                        if (cur->next == newlist) {
1234
                                cur->next = NULL;
1235
                                procmsg_msg_list_free(mlist);
1236
                                return newlist;
1237
                        }
1238
                }
1239
                procmsg_msg_list_free(mlist);
1240
                return NULL;
1241
        }
1242

    
1243
        return mlist;
1244
}
1245

    
1246
#undef THROW
1247

    
1248
static GSList *imap_get_msg_list(Folder *folder, FolderItem *item,
1249
                                 gboolean use_cache)
1250
{
1251
        return imap_get_msg_list_full(folder, item, use_cache, FALSE);
1252
}
1253

    
1254
static GSList *imap_get_uncached_msg_list(Folder *folder, FolderItem *item)
1255
{
1256
        return imap_get_msg_list_full(folder, item, TRUE, TRUE);
1257
}
1258

    
1259
static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1260
{
1261
        gchar *path, *filename;
1262
        IMAPSession *session;
1263
        gchar nstr[16];
1264
        guint32 uid32 = (guint32)uid;
1265
        gint ok;
1266

    
1267
        g_return_val_if_fail(folder != NULL, NULL);
1268
        g_return_val_if_fail(item != NULL, NULL);
1269

    
1270
        path = folder_item_get_path(item);
1271
        if (!is_dir_exist(path))
1272
                make_dir_hier(path);
1273
        g_snprintf(nstr, sizeof(nstr), "%u", uid32);
1274
        filename = g_strconcat(path, G_DIR_SEPARATOR_S, nstr, NULL);
1275
        g_free(path);
1276

    
1277
        if (is_file_exist(filename) && get_file_size(filename) > 0) {
1278
                debug_print("message %u has been already cached.\n", uid32);
1279
                return filename;
1280
        }
1281

    
1282
        session = imap_session_get(folder);
1283
        if (!session) {
1284
                g_free(filename);
1285
                return NULL;
1286
        }
1287

    
1288
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1289
                         NULL, NULL, NULL, NULL);
1290
        if (ok != IMAP_SUCCESS) {
1291
                g_warning("can't select mailbox %s\n", item->path);
1292
                g_free(filename);
1293
                return NULL;
1294
        }
1295

    
1296
        status_print(_("Getting message %u"), uid32);
1297
        debug_print("getting message %u...\n", uid32);
1298
        ok = imap_cmd_fetch(session, uid32, filename);
1299

    
1300
        if (ok != IMAP_SUCCESS) {
1301
                g_warning("can't fetch message %u\n", uid32);
1302
                g_free(filename);
1303
                return NULL;
1304
        }
1305

    
1306
        return filename;
1307
}
1308

    
1309
static MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
1310
{
1311
        IMAPSession *session;
1312
        GSList *list;
1313
        MsgInfo *msginfo = NULL;
1314
        gint ok;
1315

    
1316
        g_return_val_if_fail(folder != NULL, NULL);
1317
        g_return_val_if_fail(item != NULL, NULL);
1318

    
1319
        session = imap_session_get(folder);
1320
        g_return_val_if_fail(session != NULL, NULL);
1321

    
1322
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1323
                         NULL, NULL, NULL, NULL);
1324
        if (ok != IMAP_SUCCESS)
1325
                return NULL;
1326

    
1327
        list = imap_get_uncached_messages(session, item, uid, uid, 0, FALSE);
1328
        if (list) {
1329
                msginfo = (MsgInfo *)list->data;
1330
                list->data = NULL;
1331
        }
1332
        procmsg_msg_list_free(list);
1333

    
1334
        return msginfo;
1335
}
1336

    
1337
static gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1338
                         MsgFlags *flags, gboolean remove_source)
1339
{
1340
        GSList file_list;
1341
        MsgFileInfo fileinfo;
1342

    
1343
        g_return_val_if_fail(file != NULL, -1);
1344

    
1345
        fileinfo.file = (gchar *)file;
1346
        fileinfo.flags = flags;
1347
        file_list.data = &fileinfo;
1348
        file_list.next = NULL;
1349

    
1350
        return imap_add_msgs(folder, dest, &file_list, remove_source, NULL);
1351
}
1352

    
1353
static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1354
                          gboolean remove_source, gint *first)
1355
{
1356
        gchar *destdir;
1357
        IMAPSession *session;
1358
        gint messages, recent, unseen;
1359
        guint32 uid_next, uid_validity;
1360
        guint32 last_uid = 0;
1361
        GSList *cur;
1362
        MsgFileInfo *fileinfo;
1363
        gint count = 1;
1364
        gint total;
1365
        gint ok;
1366
        GTimeVal tv_prev, tv_cur;
1367

    
1368
        g_return_val_if_fail(folder != NULL, -1);
1369
        g_return_val_if_fail(dest != NULL, -1);
1370
        g_return_val_if_fail(file_list != NULL, -1);
1371

    
1372
        session = imap_session_get(folder);
1373
        if (!session) return -1;
1374

    
1375
        g_get_current_time(&tv_prev);
1376
        ui_update();
1377

    
1378
        ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
1379
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1380
        if (ok != IMAP_SUCCESS) {
1381
                g_warning("can't append messages\n");
1382
                return -1;
1383
        }
1384

    
1385
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1386

    
1387
        if (!session->uidplus)
1388
                last_uid = uid_next - 1;
1389
        if (first)
1390
                *first = uid_next;
1391

    
1392
        total = g_slist_length(file_list);
1393

    
1394
        for (cur = file_list; cur != NULL; cur = cur->next) {
1395
                IMAPFlags iflags = 0;
1396
                guint32 new_uid = 0;
1397

    
1398
                fileinfo = (MsgFileInfo *)cur->data;
1399

    
1400
                if (fileinfo->flags) {
1401
                        if (MSG_IS_MARKED(*fileinfo->flags))
1402
                                iflags |= IMAP_FLAG_FLAGGED;
1403
                        if (MSG_IS_REPLIED(*fileinfo->flags))
1404
                                iflags |= IMAP_FLAG_ANSWERED;
1405
                        if (!MSG_IS_UNREAD(*fileinfo->flags))
1406
                                iflags |= IMAP_FLAG_SEEN;
1407
                }
1408

    
1409
                if (dest->stype == F_OUTBOX ||
1410
                    dest->stype == F_QUEUE  ||
1411
                    dest->stype == F_DRAFT)
1412
                        iflags |= IMAP_FLAG_SEEN;
1413

    
1414
                g_get_current_time(&tv_cur);
1415
                if (tv_cur.tv_sec > tv_prev.tv_sec ||
1416
                    tv_cur.tv_usec - tv_prev.tv_usec >
1417
                    PROGRESS_UPDATE_INTERVAL * 1000) {
1418
                        status_print(_("Appending messages to %s (%d / %d)"),
1419
                                     dest->path, count, total);
1420
                        progress_show(count, total);
1421
                        ui_update();
1422
                        tv_prev = tv_cur;
1423
                }
1424
                ++count;
1425

    
1426
                ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1427
                                     &new_uid);
1428

    
1429
                if (ok != IMAP_SUCCESS) {
1430
                        g_warning("can't append message %s\n", fileinfo->file);
1431
                        g_free(destdir);
1432
                        progress_show(0, 0);
1433
                        return -1;
1434
                }
1435

    
1436
                if (syl_app_get())
1437
                        g_signal_emit_by_name(syl_app_get(), "add-msg", dest, fileinfo->file, new_uid);
1438

    
1439
                if (!session->uidplus)
1440
                        last_uid++;
1441
                else if (last_uid < new_uid)
1442
                        last_uid = new_uid;
1443

    
1444
                dest->last_num = last_uid;
1445
                dest->total++;
1446
                dest->updated = TRUE;
1447

    
1448
                if (fileinfo->flags) {
1449
                        if (MSG_IS_UNREAD(*fileinfo->flags))
1450
                                dest->unread++;
1451
                } else
1452
                        dest->unread++;
1453
        }
1454

    
1455
        progress_show(0, 0);
1456
        g_free(destdir);
1457

    
1458
        if (remove_source) {
1459
                for (cur = file_list; cur != NULL; cur = cur->next) {
1460
                        fileinfo = (MsgFileInfo *)cur->data;
1461
                        if (g_unlink(fileinfo->file) < 0)
1462
                                 FILE_OP_ERROR(fileinfo->file, "unlink");
1463
                }
1464
        }
1465

    
1466
        return last_uid;
1467
}
1468

    
1469
static gint imap_add_msg_msginfo(Folder *folder, FolderItem *dest,
1470
                                 MsgInfo *msginfo, gboolean remove_source)
1471
{
1472
        GSList msglist;
1473

    
1474
        g_return_val_if_fail(msginfo != NULL, -1);
1475

    
1476
        msglist.data = msginfo;
1477
        msglist.next = NULL;
1478

    
1479
        return imap_add_msgs_msginfo(folder, dest, &msglist, remove_source,
1480
                                     NULL);
1481
}
1482

    
1483
static gint imap_add_msgs_msginfo(Folder *folder, FolderItem *dest,
1484
                                  GSList *msglist, gboolean remove_source,
1485
                                  gint *first)
1486
{
1487
        GSList *file_list;
1488
        gint ret;
1489

    
1490
        file_list = procmsg_get_message_file_list(msglist);
1491
        g_return_val_if_fail(file_list != NULL, -1);
1492

    
1493
        ret = imap_add_msgs(folder, dest, file_list, remove_source, first);
1494

    
1495
        procmsg_message_file_list_free(file_list);
1496

    
1497
        return ret;
1498
}
1499

    
1500
static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist,
1501
                              gboolean remove_source)
1502
{
1503
        FolderItem *src;
1504
        gchar *destdir;
1505
        GSList *seq_list, *cur;
1506
        MsgInfo *msginfo;
1507
        IMAPSession *session;
1508
        gint count = 0, total;
1509
        gint ok = IMAP_SUCCESS;
1510

    
1511
        g_return_val_if_fail(folder != NULL, -1);
1512
        g_return_val_if_fail(dest != NULL, -1);
1513
        g_return_val_if_fail(msglist != NULL, -1);
1514

    
1515
        session = imap_session_get(folder);
1516
        if (!session) return -1;
1517

    
1518
        ui_update();
1519

    
1520
        msginfo = (MsgInfo *)msglist->data;
1521

    
1522
        src = msginfo->folder;
1523
        if (src == dest) {
1524
                g_warning("the src folder is identical to the dest.\n");
1525
                return -1;
1526
        }
1527

    
1528
        ok = imap_select(session, IMAP_FOLDER(folder), src->path,
1529
                         NULL, NULL, NULL, NULL);
1530
        if (ok != IMAP_SUCCESS)
1531
                return ok;
1532

    
1533
        destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1534

    
1535
        total = g_slist_length(msglist);
1536
        seq_list = imap_get_seq_set_from_msglist(msglist, IMAP_COPY_LIMIT);
1537

    
1538
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1539
                gchar *seq_set = (gchar *)cur->data;
1540

    
1541
                count += imap_seq_set_get_count(seq_set);
1542

    
1543
                if (remove_source) {
1544
                        status_print(_("Moving messages %s to %s ..."),
1545
                                     seq_set, dest->path);
1546
                        debug_print("Moving message %s/[%s] to %s ...\n",
1547
                                    src->path, seq_set, dest->path);
1548
                } else {
1549
                        status_print(_("Copying messages %s to %s ..."),
1550
                                     seq_set, dest->path);
1551
                        debug_print("Copying message %s/[%s] to %s ...\n",
1552
                                    src->path, seq_set, dest->path);
1553
                }
1554
                progress_show(count, total);
1555
                ui_update();
1556

    
1557
                ok = imap_cmd_copy(session, seq_set, destdir);
1558
                if (ok != IMAP_SUCCESS) {
1559
                        imap_seq_set_free(seq_list);
1560
                        progress_show(0, 0);
1561
                        return -1;
1562
                }
1563
        }
1564

    
1565
        progress_show(0, 0);
1566

    
1567
        dest->updated = TRUE;
1568

    
1569
        imap_seq_set_free(seq_list);
1570
        g_free(destdir);
1571

    
1572
        for (cur = msglist; cur != NULL; cur = cur->next) {
1573
                msginfo = (MsgInfo *)cur->data;
1574

    
1575
                if (syl_app_get())
1576
                        g_signal_emit_by_name(syl_app_get(), "add-msg", dest, NULL, 0);
1577

    
1578
                dest->total++;
1579
                if (MSG_IS_NEW(msginfo->flags))
1580
                        dest->new++;
1581
                if (MSG_IS_UNREAD(msginfo->flags))
1582
                        dest->unread++;
1583
        }
1584

    
1585
        if (remove_source) {
1586
                ok = imap_remove_msgs(folder, src, msglist);
1587
                if (ok != IMAP_SUCCESS)
1588
                        return ok;
1589
        }
1590

    
1591
        if (ok == IMAP_SUCCESS)
1592
                return 0;
1593
        else
1594
                return -1;
1595
}
1596

    
1597
static gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1598
{
1599
        GSList msglist;
1600

    
1601
        g_return_val_if_fail(msginfo != NULL, -1);
1602

    
1603
        msglist.data = msginfo;
1604
        msglist.next = NULL;
1605

    
1606
        return imap_move_msgs(folder, dest, &msglist);
1607
}
1608

    
1609
static gint imap_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1610
{
1611
        MsgInfo *msginfo;
1612
        GSList *file_list;
1613
        gint ret = 0;
1614

    
1615
        g_return_val_if_fail(folder != NULL, -1);
1616
        g_return_val_if_fail(dest != NULL, -1);
1617
        g_return_val_if_fail(msglist != NULL, -1);
1618

    
1619
        msginfo = (MsgInfo *)msglist->data;
1620
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1621

    
1622
        if (folder == msginfo->folder->folder)
1623
                return imap_do_copy_msgs(folder, dest, msglist, TRUE);
1624

    
1625
        file_list = procmsg_get_message_file_list(msglist);
1626
        g_return_val_if_fail(file_list != NULL, -1);
1627

    
1628
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1629

    
1630
        procmsg_message_file_list_free(file_list);
1631

    
1632
        if (ret != -1)
1633
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
1634

    
1635
        return ret;
1636
}
1637

    
1638
static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1639
{
1640
        GSList msglist;
1641

    
1642
        g_return_val_if_fail(msginfo != NULL, -1);
1643

    
1644
        msglist.data = msginfo;
1645
        msglist.next = NULL;
1646

    
1647
        return imap_copy_msgs(folder, dest, &msglist);
1648
}
1649

    
1650
static gint imap_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1651
{
1652
        MsgInfo *msginfo;
1653
        GSList *file_list;
1654
        gint ret;
1655

    
1656
        g_return_val_if_fail(folder != NULL, -1);
1657
        g_return_val_if_fail(dest != NULL, -1);
1658
        g_return_val_if_fail(msglist != NULL, -1);
1659

    
1660
        msginfo = (MsgInfo *)msglist->data;
1661
        g_return_val_if_fail(msginfo->folder != NULL, -1);
1662

    
1663
        if (folder == msginfo->folder->folder)
1664
                return imap_do_copy_msgs(folder, dest, msglist, FALSE);
1665

    
1666
        file_list = procmsg_get_message_file_list(msglist);
1667
        g_return_val_if_fail(file_list != NULL, -1);
1668

    
1669
        ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1670

    
1671
        procmsg_message_file_list_free(file_list);
1672

    
1673
        return ret;
1674
}
1675

    
1676
static gint imap_remove_msgs_by_seq_set(Folder *folder, FolderItem *item,
1677
                                        GSList *seq_list)
1678
{
1679
        gint ok;
1680
        IMAPSession *session;
1681
        GSList *cur;
1682

    
1683
        g_return_val_if_fail(seq_list != NULL, -1);
1684

    
1685
        session = imap_session_get(folder);
1686
        if (!session) return -1;
1687

    
1688
        for (cur = seq_list; cur != NULL; cur = cur->next) {
1689
                gchar *seq_set = (gchar *)cur->data;
1690

    
1691
                status_print(_("Removing messages %s"), seq_set);
1692
                ui_update();
1693

    
1694
                ok = imap_set_message_flags(session, seq_set, IMAP_FLAG_DELETED,
1695
                                            TRUE);
1696
                if (ok != IMAP_SUCCESS) {
1697
                        log_warning(_("can't set deleted flags: %s\n"),
1698
                                    seq_set);
1699
                        return ok;
1700
                }
1701
        }
1702

    
1703
        ok = imap_cmd_expunge(session);
1704
        if (ok != IMAP_SUCCESS) {
1705
                log_warning(_("can't expunge\n"));
1706
        } else {
1707
                /* for some broken IMAP servers */
1708
                ok = imap_cmd_noop(session);
1709
        }
1710

    
1711
        item->updated = TRUE;
1712

    
1713
        return ok;
1714
}
1715

    
1716
static gint imap_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1717
{
1718
        GSList msglist;
1719

    
1720
        g_return_val_if_fail(msginfo != NULL, -1);
1721

    
1722
        msglist.data = msginfo;
1723
        msglist.next = NULL;
1724

    
1725
        return imap_remove_msgs(folder, item, &msglist);
1726
}
1727

    
1728
static gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
1729
{
1730
        gint ok;
1731
        IMAPSession *session;
1732
        GSList *seq_list, *cur;
1733
        gchar *dir;
1734
        gboolean dir_exist;
1735

    
1736
        g_return_val_if_fail(folder != NULL, -1);
1737
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
1738
        g_return_val_if_fail(item != NULL, -1);
1739
        g_return_val_if_fail(msglist != NULL, -1);
1740

    
1741
        session = imap_session_get(folder);
1742
        if (!session) return -1;
1743

    
1744
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1745
                         NULL, NULL, NULL, NULL);
1746
        if (ok != IMAP_SUCCESS)
1747
                return ok;
1748

    
1749
        seq_list = imap_get_seq_set_from_msglist(msglist, 0);
1750
        ok = imap_remove_msgs_by_seq_set(folder, item, seq_list);
1751
        imap_seq_set_free(seq_list);
1752
        if (ok != IMAP_SUCCESS)
1753
                return ok;
1754

    
1755
        dir = folder_item_get_path(item);
1756
        dir_exist = is_dir_exist(dir);
1757
        for (cur = msglist; cur != NULL; cur = cur->next) {
1758
                MsgInfo *msginfo = (MsgInfo *)cur->data;
1759
                guint32 uid = msginfo->msgnum;
1760

    
1761
                if (syl_app_get())
1762
                        g_signal_emit_by_name(syl_app_get(), "remove-msg", item, NULL, uid);
1763

    
1764
                if (dir_exist)
1765
                        remove_numbered_files(dir, uid, uid);
1766
                item->total--;
1767
                if (MSG_IS_NEW(msginfo->flags))
1768
                        item->new--;
1769
                if (MSG_IS_UNREAD(msginfo->flags))
1770
                        item->unread--;
1771
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
1772
        }
1773
        g_free(dir);
1774

    
1775
        return IMAP_SUCCESS;
1776
}
1777

    
1778
static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1779
{
1780
        gint ok;
1781
        IMAPSession *session;
1782
        gchar *dir;
1783

    
1784
        g_return_val_if_fail(folder != NULL, -1);
1785
        g_return_val_if_fail(item != NULL, -1);
1786

    
1787
        session = imap_session_get(folder);
1788
        if (!session) return -1;
1789

    
1790
        ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1791
                         NULL, NULL, NULL, NULL);
1792
        if (ok != IMAP_SUCCESS)
1793
                return ok;
1794

    
1795
        status_print(_("Removing all messages in %s"), item->path);
1796
        ui_update();
1797

    
1798
        ok = imap_cmd_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1799
        if (ok != IMAP_SUCCESS) {
1800
                log_warning(_("can't set deleted flags: 1:*\n"));
1801
                return ok;
1802
        }
1803
        ok = imap_cmd_ok(session, NULL);
1804
        if (ok != IMAP_SUCCESS) {
1805
                log_warning(_("can't set deleted flags: 1:*\n"));
1806
                return ok;
1807
        }
1808

    
1809
        ok = imap_cmd_expunge(session);
1810
        if (ok != IMAP_SUCCESS) {
1811
                log_warning(_("can't expunge\n"));
1812
                return ok;
1813
        }
1814

    
1815
        if (syl_app_get())
1816
                g_signal_emit_by_name(syl_app_get(), "remove-all-msg", item);
1817

    
1818
        item->new = item->unread = item->total = 0;
1819
        item->updated = TRUE;
1820

    
1821
        dir = folder_item_get_path(item);
1822
        if (is_dir_exist(dir))
1823
                remove_all_numbered_files(dir);
1824
        g_free(dir);
1825

    
1826
        return IMAP_SUCCESS;
1827
}
1828

    
1829
static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1830
                                    MsgInfo *msginfo)
1831
{
1832
        /* TODO: properly implement this method */
1833
        return FALSE;
1834
}
1835

    
1836
static gint imap_close(Folder *folder, FolderItem *item)
1837
{
1838
        gint ok;
1839
        IMAPSession *session;
1840

    
1841
        g_return_val_if_fail(folder != NULL, -1);
1842

    
1843
        if (!item->path) return 0;
1844

    
1845
        if (!REMOTE_FOLDER(folder)->session)
1846
                return 0;
1847

    
1848
        session = imap_session_get(folder);
1849
        if (!session) return -1;
1850

    
1851
        if (session->mbox) {
1852
                if (strcmp2(session->mbox, item->path) != 0) return -1;
1853

    
1854
                ok = imap_cmd_close(session);
1855
                if (ok != IMAP_SUCCESS)
1856
                        log_warning(_("can't close folder\n"));
1857

    
1858
                g_free(session->mbox);
1859
                session->mbox = NULL;
1860

    
1861
                return ok;
1862
        } else
1863
                return 0;
1864
}
1865

    
1866
static gint imap_scan_folder(Folder *folder, FolderItem *item)
1867
{
1868
        IMAPSession *session;
1869
        gint messages, recent, unseen;
1870
        guint32 uid_next, uid_validity;
1871
        gint ok;
1872

    
1873
        g_return_val_if_fail(folder != NULL, -1);
1874
        g_return_val_if_fail(item != NULL, -1);
1875

    
1876
        session = imap_session_get(folder);
1877
        if (!session) return -1;
1878

    
1879
        ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1880
                         &messages, &recent, &uid_next, &uid_validity, &unseen);
1881
        if (ok != IMAP_SUCCESS) return -1;
1882

    
1883
        item->new = unseen > 0 ? recent : 0;
1884
        item->unread = unseen;
1885
        item->total = messages;
1886
        item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1887
        /* item->mtime = uid_validity; */
1888
        item->updated = TRUE;
1889

    
1890
        return 0;
1891
}
1892

    
1893
static gint imap_scan_tree(Folder *folder)
1894
{
1895
        FolderItem *item = NULL;
1896
        IMAPSession *session;
1897
        gchar *root_folder = NULL;
1898
        GSList *item_list, *cur;
1899

    
1900
        g_return_val_if_fail(folder != NULL, -1);
1901
        g_return_val_if_fail(folder->account != NULL, -1);
1902

    
1903
        session = imap_session_get(folder);
1904
        if (!session) {
1905
                if (!folder->node) {
1906
                        folder_tree_destroy(folder);
1907
                        item = folder_item_new(folder->name, NULL);
1908
                        item->folder = folder;
1909
                        folder->node = item->node = g_node_new(item);
1910
                }
1911
                return -1;
1912
        }
1913

    
1914
        if (folder->account->imap_dir && *folder->account->imap_dir) {
1915
                gchar *real_path;
1916
                GPtrArray *argbuf;
1917
                gint ok;
1918

    
1919
                Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1920
                extract_quote(root_folder, '"');
1921
                subst_char(root_folder,
1922
                           imap_get_path_separator(IMAP_FOLDER(folder),
1923
                                                   root_folder),
1924
                           '/');
1925
                strtailchomp(root_folder, '/');
1926
                real_path = imap_get_real_path
1927
                        (IMAP_FOLDER(folder), root_folder);
1928
                debug_print("IMAP root directory: %s\n", real_path);
1929

    
1930
                /* check if root directory exist */
1931
                argbuf = g_ptr_array_new();
1932
                ok = imap_cmd_list(session, NULL, real_path, argbuf);
1933
                if (ok != IMAP_SUCCESS ||
1934
                    search_array_str(argbuf, "LIST ") == NULL) {
1935
                        log_warning(_("root folder %s not exist\n"), real_path);
1936
                        g_ptr_array_free(argbuf, TRUE);
1937
                        g_free(real_path);
1938
                        return -1;
1939
                }
1940
                g_ptr_array_free(argbuf, TRUE);
1941
                g_free(real_path);
1942
        }
1943

    
1944
        if (folder->node)
1945
                item = FOLDER_ITEM(folder->node->data);
1946
        if (!item || ((item->path || root_folder) &&
1947
                      strcmp2(item->path, root_folder) != 0)) {
1948
                folder_tree_destroy(folder);
1949
                item = folder_item_new(folder->name, root_folder);
1950
                item->folder = folder;
1951
                folder->node = item->node = g_node_new(item);
1952
        }
1953

    
1954
        item_list = imap_get_folder_list(session, item);
1955
        imap_scan_tree_recursive(session, item, item_list);
1956
        imap_create_missing_folders(folder);
1957

    
1958
        for (cur = item_list; cur != NULL; cur = cur->next)
1959
                folder_item_destroy(FOLDER_ITEM(cur->data));
1960
        g_slist_free(item_list);
1961

    
1962
        return 0;
1963
}
1964

    
1965
static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item,
1966
                                     GSList *item_list)
1967
{
1968
        Folder *folder;
1969
        FolderItem *new_item;
1970
        GSList *part_list, *cur;
1971
        GNode *node;
1972

    
1973
        g_return_val_if_fail(item != NULL, -1);
1974
        g_return_val_if_fail(item->folder != NULL, -1);
1975
        g_return_val_if_fail(item->no_sub == FALSE, -1);
1976

    
1977
        folder = item->folder;
1978

    
1979
        part_list = imap_get_part_folder_list(item_list, item);
1980

    
1981
        node = item->node->children;
1982
        while (node != NULL) {
1983
                FolderItem *old_item = FOLDER_ITEM(node->data);
1984
                GNode *next = node->next;
1985

    
1986
                new_item = NULL;
1987

    
1988
                for (cur = part_list; cur != NULL; cur = cur->next) {
1989
                        FolderItem *cur_item = FOLDER_ITEM(cur->data);
1990
                        if (!strcmp2(old_item->path, cur_item->path)) {
1991
                                new_item = cur_item;
1992
                                break;
1993
                        }
1994
                }
1995
                if (!new_item) {
1996
                        if (old_item->stype != F_VIRTUAL) {
1997
                                debug_print("folder '%s' not found. removing...\n", old_item->path);
1998
                                folder_item_remove(old_item);
1999
                        }
2000
                } else if (old_item->stype == F_VIRTUAL) {
2001
                                debug_print("IMAP4 folder found at the location of virtual folder '%s'. removing virtual folder...\n", old_item->path);
2002
                                virtual_get_class()->remove_folder
2003
                                        (folder, old_item);
2004
                } else {
2005
                        old_item->no_sub = new_item->no_sub;
2006
                        old_item->no_select = new_item->no_select;
2007
                        if (old_item->no_select == TRUE)
2008
                                old_item->new = old_item->unread =
2009
                                        old_item->total = 0;
2010
                        if (old_item->no_sub == TRUE && node->children) {
2011
                                debug_print("folder '%s' doesn't have "
2012
                                            "subfolders. removing...\n",
2013
                                            old_item->path);
2014
                                folder_item_remove_children(old_item);
2015
                        }
2016
                }
2017

    
2018
                node = next;
2019
        }
2020

    
2021
        for (cur = part_list; cur != NULL; cur = cur->next) {
2022
                FolderItem *cur_item = FOLDER_ITEM(cur->data);
2023
                new_item = NULL;
2024
                for (node = item->node->children; node != NULL;
2025
                     node = node->next) {
2026
                        if (!strcmp2(FOLDER_ITEM(node->data)->path,
2027
                                     cur_item->path)) {
2028
                                new_item = FOLDER_ITEM(node->data);
2029
                                cur_item = NULL;
2030
                                break;
2031
                        }
2032
                }
2033
                if (!new_item) {
2034
                        new_item = folder_item_copy(cur_item);
2035
                        debug_print("new folder '%s' found.\n", new_item->path);
2036
                        folder_item_append(item, new_item);
2037
                }
2038

    
2039
                if (!g_ascii_strcasecmp(new_item->path, "INBOX")) {
2040
                        new_item->stype = F_INBOX;
2041
                        folder->inbox = new_item;
2042
                } else if (!item->parent || item->stype == F_INBOX) {
2043
                        const gchar *base;
2044

    
2045
                        base = g_basename(new_item->path);
2046

    
2047
                        if (!folder->outbox &&
2048
                            !g_ascii_strcasecmp(base, "Sent")) {
2049
                                new_item->stype = F_OUTBOX;
2050
                                folder->outbox = new_item;
2051
                        } else if (!folder->draft &&
2052
                                   !g_ascii_strcasecmp(base, "Drafts")) {
2053
                                new_item->stype = F_DRAFT;
2054
                                folder->draft = new_item;
2055
                        } else if (!folder->queue &&
2056
                                   !g_ascii_strcasecmp(base, "Queue")) {
2057
                                new_item->stype = F_QUEUE;
2058
                                folder->queue = new_item;
2059
                        } else if (!folder->trash &&
2060
                                   !g_ascii_strcasecmp(base, "Trash")) {
2061
                                new_item->stype = F_TRASH;
2062
                                folder->trash = new_item;
2063
                        }
2064
                }
2065

    
2066
#if 0
2067
                if (new_item->no_select == FALSE)
2068
                        imap_scan_folder(folder, new_item);
2069
#endif
2070
                if (new_item->no_sub == FALSE)
2071
                        imap_scan_tree_recursive(session, new_item, item_list);
2072
        }
2073

    
2074
        g_slist_free(part_list);
2075

    
2076
        return IMAP_SUCCESS;
2077
}
2078

    
2079
static GSList *imap_get_folder_list(IMAPSession *session, FolderItem *item)
2080
{
2081
        Folder *folder;
2082
        IMAPFolder *imapfolder;
2083
        gchar *real_path;
2084
        gchar *wildcard_path;
2085
        gchar separator;
2086
        GSList *item_list = NULL;
2087

    
2088
        folder = item->folder;
2089
        imapfolder = IMAP_FOLDER(folder);
2090

    
2091
        separator = imap_get_path_separator(imapfolder, item->path);
2092

    
2093
        if (folder->ui_func)
2094
                folder->ui_func(folder, item, folder->ui_func_data);
2095

    
2096
        if (item->path) {
2097
                real_path = imap_get_real_path(imapfolder, item->path);
2098
                strtailchomp(real_path, separator);
2099
                wildcard_path = g_strdup_printf("%s%c*", real_path, separator);
2100
        } else {
2101
                real_path = g_strdup("");
2102
                wildcard_path = g_strdup("*");
2103
        }
2104

    
2105
        if (imap_cmd_gen_send(session, "LIST \"\" \"%s\"", wildcard_path) == IMAP_SUCCESS) {
2106
                item_list = imap_parse_list(session, real_path, NULL);
2107
                item_list = imap_add_inter_folders(item_list, item->path);
2108
        }
2109
        g_free(real_path);
2110
        g_free(wildcard_path);
2111

    
2112
        return item_list;
2113
}
2114

    
2115
static GSList *imap_parse_list(IMAPSession *session, const gchar *real_path,
2116
                               gchar *separator)
2117
{
2118
        gchar buf[IMAPBUFSIZE];
2119
        gchar flags[256];
2120
        gchar separator_str[16];
2121
        gchar *p;
2122
        const gchar *name;
2123
        gchar *loc_name, *loc_path;
2124
        GSList *item_list = NULL;
2125
        GString *str;
2126
        FolderItem *new_item;
2127

    
2128
        debug_print("getting list of %s ...\n",
2129
                    *real_path ? real_path : "\"\"");
2130

    
2131
        str = g_string_new(NULL);
2132

    
2133
        for (;;) {
2134
                if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
2135
                        log_warning(_("error occurred while getting LIST.\n"));
2136
                        break;
2137
                }
2138
                strretchomp(buf);
2139
                if (buf[0] != '*' || buf[1] != ' ') {
2140
                        log_print("IMAP4< %s\n", buf);
2141
                        if (sscanf(buf, "%*d %16s", buf) < 1 ||
2142
                            strcmp(buf, "OK") != 0)
2143
                                log_warning(_("error occurred while getting LIST.\n"));
2144
                                
2145
                        break;
2146
                }
2147
                debug_print("IMAP4< %s\n", buf);
2148

    
2149
                g_string_assign(str, buf);
2150
                p = str->str + 2;
2151
                if (strncmp(p, "LIST ", 5) != 0) continue;
2152
                p += 5;
2153

    
2154
                if (*p != '(') continue;
2155
                p++;
2156
                p = strchr_cpy(p, ')', flags, sizeof(flags));
2157
                if (!p) continue;
2158
                while (*p == ' ') p++;
2159

    
2160
                p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
2161
                if (!p) continue;
2162
                extract_quote(separator_str, '"');
2163
                if (!strcmp(separator_str, "NIL"))
2164
                        separator_str[0] = '\0';
2165
                if (separator)
2166
                        *separator = separator_str[0];
2167

    
2168
                buf[0] = '\0';
2169
                while (*p == ' ') p++;
2170
                if ((*p == '~' && *(p + 1) == '{') || *p == '{' || *p == '"')
2171
                        p = imap_parse_atom(session, p, buf, sizeof(buf), str);
2172
                else
2173
                        strncpy2(buf, p, sizeof(buf));
2174
                strtailchomp(buf, separator_str[0]);
2175
                if (buf[0] == '\0') continue;
2176
                if (!strcmp(buf, real_path)) continue;
2177

    
2178
                if (separator_str[0] != '\0')
2179
                        subst_char(buf, separator_str[0], '/');
2180
                name = g_basename(buf);
2181
                if (name[0] == '.') continue;
2182

    
2183
                loc_name = imap_modified_utf7_to_utf8(name);
2184
                loc_path = imap_modified_utf7_to_utf8(buf);
2185
                new_item = folder_item_new(loc_name, loc_path);
2186
                if (strcasestr(flags, "\\Noinferiors") != NULL)
2187
                        new_item->no_sub = TRUE;
2188
                if (g_ascii_strcasecmp(buf, "INBOX") != 0 &&
2189
                    strcasestr(flags, "\\Noselect") != NULL)
2190
                        new_item->no_select = TRUE;
2191

    
2192
                item_list = g_slist_prepend(item_list, new_item);
2193

    
2194
                debug_print("folder '%s' found.\n", loc_path);
2195
                g_free(loc_path);
2196
                g_free(loc_name);
2197
        }
2198

    
2199
        g_string_free(str, TRUE);
2200

    
2201
        item_list = g_slist_reverse(item_list);
2202
        return item_list;
2203
}
2204

    
2205
static GSList *imap_add_inter_folders(GSList *item_list, const gchar *root_path)
2206
{
2207
        FolderItem *item;
2208
        GSList *cur;
2209
        GSList *add_list = NULL;
2210
        GHashTable *exist;
2211
        const gchar *p;
2212
        gint root_path_len = 0;
2213

    
2214
        if (root_path)
2215
                root_path_len = strlen(root_path);
2216

    
2217
        exist = g_hash_table_new(g_str_hash, g_str_equal);
2218

    
2219
        for (cur = item_list; cur != NULL; cur = cur->next) {
2220
                item = FOLDER_ITEM(cur->data);
2221

    
2222
                if (root_path_len > 0 &&
2223
                    strncmp(root_path, item->path, root_path_len) != 0)
2224
                        continue;
2225
                p = item->path + root_path_len;
2226
                if (root_path_len > 0 && *p != '/') continue;
2227
                while (*p == '/') p++;
2228
                if (*p == '\0') continue;
2229
                g_hash_table_insert(exist, (gpointer)p, GINT_TO_POINTER(1));
2230
        }
2231

    
2232
        for (cur = item_list; cur != NULL; cur = cur->next) {
2233
                const gchar *q, *r;
2234
                gchar *parent, *full_parent;
2235
                FolderItem *new_item;
2236

    
2237
                item = FOLDER_ITEM(cur->data);
2238

    
2239
                if (root_path_len > 0 &&
2240
                    strncmp(root_path, item->path, root_path_len) != 0)
2241
                        continue;
2242
                p = item->path + root_path_len;
2243
                if (root_path_len > 0 && *p != '/') continue;
2244
                while (*p == '/') p++;
2245
                if (*p == '\0') continue;
2246

    
2247
                q = p;
2248
                while ((q = strchr(q, '/')) != NULL) {
2249
                        parent = g_strndup(p, q - p);
2250
                        if (!g_hash_table_lookup(exist, parent)) {
2251
                                if (root_path_len > 0)
2252
                                        full_parent = g_strconcat
2253
                                                (root_path, "/", parent, NULL);
2254
                                else
2255
                                        full_parent = g_strdup(parent);
2256
                                new_item = folder_item_new(g_basename(parent),
2257
                                                           full_parent);
2258
                                new_item->no_select = TRUE;
2259
                                add_list = g_slist_prepend(add_list, new_item);
2260
                                r = new_item->path + root_path_len;
2261
                                while (*r == '/') r++;
2262
                                g_hash_table_insert(exist, (gpointer)r,
2263
                                                    GINT_TO_POINTER(1));
2264
                                debug_print("intermediate folder '%s' added\n",
2265
                                            full_parent);
2266
                                g_free(full_parent);
2267
                        }
2268
                        g_free(parent);
2269
                        while (*q == '/') q++;
2270
                }
2271
        }
2272

    
2273
        g_hash_table_destroy(exist);
2274

    
2275
        add_list = g_slist_reverse(add_list);
2276
        item_list = g_slist_concat(item_list, add_list);
2277

    
2278
        return item_list;
2279
}
2280

    
2281
static GSList *imap_get_part_folder_list(GSList *item_list, FolderItem *item)
2282
{
2283
        FolderItem *cur_item;
2284
        GSList *part_list = NULL, *cur;
2285
        gint len;
2286

    
2287
        if (!item->path) {
2288
                debug_print("imap_get_part_folder_list(): get root folders\n");
2289
                for (cur = item_list; cur != NULL; cur = cur->next) {
2290
                        cur_item = FOLDER_ITEM(cur->data);
2291

    
2292
                        if (!strchr(cur_item->path, '/')) {
2293
                                part_list = g_slist_prepend(part_list,
2294
                                                            cur_item);
2295
                                debug_print("append '%s'\n", cur_item->path);
2296
                        }
2297
                }
2298
                part_list = g_slist_reverse(part_list);
2299
                return part_list;
2300
        }
2301

    
2302
        len = strlen(item->path);
2303
        debug_print("imap_get_part_folder_list(): get folders under '%s'\n",
2304
                    item->path);
2305

    
2306
        for (cur = item_list; cur != NULL; cur = cur->next) {
2307
                cur_item = FOLDER_ITEM(cur->data);
2308

    
2309
                if (!strncmp(cur_item->path, item->path, len) &&
2310
                    cur_item->path[len] == '/' &&
2311
                    !strchr(cur_item->path + len + 1, '/')) {
2312
                        part_list = g_slist_prepend(part_list, cur_item);
2313
                        debug_print("append '%s'\n", cur_item->path);
2314
                }
2315
        }
2316

    
2317
        part_list = g_slist_reverse(part_list);
2318
        return part_list;
2319
}
2320

    
2321
static gint imap_create_tree(Folder *folder)
2322
{
2323
        g_return_val_if_fail(folder != NULL, -1);
2324
        g_return_val_if_fail(folder->node != NULL, -1);
2325
        g_return_val_if_fail(folder->node->data != NULL, -1);
2326
        g_return_val_if_fail(folder->account != NULL, -1);
2327

    
2328
        if (imap_scan_tree(folder) < 0)
2329
                return -1;
2330
        imap_create_missing_folders(folder);
2331

    
2332
        return 0;
2333
}
2334

    
2335
static void imap_create_missing_folders(Folder *folder)
2336
{
2337
        g_return_if_fail(folder != NULL);
2338

    
2339
        if (!folder->inbox)
2340
                folder->inbox = imap_create_special_folder
2341
                        (folder, F_INBOX, "INBOX");
2342
#if 0
2343
        if (!folder->outbox)
2344
                folder->outbox = imap_create_special_folder
2345
                        (folder, F_OUTBOX, "Sent");
2346
        if (!folder->draft)
2347
                folder->draft = imap_create_special_folder
2348
                        (folder, F_DRAFT, "Drafts");
2349
        if (!folder->queue)
2350
                folder->queue = imap_create_special_folder
2351
                        (folder, F_QUEUE, "Queue");
2352
#endif
2353
        if (!folder->trash)
2354
                folder->trash = imap_create_special_folder
2355
                        (folder, F_TRASH, "Trash");
2356
}
2357

    
2358
static FolderItem *imap_create_special_folder(Folder *folder,
2359
                                              SpecialFolderItemType stype,
2360
                                              const gchar *name)
2361
{
2362
        FolderItem *item;
2363
        FolderItem *new_item;
2364

    
2365
        g_return_val_if_fail(folder != NULL, NULL);
2366
        g_return_val_if_fail(folder->node != NULL, NULL);
2367
        g_return_val_if_fail(folder->node->data != NULL, NULL);
2368
        g_return_val_if_fail(folder->account != NULL, NULL);
2369
        g_return_val_if_fail(name != NULL, NULL);
2370

    
2371
        item = FOLDER_ITEM(folder->node->data);
2372
        new_item = imap_create_folder(folder, item, name);
2373

    
2374
        if (!new_item) {
2375
                g_warning(_("Can't create '%s'\n"), name);
2376
                if (!folder->inbox) return NULL;
2377

    
2378
                new_item = imap_create_folder(folder, folder->inbox, name);
2379
                if (!new_item)
2380
                        g_warning(_("Can't create '%s' under INBOX\n"), name);
2381
                else
2382
                        new_item->stype = stype;
2383
        } else
2384
                new_item->stype = stype;
2385

    
2386
        return new_item;
2387
}
2388

    
2389
static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2390
                                      const gchar *name)
2391
{
2392
        gchar *dirpath, *imap_path;
2393
        IMAPSession *session;
2394
        FolderItem *new_item;
2395
        gchar separator;
2396
        gchar *new_name;
2397
        const gchar *p;
2398
        gboolean no_sub = FALSE, no_select = FALSE;
2399
        gint ok;
2400

    
2401
        g_return_val_if_fail(folder != NULL, NULL);
2402
        g_return_val_if_fail(folder->account != NULL, NULL);
2403
        g_return_val_if_fail(parent != NULL, NULL);
2404
        g_return_val_if_fail(name != NULL, NULL);
2405

    
2406
        session = imap_session_get(folder);
2407
        if (!session) return NULL;
2408

    
2409
        if (!parent->parent && g_ascii_strcasecmp(name, "INBOX") == 0)
2410
                dirpath = g_strdup(name);
2411
        else if (parent->path)
2412
                dirpath = g_strconcat(parent->path, "/", name, NULL);
2413
        else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2414
                dirpath = g_strdup(name);
2415
        else if (folder->account->imap_dir && *folder->account->imap_dir) {
2416
                gchar *imap_dir;
2417

    
2418
                Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
2419
                strtailchomp(imap_dir, '/');
2420
                dirpath = g_strconcat(imap_dir, "/", name, NULL);
2421
        } else
2422
                dirpath = g_strdup(name);
2423

    
2424
        /* keep trailing directory separator to create a folder that contains
2425
           sub folder */
2426
        imap_path = imap_utf8_to_modified_utf7(dirpath);
2427
        strtailchomp(dirpath, '/');
2428
        Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
2429
        strtailchomp(new_name, '/');
2430
        separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
2431
        imap_path_separator_subst(imap_path, separator);
2432
        subst_char(new_name, '/', separator);
2433

    
2434
        if (g_ascii_strcasecmp(name, "INBOX") != 0) {
2435
                GPtrArray *argbuf;
2436
                gint i;
2437
                gboolean exist = FALSE;
2438

    
2439
                argbuf = g_ptr_array_new();
2440
                ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2441
                if (ok != IMAP_SUCCESS) {
2442
                        log_warning(_("can't create mailbox: LIST failed\n"));
2443
                        g_free(imap_path);
2444
                        g_free(dirpath);
2445
                        g_ptr_array_free(argbuf, TRUE);
2446
                        return NULL;
2447
                }
2448

    
2449
                for (i = 0; i < argbuf->len; i++) {
2450
                        gchar *str;
2451
                        str = g_ptr_array_index(argbuf, i);
2452
                        if (!strncmp(str, "LIST ", 5)) {
2453
                                exist = TRUE;
2454
                                if (strcasestr(str + 5, "\\Noinferiors"))
2455
                                        no_sub = TRUE;
2456
                                if (strcasestr(str + 5, "\\Noselect"))
2457
                                        no_select = TRUE;
2458
                                break;
2459
                        }
2460
                }
2461

    
2462
                if (!exist) {
2463
                        ok = imap_cmd_create(session, imap_path);
2464
                        if (ok != IMAP_SUCCESS) {
2465
                                log_warning(_("can't create mailbox\n"));
2466
                                g_free(imap_path);
2467
                                g_free(dirpath);
2468
                                return NULL;
2469
                        }
2470

    
2471
                        g_ptr_array_free(argbuf, TRUE);
2472
                        argbuf = g_ptr_array_new();
2473
                        ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2474
                        if (ok != IMAP_SUCCESS) {
2475
                                log_warning("LIST failed\n");
2476
                                g_free(imap_path);
2477
                                g_free(dirpath);
2478
                                g_ptr_array_free(argbuf, TRUE);
2479
                                return NULL;
2480
                        }
2481

    
2482
                        for (i = 0; i < argbuf->len; i++) {
2483
                                gchar *str;
2484
                                str = g_ptr_array_index(argbuf, i);
2485
                                if (!strncmp(str, "LIST ", 5)) {
2486
                                        if (strcasestr(str + 5, "\\Noinferiors"))
2487
                                                no_sub = TRUE;
2488
                                        if (strcasestr(str + 5, "\\Noselect"))
2489
                                                no_select = TRUE;
2490
                                        break;
2491
                                }
2492
                        }
2493
                }
2494

    
2495
                g_ptr_array_free(argbuf, TRUE);
2496
        }
2497

    
2498
        new_item = folder_item_new(new_name, dirpath);
2499
        new_item->no_sub = no_sub;
2500
        new_item->no_select = no_select;
2501
        folder_item_append(parent, new_item);
2502
        g_free(imap_path);
2503
        g_free(dirpath);
2504

    
2505
        dirpath = folder_item_get_path(new_item);
2506
        if (!is_dir_exist(dirpath))
2507
                make_dir_hier(dirpath);
2508
        g_free(dirpath);
2509

    
2510
        return new_item;
2511
}
2512

    
2513
static gint imap_rename_folder_real(Folder *folder, FolderItem *item,
2514
                                    FolderItem *new_parent, const gchar *name)
2515
{
2516
        gchar *newpath;
2517
        gchar *real_oldpath;
2518
        gchar *real_newpath;
2519
        gchar *paths[2];
2520
        gchar *old_cache_dir;
2521
        gchar *new_cache_dir;
2522
        IMAPSession *session;
2523
        gchar separator;
2524
        gint ok;
2525
        gint exists, recent, unseen;
2526
        guint32 uid_validity;
2527
        gchar *old_id, *new_id;
2528

    
2529
        g_return_val_if_fail(folder != NULL, -1);
2530
        g_return_val_if_fail(item != NULL, -1);
2531
        g_return_val_if_fail(folder == item->folder, -1);
2532
        g_return_val_if_fail(item->path != NULL, -1);
2533
        g_return_val_if_fail(new_parent != NULL || name != NULL, -1);
2534
        if (new_parent) {
2535
                g_return_val_if_fail(item != new_parent, -1);
2536
                g_return_val_if_fail(item->parent != new_parent, -1);
2537
                g_return_val_if_fail(item->folder == new_parent->folder, -1);
2538
                if (g_node_is_ancestor(item->node, new_parent->node)) {
2539
                        g_warning("folder to be moved is ancestor of new parent\n");
2540
                        return -1;
2541
                }
2542
        }
2543

    
2544
        session = imap_session_get(folder);
2545
        if (!session) return -1;
2546

    
2547
        real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2548

    
2549
        g_free(session->mbox);
2550
        session->mbox = NULL;
2551
        ok = imap_cmd_examine(session, "INBOX",
2552
                              &exists, &recent, &unseen, &uid_validity);
2553
        if (ok != IMAP_SUCCESS) {
2554
                g_free(real_oldpath);
2555
                return -1;
2556
        }
2557

    
2558
        separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
2559
        if (new_parent) {
2560
                if (name) {
2561
                        if (new_parent->path)
2562
                                newpath = g_strconcat(new_parent->path,
2563
                                                      "/", name, NULL);
2564
                        else
2565
                                newpath = g_strdup(name);
2566
                } else {
2567
                        gchar *name_;
2568

    
2569
                        name_ = g_path_get_basename(item->path);
2570
                        if (new_parent->path)
2571
                                newpath = g_strconcat(new_parent->path,
2572
                                                      "/", name_, NULL);
2573
                        else
2574
                                newpath = g_strdup(name_);
2575
                        AUTORELEASE_STR(name_, );
2576
                        name = name_;
2577
                }
2578
        } else {
2579
                if (strchr(item->path, '/')) {
2580
                        gchar *dirpath;
2581

    
2582
                        dirpath = g_dirname(item->path);
2583
                        newpath = g_strconcat(dirpath, "/", name, NULL);
2584
                        g_free(dirpath);
2585
                } else
2586
                        newpath = g_strdup(name);
2587
        }
2588

    
2589
        real_newpath = imap_utf8_to_modified_utf7(newpath);
2590
        imap_path_separator_subst(real_newpath, separator);
2591

    
2592
        ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2593
        if (ok != IMAP_SUCCESS) {
2594
                log_warning(_("can't rename mailbox: %s to %s\n"),
2595
                            real_oldpath, real_newpath);
2596
                g_free(real_oldpath);
2597
                g_free(newpath);
2598
                g_free(real_newpath);
2599
                return -1;
2600
        }
2601

    
2602
        old_id = folder_item_get_identifier(item);
2603

    
2604
        if (new_parent) {
2605
                g_node_unlink(item->node);
2606
                g_node_append(new_parent->node, item->node);
2607
                item->parent = new_parent;
2608
        }
2609

    
2610
        g_free(item->name);
2611
        item->name = g_strdup(name);
2612

    
2613
        old_cache_dir = folder_item_get_path(item);
2614

    
2615
        paths[0] = g_strdup(item->path);
2616
        paths[1] = newpath;
2617
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2618
                        imap_rename_folder_func, paths);
2619

    
2620
        if (is_dir_exist(old_cache_dir)) {
2621
                new_cache_dir = folder_item_get_path(item);
2622
                if (g_rename(old_cache_dir, new_cache_dir) < 0) {
2623
                        FILE_OP_ERROR(old_cache_dir, "rename");
2624
                }
2625
                g_free(new_cache_dir);
2626
        }
2627

    
2628
        g_free(old_cache_dir);
2629
        g_free(paths[0]);
2630
        g_free(newpath);
2631
        g_free(real_oldpath);
2632
        g_free(real_newpath);
2633

    
2634
        new_id = folder_item_get_identifier(item);
2635
        if (syl_app_get())
2636
                g_signal_emit_by_name(syl_app_get(), "move-folder", item,
2637
                                      old_id, new_id);
2638
        g_free(new_id);
2639
        g_free(old_id);
2640

    
2641
        return 0;
2642
}
2643

    
2644
static gint imap_rename_folder(Folder *folder, FolderItem *item,
2645
                               const gchar *name)
2646
{
2647
        return imap_rename_folder_real(folder, item, NULL, name);
2648
}
2649

    
2650
static gint imap_move_folder(Folder *folder, FolderItem *item,
2651
                             FolderItem *new_parent)
2652
{
2653
        return imap_rename_folder_real(folder, item, new_parent, NULL);
2654
}
2655

    
2656
static gint imap_remove_folder(Folder *folder, FolderItem *item)
2657
{
2658
        gint ok;
2659
        IMAPSession *session;
2660
        gchar *path;
2661
        gchar *cache_dir;
2662
        gint exists, recent, unseen;
2663
        guint32 uid_validity;
2664

    
2665
        g_return_val_if_fail(folder != NULL, -1);
2666
        g_return_val_if_fail(item != NULL, -1);
2667
        g_return_val_if_fail(item->path != NULL, -1);
2668

    
2669
        session = imap_session_get(folder);
2670
        if (!session) return -1;
2671

    
2672
        path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2673

    
2674
        ok = imap_cmd_examine(session, "INBOX",
2675
                              &exists, &recent, &unseen, &uid_validity);
2676
        if (ok != IMAP_SUCCESS) {
2677
                g_free(path);
2678
                return -1;
2679
        }
2680

    
2681
        ok = imap_cmd_delete(session, path);
2682
        if (ok != IMAP_SUCCESS) {
2683
                log_warning(_("can't delete mailbox\n"));
2684
                g_free(path);
2685
                return -1;
2686
        }
2687

    
2688
        g_free(path);
2689
        cache_dir = folder_item_get_path(item);
2690
        if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2691
                g_warning("can't remove directory '%s'\n", cache_dir);
2692
        g_free(cache_dir);
2693

    
2694
        if (syl_app_get())
2695
                g_signal_emit_by_name(syl_app_get(), "remove-folder", item);
2696
        folder_item_remove(item);
2697

    
2698
        return 0;
2699
}
2700

    
2701
typedef struct _IMAPGetData
2702
{
2703
        FolderItem *item;
2704
        gint exists;
2705
        gboolean update_count;
2706
        GSList *newlist;
2707
} IMAPGetData;
2708

    
2709
static gint imap_get_uncached_messages_progress_func(IMAPSession *session,
2710
                                                     gint count, gint total,
2711
                                                     gpointer data)
2712
{
2713
        status_print(_("Getting message headers (%d / %d)"), count, total);
2714
        progress_show(count, total);
2715
#ifndef USE_THREADS
2716
        ui_update();
2717
#endif
2718
        return 0;
2719
}
2720

    
2721
static gint imap_get_uncached_messages_func(IMAPSession *session, gpointer data)
2722
{
2723
        gchar *tmp;
2724
        GSList *newlist = NULL;
2725
        GSList *llast = NULL;
2726
        GString *str;
2727
        MsgInfo *msginfo;
2728
        gint count = 1;
2729
        GTimeVal tv_prev, tv_cur;
2730
        IMAPGetData *get_data = (IMAPGetData *)data;
2731
        FolderItem *item = get_data->item;
2732
        gint exists = get_data->exists;
2733
        gboolean update_count = get_data->update_count;
2734

    
2735
        g_get_current_time(&tv_prev);
2736
#ifndef USE_THREADS
2737
        ui_update();
2738
#endif
2739

    
2740
#if USE_THREADS
2741
        ((IMAPRealSession *)session)->prog_total = exists;
2742
#endif
2743

    
2744
        str = g_string_new(NULL);
2745

    
2746
        for (;;) {
2747
                if (exists > 0 && count <= exists) {
2748
                        g_get_current_time(&tv_cur);
2749
                        if (tv_cur.tv_sec > tv_prev.tv_sec ||
2750
                            tv_cur.tv_usec - tv_prev.tv_usec >
2751
                            PROGRESS_UPDATE_INTERVAL * 1000) {
2752
#if USE_THREADS
2753
                                ((IMAPRealSession *)session)->prog_count = count;
2754
                                g_main_context_wakeup(NULL);
2755
#else
2756
                                imap_get_uncached_messages_progress_func
2757
                                        (session, count, exists, data);
2758
#endif
2759
                                tv_prev = tv_cur;
2760
                        }
2761
                }
2762
                ++count;
2763

    
2764
                if (sock_getline(SESSION(session)->sock, &tmp) < 0) {
2765
                        log_warning(_("error occurred while getting envelope.\n"));
2766
                        g_string_free(str, TRUE);
2767
                        return IMAP_SOCKET;
2768
                }
2769
                strretchomp(tmp);
2770
                if (tmp[0] != '*' || tmp[1] != ' ') {
2771
                        log_print("IMAP4< %s\n", tmp);
2772
                        g_free(tmp);
2773
                        break;
2774
                }
2775
                if (strstr(tmp, "FETCH") == NULL) {
2776
                        log_print("IMAP4< %s\n", tmp);
2777
                        g_free(tmp);
2778
                        continue;
2779
                }
2780
                log_print("IMAP4< %s\n", tmp);
2781
                g_string_assign(str, tmp);
2782
                g_free(tmp);
2783

    
2784
                msginfo = imap_parse_envelope(session, item, str);
2785
                if (!msginfo) {
2786
                        log_warning(_("can't parse envelope: %s\n"), str->str);
2787
                        continue;
2788
                }
2789
                if (update_count) {
2790
                        if (MSG_IS_NEW(msginfo->flags))
2791
                                item->new++;
2792
                        if (MSG_IS_UNREAD(msginfo->flags))
2793
                                item->unread++;
2794
                }
2795
                if (item->stype == F_QUEUE) {
2796
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2797
                } else if (item->stype == F_DRAFT) {
2798
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2799
                }
2800

    
2801
                msginfo->folder = item;
2802

    
2803
                if (!newlist)
2804
                        llast = newlist = g_slist_append(newlist, msginfo);
2805
                else {
2806
                        llast = g_slist_append(llast, msginfo);
2807
                        llast = llast->next;
2808
                }
2809

    
2810
                if (update_count)
2811
                        item->total++;
2812
        }
2813

    
2814
        g_string_free(str, TRUE);
2815

    
2816
        session_set_access_time(SESSION(session));
2817

    
2818
        get_data->newlist = newlist;
2819
        return IMAP_SUCCESS;
2820
}
2821

    
2822
static GSList *imap_get_uncached_messages(IMAPSession *session,
2823
                                          FolderItem *item,
2824
                                          guint32 first_uid, guint32 last_uid,
2825
                                          gint exists, gboolean update_count)
2826
{
2827
        IMAPGetData get_data = {item, exists, update_count, NULL};
2828
        gchar seq_set[22];
2829
        gint ok;
2830

    
2831
        g_return_val_if_fail(session != NULL, NULL);
2832
        g_return_val_if_fail(item != NULL, NULL);
2833
        g_return_val_if_fail(item->folder != NULL, NULL);
2834
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, NULL);
2835
        g_return_val_if_fail(first_uid <= last_uid, NULL);
2836

    
2837
        if (first_uid == 0 && last_uid == 0)
2838
                strcpy(seq_set, "1:*");
2839
        else
2840
                g_snprintf(seq_set, sizeof(seq_set), "%u:%u",
2841
                           first_uid, last_uid);
2842
        if (imap_cmd_envelope(session, seq_set) != IMAP_SUCCESS) {
2843
                log_warning(_("can't get envelope\n"));
2844
                return NULL;
2845
        }
2846

    
2847
#if USE_THREADS
2848
        ok = imap_thread_run_progress(session, imap_get_uncached_messages_func,
2849
                                      imap_get_uncached_messages_progress_func,
2850
                                      &get_data);
2851
#else
2852
        ok = imap_get_uncached_messages_func(session, &get_data);
2853
#endif
2854

    
2855
        progress_show(0, 0);
2856
        return get_data.newlist;
2857
}
2858

    
2859
static void imap_delete_cached_message(FolderItem *item, guint32 uid)
2860
{
2861
        gchar *dir;
2862
        gchar *file;
2863

    
2864
        g_return_if_fail(item != NULL);
2865
        g_return_if_fail(item->folder != NULL);
2866
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2867

    
2868
        dir = folder_item_get_path(item);
2869
        file = g_strdup_printf("%s%c%u", dir, G_DIR_SEPARATOR, uid);
2870

    
2871
        debug_print("Deleting cached message: %s\n", file);
2872

    
2873
        g_unlink(file);
2874

    
2875
        g_free(file);
2876
        g_free(dir);
2877
}
2878

    
2879
static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
2880
                                           guint32 first_uid, guint32 last_uid)
2881
{
2882
        GSList *cur, *next;
2883
        MsgInfo *msginfo;
2884
        gchar *dir;
2885

    
2886
        g_return_val_if_fail(item != NULL, mlist);
2887
        g_return_val_if_fail(item->folder != NULL, mlist);
2888
        g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, mlist);
2889

    
2890
        if (first_uid == 0 && last_uid == 0)
2891
                return mlist;
2892

    
2893
        debug_print("Deleting cached messages %u - %u ... ",
2894
                    first_uid, last_uid);
2895

    
2896
        dir = folder_item_get_path(item);
2897
        if (is_dir_exist(dir))
2898
                remove_numbered_files(dir, first_uid, last_uid);
2899
        g_free(dir);
2900

    
2901
        for (cur = mlist; cur != NULL; ) {
2902
                next = cur->next;
2903

    
2904
                msginfo = (MsgInfo *)cur->data;
2905
                if (msginfo != NULL && first_uid <= msginfo->msgnum &&
2906
                    msginfo->msgnum <= last_uid) {
2907
                        procmsg_msginfo_free(msginfo);
2908
                        mlist = g_slist_remove(mlist, msginfo);
2909
                }
2910

    
2911
                cur = next;
2912
        }
2913

    
2914
        debug_print("done.\n");
2915

    
2916
        return mlist;
2917
}
2918

    
2919
static void imap_delete_all_cached_messages(FolderItem *item)
2920
{
2921
        gchar *dir;
2922

    
2923
        g_return_if_fail(item != NULL);
2924
        g_return_if_fail(item->folder != NULL);
2925
        g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2926

    
2927
        debug_print("Deleting all cached messages... ");
2928

    
2929
        dir = folder_item_get_path(item);
2930
        if (is_dir_exist(dir))
2931
                remove_all_numbered_files(dir);
2932
        g_free(dir);
2933

    
2934
        debug_print("done.\n");
2935
}
2936

    
2937
#if USE_SSL
2938
static SockInfo *imap_open(const gchar *server, gushort port,
2939
                           SocksInfo *socks_info, SSLType ssl_type)
2940
#else
2941
static SockInfo *imap_open(const gchar *server, gushort port,
2942
                           SocksInfo *socks_info)
2943
#endif
2944
{
2945
        SockInfo *sock = NULL;
2946
        const gchar *server_;
2947
        gushort port_;
2948
#if USE_THREADS
2949
        gint conn_id;
2950
#endif
2951

    
2952
        if (socks_info) {
2953
                server_ = socks_info->proxy_host;
2954
                port_ = socks_info->proxy_port;
2955
        } else {
2956
                server_ = server;
2957
                port_ = port;
2958
        }
2959

    
2960
#if USE_THREADS
2961
        if ((conn_id = sock_connect_async_thread(server_, port_)) < 0 ||
2962
            sock_connect_async_thread_wait(conn_id, &sock) < 0) {
2963
                log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2964
                            server, port);
2965
                return NULL;
2966
        }
2967
#else
2968
        if ((sock = sock_connect(server_, port_)) == NULL) {
2969
                log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2970
                            server, port);
2971
                return NULL;
2972
        }
2973
#endif
2974

    
2975
        if (socks_info) {
2976
                if (socks_connect(sock, server, port, socks_info) < 0) {
2977
                        log_warning("Can't establish SOCKS connection: %s:%d\n",
2978
                                    server, port);
2979
                        sock_close(sock);
2980
                        return NULL;
2981
                }
2982
        }
2983

    
2984
#if USE_SSL
2985
        if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2986
                log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2987
                            server, port);
2988
                sock_close(sock);
2989
                return NULL;
2990
        }
2991
#endif
2992

    
2993
        return sock;
2994
}
2995

    
2996
static GList *imap_parse_namespace_str(gchar *str)
2997
{
2998
        gchar *p = str;
2999
        gchar *name;
3000
        gchar *separator;
3001
        IMAPNameSpace *namespace;
3002
        GList *ns_list = NULL;
3003

    
3004
        while (*p != '\0') {
3005
                /* parse ("#foo" "/") */
3006

    
3007
                while (*p && *p != '(') p++;
3008
                if (*p == '\0') break;
3009
                p++;
3010

    
3011
                while (*p && *p != '"') p++;
3012
                if (*p == '\0') break;
3013
                p++;
3014
                name = p;
3015

    
3016
                while (*p && *p != '"') p++;
3017
                if (*p == '\0') break;
3018
                *p = '\0';
3019
                p++;
3020

    
3021
                while (*p && g_ascii_isspace(*p)) p++;
3022
                if (*p == '\0') break;
3023
                if (strncmp(p, "NIL", 3) == 0)
3024
                        separator = NULL;
3025
                else if (*p == '"') {
3026
                        p++;
3027
                        separator = p;
3028
                        while (*p && *p != '"') p++;
3029
                        if (*p == '\0') break;
3030
                        *p = '\0';
3031
                        p++;
3032
                } else break;
3033

    
3034
                while (*p && *p != ')') p++;
3035
                if (*p == '\0') break;
3036
                p++;
3037

    
3038
                namespace = g_new(IMAPNameSpace, 1);
3039
                namespace->name = g_strdup(name);
3040
                namespace->separator = separator ? separator[0] : '\0';
3041
                ns_list = g_list_append(ns_list, namespace);
3042
        }
3043

    
3044
        return ns_list;
3045
}
3046

    
3047
static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
3048
{
3049
        gchar *ns_str = NULL;
3050
        gchar **str_array;
3051

    
3052
        g_return_if_fail(session != NULL);
3053
        g_return_if_fail(folder != NULL);
3054

    
3055
        if (folder->ns_personal != NULL ||
3056
            folder->ns_others   != NULL ||
3057
            folder->ns_shared   != NULL)
3058
                return;
3059

    
3060
        if (imap_cmd_namespace(session, &ns_str) != IMAP_SUCCESS) {
3061
                log_warning(_("can't get namespace\n"));
3062
                imap_get_namespace_by_list(session, folder);
3063
                return;
3064
        }
3065

    
3066
        str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
3067
        if (str_array[0])
3068
                folder->ns_personal = imap_parse_namespace_str(str_array[0]);
3069
        if (str_array[0] && str_array[1])
3070
                folder->ns_others = imap_parse_namespace_str(str_array[1]);
3071
        if (str_array[0] && str_array[1] && str_array[2])
3072
                folder->ns_shared = imap_parse_namespace_str(str_array[2]);
3073
        g_strfreev(str_array);
3074
        g_free(ns_str);
3075
}
3076

    
3077
static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
3078
{
3079
        GSList *item_list, *cur;
3080
        gchar separator = '\0';
3081
        IMAPNameSpace *namespace;
3082

    
3083
        g_return_if_fail(session != NULL);
3084
        g_return_if_fail(folder != NULL);
3085

    
3086
        if (folder->ns_personal != NULL ||
3087
            folder->ns_others   != NULL ||
3088
            folder->ns_shared   != NULL)
3089
                return;
3090

    
3091
        if (imap_cmd_gen_send(session, "LIST \"\" \"\"") != IMAP_SUCCESS)
3092
                return;
3093
        item_list = imap_parse_list(session, "", &separator);
3094
        for (cur = item_list; cur != NULL; cur = cur->next)
3095
                folder_item_destroy(FOLDER_ITEM(cur->data));
3096
        g_slist_free(item_list);
3097

    
3098
        namespace = g_new(IMAPNameSpace, 1);
3099
        namespace->name = g_strdup("");
3100
        namespace->separator = separator;
3101
        folder->ns_personal = g_list_append(NULL, namespace);
3102
}
3103

    
3104
static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
3105
                                                    const gchar *path)
3106
{
3107
        IMAPNameSpace *namespace = NULL;
3108
        gchar *tmp_path, *name;
3109

    
3110
        if (!path) path = "";
3111

    
3112
        for (; ns_list != NULL; ns_list = ns_list->next) {
3113
                IMAPNameSpace *tmp_ns = ns_list->data;
3114

    
3115
                Xstrcat_a(tmp_path, path, "/", return namespace);
3116
                Xstrdup_a(name, tmp_ns->name, return namespace);
3117
                if (tmp_ns->separator && tmp_ns->separator != '/') {
3118
                        subst_char(tmp_path, tmp_ns->separator, '/');
3119
                        subst_char(name, tmp_ns->separator, '/');
3120
                }
3121
                if (strncmp(tmp_path, name, strlen(name)) == 0)
3122
                        namespace = tmp_ns;
3123
        }
3124

    
3125
        return namespace;
3126
}
3127

    
3128
static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
3129
                                          const gchar *path)
3130
{
3131
        IMAPNameSpace *namespace;
3132

    
3133
        g_return_val_if_fail(folder != NULL, NULL);
3134

    
3135
        namespace = imap_find_namespace_from_list(folder->ns_personal, path);
3136
        if (namespace) return namespace;
3137
        namespace = imap_find_namespace_from_list(folder->ns_others, path);
3138
        if (namespace) return namespace;
3139
        namespace = imap_find_namespace_from_list(folder->ns_shared, path);
3140
        if (namespace) return namespace;
3141

    
3142
        return NULL;
3143
}
3144

    
3145
static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
3146
{
3147
        IMAPNameSpace *namespace;
3148
        gchar separator = '/';
3149

    
3150
        namespace = imap_find_namespace(folder, path);
3151
        if (namespace && namespace->separator)
3152
                separator = namespace->separator;
3153

    
3154
        return separator;
3155
}
3156

    
3157
static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
3158
{
3159
        gchar *real_path;
3160
        gchar separator;
3161

    
3162
        g_return_val_if_fail(folder != NULL, NULL);
3163
        g_return_val_if_fail(path != NULL, NULL);
3164

    
3165
        real_path = imap_utf8_to_modified_utf7(path);
3166
        separator = imap_get_path_separator(folder, path);
3167
        imap_path_separator_subst(real_path, separator);
3168

    
3169
        return real_path;
3170
}
3171

    
3172
static gchar *imap_parse_atom(IMAPSession *session, gchar *src,
3173
                              gchar *dest, gint dest_len, GString *str)
3174
{
3175
        gchar *cur_pos = src;
3176
        gchar *nextline;
3177

    
3178
        g_return_val_if_fail(str != NULL, cur_pos);
3179

    
3180
        /* read the next line if the current response buffer is empty */
3181
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
3182
        while (*cur_pos == '\0') {
3183
                if (sock_getline(SESSION(session)->sock, &nextline) < 0)
3184
                        return cur_pos;
3185
                g_string_assign(str, nextline);
3186
                cur_pos = str->str;
3187
                strretchomp(nextline);
3188
                /* log_print("IMAP4< %s\n", nextline); */
3189
                debug_print("IMAP4< %s\n", nextline);
3190
                g_free(nextline);
3191

    
3192
                while (g_ascii_isspace(*cur_pos)) cur_pos++;
3193
        }
3194

    
3195
        if (*cur_pos == '~' && *(cur_pos + 1) == '{')
3196
                cur_pos++;
3197

    
3198
        if (!strncmp(cur_pos, "NIL", 3)) {
3199
                *dest = '\0';
3200
                cur_pos += 3;
3201
        } else if (*cur_pos == '\"') {
3202
                gchar *p;
3203

    
3204
                p = get_quoted(cur_pos, '\"', dest, dest_len);
3205
                cur_pos = p ? p : cur_pos + 2;
3206
        } else if (*cur_pos == '{') {
3207
                gchar buf[32];
3208
                gint len;
3209
                gint block_len = 0;
3210

    
3211
                cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
3212
                len = atoi(buf);
3213
                g_return_val_if_fail(len >= 0, cur_pos);
3214

    
3215
                g_string_truncate(str, 0);
3216
                cur_pos = str->str;
3217

    
3218
                do {
3219
                        gint cur_len;
3220

    
3221
                        cur_len = sock_getline(SESSION(session)->sock,
3222
                                               &nextline);
3223
                        if (cur_len < 0)
3224
                                return cur_pos;
3225
                        block_len += cur_len;
3226
                        subst_null(nextline, cur_len, ' ');
3227
                        g_string_append(str, nextline);
3228
                        cur_pos = str->str;
3229
                        strretchomp(nextline);
3230
                        /* log_print("IMAP4< %s\n", nextline); */
3231
                        debug_print("IMAP4< %s\n", nextline);
3232
                        g_free(nextline);
3233
                } while (block_len < len);
3234

    
3235
                memcpy(dest, cur_pos, MIN(len, dest_len - 1));
3236
                dest[MIN(len, dest_len - 1)] = '\0';
3237
                cur_pos += len;
3238
        }
3239

    
3240
        return cur_pos;
3241
}
3242

    
3243
static gchar *imap_get_header(IMAPSession *session, gchar *cur_pos,
3244
                              gchar **headers, GString *str)
3245
{
3246
        gchar *nextline;
3247
        gchar buf[IMAPBUFSIZE];
3248
        gint len;
3249
        gint block_len = 0;
3250

    
3251
        *headers = NULL;
3252

    
3253
        g_return_val_if_fail(str != NULL, cur_pos);
3254

    
3255
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
3256
        if (*cur_pos == '~' && *(cur_pos + 1) == '{')
3257
                cur_pos++;
3258

    
3259
        if (*cur_pos == '"') {
3260
                cur_pos = strchr_cpy(cur_pos + 1, '"', buf, sizeof(buf));
3261
                if (!cur_pos)
3262
                        return NULL;
3263
                len = strlen(buf);
3264
                *headers = g_strdup(buf);
3265
                while (g_ascii_isspace(*cur_pos)) cur_pos++;
3266
                return cur_pos;
3267
        }
3268

    
3269
        g_return_val_if_fail(*cur_pos == '{', cur_pos);
3270

    
3271
        cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
3272
        len = atoi(buf);
3273
        g_return_val_if_fail(len >= 0, cur_pos);
3274

    
3275
        g_string_truncate(str, 0);
3276
        cur_pos = str->str;
3277

    
3278
        do {
3279
                gint cur_len;
3280

    
3281
                cur_len = sock_getline(SESSION(session)->sock, &nextline);
3282
                if (cur_len < 0)
3283
                        return cur_pos;
3284
                block_len += cur_len;
3285
                subst_null(nextline, cur_len, ' ');
3286
                g_string_append(str, nextline);
3287
                cur_pos = str->str;
3288
                /* strretchomp(nextline); */
3289
                /* debug_print("IMAP4< %s\n", nextline); */
3290
                g_free(nextline);
3291
        } while (block_len < len);
3292

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

    
3295
        *headers = g_strndup(cur_pos, len);
3296
        cur_pos += len;
3297

    
3298
        while (g_ascii_isspace(*cur_pos)) cur_pos++;
3299
        while (*cur_pos == '\0') {
3300
                if (sock_getline(SESSION(session)->sock, &nextline) < 0)
3301
                        return cur_pos;
3302
                g_string_assign(str, nextline);
3303
                cur_pos = str->str;
3304
                strretchomp(nextline);
3305
                debug_print("IMAP4< %s\n", nextline);
3306
                g_free(nextline);
3307

    
3308
                while (g_ascii_isspace(*cur_pos)) cur_pos++;
3309
        }
3310

    
3311
        return cur_pos;
3312
}
3313

    
3314
static MsgFlags imap_parse_flags(const gchar *flag_str)  
3315
{
3316
        const gchar *p = flag_str;
3317
        MsgFlags flags = {0, 0};
3318

    
3319
        flags.perm_flags = MSG_UNREAD;
3320

    
3321
        while (*p != '\0') {
3322
                if (g_ascii_strncasecmp(p, "\\Recent", 7) == 0 &&
3323
                    MSG_IS_UNREAD(flags)) {
3324
                        MSG_SET_PERM_FLAGS(flags, MSG_NEW);
3325
                } else if (g_ascii_strncasecmp(p, "\\Seen", 5) == 0) {
3326
                        MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
3327
                } else if (g_ascii_strncasecmp(p, "\\Deleted", 8) == 0) {
3328
                        MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
3329
                } else if (g_ascii_strncasecmp(p, "\\Flagged", 8) == 0) {
3330
                        MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
3331
                } else if (g_ascii_strncasecmp(p, "\\Answered", 9) == 0) {
3332
                        MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
3333
                } else if (g_ascii_strncasecmp(p, "$label", 6) == 0) {
3334
                        /* color labels */
3335
                        if (*(p + 6) >= '1' && *(p + 6) <= '7') {
3336
                                guint color = *(p + 6) - '1' + 1;
3337
                                MSG_UNSET_PERM_FLAGS(flags,
3338
                                                     MSG_CLABEL_FLAG_MASK);
3339
                                MSG_SET_COLORLABEL_VALUE(flags, color);
3340
                        }
3341
                }
3342

    
3343
                while (*p && !g_ascii_isspace(*p)) p++;
3344
                while (g_ascii_isspace(*p)) p++;
3345
        }
3346

    
3347
        return flags;
3348
}
3349

    
3350
static IMAPFlags imap_parse_imap_flags(const gchar *flag_str)  
3351
{
3352
        const gchar *p = flag_str;
3353
        IMAPFlags flags = 0;
3354

    
3355
        while (*p != '\0') {
3356
                if (g_ascii_strncasecmp(p, "\\Seen", 5) == 0) {
3357
                        flags |= IMAP_FLAG_SEEN;
3358
                } else if (g_ascii_strncasecmp(p, "\\Deleted", 8) == 0) {
3359
                        flags |= IMAP_FLAG_DELETED;
3360
                } else if (g_ascii_strncasecmp(p, "\\Flagged", 8) == 0) {
3361
                        flags |= IMAP_FLAG_FLAGGED;
3362
                } else if (g_ascii_strncasecmp(p, "\\Answered", 9) == 0) {
3363
                        flags |= IMAP_FLAG_ANSWERED;
3364
                } else if (g_ascii_strncasecmp(p, "$label", 6) == 0) {
3365
                        /* color labels */
3366
                        if (*(p + 6) >= '1' && *(p + 6) <= '7') {
3367
                                guint color = *(p + 6) - '1' + 1;
3368
                                MSG_UNSET_FLAGS(flags, MSG_CLABEL_FLAG_MASK);
3369
                                IMAP_SET_COLORLABEL_VALUE(flags, color);
3370
                        }
3371
                }
3372

    
3373
                while (*p && !g_ascii_isspace(*p)) p++;
3374
                while (g_ascii_isspace(*p)) p++;
3375
        }
3376

    
3377
        return flags;
3378
}
3379

    
3380
static MsgInfo *imap_parse_envelope(IMAPSession *session, FolderItem *item,
3381
                                    GString *line_str)
3382
{
3383
        gchar buf[IMAPBUFSIZE];
3384
        MsgInfo *msginfo = NULL;
3385
        gchar *cur_pos;
3386
        gint msgnum;
3387
        guint32 uid = 0;
3388
        size_t size = 0;
3389
        MsgFlags flags = {0, 0}, imap_flags = {0, 0};
3390

    
3391
        g_return_val_if_fail(line_str != NULL, NULL);
3392
        g_return_val_if_fail(line_str->str[0] == '*' &&
3393
                             line_str->str[1] == ' ', NULL);
3394

    
3395
        MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3396
        if (item->stype == F_QUEUE) {
3397
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3398
        } else if (item->stype == F_DRAFT) {
3399
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3400
        }
3401

    
3402
        cur_pos = line_str->str + 2;
3403

    
3404
#define PARSE_ONE_ELEMENT(ch)                                        \
3405
{                                                                \
3406
        cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));        \
3407
        if (cur_pos == NULL) {                                        \
3408
                g_warning("cur_pos == NULL\n");                        \
3409
                procmsg_msginfo_free(msginfo);                        \
3410
                return NULL;                                        \
3411
        }                                                        \
3412
}
3413

    
3414
        PARSE_ONE_ELEMENT(' ');
3415
        msgnum = atoi(buf);
3416

    
3417
        PARSE_ONE_ELEMENT(' ');
3418
        g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
3419

    
3420
        g_return_val_if_fail(*cur_pos == '(', NULL);
3421
        cur_pos++;
3422

    
3423
        while (*cur_pos != '\0' && *cur_pos != ')') {
3424
                while (*cur_pos == ' ') cur_pos++;
3425

    
3426
                if (!strncmp(cur_pos, "UID ", 4)) {
3427
                        cur_pos += 4;
3428
                        uid = strtoul(cur_pos, &cur_pos, 10);
3429
                } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
3430
                        cur_pos += 6;
3431
                        if (*cur_pos != '(') {
3432
                                g_warning("FLAGS: *cur_pos != '('\n");
3433
                                procmsg_msginfo_free(msginfo);
3434
                                return NULL;
3435
                        }
3436
                        cur_pos++;
3437
                        PARSE_ONE_ELEMENT(')');
3438
                        imap_flags = imap_parse_flags(buf);
3439
                } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
3440
                        cur_pos += 12;
3441
                        size = strtol(cur_pos, &cur_pos, 10);
3442
                } else if (!strncmp(cur_pos, "RFC822.HEADER", 13)) {
3443
                        gchar *headers;
3444

    
3445
                        cur_pos += 13;
3446
                        cur_pos = imap_get_header(session, cur_pos, &headers,
3447
                                                  line_str);
3448
                        if (cur_pos == NULL) {
3449
                                g_warning("RFC822.HEADER: cur_pos == NULL\n");
3450
                                procmsg_msginfo_free(msginfo);
3451
                                return NULL;
3452
                        }
3453
                        if (!msginfo)
3454
                                msginfo = procheader_parse_str(headers, flags, FALSE);
3455
                        g_free(headers);
3456
                } else {
3457
                        g_warning("invalid FETCH response: %s\n", cur_pos);
3458
                        break;
3459
                }
3460
        }
3461

    
3462
#undef PARSE_ONE_ELEMENT
3463

    
3464
        if (msginfo) {
3465
                msginfo->msgnum = uid;
3466
                msginfo->size = size;
3467
                msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
3468
                msginfo->flags.perm_flags = imap_flags.perm_flags;
3469
        }
3470

    
3471
        return msginfo;
3472
}
3473

    
3474
static gint imap_msg_list_change_perm_flags(GSList *msglist, MsgPermFlags flags,
3475
                                            gboolean is_set)
3476
{
3477
        Folder *folder;
3478
        IMAPSession *session;
3479
        IMAPFlags iflags = 0;
3480
        MsgInfo *msginfo;
3481
        GSList *seq_list, *cur;
3482
        gint ok = IMAP_SUCCESS;
3483

    
3484
        if (msglist == NULL) return IMAP_SUCCESS;
3485

    
3486
        msginfo = (MsgInfo *)msglist->data;
3487
        g_return_val_if_fail(msginfo != NULL, -1);
3488

    
3489
        g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
3490
        g_return_val_if_fail(msginfo->folder != NULL, -1);
3491
        g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
3492

    
3493
        folder = msginfo->folder->folder;
3494
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
3495

    
3496
        session = imap_session_get(folder);
3497
        if (!session) return -1;
3498

    
3499
        ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3500
                         NULL, NULL, NULL, NULL);
3501
        if (ok != IMAP_SUCCESS)
3502
                return ok;
3503

    
3504
        seq_list = imap_get_seq_set_from_msglist(msglist, 0);
3505

    
3506
        if (flags & MSG_MARKED)  iflags |= IMAP_FLAG_FLAGGED;
3507
        if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
3508

    
3509
        for (cur = seq_list; cur != NULL; cur = cur->next) {
3510
                gchar *seq_set = (gchar *)cur->data;
3511

    
3512
                if (iflags) {
3513
                        ok = imap_set_message_flags(session, seq_set, iflags,
3514
                                                    is_set);
3515
                        if (ok != IMAP_SUCCESS) break;
3516
                }
3517

    
3518
                if (flags & MSG_UNREAD) {
3519
                        ok = imap_set_message_flags(session, seq_set,
3520
                                                    IMAP_FLAG_SEEN, !is_set);
3521
                        if (ok != IMAP_SUCCESS) break;
3522
                }
3523
        }
3524

    
3525
        imap_seq_set_free(seq_list);
3526

    
3527
        return ok;
3528
}
3529

    
3530
gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3531
{
3532
        GSList msglist;
3533

    
3534
        msglist.data = msginfo;
3535
        msglist.next = NULL;
3536

    
3537
        return imap_msg_list_change_perm_flags(&msglist, flags, TRUE);
3538
}
3539

    
3540
gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3541
{
3542
        GSList msglist;
3543

    
3544
        msglist.data = msginfo;
3545
        msglist.next = NULL;
3546

    
3547
        return imap_msg_list_change_perm_flags(&msglist, flags, FALSE);
3548
}
3549

    
3550
gint imap_msg_list_set_perm_flags(GSList *msglist, MsgPermFlags flags)
3551
{
3552
        return imap_msg_list_change_perm_flags(msglist, flags, TRUE);
3553
}
3554

    
3555
gint imap_msg_list_unset_perm_flags(GSList *msglist, MsgPermFlags flags)
3556
{
3557
        return imap_msg_list_change_perm_flags(msglist, flags, FALSE);
3558
}
3559

    
3560
gint imap_msg_list_set_colorlabel_flags(GSList *msglist, guint color)
3561
{
3562
        Folder *folder;
3563
        IMAPSession *session;
3564
        IMAPFlags iflags = 0;
3565
        MsgInfo *msginfo;
3566
        GSList *seq_list, *cur;
3567
        gint ok = IMAP_SUCCESS;
3568

    
3569
        if (msglist == NULL) return IMAP_SUCCESS;
3570

    
3571
        msginfo = (MsgInfo *)msglist->data;
3572
        g_return_val_if_fail(msginfo != NULL, -1);
3573

    
3574
        g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
3575
        g_return_val_if_fail(msginfo->folder != NULL, -1);
3576
        g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
3577

    
3578
        folder = msginfo->folder->folder;
3579
        g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
3580

    
3581
        session = imap_session_get(folder);
3582
        if (!session) return -1;
3583

    
3584
        ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3585
                         NULL, NULL, NULL, NULL);
3586
        if (ok != IMAP_SUCCESS)
3587
                return ok;
3588

    
3589
        seq_list = imap_get_seq_set_from_msglist(msglist, 0);
3590

    
3591
        IMAP_SET_COLORLABEL_VALUE(iflags, color);
3592

    
3593
        for (cur = seq_list; cur != NULL; cur = cur->next) {
3594
                gchar *seq_set = (gchar *)cur->data;
3595

    
3596
                ok = imap_cmd_store(session, seq_set,
3597
                                    "-FLAGS.SILENT ($label1 $label2 $label3 $label4 $label5 $label6 $label7)");
3598
                if (ok != IMAP_SUCCESS) break;
3599

    
3600
                if (iflags) {
3601
                        ok = imap_set_message_flags(session, seq_set, iflags,
3602
                                                    TRUE);
3603
                        if (ok != IMAP_SUCCESS) break;
3604
                }
3605
        }
3606

    
3607
        imap_seq_set_free(seq_list);
3608

    
3609
        return ok;
3610
}
3611

    
3612
static gchar *imap_get_flag_str(IMAPFlags flags)
3613
{
3614
        GString *str;
3615
        gchar *ret;
3616
        guint color;
3617

    
3618
        str = g_string_new(NULL);
3619

    
3620
        if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
3621
        if (IMAP_IS_ANSWERED(flags))        g_string_append(str, "\\Answered ");
3622
        if (IMAP_IS_FLAGGED(flags))        g_string_append(str, "\\Flagged ");
3623
        if (IMAP_IS_DELETED(flags))        g_string_append(str, "\\Deleted ");
3624
        if (IMAP_IS_DRAFT(flags))        g_string_append(str, "\\Draft ");
3625

    
3626
        if ((color = IMAP_GET_COLORLABEL_VALUE(flags)) != 0) {
3627
                g_string_append_printf(str, "$label%u", color);
3628
        }
3629

    
3630
        if (str->len > 0 && str->str[str->len - 1] == ' ')
3631
                g_string_truncate(str, str->len - 1);
3632

    
3633
        ret = str->str;
3634
        g_string_free(str, FALSE);
3635

    
3636
        return ret;
3637
}
3638

    
3639
static gint imap_set_message_flags(IMAPSession *session,
3640
                                   const gchar *seq_set,
3641
                                   IMAPFlags flags,
3642
                                   gboolean is_set)
3643
{
3644
        gchar *cmd;
3645
        gchar *flag_str;
3646
        gint ok;
3647

    
3648
        flag_str = imap_get_flag_str(flags);
3649
        cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
3650
                          flag_str, ")", NULL);
3651
        g_free(flag_str);
3652

    
3653
        ok = imap_cmd_store(session, seq_set, cmd);
3654
        g_free(cmd);
3655

    
3656
        return ok;
3657
}
3658

    
3659
static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3660
                        const gchar *path,
3661
                        gint *exists, gint *recent, gint *unseen,
3662
                        guint32 *uid_validity)
3663
{
3664
        gchar *real_path;
3665
        gint ok;
3666
        gint exists_, recent_, unseen_;
3667
        guint32 uid_validity_;
3668

    
3669
        if (!exists || !recent || !unseen || !uid_validity) {
3670
                if (session->mbox && strcmp(session->mbox, path) == 0)
3671
                        return IMAP_SUCCESS;
3672
                exists = &exists_;
3673
                recent = &recent_;
3674
                unseen = &unseen_;
3675
                uid_validity = &uid_validity_;
3676
        }
3677

    
3678
        g_free(session->mbox);
3679
        session->mbox = NULL;
3680

    
3681
        real_path = imap_get_real_path(folder, path);
3682
        ok = imap_cmd_select(session, real_path,
3683
                             exists, recent, unseen, uid_validity);
3684
        if (ok != IMAP_SUCCESS)
3685
                log_warning(_("can't select folder: %s\n"), real_path);
3686
        else
3687
                session->mbox = g_strdup(path);
3688
        g_free(real_path);
3689

    
3690
        return ok;
3691
}
3692

    
3693
#define THROW(err) { ok = err; goto catch; }
3694

    
3695
static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3696
                        const gchar *path,
3697
                        gint *messages, gint *recent,
3698
                        guint32 *uid_next, guint32 *uid_validity,
3699
                        gint *unseen)
3700
{
3701
        gchar *real_path;
3702
        gchar *real_path_;
3703
        gint ok;
3704
        GPtrArray *argbuf = NULL;
3705
        gchar *str;
3706

    
3707
        if (messages && recent && uid_next && uid_validity && unseen) {
3708
                *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
3709
                argbuf = g_ptr_array_new();
3710
        }
3711

    
3712
        real_path = imap_get_real_path(folder, path);
3713
        QUOTE_IF_REQUIRED(real_path_, real_path);
3714
        ok = imap_cmd_gen_send(session, "STATUS %s "
3715
                               "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
3716
                               real_path_);
3717
        if (ok != IMAP_SUCCESS) {
3718
                log_warning("error on sending imap command: STATUS\n");
3719
                THROW(ok);
3720
        }
3721
        ok = imap_cmd_ok(session, argbuf);
3722
        if (ok != IMAP_SUCCESS)
3723
                log_warning(_("error on imap command: STATUS\n"));
3724
        if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
3725

    
3726
        str = search_array_str(argbuf, "STATUS");
3727
        if (!str) THROW(IMAP_ERROR);
3728

    
3729
        str = strrchr_with_skip_quote(str, '"', '(');
3730
        if (!str) THROW(IMAP_ERROR);
3731
        str++;
3732
        while (*str != '\0' && *str != ')') {
3733
                while (*str == ' ') str++;
3734

    
3735
                if (!strncmp(str, "MESSAGES ", 9)) {
3736
                        str += 9;
3737
                        *messages = strtol(str, &str, 10);
3738
                } else if (!strncmp(str, "RECENT ", 7)) {
3739
                        str += 7;
3740
                        *recent = strtol(str, &str, 10);
3741
                } else if (!strncmp(str, "UIDNEXT ", 8)) {
3742
                        str += 8;
3743
                        *uid_next = strtoul(str, &str, 10);
3744
                } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
3745
                        str += 12;
3746
                        *uid_validity = strtoul(str, &str, 10);
3747
                } else if (!strncmp(str, "UNSEEN ", 7)) {
3748
                        str += 7;
3749
                        *unseen = strtol(str, &str, 10);
3750
                } else {
3751
                        g_warning("invalid STATUS response: %s\n", str);
3752
                        break;
3753
                }
3754
        }
3755

    
3756
catch:
3757
        g_free(real_path);
3758
        if (argbuf) {
3759
                ptr_array_free_strings(argbuf);
3760
                g_ptr_array_free(argbuf, TRUE);
3761
        }
3762

    
3763
        return ok;
3764
}
3765

    
3766
#undef THROW
3767

    
3768
static gboolean imap_has_capability(IMAPSession        *session,
3769
                                    const gchar *capability)
3770
{
3771
        gchar **p;
3772

    
3773
        for (p = session->capability; *p != NULL; ++p) {
3774
                if (!g_ascii_strcasecmp(*p, capability))
3775
                        return TRUE;
3776
        }
3777

    
3778
        return FALSE;
3779
}
3780

    
3781
static void imap_capability_free(IMAPSession *session)
3782
{
3783
        if (session->capability) {
3784
                g_strfreev(session->capability);
3785
                session->capability = NULL;
3786
        }
3787
}
3788

    
3789

    
3790
/* low-level IMAP4rev1 commands */
3791

    
3792
#define THROW(err) { ok = err; goto catch; }
3793

    
3794
static gint imap_cmd_capability(IMAPSession *session)
3795
{
3796
        gint ok;
3797
        GPtrArray *argbuf;
3798
        gchar *capability;
3799

    
3800
        argbuf = g_ptr_array_new();
3801

    
3802
        if ((ok = imap_cmd_gen_send(session, "CAPABILITY")) != IMAP_SUCCESS)
3803
                THROW(ok);
3804
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3805

    
3806
        capability = search_array_str(argbuf, "CAPABILITY ");
3807
        if (!capability) THROW(IMAP_ERROR);
3808

    
3809
        capability += strlen("CAPABILITY ");
3810

    
3811
        imap_capability_free(session);
3812
        session->capability = g_strsplit(capability, " ", -1);
3813

    
3814
catch:
3815
        ptr_array_free_strings(argbuf);
3816
        g_ptr_array_free(argbuf, TRUE);
3817

    
3818
        return ok;
3819
}
3820

    
3821
#undef THROW
3822

    
3823
static gint imap_cmd_auth_plain(IMAPSession *session, const gchar *user,
3824
                                const gchar *pass)
3825
{
3826
        gchar *p;
3827
        gchar *response;
3828
        gchar *response64;
3829
        gint ok;
3830

    
3831
        p = response = g_malloc(strlen(user) * 2 + 2 + strlen(pass) + 1);
3832
        strcpy(p, user);
3833
        p += strlen(user) + 1;
3834
        strcpy(p, user);
3835
        p += strlen(user) + 1;
3836
        strcpy(p, pass);
3837
        p += strlen(pass);
3838

    
3839
        response64 = g_malloc((p - response) * 2 + 1);
3840
        base64_encode(response64, (guchar *)response, p - response);
3841
        g_free(response);
3842

    
3843
        log_print("IMAP4> ****************\n");
3844
        sock_puts(SESSION(session)->sock, response64);
3845
        ok = imap_cmd_ok(session, NULL);
3846
        if (ok != IMAP_SUCCESS)
3847
                log_warning(_("IMAP4 authentication failed.\n"));
3848
        g_free(response64);
3849

    
3850
        return ok;
3851
}
3852

    
3853
static gint imap_cmd_auth_cram_md5(IMAPSession *session, const gchar *user,
3854
                                   const gchar *pass, const gchar *challenge64)
3855
{
3856
        gchar *challenge;
3857
        gint challenge_len;
3858
        gchar hexdigest[33];
3859
        gchar *response;
3860
        gchar *response64;
3861
        gint ok;
3862

    
3863
        challenge = g_malloc(strlen(challenge64 + 2) + 1);
3864
        challenge_len = base64_decode((guchar *)challenge, challenge64 + 2, -1);
3865
        challenge[challenge_len] = '\0';
3866
        log_print("IMAP< [Decoded: %s]\n", challenge);
3867

    
3868
        md5_hex_hmac(hexdigest, (guchar *)challenge, challenge_len,
3869
                     (guchar *)pass, strlen(pass));
3870
        g_free(challenge);
3871

    
3872
        response = g_strdup_printf("%s %s", user, hexdigest);
3873
        log_print("IMAP> [Encoded: %s]\n", response);
3874
        response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3875
        base64_encode(response64, (guchar *)response, strlen(response));
3876
        g_free(response);
3877

    
3878
        log_print("IMAP> %s\n", response64);
3879
        sock_puts(SESSION(session)->sock, response64);
3880
        ok = imap_cmd_ok(session, NULL);
3881
        if (ok != IMAP_SUCCESS)
3882
                log_warning(_("IMAP4 authentication failed.\n"));
3883

    
3884
        return ok;
3885
}
3886

    
3887
static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
3888
                                  const gchar *pass, IMAPAuthType type)
3889
{
3890
        gchar *auth_type;
3891
        gint ok;
3892
        gchar *buf = NULL;
3893

    
3894
        g_return_val_if_fail((type == 0 || type == IMAP_AUTH_CRAM_MD5 ||
3895
                              type == IMAP_AUTH_PLAIN), IMAP_ERROR);
3896

    
3897
        if (type == IMAP_AUTH_PLAIN)
3898
                auth_type = "PLAIN";
3899
        else
3900
                auth_type = "CRAM-MD5";
3901

    
3902
        ok = imap_cmd_gen_send(session, "AUTHENTICATE %s", auth_type);
3903
        if (ok != IMAP_SUCCESS) {
3904
                g_free(buf);
3905
                return ok;
3906
        }
3907
        ok = imap_cmd_gen_recv(session, &buf);
3908
        if (ok != IMAP_SUCCESS || buf[0] != '+') {
3909
                g_free(buf);
3910
                return IMAP_ERROR;
3911
        }
3912

    
3913
        if (type == IMAP_AUTH_PLAIN)
3914
                ok = imap_cmd_auth_plain(session, user, pass);
3915
        else
3916
                ok = imap_cmd_auth_cram_md5(session, user, pass, buf);
3917

    
3918
        g_free(buf);
3919

    
3920
        return ok;
3921
}
3922

    
3923
static gint imap_cmd_login(IMAPSession *session,
3924
                           const gchar *user, const gchar *pass)
3925
{
3926
        gchar *user_, *pass_;
3927
        gint ok;
3928

    
3929
        QUOTE_IF_REQUIRED(user_, user);
3930
        QUOTE_IF_REQUIRED(pass_, pass);
3931
        ok = imap_cmd_gen_send(session, "LOGIN %s %s", user_, pass_);
3932
        if (ok == IMAP_SUCCESS)
3933
                ok = imap_cmd_ok(session, NULL);
3934
        if (ok != IMAP_SUCCESS)
3935
                log_warning(_("IMAP4 login failed.\n"));
3936

    
3937
        return ok;
3938
}
3939

    
3940
static gint imap_cmd_logout(IMAPSession *session)
3941
{
3942
        if (imap_cmd_gen_send(session, "LOGOUT") != IMAP_SUCCESS)
3943
                return IMAP_ERROR;
3944
        return imap_cmd_ok(session, NULL);
3945
}
3946

    
3947
static gint imap_cmd_noop(IMAPSession *session)
3948
{
3949
        gint ret;
3950

    
3951
        ret = imap_cmd_gen_send(session, "NOOP");
3952
        if (ret != IMAP_SUCCESS)
3953
                return ret;
3954
        return imap_cmd_ok(session, NULL);
3955
}
3956

    
3957
#if USE_SSL
3958
static gint imap_cmd_starttls(IMAPSession *session)
3959
{
3960
        if (imap_cmd_gen_send(session, "STARTTLS") != IMAP_SUCCESS)
3961
                return IMAP_ERROR;
3962
        return imap_cmd_ok(session, NULL);
3963
}
3964
#endif
3965

    
3966
#define THROW(err) { ok = err; goto catch; }
3967

    
3968
static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3969
{
3970
        gint ok;
3971
        GPtrArray *argbuf;
3972
        gchar *str;
3973

    
3974
        argbuf = g_ptr_array_new();
3975

    
3976
        if ((ok = imap_cmd_gen_send(session, "NAMESPACE")) != IMAP_SUCCESS)
3977
                THROW(ok);
3978
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3979

    
3980
        str = search_array_str(argbuf, "NAMESPACE");
3981
        if (!str) THROW(IMAP_ERROR);
3982

    
3983
        *ns_str = g_strdup(str);
3984

    
3985
catch:
3986
        ptr_array_free_strings(argbuf);
3987
        g_ptr_array_free(argbuf, TRUE);
3988

    
3989
        return ok;
3990
}
3991

    
3992
#undef THROW
3993

    
3994
static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3995
                          const gchar *mailbox, GPtrArray *argbuf)
3996
{
3997
        gchar *ref_, *mailbox_;
3998

    
3999
        if (!ref) ref = "";
4000
        if (!mailbox) mailbox = "";
4001

    
4002
        QUOTE_IF_REQUIRED(ref_, ref);
4003
        QUOTE_IF_REQUIRED(mailbox_, mailbox);
4004
        if (imap_cmd_gen_send(session, "LIST %s %s", ref_, mailbox_) != IMAP_SUCCESS)
4005
                return IMAP_ERROR;
4006

    
4007
        return imap_cmd_ok(session, argbuf);
4008
}
4009

    
4010
#define THROW goto catch
4011

    
4012
static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
4013
                               gboolean examine,
4014
                               gint *exists, gint *recent, gint *unseen,
4015
                               guint32 *uid_validity)
4016
{
4017
        gint ok;
4018
        gchar *resp_str;
4019
        GPtrArray *argbuf;
4020
        gchar *select_cmd;
4021
        gchar *folder_;
4022
        guint uid_validity_;
4023

    
4024
        *exists = *recent = *unseen = *uid_validity = 0;
4025
        argbuf = g_ptr_array_new();
4026

    
4027
        if (examine)
4028
                select_cmd = "EXAMINE";
4029
        else
4030
                select_cmd = "SELECT";
4031

    
4032
        QUOTE_IF_REQUIRED(folder_, folder);
4033
        if ((ok = imap_cmd_gen_send(session, "%s %s", select_cmd, folder_)) != IMAP_SUCCESS)
4034
                THROW;
4035

    
4036
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
4037

    
4038
        resp_str = search_array_contain_str(argbuf, "EXISTS");
4039
        if (resp_str) {
4040
                if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
4041
                        g_warning("imap_cmd_select(): invalid EXISTS line.\n");
4042
                        THROW;
4043
                }
4044
        }
4045

    
4046
        resp_str = search_array_contain_str(argbuf, "RECENT");
4047
        if (resp_str) {
4048
                if (sscanf(resp_str, "%d RECENT", recent) != 1) {
4049
                        g_warning("imap_cmd_select(): invalid RECENT line.\n");
4050
                        THROW;
4051
                }
4052
        }
4053

    
4054
        resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
4055
        if (resp_str) {
4056
                if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
4057
                    != 1) {
4058
                        g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
4059
                        THROW;
4060
                }
4061
                *uid_validity = uid_validity_;
4062
        }
4063

    
4064
        resp_str = search_array_contain_str(argbuf, "UNSEEN");
4065
        if (resp_str) {
4066
                if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
4067
                        g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
4068
                        THROW;
4069
                }
4070
        }
4071

    
4072
catch:
4073
        ptr_array_free_strings(argbuf);
4074
        g_ptr_array_free(argbuf, TRUE);
4075

    
4076
        return ok;
4077
}
4078

    
4079
static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
4080
                            gint *exists, gint *recent, gint *unseen,
4081
                            guint32 *uid_validity)
4082
{
4083
        return imap_cmd_do_select(session, folder, FALSE,
4084
                                  exists, recent, unseen, uid_validity);
4085
}
4086

    
4087
static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
4088
                             gint *exists, gint *recent, gint *unseen,
4089
                             guint32 *uid_validity)
4090
{
4091
        return imap_cmd_do_select(session, folder, TRUE,
4092
                                  exists, recent, unseen, uid_validity);
4093
}
4094

    
4095
#undef THROW
4096

    
4097
static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
4098
{
4099
        gchar *folder_;
4100

    
4101
        QUOTE_IF_REQUIRED(folder_, folder);
4102
        if (imap_cmd_gen_send(session, "CREATE %s", folder_) != IMAP_SUCCESS)
4103
                return IMAP_ERROR;
4104

    
4105
        return imap_cmd_ok(session, NULL);
4106
}
4107

    
4108
static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
4109
                            const gchar *new_folder)
4110
{
4111
        gchar *old_folder_, *new_folder_;
4112

    
4113
        QUOTE_IF_REQUIRED(old_folder_, old_folder);
4114
        QUOTE_IF_REQUIRED(new_folder_, new_folder);
4115
        if (imap_cmd_gen_send(session, "RENAME %s %s", old_folder_, new_folder_) != IMAP_SUCCESS)
4116
                return IMAP_ERROR;
4117

    
4118
        return imap_cmd_ok(session, NULL);
4119
}
4120

    
4121
static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
4122
{
4123
        gchar *folder_;
4124

    
4125
        QUOTE_IF_REQUIRED(folder_, folder);
4126
        if (imap_cmd_gen_send(session, "DELETE %s", folder_) != IMAP_SUCCESS)
4127
                return IMAP_ERROR;
4128

    
4129
        return imap_cmd_ok(session, NULL);
4130
}
4131

    
4132
#define THROW(err) { ok = err; goto catch; }
4133

    
4134
static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
4135
                            GArray **result)
4136
{
4137
        gint ok;
4138
        GPtrArray *argbuf;
4139
        GArray *array;
4140
        gchar *str;
4141
        gchar *p, *ep;
4142
        gint i;
4143
        guint32 uid;
4144

    
4145
        g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
4146
        g_return_val_if_fail(result != NULL, IMAP_ERROR);
4147

    
4148
        argbuf = g_ptr_array_new();
4149

    
4150
        if ((ok = imap_cmd_gen_send(session, "UID SEARCH %s", criteria)) != IMAP_SUCCESS)
4151
                THROW(ok);
4152
        if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
4153

    
4154
        array = g_array_new(FALSE, FALSE, sizeof(guint32));
4155

    
4156
        for (i = 0; i < argbuf->len; i++) {
4157
                str = g_ptr_array_index(argbuf, i);
4158
                if (strncmp(str, "SEARCH", 6) != 0)
4159
                        continue;
4160

    
4161
                p = str + 6;
4162
                while (*p != '\0') {
4163
                        uid = strtoul(p, &ep, 10);
4164
                        if (p < ep && uid > 0) {
4165
                                g_array_append_val(array, uid);
4166
                                p = ep;
4167
                        } else
4168
                                break;
4169
                }
4170
        }
4171

    
4172
        *result = array;
4173

    
4174
catch:
4175
        ptr_array_free_strings(argbuf);
4176
        g_ptr_array_free(argbuf, TRUE);
4177

    
4178
        return ok;
4179
}
4180

    
4181
typedef struct _IMAPCmdFetchData
4182
{
4183
        guint32 uid;
4184
        const gchar *filename;
4185
} IMAPCmdFetchData;
4186

    
4187
#define THROW(err) { ok = err; goto catch; }
4188

    
4189
static gint imap_cmd_fetch_func(IMAPSession *session, gpointer data)
4190
{
4191
        const gchar *filename = ((IMAPCmdFetchData *)data)->filename;
4192
        gint ok;
4193
        gchar *buf;
4194
        gchar *cur_pos;
4195
        gchar size_str[32];
4196
        glong size_num;
4197
        gint ret;
4198

    
4199
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
4200
                if (buf[0] != '*' || buf[1] != ' ') {
4201
                        g_free(buf);
4202
                        return IMAP_ERROR;
4203
                }
4204
                if (strstr(buf, "FETCH") != NULL && strstr(buf, "BODY") != NULL)
4205
                        break;
4206
                g_free(buf);
4207
        }
4208
        if (ok != IMAP_SUCCESS)
4209
                THROW(ok);
4210

    
4211
#define RETURN_ERROR_IF_FAIL(cond)                        \
4212
        if (!(cond)) {                                        \
4213
                g_free(buf);                                \
4214
                ok = imap_cmd_ok_real(session, NULL);        \
4215
                THROW(IMAP_ERROR);                        \
4216
        }
4217

    
4218
        cur_pos = strchr(buf, '{');
4219
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
4220
        cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
4221
        RETURN_ERROR_IF_FAIL(cur_pos != NULL);
4222
        size_num = atol(size_str);
4223
        RETURN_ERROR_IF_FAIL(size_num >= 0);
4224

    
4225
        RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
4226

    
4227
#undef RETURN_ERROR_IF_FAIL
4228

    
4229
        g_free(buf);
4230

    
4231
        if ((ret = recv_bytes_write_to_file(SESSION(session)->sock,
4232
                                            size_num, filename)) != 0) {
4233
                if (ret == -2)
4234
                        THROW(IMAP_SOCKET);
4235
        }
4236

    
4237
        if (imap_cmd_gen_recv(session, &buf) != IMAP_SUCCESS)
4238
                THROW(IMAP_ERROR);
4239

    
4240
        if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
4241
                g_free(buf);
4242
                THROW(IMAP_ERROR);
4243
        }
4244
        g_free(buf);
4245

    
4246
        ok = imap_cmd_ok_real(session, NULL);
4247

    
4248
        if (ret != 0)
4249
                THROW(IMAP_ERROR);
4250

    
4251
catch:
4252
        return ok;
4253
}
4254

    
4255
#undef THROW
4256

    
4257
static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
4258
                           const gchar *filename)
4259
{
4260
        gint ok;
4261
        IMAPCmdFetchData fetch_data = {uid, filename};
4262

    
4263
        g_return_val_if_fail(filename != NULL, IMAP_ERROR);
4264

    
4265
        ok = imap_cmd_gen_send(session, "UID FETCH %u BODY.PEEK[]", uid);
4266
        if (ok != IMAP_SUCCESS)
4267
                return ok;
4268

    
4269
#if USE_THREADS
4270
        ok = imap_thread_run(session, imap_cmd_fetch_func, &fetch_data);
4271
#else
4272
        ok = imap_cmd_fetch_func(session, &fetch_data);
4273
#endif
4274

    
4275
        return ok;
4276
}
4277

    
4278
static void imap_get_date_time(gchar *buf, size_t len, time_t timer)
4279
{
4280
        static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
4281
        struct tm *lt;
4282
        gchar date_time[64];
4283
        gchar tz[6];
4284

    
4285
        lt = localtime(&timer);
4286
        if (lt && lt->tm_mon >= 0 && lt->tm_mon < 12) {
4287
                strftime(date_time, sizeof(date_time), "%d-___-%Y %H:%M:%S",
4288
                         lt);
4289
                tzoffset_buf(tz, &timer);
4290
                memcpy(date_time + 3, monthstr + lt->tm_mon * 3, 3);
4291
                g_snprintf(buf, len, "%s %s", date_time, tz);
4292
        }
4293
}
4294

    
4295
static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
4296
                            const gchar *file, IMAPFlags flags,
4297
                            guint32 *new_uid)
4298
{
4299
        gint ok;
4300
        MsgInfo *msginfo;
4301
        MsgFlags flags_ = {0, 0};
4302
        gchar date_time[64] = "";
4303
        gint size;
4304
        gchar *destfolder_;
4305
        gchar *flag_str;
4306
        guint new_uid_;
4307
        gchar *ret = NULL;
4308
        gchar buf[BUFFSIZE];
4309
        FILE *fp;
4310
        FILE *tmp;
4311
        size_t read_len;
4312
        GPtrArray *argbuf;
4313
        gchar *resp_str;
4314

    
4315
        g_return_val_if_fail(file != NULL, IMAP_ERROR);
4316

    
4317
        if ((fp = g_fopen(file, "rb")) == NULL) {
4318
                FILE_OP_ERROR(file, "fopen");
4319
                return -1;
4320
        }
4321

    
4322
        /* use Date: header as received date */
4323
        msginfo = procheader_parse_stream(fp, flags_, FALSE);
4324
        imap_get_date_time(date_time, sizeof(date_time), msginfo->date_t);
4325
        procmsg_msginfo_free(msginfo);
4326

    
4327
        rewind(fp);
4328
        tmp = canonicalize_file_stream(fp, &size);
4329
        fclose(fp);
4330
        if (!tmp)
4331
                return -1;
4332

    
4333
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
4334
        flag_str = imap_get_flag_str(flags);
4335
        if (date_time[0])
4336
                ok = imap_cmd_gen_send(session, "APPEND %s (%s) \"%s\" {%d}",
4337
                                       destfolder_, flag_str, date_time, size);
4338
        else
4339
                ok = imap_cmd_gen_send(session, "APPEND %s (%s) {%d}",
4340
                                       destfolder_, flag_str, size);
4341
        g_free(flag_str);
4342
        if (ok != IMAP_SUCCESS) {
4343
                log_warning(_("can't append %s to %s\n"), file, destfolder_);
4344
                fclose(tmp);
4345
                return ok;
4346
        }
4347

    
4348
        ok = imap_cmd_gen_recv(session, &ret);
4349
        if (ok != IMAP_SUCCESS || ret[0] != '+') {
4350
                log_warning(_("can't append %s to %s\n"), file, destfolder_);
4351
                g_free(ret);
4352
                fclose(tmp);
4353
                return IMAP_ERROR;
4354
        }
4355
        g_free(ret);
4356

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

    
4359
        while ((read_len = fread(buf, 1, sizeof(buf), tmp)) > 0) {
4360
                if (read_len < sizeof(buf) && ferror(tmp))
4361
                        break;
4362
                if (sock_write_all(SESSION(session)->sock, buf, read_len) < 0) {
4363
                        fclose(tmp);
4364
                        return -1;
4365
                }
4366
        }
4367

    
4368
        if (ferror(tmp)) {
4369
                FILE_OP_ERROR(file, "fread");
4370
                fclose(tmp);
4371
                return -1;
4372
        }
4373

    
4374
        sock_puts(SESSION(session)->sock, "");
4375

    
4376
        fclose(tmp);
4377

    
4378
        if (new_uid != NULL)
4379
                *new_uid = 0;
4380

    
4381
        if (new_uid != NULL && session->uidplus) {
4382
                argbuf = g_ptr_array_new();
4383

    
4384
                ok = imap_cmd_ok(session, argbuf);
4385
                if (ok != IMAP_SUCCESS)
4386
                        log_warning(_("can't append message to %s\n"),
4387
                                    destfolder_);
4388
                else if (argbuf->len > 0) {
4389
                        resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
4390
                        if (resp_str &&
4391
                            sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
4392
                                   &new_uid_) == 1) {
4393
                                *new_uid = new_uid_;
4394
                        }
4395
                }
4396

    
4397
                ptr_array_free_strings(argbuf);
4398
                g_ptr_array_free(argbuf, TRUE);
4399
        } else
4400
                ok = imap_cmd_ok(session, NULL);
4401

    
4402
        return ok;
4403
}
4404

    
4405
static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
4406
                          const gchar *destfolder)
4407
{
4408
        gint ok;
4409
        gchar *destfolder_;
4410

    
4411
        g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
4412

    
4413
        QUOTE_IF_REQUIRED(destfolder_, destfolder);
4414
        ok = imap_cmd_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
4415
        if (ok == IMAP_SUCCESS)
4416
                ok = imap_cmd_ok(session, NULL);
4417
        if (ok != IMAP_SUCCESS) {
4418
                log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
4419
                return -1;
4420
        }
4421

    
4422
        return ok;
4423
}
4424

    
4425
gint imap_cmd_envelope(IMAPSession *session, const gchar *seq_set)
4426
{
4427
        return imap_cmd_gen_send
4428
                (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
4429
                 seq_set);
4430
}
4431

    
4432
static gint imap_cmd_store(IMAPSession *session, const gchar *seq_set,
4433
                           const gchar *sub_cmd)
4434
{
4435
        gint ok;
4436

    
4437
        ok = imap_cmd_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
4438
        if (ok == IMAP_SUCCESS)
4439
                ok = imap_cmd_ok(session, NULL);
4440
        if (ok != IMAP_SUCCESS) {
4441
                log_warning(_("error while imap command: STORE %s %s\n"),
4442
                            seq_set, sub_cmd);
4443
                return ok;
4444
        }
4445

    
4446
        return IMAP_SUCCESS;
4447
}
4448

    
4449
static gint imap_cmd_expunge(IMAPSession *session)
4450
{
4451
        gint ok;
4452

    
4453
        ok = imap_cmd_gen_send(session, "EXPUNGE");
4454
        if (ok == IMAP_SUCCESS)
4455
                ok = imap_cmd_ok(session, NULL);
4456
        if (ok != IMAP_SUCCESS) {
4457
                log_warning(_("error while imap command: EXPUNGE\n"));
4458
                return ok;
4459
        }
4460

    
4461
        return IMAP_SUCCESS;
4462
}
4463

    
4464
static gint imap_cmd_close(IMAPSession *session)
4465
{
4466
        gint ok;
4467

    
4468
        ok = imap_cmd_gen_send(session, "CLOSE");
4469
        if (ok == IMAP_SUCCESS)
4470
                ok = imap_cmd_ok(session, NULL);
4471
        if (ok != IMAP_SUCCESS)
4472
                log_warning(_("error while imap command: CLOSE\n"));
4473

    
4474
        return ok;
4475
}
4476

    
4477
static gint imap_cmd_ok_real(IMAPSession *session, GPtrArray *argbuf)
4478
{
4479
        gint ok;
4480
        gchar *buf;
4481
        gint cmd_num;
4482
        gchar cmd_status[IMAPBUFSIZE + 1];
4483
        GString *str;
4484
        gchar *p;
4485
        gchar obuf[32];
4486
        gint len;
4487
        gchar *literal;
4488

    
4489
        str = g_string_sized_new(256);
4490

    
4491
        //g_usleep(800000);
4492
        while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
4493
                g_string_append(str, buf);
4494

    
4495
                if ((p = strrchr_with_skip_quote(buf, '"', '{'))) {
4496
                        /* literal */
4497
                        p = strchr_cpy(p + 1, '}', obuf, sizeof(obuf));
4498
                        len = atoi(obuf);
4499
                        if (len < 0 || p == NULL || *p != '\0') {
4500
                                g_free(buf);
4501
                                ok = IMAP_ERROR;
4502
                                break;
4503
                        }
4504

    
4505
                        literal = recv_bytes(SESSION(session)->sock, len);
4506
                        if (!literal) {
4507
                                g_free(buf);
4508
                                ok = IMAP_SOCKET;
4509
                                break;
4510
                        }
4511
                        if (memchr(literal, '\n', len))
4512
                                log_print("IMAP4< (literal: %d bytes)\n", len);
4513
                        else
4514
                                log_print("IMAP4< %s\n", literal);
4515

    
4516
                        g_string_append(str, "\r\n");
4517
                        g_string_append_len(str, literal, len);
4518
                        g_free(literal);
4519
                        g_free(buf);
4520
                        continue;
4521
                }
4522

    
4523
                g_free(buf);
4524

    
4525
                if (str->str[0] == '*' && str->str[1] == ' ') {
4526
                        if (argbuf)
4527
                                g_ptr_array_add(argbuf, g_strdup(str->str + 2));
4528

    
4529
                        g_string_truncate(str, 0);
4530
                        continue;
4531
                } else if (sscanf(str->str, "%d %" Xstr(IMAPBUFSIZE) "s",
4532
                           &cmd_num, cmd_status) < 2) {
4533
                        ok = IMAP_ERROR;
4534
                } else if (cmd_num == session->cmd_count &&
4535
                           !strcmp(cmd_status, "OK")) {
4536
                        if (argbuf)
4537
                                g_ptr_array_add(argbuf, g_strdup(str->str));
4538
                } else {
4539
                        ok = IMAP_ERROR;
4540
                }
4541

    
4542
                break;
4543
        }
4544

    
4545
        g_string_free(str, TRUE);
4546
        return ok;
4547
}
4548

    
4549
#if USE_THREADS
4550
static gint imap_cmd_ok_func(IMAPSession *session, gpointer data)
4551
{
4552
        GPtrArray *argbuf = (GPtrArray *)data;
4553
        gint ok;
4554

    
4555
        ok = imap_cmd_ok_real(session, argbuf);
4556
        return ok;
4557
}
4558
#endif
4559

    
4560
static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
4561
{
4562
#if USE_THREADS
4563
        return imap_thread_run(session, imap_cmd_ok_func, argbuf);
4564
#else
4565
        return imap_cmd_ok_real(session, argbuf);
4566
#endif
4567
}
4568

    
4569
static gint imap_cmd_gen_send(IMAPSession *session, const gchar *format, ...)
4570
{
4571
        IMAPRealSession *real = (IMAPRealSession *)session;
4572
        gchar buf[IMAPBUFSIZE];
4573
        gchar tmp[IMAPBUFSIZE];
4574
        gchar *p;
4575
        va_list args;
4576

    
4577
        va_start(args, format);
4578
        g_vsnprintf(tmp, sizeof(tmp), format, args);
4579
        va_end(args);
4580

    
4581
#if USE_THREADS
4582
        if (real->is_running) {
4583
                g_warning("imap_cmd_gen_send: cannot send command because another command is already running.");
4584
                return IMAP_EAGAIN;
4585
        }
4586
#endif
4587

    
4588
        session->cmd_count++;
4589

    
4590
        g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp);
4591
        if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) &&
4592
            (p = strchr(tmp + 6, ' '))) {
4593
                *p = '\0';
4594
                log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
4595
        } else
4596
                log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
4597

    
4598
        sock_write_all(SESSION(session)->sock, buf, strlen(buf));
4599

    
4600
        return IMAP_SUCCESS;
4601
}
4602

    
4603
static gint imap_cmd_gen_recv(IMAPSession *session, gchar **ret)
4604
{
4605
        gint len;
4606

    
4607
        if ((len = sock_getline(SESSION(session)->sock, ret)) < 0)
4608
                return IMAP_SOCKET;
4609

    
4610
        strretchomp(*ret);
4611

    
4612
        if (len > 1000) {
4613
                gchar *str;
4614

    
4615
                str = trim_string(*ret, 1000);
4616
                log_print("IMAP4< %s\n", str);
4617
                g_free(str);
4618
        } else
4619
                log_print("IMAP4< %s\n", *ret);
4620

    
4621
        session_set_access_time(SESSION(session));
4622

    
4623
        return IMAP_SUCCESS;
4624
}
4625

    
4626
static gint imap_cmd_gen_recv_silent(IMAPSession *session, gchar **ret)
4627
{
4628
        gint len;
4629

    
4630
        if ((len = sock_getline(SESSION(session)->sock, ret)) < 0)
4631
                return IMAP_SOCKET;
4632

    
4633
        strretchomp(*ret);
4634

    
4635
        session_set_access_time(SESSION(session));
4636

    
4637
        return IMAP_SUCCESS;
4638
}
4639

    
4640

    
4641
/* misc utility functions */
4642

    
4643
static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
4644
{
4645
        gchar *tmp;
4646

    
4647
        dest[0] = '\0';
4648
        tmp = strchr(src, ch);
4649
        if (!tmp)
4650
                return NULL;
4651

    
4652
        memcpy(dest, src, MIN(tmp - src, len - 1));
4653
        dest[MIN(tmp - src, len - 1)] = '\0';
4654

    
4655
        return tmp + 1;
4656
}
4657

    
4658
static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
4659
{
4660
        const gchar *p = src;
4661
        gint n = 0;
4662

    
4663
        g_return_val_if_fail(*p == ch, NULL);
4664

    
4665
        *dest = '\0';
4666
        p++;
4667

    
4668
        while (*p != '\0' && *p != ch) {
4669
                if (n < len - 1) {
4670
                        if (*p == '\\' && *(p + 1) != '\0')
4671
                                p++;
4672
                        *dest++ = *p++;
4673
                } else
4674
                        p++;
4675
                n++;
4676
        }
4677

    
4678
        *dest = '\0';
4679
        return (gchar *)(*p == ch ? p + 1 : p);
4680
}
4681

    
4682
static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
4683
{
4684
        gint i;
4685

    
4686
        for (i = 0; i < array->len; i++) {
4687
                gchar *tmp;
4688

    
4689
                tmp = g_ptr_array_index(array, i);
4690
                if (strstr(tmp, str) != NULL)
4691
                        return tmp;
4692
        }
4693

    
4694
        return NULL;
4695
}
4696

    
4697
static gchar *search_array_str(GPtrArray *array, gchar *str)
4698
{
4699
        gint i;
4700
        gint len;
4701

    
4702
        len = strlen(str);
4703

    
4704
        for (i = 0; i < array->len; i++) {
4705
                gchar *tmp;
4706

    
4707
                tmp = g_ptr_array_index(array, i);
4708
                if (!strncmp(tmp, str, len))
4709
                        return tmp;
4710
        }
4711

    
4712
        return NULL;
4713
}
4714

    
4715
static void imap_path_separator_subst(gchar *str, gchar separator)
4716
{
4717
        gchar *p;
4718
        gboolean in_escape = FALSE;
4719

    
4720
        if (!separator || separator == '/') return;
4721

    
4722
        for (p = str; *p != '\0'; p++) {
4723
                if (*p == '/' && !in_escape)
4724
                        *p = separator;
4725
                else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4726
                        in_escape = TRUE;
4727
                else if (*p == '-' && in_escape)
4728
                        in_escape = FALSE;
4729
        }
4730
}
4731

    
4732
static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
4733
{
4734
        static iconv_t cd = (iconv_t)-1;
4735
        static gboolean iconv_ok = TRUE;
4736
        GString *norm_utf7;
4737
        gchar *norm_utf7_p;
4738
        size_t norm_utf7_len;
4739
        const gchar *p;
4740
        gchar *to_str, *to_p;
4741
        size_t to_len;
4742
        gboolean in_escape = FALSE;
4743

    
4744
        if (!iconv_ok) return g_strdup(mutf7_str);
4745

    
4746
        if (cd == (iconv_t)-1) {
4747
                cd = iconv_open(CS_INTERNAL, CS_UTF_7);
4748
                if (cd == (iconv_t)-1) {
4749
                        g_warning("iconv cannot convert UTF-7 to %s\n",
4750
                                  CS_INTERNAL);
4751
                        iconv_ok = FALSE;
4752
                        return g_strdup(mutf7_str);
4753
                }
4754
        }
4755

    
4756
        /* modified UTF-7 to normal UTF-7 conversion */
4757
        norm_utf7 = g_string_new(NULL);
4758

    
4759
        for (p = mutf7_str; *p != '\0'; p++) {
4760
                /* replace: '&'  -> '+',
4761
                            "&-" -> '&',
4762
                            "+"  -> "+-",
4763
                            escaped ','  -> '/' */
4764
                if (!in_escape && *p == '&') {
4765
                        if (*(p + 1) != '-') {
4766
                                g_string_append_c(norm_utf7, '+');
4767
                                in_escape = TRUE;
4768
                        } else {
4769
                                g_string_append_c(norm_utf7, '&');
4770
                                p++;
4771
                        }
4772
                } else if (!in_escape && *p == '+') {
4773
                        g_string_append(norm_utf7, "+-");
4774
                } else if (in_escape && *p == ',') {
4775
                        g_string_append_c(norm_utf7, '/');
4776
                } else if (in_escape && *p == '-') {
4777
                        g_string_append_c(norm_utf7, '-');
4778
                        in_escape = FALSE;
4779
                } else {
4780
                        g_string_append_c(norm_utf7, *p);
4781
                }
4782
        }
4783

    
4784
        /* somehow iconv() returns error when the last of the string is "+-" */
4785
        g_string_append_c(norm_utf7, '\n');
4786
        norm_utf7_p = norm_utf7->str;
4787
        norm_utf7_len = norm_utf7->len;
4788
        to_len = strlen(mutf7_str) * 5;
4789
        to_p = to_str = g_malloc(to_len + 1);
4790

    
4791
        if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
4792
                  &to_p, &to_len) == -1) {
4793
                g_warning(_("iconv cannot convert UTF-7 to %s\n"), CS_INTERNAL);
4794
                g_string_free(norm_utf7, TRUE);
4795
                g_free(to_str);
4796
                return g_strdup(mutf7_str);
4797
        }
4798

    
4799
        /* second iconv() call for flushing */
4800
        iconv(cd, NULL, NULL, &to_p, &to_len);
4801
        g_string_free(norm_utf7, TRUE);
4802
        *to_p = '\0';
4803
        strretchomp(to_str);
4804

    
4805
        return to_str;
4806
}
4807

    
4808
static gchar *imap_utf8_to_modified_utf7(const gchar *from)
4809
{
4810
        static iconv_t cd = (iconv_t)-1;
4811
        static gboolean iconv_ok = TRUE;
4812
        gchar *norm_utf7, *norm_utf7_p;
4813
        size_t from_len, norm_utf7_len;
4814
        GString *to_str;
4815
        const gchar *from_tmp;
4816
        const gchar *p;
4817
        gchar *to;
4818
        gboolean in_escape = FALSE;
4819

    
4820
        if (!iconv_ok) return g_strdup(from);
4821

    
4822
        if (cd == (iconv_t)-1) {
4823
                cd = iconv_open(CS_UTF_7, CS_INTERNAL);
4824
                if (cd == (iconv_t)-1) {
4825
                        g_warning(_("iconv cannot convert %s to UTF-7\n"),
4826
                                  CS_INTERNAL);
4827
                        iconv_ok = FALSE;
4828
                        return g_strdup(from);
4829
                }
4830
        }
4831

    
4832
        /* UTF-8 to normal UTF-7 conversion */
4833
        from_tmp = from;
4834
        from_len = strlen(from);
4835
        norm_utf7_len = from_len * 5;
4836
        norm_utf7 = g_malloc(norm_utf7_len + 1);
4837
        norm_utf7_p = norm_utf7;
4838

    
4839
        while (from_len > 0) {
4840
                if (*from_tmp == '+') {
4841
                        *norm_utf7_p++ = '+';
4842
                        *norm_utf7_p++ = '-';
4843
                        norm_utf7_len -= 2;
4844
                        from_tmp++;
4845
                        from_len--;
4846
                } else if (g_ascii_isprint(*from_tmp)) {
4847
                        /* printable ascii char */
4848
                        *norm_utf7_p = *from_tmp;
4849
                        norm_utf7_p++;
4850
                        norm_utf7_len--;
4851
                        from_tmp++;
4852
                        from_len--;
4853
                } else {
4854
                        size_t conv_len = 0;
4855

    
4856
                        /* unprintable char: convert to UTF-7 */
4857
                        p = from_tmp;
4858
                        while (!g_ascii_isprint(*p) && conv_len < from_len) {
4859
                                conv_len += g_utf8_skip[*(guchar *)p];
4860
                                p += g_utf8_skip[*(guchar *)p];
4861
                        }
4862

    
4863
                        from_len -= conv_len;
4864
                        if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
4865
                                  &conv_len,
4866
                                  &norm_utf7_p, &norm_utf7_len) == -1) {
4867
                                g_warning("iconv cannot convert %s to UTF-7\n",
4868
                                          CS_INTERNAL);
4869
                                g_free(norm_utf7);
4870
                                return g_strdup(from);
4871
                        }
4872

    
4873
                        /* second iconv() call for flushing */
4874
                        iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
4875
                }
4876
        }
4877

    
4878
        *norm_utf7_p = '\0';
4879
        to_str = g_string_new(NULL);
4880
        for (p = norm_utf7; p < norm_utf7_p; p++) {
4881
                /* replace: '&' -> "&-",
4882
                            '+' -> '&',
4883
                            "+-" -> '+',
4884
                            BASE64 '/' -> ',' */
4885
                if (!in_escape && *p == '&') {
4886
                        g_string_append(to_str, "&-");
4887
                } else if (!in_escape && *p == '+') {
4888
                        if (*(p + 1) == '-') {
4889
                                g_string_append_c(to_str, '+');
4890
                                p++;
4891
                        } else {
4892
                                g_string_append_c(to_str, '&');
4893
                                in_escape = TRUE;
4894
                        }
4895
                } else if (in_escape && *p == '/') {
4896
                        g_string_append_c(to_str, ',');
4897
                } else if (in_escape && *p == '-') {
4898
                        g_string_append_c(to_str, '-');
4899
                        in_escape = FALSE;
4900
                } else {
4901
                        g_string_append_c(to_str, *p);
4902
                }
4903
        }
4904

    
4905
        if (in_escape) {
4906
                in_escape = FALSE;
4907
                g_string_append_c(to_str, '-');
4908
        }
4909

    
4910
        to = g_string_free(to_str, FALSE);
4911
        g_free(norm_utf7);
4912

    
4913
        return to;
4914
}
4915

    
4916
static GSList *imap_get_seq_set_from_msglist(GSList *msglist, gint limit)
4917
{
4918
        GString *str;
4919
        GSList *sorted_list, *cur;
4920
        guint first, last, next;
4921
        gchar *ret_str;
4922
        GSList *ret_list = NULL;
4923
        gint count = 0;
4924

    
4925
        if (msglist == NULL)
4926
                return NULL;
4927

    
4928
        str = g_string_sized_new(256);
4929

    
4930
        sorted_list = g_slist_copy(msglist);
4931
        sorted_list = procmsg_sort_msg_list(sorted_list, SORT_BY_NUMBER,
4932
                                            SORT_ASCENDING);
4933

    
4934
        first = ((MsgInfo *)sorted_list->data)->msgnum;
4935

    
4936
        for (cur = sorted_list; cur != NULL; cur = cur->next) {
4937
                ++count;
4938
                last = ((MsgInfo *)cur->data)->msgnum;
4939
                if (cur->next)
4940
                        next = ((MsgInfo *)cur->next->data)->msgnum;
4941
                else
4942
                        next = 0;
4943

    
4944
                if (limit > 0 && count >= limit) {
4945
                        if (str->len > 0)
4946
                                g_string_append_c(str, ',');
4947
                        if (first == last)
4948
                                g_string_sprintfa(str, "%u", first);
4949
                        else
4950
                                g_string_sprintfa(str, "%u:%u", first, last);
4951

    
4952
                        first = next;
4953

    
4954
                        ret_str = g_strdup(str->str);
4955
                        ret_list = g_slist_append(ret_list, ret_str);
4956
                        g_string_truncate(str, 0);
4957