8a6998fa76f1535f1ee1fb833e361e37c28a7afd
[platform/upstream/evolution-data-server.git] / camel / providers / imap / camel-imap-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /* camel-imap-folder.c: Abstract class for an imap folder */
3
4 /* 
5  * Authors: Jeffrey Stedfast <fejj@helixcode.com> 
6  *
7  * Copyright (C) 2000 Helix Code, Inc.
8  *
9  * This program is free software; you can redistribute it and/or 
10  * modify it under the terms of the GNU General Public License as 
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA
23  */
24
25
26 #include <config.h> 
27
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <dirent.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <fcntl.h>
36
37 #include <e-util/e-util.h>
38
39 #include "camel-imap-folder.h"
40 #include "camel-imap-store.h"
41 #include "camel-imap-stream.h"
42 #include "camel-imap-utils.h"
43 #include "string-utils.h"
44 #include "camel-stream.h"
45 #include "camel-stream-fs.h"
46 #include "camel-stream-mem.h"
47 #include "camel-stream-buffer.h"
48 #include "camel-data-wrapper.h"
49 #include "camel-mime-message.h"
50 #include "camel-stream-filter.h"
51 #include "camel-mime-filter-from.h"
52 #include "camel-mime-filter-crlf.h"
53 #include "camel-exception.h"
54 #include "camel-mime-utils.h"
55
56 #define d(x) x
57
58 #define CF_CLASS(o) (CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(o)))
59
60 static CamelFolderClass *parent_class = NULL;
61
62 static void imap_init (CamelFolder *folder, CamelStore *parent_store,
63                        CamelFolder *parent_folder, const gchar *name,
64                        gchar *separator, gboolean path_begns_with_sep,
65                        CamelException *ex);
66 static void imap_finalize (CamelObject *object);
67 static void imap_refresh_info (CamelFolder *folder, CamelException *ex);
68 static void imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
69 static void imap_expunge (CamelFolder *folder, CamelException *ex);
70
71 /* message counts */
72 static gint imap_get_message_count_internal (CamelFolder *folder, CamelException *ex);
73 static gint imap_get_message_count (CamelFolder *folder);
74 static gint imap_get_unread_message_count (CamelFolder *folder);
75
76 /* message manipulation */
77 static CamelMimeMessage *imap_get_message (CamelFolder *folder, const gchar *uid,
78                                            CamelException *ex);
79 static void imap_append_message (CamelFolder *folder, CamelMimeMessage *message,
80                                  const CamelMessageInfo *info, CamelException *ex);
81 static void imap_copy_message_to (CamelFolder *source, const char *uid,
82                                   CamelFolder *destination, CamelException *ex);
83 static void imap_move_message_to (CamelFolder *source, const char *uid,
84                                   CamelFolder *destination, CamelException *ex);
85
86 /* subfolder listing */
87 static GPtrArray *imap_get_subfolder_names_internal (CamelFolder *folder, CamelException *ex);
88 static GPtrArray *imap_get_subfolder_names (CamelFolder *folder);
89
90 /* summary info */
91 static GPtrArray *imap_get_uids (CamelFolder *folder);
92 static GPtrArray *imap_get_summary_internal (CamelFolder *folder, CamelException *ex);
93 static GPtrArray *imap_get_summary (CamelFolder *folder);
94 static const CamelMessageInfo *imap_get_message_info (CamelFolder *folder, const char *uid);
95
96 /* searching */
97 static GPtrArray *imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex);
98
99 /* flag methods */
100 /*static guint32  imap_get_permanent_flags   (CamelFolder *folder, CamelException *ex);*/
101 static guint32  imap_get_message_flags     (CamelFolder *folder, const char *uid);
102 static void     imap_set_message_flags     (CamelFolder *folder, const char *uid, guint32 flags, guint32 set);
103 static gboolean imap_get_message_user_flag (CamelFolder *folder, const char *uid, const char *name);
104 static void     imap_set_message_user_flag (CamelFolder *folder, const char *uid, const char *name,
105                                             gboolean value);
106
107
108 static void
109 camel_imap_folder_class_init (CamelImapFolderClass *camel_imap_folder_class)
110 {
111         CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_imap_folder_class);
112
113         parent_class = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ()));
114         
115         /* virtual method definition */
116         
117         /* virtual method overload */
118         camel_folder_class->init = imap_init;
119         camel_folder_class->refresh_info = imap_refresh_info;
120         camel_folder_class->sync = imap_sync;
121         camel_folder_class->expunge = imap_expunge;
122         
123         camel_folder_class->get_uids = imap_get_uids;
124         camel_folder_class->free_uids = camel_folder_free_nop;
125         camel_folder_class->get_subfolder_names = imap_get_subfolder_names;
126         camel_folder_class->free_subfolder_names = camel_folder_free_nop;
127         
128         camel_folder_class->get_message_count = imap_get_message_count;
129         camel_folder_class->get_unread_message_count = imap_get_unread_message_count;
130         camel_folder_class->get_message = imap_get_message;
131         camel_folder_class->append_message = imap_append_message;
132         camel_folder_class->copy_message_to = imap_copy_message_to;
133         camel_folder_class->move_message_to = imap_move_message_to;
134         
135         camel_folder_class->get_summary = imap_get_summary;
136         camel_folder_class->get_message_info = imap_get_message_info;
137         camel_folder_class->free_summary = camel_folder_free_nop;
138         
139         camel_folder_class->search_by_expression = imap_search_by_expression;
140         
141         /*camel_folder_class->get_permanent_flags = imap_get_permanent_flags;*/
142         camel_folder_class->get_message_flags = imap_get_message_flags;
143         camel_folder_class->set_message_flags = imap_set_message_flags;
144         camel_folder_class->get_message_user_flag = imap_get_message_user_flag;
145         camel_folder_class->set_message_user_flag = imap_set_message_user_flag;
146 }
147
148 static void
149 camel_imap_folder_init (gpointer object, gpointer klass)
150 {
151         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (object);
152         CamelFolder *folder = CAMEL_FOLDER (object);
153         
154         folder->can_hold_messages = TRUE;
155         folder->can_hold_folders = TRUE;
156         folder->has_summary_capability = TRUE;
157         folder->has_search_capability = TRUE;
158         
159         imap_folder->summary = NULL;
160         imap_folder->summary_hash = NULL;
161         imap_folder->lsub = NULL;
162 }
163
164 CamelType
165 camel_imap_folder_get_type (void)
166 {
167         static CamelType camel_imap_folder_type = CAMEL_INVALID_TYPE;
168         
169         if (camel_imap_folder_type == CAMEL_INVALID_TYPE) {
170                 camel_imap_folder_type =
171                         camel_type_register (CAMEL_FOLDER_TYPE, "CamelImapFolder",
172                                              sizeof (CamelImapFolder),
173                                              sizeof (CamelImapFolderClass),
174                                              (CamelObjectClassInitFunc) camel_imap_folder_class_init,
175                                              NULL,
176                                              (CamelObjectInitFunc) camel_imap_folder_init,
177                                              (CamelObjectFinalizeFunc) imap_finalize);
178         }
179         
180         return camel_imap_folder_type;
181 }
182
183 CamelFolder *
184 camel_imap_folder_new (CamelStore *parent, char *folder_name, CamelException *ex)
185 {
186         CamelFolder *folder = CAMEL_FOLDER (camel_object_new (camel_imap_folder_get_type ()));
187         CamelURL *url = CAMEL_SERVICE (parent)->url;
188         char *dir_sep;
189         
190         dir_sep = CAMEL_IMAP_STORE (parent)->dir_sep;
191         
192         CF_CLASS (folder)->init (folder, parent, NULL, folder_name, dir_sep, FALSE, ex);
193         
194         if (camel_exception_is_set (ex)) {
195                 camel_object_unref (CAMEL_OBJECT (folder));
196                 return NULL;
197         }
198         
199         if (!strcmp (folder_name, url->path + 1))
200                 folder->can_hold_messages = FALSE;
201         
202         /*CF_CLASS (folder)->refresh_info (folder, ex);*/
203         
204         if (camel_exception_is_set (ex)) {
205                 camel_object_unref (CAMEL_OBJECT (folder));
206                 return NULL;
207         }
208         
209         return folder;
210 }
211
212 static void
213 imap_summary_free (GPtrArray **summary)
214 {
215         CamelMessageInfo *info;
216         GPtrArray *array = *summary;
217         gint i, max;
218         
219         if (array) {
220                 max = array->len;
221                 for (i = 0; i < max; i++) {
222                         info = g_ptr_array_index (array, i);
223                         g_free (info->subject);
224                         g_free (info->from);
225                         g_free (info->to);
226                         g_free (info->cc);
227                         g_free (info->uid);
228                         g_free (info->message_id);
229                         header_references_list_clear (&info->references);
230                         g_free (info);
231                         info = NULL;
232                 }
233                 
234                 g_ptr_array_free (array, TRUE);
235                 *summary = NULL;
236         }
237 }
238
239 static void
240 imap_folder_summary_free (CamelImapFolder *imap_folder)
241 {
242         if (imap_folder->summary_hash) {
243                 g_hash_table_destroy (imap_folder->summary_hash);
244                 imap_folder->summary_hash = NULL;
245         }
246         
247         imap_summary_free (&imap_folder->summary);
248 }
249
250 static void           
251 imap_finalize (CamelObject *object)
252 {
253         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (object);
254         gint max, i;
255         
256         imap_folder_summary_free (imap_folder);
257         
258         if (imap_folder->lsub) {
259                 max = imap_folder->lsub->len;
260                 
261                 for (i = 0; i < max; i++) {
262                         g_free (imap_folder->lsub->pdata[i]);
263                         imap_folder->lsub->pdata[i] = NULL;
264                 }
265                 
266                 g_ptr_array_free (imap_folder->lsub, TRUE);
267         }
268 }
269
270 static void 
271 imap_init (CamelFolder *folder, CamelStore *parent_store, CamelFolder *parent_folder,
272            const gchar *name, gchar *separator, gboolean path_begins_with_sep, CamelException *ex)
273 {
274         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
275         
276         /* call parent method */
277         parent_class->init (folder, parent_store, parent_folder, name, separator, path_begins_with_sep, ex);
278         if (camel_exception_get_id (ex))
279                 return;
280         
281         /* we assume that the parent init
282            method checks for the existance of @folder */
283         folder->can_hold_messages = TRUE;
284         folder->can_hold_folders = TRUE;
285         folder->has_summary_capability = TRUE;
286         folder->has_search_capability = TRUE;
287         
288         /* some IMAP daemons support user-flags              *
289          * I would not, however, rely on this feature as     *
290          * most IMAP daemons do not support all the features */
291         folder->permanent_flags = CAMEL_MESSAGE_SEEN |
292                 CAMEL_MESSAGE_ANSWERED |
293                 CAMEL_MESSAGE_FLAGGED |
294                 CAMEL_MESSAGE_DELETED |
295                 CAMEL_MESSAGE_DRAFT |
296                 CAMEL_MESSAGE_USER;
297         
298         imap_folder->search = NULL;
299         imap_folder->summary = NULL;
300         imap_folder->summary_hash = NULL;
301         imap_folder->lsub = NULL;
302 }
303
304 static void
305 imap_refresh_info (CamelFolder *folder, CamelException *ex)
306 {
307         imap_get_subfolder_names_internal (folder, ex);
308         
309         if (folder->can_hold_messages)
310                 imap_get_summary_internal (folder, ex);
311 }
312
313 static void
314 imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
315 {
316         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
317         gint i, max;
318         
319         if (expunge) {
320                 imap_expunge (folder, ex);
321                 return;
322         }
323         
324         /* Set the flags on any messages that have changed this session */
325         if (imap_folder->summary) {
326                 max = imap_folder->summary->len;
327                 for (i = 0; i < max; i++) {
328                         CamelMessageInfo *info;
329                         
330                         info = (CamelMessageInfo *) g_ptr_array_index (imap_folder->summary, i);
331                         if (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) {
332                                 char *flags;
333                                 
334                                 flags = g_strconcat (info->flags & CAMEL_MESSAGE_SEEN ? "\\Seen " : "",
335                                                      info->flags & CAMEL_MESSAGE_DRAFT ? "\\Draft " : "",
336                                                      info->flags & CAMEL_MESSAGE_DELETED ? "\\Deleted " : "",
337                                                      info->flags & CAMEL_MESSAGE_ANSWERED ? "\\Answered " : "",
338                                                      NULL);
339                                 if (*flags) {
340                                         gchar *result;
341                                         gint s;
342                                         
343                                         *(flags + strlen (flags) - 1) = '\0';
344                                         s = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store),
345                                                                          folder, &result, ex,
346                                                                          "UID STORE %s FLAGS.SILENT (%s)",
347                                                                          info->uid, flags);
348                                         
349                                         if (s != CAMEL_IMAP_OK)
350                                                 return;
351                                         
352                                         g_free (result);
353                                 }
354                                 g_free (flags);
355                         }
356                 }
357         }
358 }
359
360 static void
361 imap_expunge (CamelFolder *folder, CamelException *ex)
362 {
363         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
364         gchar *result;
365         gint status;
366         
367         imap_sync (folder, FALSE, ex);
368         
369         status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, 
370                                               &result, ex, "EXPUNGE");
371         g_free (result);
372 }
373
374 static gint
375 imap_get_message_count_internal (CamelFolder *folder, CamelException *ex)
376 {
377         CamelStore *store = CAMEL_STORE (folder->parent_store);
378         CamelURL *url = CAMEL_SERVICE (store)->url;
379         gchar *result, *msg_count, *folder_path, *dir_sep;
380         gint status, count = 0;
381         
382         g_return_val_if_fail (folder != NULL, 0);
383         g_return_val_if_fail (folder->can_hold_messages, 0);
384         
385         dir_sep = CAMEL_IMAP_STORE (folder->parent_store)->dir_sep;
386         
387         if (url && url->path && *(url->path + 1) && strcmp (folder->full_name, "INBOX"))
388                 folder_path = g_strdup_printf ("%s%s%s", url->path + 1, dir_sep, folder->full_name);
389         else
390                 folder_path = g_strdup (folder->full_name);
391         
392         if (CAMEL_IMAP_STORE (store)->has_status_capability)
393                 status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), folder,
394                                                       &result, ex, "STATUS %s (MESSAGES)", folder_path);
395         else
396                 status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), folder,
397                                                       &result, ex, "EXAMINE %s", folder_path);
398         
399         g_free (folder_path);
400         
401         if (status != CAMEL_IMAP_OK)
402                 return 0;
403         
404         /* parse out the message count */
405         if (result && *result == '*') {
406                 if (CAMEL_IMAP_STORE (store)->has_status_capability) {
407                         /* should come in the form: "* STATUS <folder> (MESSAGES <count>)" */
408                         if ((msg_count = strstr (result, "MESSAGES")) != NULL) {
409                                 msg_count = imap_next_word (msg_count);
410                         
411                                 /* we should now be pointing to the message count */
412                                 count = atoi (msg_count);
413                         }
414                 } else {
415                         /* should come in the form: "* <count> EXISTS" */
416                         if ((msg_count = strstr (result, "EXISTS")) != NULL) {
417                                 for ( ; msg_count > result && *msg_count != '*'; msg_count--);
418                                 
419                                 msg_count = imap_next_word (msg_count);
420                                 
421                                 /* we should now be pointing to the message count */
422                                 count = atoi (msg_count);
423                         }
424                 }
425         }
426         g_free (result);
427         
428         return count;
429 }
430
431 static gint
432 imap_get_message_count (CamelFolder *folder)
433 {
434         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
435         
436         if (imap_folder->summary)
437                 return imap_folder->summary->len;
438         else
439                 return 0;
440 }
441
442 static gint
443 imap_get_unread_message_count (CamelFolder *folder)
444 {
445         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
446         CamelMessageInfo *info;
447         GPtrArray *infolist;
448         gint i, count = 0;
449         
450         g_return_val_if_fail (folder != NULL, 0);
451         
452         /* If we don't have a message count, return 0 */
453         if (!imap_folder->summary)
454                 return 0;
455         
456         infolist = imap_folder->summary;
457         
458         for (i = 0; i < infolist->len; i++) {
459                 info = (CamelMessageInfo *) g_ptr_array_index (infolist, i);
460                 if (!(info->flags & CAMEL_MESSAGE_SEEN))
461                         count++;
462         }
463         
464         return count;
465 }
466
467 static void
468 imap_append_message (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex)
469 {
470         CamelStore *store = CAMEL_STORE (folder->parent_store);
471         CamelURL *url = CAMEL_SERVICE (store)->url;
472         CamelStream *memstream;
473         GByteArray *ba;
474         gchar *result, *cmdid, *dir_sep;
475         gchar *folder_path, *flagstr = NULL;
476         gint status;
477         
478         g_return_if_fail (folder != NULL);
479         g_return_if_fail (message != NULL);
480                 
481         dir_sep = CAMEL_IMAP_STORE (folder->parent_store)->dir_sep;
482         
483         if (url && url->path && *(url->path + 1) && strcmp (folder->full_name, "INBOX"))
484                 folder_path = g_strdup_printf ("%s%s%s", url->path + 1, dir_sep, folder->full_name);
485         else
486                 folder_path = g_strdup (folder->full_name);
487         
488         /* create flag string param */
489         if (info && info->flags) {
490                 flagstr = g_strconcat (" (", info->flags & CAMEL_MESSAGE_SEEN ? "\\Seen " : "",
491                                        info->flags & CAMEL_MESSAGE_DRAFT ? "\\Draft " : "",
492                                        info->flags & CAMEL_MESSAGE_DELETED ? "\\Answered " : "",
493                                        NULL);
494                 if (flagstr)
495                         *(flagstr + strlen (flagstr) - 1) = ')';
496         }
497         
498         ba = g_byte_array_new ();
499         memstream = camel_stream_mem_new_with_byte_array (ba);
500         /* FIXME: we need to crlf/dot filter */
501         camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), memstream);
502         camel_stream_write_string (memstream, "\r\n");
503         camel_stream_reset (memstream);
504         
505         status = camel_imap_command_preliminary (CAMEL_IMAP_STORE (folder->parent_store),
506                                                  &cmdid, ex, "APPEND %s%s {%d}",
507                                                  folder_path, flagstr ? flagstr : "", ba->len - 2);
508         g_free (folder_path);
509         
510         if (status != CAMEL_IMAP_PLUS) {
511                 g_free (cmdid);
512                 return;
513         }
514         
515         /* send the rest of our data - the mime message */
516         status = camel_imap_command_continuation_with_stream (CAMEL_IMAP_STORE (folder->parent_store),
517                                                               &result, cmdid, memstream, ex);
518         g_free (cmdid);
519         
520         if (status != CAMEL_IMAP_OK)
521                 return;
522         
523         g_free (result);
524         
525         camel_object_unref (CAMEL_OBJECT (memstream));
526         camel_imap_folder_changed (folder, 1, NULL, ex);
527 }
528
529 static void
530 imap_copy_message_to (CamelFolder *source, const char *uid, CamelFolder *destination, CamelException *ex)
531 {
532         CamelStore *store = CAMEL_STORE (source->parent_store);
533         CamelURL *url = CAMEL_SERVICE (store)->url;
534         char *result, *folder_path, *dir_sep;
535         int status;
536         
537         dir_sep = CAMEL_IMAP_STORE (source->parent_store)->dir_sep;
538         
539         if (url && url->path && *(url->path + 1) && strcmp (destination->full_name, "INBOX"))
540                 folder_path = g_strdup_printf ("%s%s%s", url->path + 1, dir_sep, destination->full_name);
541         else
542                 folder_path = g_strdup (destination->full_name);
543         
544         status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), source, &result, ex,
545                                               "UID COPY %s %s", uid, folder_path);
546         g_free (folder_path);
547         
548         if (status != CAMEL_IMAP_OK)
549                 return;
550         
551         g_free (result);
552         
553         camel_imap_folder_changed (destination, 1, NULL, ex);
554 }
555
556 /* FIXME: Duplication of code! */
557 static void
558 imap_move_message_to (CamelFolder *source, const char *uid, CamelFolder *destination, CamelException *ex)
559 {
560         CamelStore *store = CAMEL_STORE (source->parent_store);
561         CamelURL *url = CAMEL_SERVICE (store)->url;
562         CamelMessageInfo *info;
563         char *result, *folder_path, *dir_sep;
564         int status;
565         
566         dir_sep = CAMEL_IMAP_STORE (source->parent_store)->dir_sep;
567         
568         if (url && url->path && *(url->path + 1) && strcmp (destination->full_name, "INBOX"))
569                 folder_path = g_strdup_printf ("%s%s%s", url->path + 1, dir_sep, destination->full_name);
570         else
571                 folder_path = g_strdup (destination->full_name);
572         
573         status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), source, &result, ex,
574                                               "UID COPY %s %s", uid, folder_path);
575         g_free (folder_path);
576         
577         if (status != CAMEL_IMAP_OK)
578                 return;
579         
580         g_free (result);
581         
582         if (!(info = (CamelMessageInfo *)imap_get_message_info (source, uid))) {
583                 CamelService *service = CAMEL_SERVICE (store);
584                 
585                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
586                                       "Could not set flags for message %s on IMAP server %s: %s",
587                                       uid, service->url->host, "Unknown error");
588                 return;
589         }
590         
591         imap_set_message_flags (source, uid, CAMEL_MESSAGE_DELETED, ~(info->flags));
592         
593         camel_imap_folder_changed (destination, 1, NULL, ex);
594 }
595
596 static GPtrArray *
597 imap_get_uids (CamelFolder *folder) 
598 {
599         CamelMessageInfo *info;
600         GPtrArray *array, *infolist;
601         gint i, count;
602         
603         infolist = imap_get_summary (folder);
604         
605         count = infolist ? infolist->len : 0;
606         
607         array = g_ptr_array_new ();
608         g_ptr_array_set_size (array, count);
609         
610         for (i = 0; i < count; i++) {
611                 info = (CamelMessageInfo *) g_ptr_array_index (infolist, i);
612                 array->pdata[i] = g_strdup (info->uid);
613         }
614         
615         return array;
616 }
617
618 static GPtrArray *
619 imap_get_subfolder_names_internal (CamelFolder *folder, CamelException *ex)
620 {
621         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
622         CamelStore *store = CAMEL_STORE (folder->parent_store);
623         CamelURL *url = CAMEL_SERVICE (store)->url;
624         GPtrArray *listing;
625         gboolean found_inbox = FALSE;
626         gint status;
627         gchar *result, *namespace, *dir_sep;
628         
629         g_return_val_if_fail (folder != NULL, g_ptr_array_new ());
630         
631         dir_sep = CAMEL_IMAP_STORE (folder->parent_store)->dir_sep;
632         
633         if (url && url->path) {
634                 char *path = url->path + 1;
635                 
636                 if (*path) {
637                         if (!strcmp (folder->full_name, path))
638                                 namespace = g_strdup (path);
639                         else if (!g_strcasecmp (folder->full_name, "INBOX"))
640                                 namespace = g_strdup (path); /* FIXME: erm...not sure */
641                         else
642                                 namespace = g_strdup_printf ("%s%s%s", path, dir_sep, folder->full_name);
643                 } else {
644                         if (!strcmp (folder->full_name, "/"))
645                                 namespace = g_strdup ("");
646                         else
647                                 namespace = g_strdup (folder->full_name);
648                 }
649         } else {
650                 namespace = g_strdup (folder->full_name);
651         }
652         
653         status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), NULL,
654                                               &result, ex, "LIST \"\" \"%s%s*\"", namespace,
655                                               *namespace ? dir_sep : "");
656         
657         if (status != CAMEL_IMAP_OK) {
658                 g_free (namespace);
659                 
660                 imap_folder->lsub = g_ptr_array_new ();
661                 return imap_folder->lsub;
662         }
663         
664         /* parse out the subfolders */
665         listing = g_ptr_array_new ();
666         if (result) {
667                 char *ptr = result;
668                 
669                 while (ptr && *ptr == '*') {
670                         gchar *flags, *sep, *dir, *buf, *end;
671                         
672                         for (end = ptr; *end && *end != '\n'; end++);
673                         buf = g_strndup (ptr, (gint)(end - ptr));
674                         ptr = end;
675                         
676                         if (!imap_parse_list_response (buf, namespace, &flags, &sep, &dir)) {
677                                 g_free (buf);
678                                 g_free (flags);
679                                 g_free (sep);
680                                 g_free (dir);
681                                 
682                                 if (*ptr == '\n')
683                                         ptr++;
684                                 
685                                 continue;
686                         }
687                         
688                         g_free (buf);
689                         g_free (flags);
690                         
691                         if (*dir) {
692                                 d(fprintf (stderr, "adding folder: %s\n", dir));
693                                 if (!g_strcasecmp (dir, "INBOX"))
694                                         found_inbox = TRUE;
695                                 g_ptr_array_add (listing, dir);
696                         }
697                         
698                         g_free (sep);
699                         
700                         if (*ptr == '\n')
701                                 ptr++;
702                 }
703         }
704         
705         if (!strcmp (folder->name, namespace) && !found_inbox) {
706                 g_ptr_array_add (listing, g_strdup ("INBOX"));
707         }
708         
709         g_free (result);
710         g_free (namespace);
711         
712         imap_folder->lsub = listing;
713         
714         return listing;
715 }
716
717 static GPtrArray *
718 imap_get_subfolder_names (CamelFolder *folder)
719 {
720         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
721         
722         return imap_folder->lsub;
723 }
724
725 static CamelMimeMessage *
726 imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
727 {
728         CamelStream *msgstream = NULL;
729         CamelMimeMessage *msg = NULL;
730         gchar *result, *header, *body, *mesg, *p, *q, *data_item;
731         int status, part_len;
732         
733         if (CAMEL_IMAP_STORE (folder->parent_store)->server_level >= IMAP_LEVEL_IMAP4REV1)
734                 data_item = "BODY.PEEK[HEADER]";
735         else
736                 data_item = "RFC822.HEADER";
737         
738         status = camel_imap_fetch_command (CAMEL_IMAP_STORE (folder->parent_store), folder,
739                                            &result, ex, "UID FETCH %s %s", uid,
740                                            data_item);
741         
742         if (!result || status != CAMEL_IMAP_OK)
743                 return NULL;
744         
745         /* parse out the message part */
746         for (p = result; *p && *p != '{' && *p != '"' && *p != '\n'; p++);
747         switch (*p) {
748         case '"':
749                 /* a quoted string - section 4.3 */
750                 p++;
751                 for (q = p; *q && *q != '"' && *q != '\n'; q++);
752                 part_len = (gint) (q - p);
753                 
754                 break;
755         case '{':
756                 /* a literal string - section 4.3 */
757                 part_len = atoi (p + 1);
758                 for ( ; *p && *p != '\n'; p++);
759                 if (*p != '\n') {
760                         g_free (result);
761                         return NULL;
762                 }
763                 
764                 /* calculate the new part-length */
765                 for (q = p; *q && (q - p) <= part_len; q++) {
766                         if (*q == '\n')
767                                 part_len--;
768                 }
769                 
770                 /* FIXME: This is a hack for IMAP daemons that send us a UID at the end of each FETCH */
771                 for ( ; q > p && *(q-1) != '\n'; q--, part_len--);
772                 part_len++;
773                 
774                 break;
775         default:
776                 /* Bad input */
777                 g_free (result);
778                 return NULL;
779         }
780         
781         header = g_strndup (p, part_len);
782         
783         g_free (result);
784         d(fprintf (stderr, "*** We got the header ***\n"));
785         
786         if (CAMEL_IMAP_STORE (folder->parent_store)->server_level >= IMAP_LEVEL_IMAP4REV1)
787                 data_item = "BODY[TEXT]";
788         else
789                 data_item = "RFC822.TEXT";
790         
791         status = camel_imap_fetch_command (CAMEL_IMAP_STORE (folder->parent_store), folder,
792                                            &result, ex, "UID FETCH %s %s", uid,
793                                            data_item);
794         
795         if (!result || status != CAMEL_IMAP_OK) {
796                 g_free (header);
797                 return NULL;
798         }
799         
800         /* parse out the message part */
801         for (p = result; *p && *p != '{' && *p != '"' && *p != '\n'; p++);
802         switch (*p) {
803         case '"':
804                 /* a quoted string - section 4.3 */
805                 p++;
806                 for (q = p; *q && *q != '"' && *q != '\n'; q++);
807                 part_len = (gint) (q - p);
808                 
809                 break;
810         case '{':
811                 /* a literal string - section 4.3 */
812                 part_len = atoi (p + 1);
813                 for ( ; *p && *p != '\n'; p++);
814                 if (*p != '\n') {
815                         g_free (result);
816                         g_free (header);
817                         return NULL;
818                 }
819                 
820                 /* calculate the new part-length */
821                 for (q = p; *q && (q - p) <= part_len; q++) {
822                         if (*q == '\n')
823                                 part_len--;
824                 }
825                 
826                 /* FIXME: This is a hack for IMAP daemons that send us a UID at the end of each FETCH */
827                 for ( ; q > p && *(q-1) != '\n'; q--, part_len--);
828                 part_len++;
829                 
830                 break;
831         default:
832                 /* Bad input */
833                 g_free (result);
834                 g_free (header);
835                 return NULL;
836         }
837         
838         body = g_strndup (p, part_len);
839         
840         g_free (result);
841         d(fprintf (stderr, "*** We got the body ***\n"));
842         
843         mesg = g_strdup_printf ("%s\n%s", header, body);
844         g_free (header);
845         g_free (body);
846         d(fprintf (stderr, "*** We got the mesg ***\n"));
847         
848         d(fprintf (stderr, "Message:\n%s\n", mesg));
849         
850         msgstream = camel_stream_mem_new_with_buffer (mesg, strlen (mesg) + 1);
851 #if 0
852         f_stream = camel_stream_filter_new_with_stream (msgstream);
853         filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_DECODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
854         id = camel_stream_filter_add (f_stream, CAMEL_MIME_FILTER (filter));
855 #endif  
856         msg = camel_mime_message_new ();
857         d(fprintf (stderr, "*** We created the camel_mime_message ***\n"));
858         
859         camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), msgstream);
860 #if 0   
861         camel_stream_filter_remove (f_stream, id);
862         camel_stream_close (CAMEL_STREAM (f_stream));
863 #endif
864         camel_object_unref (CAMEL_OBJECT (msgstream));
865         /*camel_object_unref (CAMEL_OBJECT (f_stream));*/
866         
867         d(fprintf (stderr, "*** We're returning... ***\n"));
868         
869         g_free (mesg);
870         return msg;
871 }
872
873 /* This probably shouldn't go here...but it will for now */
874 static gchar *
875 get_header_field (gchar *header, gchar *field)
876 {
877         gchar *part, *index, *p, *q;
878         
879         index = (char *) e_strstrcase (header, field);
880         if (index == NULL)
881                 return NULL;
882         
883         p = index + strlen (field) + 1;
884         for (q = p; *q; q++)
885                 if (*q == '\n' && (*(q + 1) != ' ' && *(q + 1) != '\t'))
886                         break;
887         
888         part = g_strndup (p, (gint)(q - p));
889         
890         /* it may be wrapped on multiple lines, so lets strip out \n's */
891         for (p = part; *p; ) {
892                 if (*p == '\n')
893                         memmove (p, p + 1, strlen (p));
894                 else
895                         p++;
896         }
897         
898         return part;
899 }
900
901 static char *header_fields[] = { "subject", "from", "to", "cc", "date",
902                                  "received", "message-id", "references",
903                                  "in-reply-to", "" };
904 /**
905  * imap_protocol_get_summary_specifier
906  *
907  * Make a data item specifier for the header lines we need,
908  * appropriate to the server level.
909  *
910  * IMAP4rev1:  UID FLAGS BODY.PEEK[HEADER.FIELDS (SUBJECT FROM .. IN-REPLY-TO)]
911  * IMAP4:      UID FLAGS RFC822.HEADER.LINES (SUBJECT FROM .. IN-REPLY-TO)
912  **/
913 static char *
914 imap_protocol_get_summary_specifier (CamelFolder *folder)
915 {
916         char *sect_begin, *sect_end;
917         char *headers_wanted = "SUBJECT FROM TO CC DATE MESSAGE-ID REFERENCES IN-REPLY-TO";
918         
919         if (CAMEL_IMAP_STORE (folder->parent_store)->server_level >= IMAP_LEVEL_IMAP4REV1) {
920                 sect_begin = "BODY.PEEK[HEADER.FIELDS";
921                 sect_end = "]";
922         } else {
923                 sect_begin = "RFC822.HEADER.LINES";
924                 sect_end   = "";
925         }
926         
927         return g_strdup_printf ("UID FLAGS %s (%s)%s", sect_begin, headers_wanted, sect_end);
928 }
929
930 static GPtrArray *
931 imap_get_summary_internal (CamelFolder *folder, CamelException *ex)
932 {
933         /* This ALWAYS updates the summary except on fail */
934         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
935         GPtrArray *summary = NULL, *headers = NULL;
936         GHashTable *hash = NULL;
937         gint num, i, j, status = 0;
938         char *result, *q, *node;
939         const char *received;
940         char *summary_specifier;
941         struct _header_raw *h = NULL, *tail = NULL;
942         
943         num = imap_get_message_count_internal (folder, ex);
944         
945         /* sync any previously set/changed message flags */
946         imap_sync (folder, FALSE, ex);
947         
948         if (num == 0) {
949                 /* clean up any previous summary data */
950                 imap_folder_summary_free (imap_folder);
951                 
952                 imap_folder->summary = g_ptr_array_new ();
953                 imap_folder->summary_hash = g_hash_table_new (g_str_hash, g_str_equal);
954                 
955                 return imap_folder->summary;
956         }
957         
958         summary_specifier = imap_protocol_get_summary_specifier (folder);
959         
960         /* We use camel_imap_command_extended here because it's safe */
961         if (num == 1) {
962                 status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
963                                                       &result, ex, "FETCH 1 (%s)", summary_specifier);
964         } else {
965                 status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
966                                                       &result, ex, "FETCH 1:%d (%s)", num, summary_specifier);
967         }
968         g_free (summary_specifier);
969         
970         if (status != CAMEL_IMAP_OK) {
971                 if (!imap_folder->summary) {
972                         imap_folder->summary = g_ptr_array_new ();
973                         imap_folder->summary_hash = g_hash_table_new (g_str_hash, g_str_equal);
974                 }
975                 
976                 return imap_folder->summary;
977         }
978         
979         /* initialize our new summary-to-be */
980         summary = g_ptr_array_new ();
981         hash = g_hash_table_new (g_str_hash, g_str_equal);
982         
983         /* create our array of headers from the server response */
984         headers = g_ptr_array_new ();
985         node = result;
986         for (i = 1; node; i++) {
987                 char *end;
988                 
989                 if ((end = strstr (node + 2, "\n*"))) {
990                         g_ptr_array_add (headers, g_strndup (node, (gint)(end - node)));
991                 } else {
992                         g_ptr_array_add (headers, g_strdup (node));
993                 }
994                 node = end;
995         }
996         if (i < num) {
997                 d(fprintf (stderr, "IMAP server didn't respond with as many headers as we expected...\n"));
998                 /* should we error?? */
999         }
1000         
1001         g_free (result);
1002         result = NULL;
1003         
1004         for (i = 0; i < headers->len; i++) {
1005                 CamelMessageInfo *info;
1006                 char *uid, *flags, *header;
1007                 
1008                 info = g_malloc0 (sizeof (CamelMessageInfo));
1009                 
1010                 /* lets grab the UID... */
1011                 if (!(uid = strstr (headers->pdata[i], "UID "))) {
1012                         d(fprintf (stderr, "Cannot get a uid for %d\n\n%s\n\n", i+1, (char *) headers->pdata[i]));
1013                         g_free (info);
1014                         break;
1015                 }
1016                 
1017                 for (uid += 4; *uid && (*uid < '0' || *uid > '9'); uid++); /* advance to <uid> */
1018                 for (q = uid; *q && *q >= '0' && *q <= '9'; q++); /* find the end of the <uid> */
1019                 info->uid = g_strndup (uid, (gint)(q - uid));
1020                 /*d(fprintf (stderr, "*** info->uid = %s\n", info->uid));*/
1021                 
1022                 /* now lets grab the FLAGS */
1023                 if (!(flags = strstr (headers->pdata[i], "FLAGS "))) {
1024                         d(fprintf (stderr, "We didn't seem to get any flags for %d...\n", i));
1025                         g_free (info->uid);
1026                         g_free (info);
1027                         break;
1028                 }
1029                 
1030                 for (flags += 6; *flags && *flags != '('; flags++); /* advance to <flags> */
1031                 for (q = flags; *q && *q != ')'; q++);         /* find the end of <flags> */
1032                 flags = g_strndup (flags, (gint)(q - flags + 1));
1033                 /*d(fprintf (stderr, "*** info->flags = %s\n", flags));*/
1034                 
1035                 /* now we gotta parse for the flags */
1036                 info->flags = 0;
1037                 if (strstr (flags, "\\Seen"))
1038                         info->flags |= CAMEL_MESSAGE_SEEN;
1039                 if (strstr (flags, "\\Answered"))
1040                         info->flags |= CAMEL_MESSAGE_ANSWERED;
1041                 if (strstr (flags, "\\Flagged"))
1042                         info->flags |= CAMEL_MESSAGE_FLAGGED;
1043                 if (strstr (flags, "\\Deleted"))
1044                         info->flags |= CAMEL_MESSAGE_DELETED;
1045                 if (strstr (flags, "\\Draft"))
1046                         info->flags |= CAMEL_MESSAGE_DRAFT;
1047                 g_free (flags);
1048                 flags = NULL;
1049                 
1050                 /* construct the header list */
1051                 /* fast-forward to beginning of header info... */
1052                 for (header = headers->pdata[i]; *header && *header != '\n'; header++);
1053                 h = NULL;
1054                 for (j = 0; *header_fields[j]; j++) {
1055                         struct _header_raw *raw;
1056                         char *field, *value;
1057                         
1058                         field = g_strdup_printf ("\n%s:", header_fields[j]);
1059                         value = get_header_field (header, field);
1060                         g_free (field);
1061                         if (!value)
1062                                 continue;
1063                         
1064                         raw = g_malloc0 (sizeof (struct _header_raw));
1065                         raw->next = NULL;
1066                         raw->name = g_strdup (header_fields[j]);
1067                         raw->value = value;
1068                         raw->offset = -1;
1069                         
1070                         if (!h) {
1071                                 h = raw;
1072                                 tail = h;
1073                         } else {
1074                                 tail->next = raw;
1075                                 tail = raw;
1076                         }
1077                 }
1078                 
1079                 /* construct the CamelMessageInfo */
1080                 info->subject = camel_summary_format_string (h, "subject");
1081                 info->from = camel_summary_format_address (h, "from");
1082                 info->to = camel_summary_format_address (h, "to");
1083                 info->cc = camel_summary_format_address (h, "cc");
1084                 info->user_flags = NULL;
1085                 info->date_sent = header_decode_date (header_raw_find (&h, "date", NULL), NULL);
1086                 received = header_raw_find (&h, "received", NULL);
1087                 if (received)
1088                         received = strrchr (received, ';');
1089                 if (received)
1090                         info->date_received = header_decode_date (received + 1, NULL);
1091                 else
1092                         info->date_received = 0;
1093                 info->message_id = header_msgid_decode (header_raw_find (&h, "message-id", NULL));
1094                 /* if we have a references, use that, otherwise, see if we have an in-reply-to
1095                    header, with parsable content, otherwise *shrug* */
1096                 info->references = header_references_decode (header_raw_find (&h, "references", NULL));
1097                 if (info->references == NULL)
1098                         info->references = header_references_decode (header_raw_find (&h, "in-reply-to", NULL));
1099                 
1100                 while (h) {
1101                         struct _header_raw *next = h->next;
1102                         
1103                         g_free (h->name);
1104                         g_free (h->value);
1105                         g_free (h);
1106                         h = next;
1107                 }
1108                 
1109                 g_ptr_array_add (summary, info);
1110                 g_hash_table_insert (hash, info->uid, info);
1111         }
1112         
1113         for (i = 0; i < headers->len; i++)
1114                 g_free (headers->pdata[i]);
1115         g_ptr_array_free (headers, TRUE);
1116         
1117         /* clean up any previous summary data */
1118         imap_folder_summary_free (imap_folder);
1119         
1120         imap_folder->summary = summary;
1121         imap_folder->summary_hash = hash;
1122         
1123         return imap_folder->summary;
1124 }
1125
1126 static GPtrArray *
1127 imap_get_summary (CamelFolder *folder)
1128 {
1129         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
1130         
1131         return imap_folder->summary;
1132 }
1133
1134 /* get a single message info from the server */
1135 static CamelMessageInfo *
1136 imap_get_message_info_internal (CamelFolder *folder, guint id, CamelException *ex)
1137 {
1138         CamelMessageInfo *info = NULL;
1139         struct _header_raw *h, *tail = NULL;
1140         const char *received;
1141         char *result, *uid, *flags, *header, *q;
1142         char *summary_specifier;
1143         int j, status;
1144         
1145         /* we don't have a cached copy, so fetch it */
1146         summary_specifier = imap_protocol_get_summary_specifier (folder);
1147         
1148         /* again, we use camel_imap_command_extended here because it's safe to do so */
1149         status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
1150                                               &result, ex, "FETCH %d (%s)", id, summary_specifier);
1151         
1152         g_free (summary_specifier);
1153         
1154         if (status != CAMEL_IMAP_OK)
1155                 return NULL;
1156         
1157         /* lets grab the UID... */
1158         if (!(uid = (char *) e_strstrcase (result, "UID "))) {
1159                 d(fprintf (stderr, "Cannot get a uid for %d\n\n%s\n\n", id, result));
1160                 g_free (result);
1161                 return NULL;
1162         }
1163                 
1164         for (uid += 4; *uid && (*uid < '0' || *uid > '9'); uid++); /* advance to <uid> */
1165         for (q = uid; *q && *q >= '0' && *q <= '9'; q++); /* find the end of the <uid> */
1166         uid = g_strndup (uid, (gint)(q - uid));
1167         
1168         info = g_malloc0 (sizeof (CamelMessageInfo));
1169         info->uid = uid;
1170         d(fprintf (stderr, "*** info->uid = %s\n", info->uid));
1171         
1172         /* now lets grab the FLAGS */
1173         if (!(flags = strstr (q, "FLAGS "))) {
1174                 d(fprintf (stderr, "We didn't seem to get any flags for %s...\n", uid));
1175                 g_free (info->uid);
1176                 g_free (info);
1177                 g_free (result);
1178                 return NULL;
1179         }
1180         
1181         for (flags += 6; *flags && *flags != '('; flags++); /* advance to <flags> */
1182         for (q = flags; *q && *q != ')'; q++);              /* find the end of <flags> */
1183         flags = g_strndup (flags, (gint)(q - flags + 1));
1184         d(fprintf (stderr, "*** info->flags = %s\n", flags));
1185         
1186         /* now we gotta parse for the flags */
1187         info->flags = 0;
1188         if (strstr (flags, "\\Seen"))
1189                 info->flags |= CAMEL_MESSAGE_SEEN;
1190         if (strstr (flags, "\\Answered"))
1191                 info->flags |= CAMEL_MESSAGE_ANSWERED;
1192         if (strstr (flags, "\\Flagged"))
1193                 info->flags |= CAMEL_MESSAGE_FLAGGED;
1194         if (strstr (flags, "\\Deleted"))
1195                 info->flags |= CAMEL_MESSAGE_DELETED;
1196         if (strstr (flags, "\\Draft"))
1197                 info->flags |= CAMEL_MESSAGE_DRAFT;
1198         g_free (flags);
1199         flags = NULL;
1200         
1201         /* construct the header list */
1202         /* fast-forward to beginning of header info... */
1203         for (header = q; *header && *header != '\n'; header++);
1204         h = NULL;
1205         for (j = 0; *header_fields[j]; j++) {
1206                 struct _header_raw *raw;
1207                 char *field, *value;
1208                 
1209                 field = g_strdup_printf ("\n%s:", header_fields[j]);
1210                 value = get_header_field (header, field);
1211                 g_free (field);
1212                 if (!value)
1213                         continue;
1214                 
1215                 raw = g_malloc0 (sizeof (struct _header_raw));
1216                 raw->next = NULL;
1217                 raw->name = g_strdup (header_fields[j]);
1218                 raw->value = value;
1219                 raw->offset = -1;
1220                 
1221                 if (!h) {
1222                         h = raw;
1223                         tail = h;
1224                 } else {
1225                         tail->next = raw;
1226                         tail = raw;
1227                 }
1228         }
1229         
1230         /* construct the CamelMessageInfo */
1231         info->subject = camel_summary_format_string (h, "subject");
1232         info->from = camel_summary_format_address (h, "from");
1233         info->to = camel_summary_format_address (h, "to");
1234         info->cc = camel_summary_format_address (h, "cc");
1235         info->user_flags = NULL;
1236         info->date_sent = header_decode_date (header_raw_find (&h, "date", NULL), NULL);
1237         received = header_raw_find (&h, "received", NULL);
1238         if (received)
1239                 received = strrchr (received, ';');
1240         if (received)
1241                 info->date_received = header_decode_date (received + 1, NULL);
1242         else
1243                 info->date_received = 0;
1244         info->message_id = header_msgid_decode (header_raw_find (&h, "message-id", NULL));
1245         /* if we have a references, use that, otherwise, see if we have an in-reply-to
1246            header, with parsable content, otherwise *shrug* */
1247         info->references = header_references_decode (header_raw_find (&h, "references", NULL));
1248         if (info->references == NULL)
1249                 info->references = header_references_decode (header_raw_find (&h, "in-reply-to", NULL));
1250         
1251         while (h->next) {
1252                 struct _header_raw *next = h->next;
1253                 
1254                 g_free (h->name);
1255                 g_free (h->value);
1256                 g_free (h);
1257                 h = next;
1258         }
1259         
1260         g_free (result);
1261         
1262         return info;
1263 }
1264
1265 /* get a single message info, by uid */
1266 static const CamelMessageInfo *
1267 imap_get_message_info (CamelFolder *folder, const char *uid)
1268 {
1269         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
1270         
1271         g_return_val_if_fail (*uid != '\0', NULL);
1272         
1273         if (imap_folder->summary)
1274                 return (CamelMessageInfo *) g_hash_table_lookup (imap_folder->summary_hash, uid);
1275         
1276         return NULL;
1277 }
1278
1279 static GPtrArray *
1280 imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex)
1281 {
1282         GPtrArray *uids = NULL;
1283         char *result, *sexp, *p;
1284         int status;
1285         
1286         d(fprintf (stderr, "camel sexp: '%s'\n", expression));
1287         sexp = imap_translate_sexp (expression);
1288         d(fprintf (stderr, "imap sexp: '%s'\n", sexp));
1289         
1290         uids = g_ptr_array_new ();
1291         
1292         if (!folder->has_search_capability) {
1293                 g_free (sexp);
1294                 return uids;
1295         }
1296         
1297         status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
1298                                               &result, ex, "UID SEARCH %s", sexp);
1299         
1300         if (status != CAMEL_IMAP_OK) {
1301                 g_free (sexp);
1302                 return uids;
1303         }
1304         
1305         if ((p = strstr (result, "* SEARCH"))) {
1306                 char *word;
1307                 
1308                 word = imap_next_word (p); /* word now points to SEARCH */
1309                 
1310                 for (word = imap_next_word (word); *word && *word != '*'; word = imap_next_word (word)) {
1311                         gboolean word_is_numeric = TRUE;
1312                         char *ep;
1313                         
1314                         /* find the end of this word and make sure it's a numeric uid */
1315                         for (ep = word; *ep && *ep != ' ' && *ep != '\n'; ep++)
1316                                 if (*ep < '0' || *ep > '9')
1317                                         word_is_numeric = FALSE;
1318                         
1319                         if (word_is_numeric)
1320                                 g_ptr_array_add (uids, g_strndup (word, (gint)(ep - word)));
1321                 }
1322         }
1323         
1324         g_free (result);
1325         g_free (sexp);
1326         
1327         return uids;
1328 }
1329
1330 #if 0
1331 static guint32
1332 imap_get_permanent_flags (CamelFolder *folder, CamelException *ex)
1333 {
1334         /* return permamnant flags */
1335         return folder->permanent_flags;
1336 }
1337 #endif
1338
1339 static guint32
1340 imap_get_message_flags (CamelFolder *folder, const char *uid)
1341 {
1342         const CamelMessageInfo *info;
1343         
1344         info = imap_get_message_info (folder, uid);
1345         g_return_val_if_fail (info != NULL, 0);
1346         
1347         return info->flags;
1348 }
1349
1350 static void
1351 imap_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
1352 {
1353         CamelMessageInfo *info;
1354         
1355         info = (CamelMessageInfo*)imap_get_message_info (folder, uid);
1356         g_return_if_fail (info != NULL);
1357         
1358         info->flags = (info->flags & ~flags) | (set & flags) | CAMEL_MESSAGE_FOLDER_FLAGGED;
1359         
1360         /*gtk_signal_emit_by_name (GTK_OBJECT (folder), "message_changed", uid);*/
1361         camel_object_trigger_event (CAMEL_OBJECT (folder), "message_changed", (gpointer *) uid);
1362 }
1363
1364 static gboolean
1365 imap_get_message_user_flag (CamelFolder *folder, const char *uid, const char *name)
1366 {
1367         return FALSE;
1368 }
1369
1370 static void
1371 imap_set_message_user_flag (CamelFolder *folder, const char *uid, const char *name, gboolean value)
1372 {
1373         /*gtk_signal_emit_by_name (GTK_OBJECT (folder), "message_changed", uid);*/
1374         camel_object_trigger_event (CAMEL_OBJECT (folder), "message_changed", (gpointer *) uid);
1375 }
1376
1377 void
1378 camel_imap_folder_changed (CamelFolder *folder, gint recent, GPtrArray *expunged, CamelException *ex)
1379 {
1380         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
1381         
1382         if (expunged) {
1383                 gint i, id;
1384                 
1385                 for (i = 0; i < expunged->len; i++) {
1386                         id = atoi (expunged->pdata[i]);
1387                         d(fprintf (stderr, "Expunging message %d from the summary (i = %d)\n", id + i, i));
1388                         
1389                         if (id <= imap_folder->summary->len) {
1390                                 CamelMessageInfo *info;
1391                                 
1392                                 info = (CamelMessageInfo *) imap_folder->summary->pdata[id - 1];
1393                                 
1394                                 /* remove from the lookup table and summary */
1395                                 g_hash_table_remove (imap_folder->summary_hash, info->uid);
1396                                 g_ptr_array_remove_index (imap_folder->summary, id - 1);
1397                                 
1398                                 /* free the info data */
1399                                 g_free (info->subject);
1400                                 g_free (info->from);
1401                                 g_free (info->to);
1402                                 g_free (info->cc);
1403                                 g_free (info->uid);
1404                                 g_free (info->message_id);
1405                                 header_references_list_clear (&info->references);
1406                                 g_free (info);
1407                                 info = NULL;
1408                         } else {
1409                                 /* Hopefully this should never happen */
1410                                 d(fprintf (stderr, "imap expunge-error: message %d is out of range\n", id));
1411                         }
1412                 }
1413         }
1414         
1415         if (recent > 0) {
1416                 CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
1417                 CamelMessageInfo *info;
1418                 gint i, j, last, slast;
1419                 
1420                 if (!imap_folder->summary) {
1421                         imap_folder->summary = g_ptr_array_new ();
1422                         imap_folder->summary_hash = g_hash_table_new (g_str_hash, g_str_equal);
1423                 }
1424                 
1425                 last = imap_folder->summary->len + 1;
1426                 slast = imap_get_message_count_internal (folder, ex);
1427                 fprintf (stderr, "calculated next message is: %d\n", last);
1428                 fprintf (stderr, "server says %d mesgs total\n", slast);
1429                 slast -= (recent - 1);
1430                 fprintf (stderr, "based on total, new guess is: %d\n", slast);
1431                 
1432                 for (i = slast, j = 0; j < recent; i++, j++) {
1433                         info = imap_get_message_info_internal (folder, i, ex);
1434                         if (info) {
1435                                 if (!imap_get_message_info (folder, info->uid)) {
1436                                         /* add to our summary */
1437                                         g_ptr_array_add (imap_folder->summary, info);
1438                                         g_hash_table_insert (imap_folder->summary_hash, info->uid, info);
1439                                 } else {
1440                                         /* we already have a record of it */
1441                                         g_free (info->subject);
1442                                         g_free (info->from);
1443                                         g_free (info->to);
1444                                         g_free (info->cc);
1445                                         g_free (info->uid);
1446                                         g_free (info->message_id);
1447                                         header_references_list_clear (&info->references);
1448                                         g_free (info);
1449                                         info = NULL;
1450                                         d(fprintf (stderr, "we already had message %d!!\n", i));
1451                                 }
1452                         } else {
1453                                 /* our hack failed so now we need to do it the old fashioned way */
1454                                 /*imap_get_summary_internal (folder, ex);*/
1455                                 d(fprintf (stderr, "*** we tried to get message %d but failed\n", i));
1456                                 break;
1457                         }
1458                 }
1459         }
1460         
1461         /*gtk_signal_emit_by_name (GTK_OBJECT (folder), "folder_changed", 0);*/
1462         camel_object_trigger_event (CAMEL_OBJECT (folder), "folder_changed", GINT_TO_POINTER (0));
1463 }