Statistics
| Revision:

root / libsylph / imap.c @ 2428

History | View | Annotate | Download (118.4 kB)

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