Statistics
| Revision:

root / libsylph / imap.c @ 639

History | View | Annotate | Download (101.2 kB)

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