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