This commit implements a new handling of image attachments. We follow
[modest] / src / modest-mail-operation.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include <stdarg.h>
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-camel-pop-folder.h>
39 #include <tny-camel-imap-folder.h>
40 #include <tny-camel-mem-stream.h>
41 #include <tny-simple-list.h>
42 #include <tny-send-queue.h>
43 #include <tny-status.h>
44 #include <tny-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include "modest-account-mgr-helpers.h"
49 #include <modest-tny-account.h>
50 #include <modest-tny-send-queue.h>
51 #include <modest-runtime.h>
52 #include "modest-text-utils.h"
53 #include "modest-tny-msg.h"
54 #include "modest-tny-folder.h"
55 #include "modest-tny-account-store.h"
56 #include "modest-tny-platform-factory.h"
57 #include "modest-marshal.h"
58 #include "modest-error.h"
59 #include "modest-mail-operation.h"
60
61 #define KB 1024
62
63 /* 
64  * Remove all these #ifdef stuff when the tinymail's idle calls become
65  * locked
66  */
67 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
68
69 /* 'private'/'protected' functions */
70 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
71 static void modest_mail_operation_init       (ModestMailOperation *obj);
72 static void modest_mail_operation_finalize   (GObject *obj);
73
74 static void     get_msg_cb (TnyFolder *folder, 
75                             gboolean cancelled, 
76                             TnyMsg *msg, 
77                             GError *rr, 
78                             gpointer user_data);
79
80 static void     get_msg_status_cb (GObject *obj,
81                                    TnyStatus *status,  
82                                    gpointer user_data);
83
84 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
85
86 enum _ModestMailOperationSignals 
87 {
88         PROGRESS_CHANGED_SIGNAL,
89
90         NUM_SIGNALS
91 };
92
93 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
94 struct _ModestMailOperationPrivate {
95         TnyAccount                 *account;
96         gchar                                                                            *account_name;
97         guint                      done;
98         guint                      total;
99         GObject                   *source;
100         GError                    *error;
101         ErrorCheckingUserCallback  error_checking;
102         gpointer                   error_checking_user_data;
103         ModestMailOperationStatus  status;      
104         ModestMailOperationTypeOperation op_type;
105 };
106
107 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
108                                                    MODEST_TYPE_MAIL_OPERATION, \
109                                                    ModestMailOperationPrivate))
110
111 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
112                                                    priv->status = new_status;\
113                                                }
114
115 typedef struct _GetMsgAsyncHelper {     
116         ModestMailOperation *mail_op;
117         TnyHeader *header;
118         GetMsgAsyncUserCallback user_callback;  
119         gpointer user_data;
120 } GetMsgAsyncHelper;
121
122 typedef struct _RefreshAsyncHelper {    
123         ModestMailOperation *mail_op;
124         RefreshAsyncUserCallback user_callback; 
125         gpointer user_data;
126 } RefreshAsyncHelper;
127
128 typedef struct _XFerMsgAsyncHelper
129 {
130         ModestMailOperation *mail_op;
131         TnyList *headers;
132         TnyFolder *dest_folder;
133         XferAsyncUserCallback user_callback;    
134         gboolean delete;
135         gpointer user_data;
136 } XFerMsgAsyncHelper;
137
138 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
139                                                       TnyMsg *msg,
140                                                       gpointer userdata);
141
142 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
143                                                        const gchar *from, const gchar *to,
144                                                        const gchar *cc, const gchar *bcc,
145                                                        const gchar *subject, const gchar *plain_body,
146                                                        const gchar *html_body, const GList *attachments_list,
147                                                        const GList *images_list,
148                                                        TnyHeaderFlags priority_flags,
149                                                        ModestMailOperationCreateMsgCallback callback,
150                                                        gpointer userdata);
151
152 static gboolean      idle_notify_queue (gpointer data);
153 typedef struct
154 {
155         ModestMailOperation *mail_op;
156         gchar *from;
157         gchar *to;
158         gchar *cc;
159         gchar *bcc;
160         gchar *subject;
161         gchar *plain_body;
162         gchar *html_body;
163         GList *attachments_list;
164         GList *images_list;
165         TnyHeaderFlags priority_flags;
166         ModestMailOperationCreateMsgCallback callback;
167         gpointer userdata;
168 } CreateMsgInfo;
169
170 typedef struct
171 {
172         ModestMailOperation *mail_op;
173         TnyMsg *msg;
174         ModestMailOperationCreateMsgCallback callback;
175         gpointer userdata;
176 } CreateMsgIdleInfo;
177
178 /* globals */
179 static GObjectClass *parent_class = NULL;
180
181 static guint signals[NUM_SIGNALS] = {0};
182
183 GType
184 modest_mail_operation_get_type (void)
185 {
186         static GType my_type = 0;
187         if (!my_type) {
188                 static const GTypeInfo my_info = {
189                         sizeof(ModestMailOperationClass),
190                         NULL,           /* base init */
191                         NULL,           /* base finalize */
192                         (GClassInitFunc) modest_mail_operation_class_init,
193                         NULL,           /* class finalize */
194                         NULL,           /* class data */
195                         sizeof(ModestMailOperation),
196                         1,              /* n_preallocs */
197                         (GInstanceInitFunc) modest_mail_operation_init,
198                         NULL
199                 };
200                 my_type = g_type_register_static (G_TYPE_OBJECT,
201                                                   "ModestMailOperation",
202                                                   &my_info, 0);
203         }
204         return my_type;
205 }
206
207 static void
208 modest_mail_operation_class_init (ModestMailOperationClass *klass)
209 {
210         GObjectClass *gobject_class;
211         gobject_class = (GObjectClass*) klass;
212
213         parent_class            = g_type_class_peek_parent (klass);
214         gobject_class->finalize = modest_mail_operation_finalize;
215
216         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
217
218         /**
219          * ModestMailOperation::progress-changed
220          * @self: the #MailOperation that emits the signal
221          * @user_data: user data set when the signal handler was connected
222          *
223          * Emitted when the progress of a mail operation changes
224          */
225         signals[PROGRESS_CHANGED_SIGNAL] = 
226                 g_signal_new ("progress-changed",
227                               G_TYPE_FROM_CLASS (gobject_class),
228                               G_SIGNAL_RUN_FIRST,
229                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
230                               NULL, NULL,
231                               g_cclosure_marshal_VOID__POINTER,
232                               G_TYPE_NONE, 1, G_TYPE_POINTER);
233
234 }
235
236 static void
237 modest_mail_operation_init (ModestMailOperation *obj)
238 {
239         ModestMailOperationPrivate *priv;
240
241         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
242
243         priv->account        = NULL;
244         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
245         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
246         priv->error          = NULL;
247         priv->done           = 0;
248         priv->total          = 0;
249         priv->source         = NULL;
250         priv->error_checking = NULL;
251         priv->error_checking_user_data = NULL;
252 }
253
254 static void
255 modest_mail_operation_finalize (GObject *obj)
256 {
257         ModestMailOperationPrivate *priv;
258
259         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
260
261         
262         
263         if (priv->error) {
264                 g_error_free (priv->error);
265                 priv->error = NULL;
266         }
267         if (priv->source) {
268                 g_object_unref (priv->source);
269                 priv->source = NULL;
270         }
271         if (priv->account) {
272                 g_object_unref (priv->account);
273                 priv->account = NULL;
274         }
275
276
277         G_OBJECT_CLASS(parent_class)->finalize (obj);
278 }
279
280 ModestMailOperation*
281 modest_mail_operation_new (ModestMailOperationTypeOperation op_type, 
282                            GObject *source)
283 {
284         ModestMailOperation *obj;
285         ModestMailOperationPrivate *priv;
286                 
287         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
288         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
289
290         priv->op_type = op_type;
291         if (source != NULL)
292                 priv->source = g_object_ref(source);
293
294         return obj;
295 }
296
297 ModestMailOperation*
298 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
299                                                GObject *source,
300                                                ErrorCheckingUserCallback error_handler,
301                                                gpointer user_data)
302 {
303         ModestMailOperation *obj;
304         ModestMailOperationPrivate *priv;
305                 
306         obj = modest_mail_operation_new (op_type, source);
307         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
308         
309         g_return_val_if_fail (error_handler != NULL, obj);
310         priv->error_checking = error_handler;
311         priv->error_checking_user_data = user_data;
312
313         return obj;
314 }
315
316 void
317 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
318 {
319         ModestMailOperationPrivate *priv;
320         
321         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
322         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
323
324         if (priv->error_checking != NULL)
325                 priv->error_checking (self, priv->error_checking_user_data);
326 }
327
328
329 ModestMailOperationTypeOperation
330 modest_mail_operation_get_type_operation (ModestMailOperation *self)
331 {
332         ModestMailOperationPrivate *priv;
333
334         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
335         
336         return priv->op_type;
337 }
338
339 gboolean 
340 modest_mail_operation_is_mine (ModestMailOperation *self, 
341                                GObject *me)
342 {
343         ModestMailOperationPrivate *priv;
344
345         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
346         if (priv->source == NULL) return FALSE;
347
348         return priv->source == me;
349 }
350
351 GObject *
352 modest_mail_operation_get_source (ModestMailOperation *self)
353 {
354         ModestMailOperationPrivate *priv;
355
356         g_return_val_if_fail (self, NULL);
357         
358         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
359         if (!priv) {
360                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
361                 return NULL;
362         }
363         
364         return (priv->source) ? g_object_ref (priv->source) : NULL;
365 }
366
367 ModestMailOperationStatus
368 modest_mail_operation_get_status (ModestMailOperation *self)
369 {
370         ModestMailOperationPrivate *priv;
371
372         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
373         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
374                               MODEST_MAIL_OPERATION_STATUS_INVALID);
375
376         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
377         if (!priv) {
378                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
379                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
380         }
381         
382         return priv->status;
383 }
384
385 const GError *
386 modest_mail_operation_get_error (ModestMailOperation *self)
387 {
388         ModestMailOperationPrivate *priv;
389
390         g_return_val_if_fail (self, NULL);
391         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
392
393         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
394
395         if (!priv) {
396                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
397                 return NULL;
398         }
399
400         return priv->error;
401 }
402
403 gboolean 
404 modest_mail_operation_cancel (ModestMailOperation *self)
405 {
406         ModestMailOperationPrivate *priv;
407         gboolean canceled = FALSE;
408
409         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
410
411         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
412
413         /* Note that if we call cancel with an already canceled mail
414            operation the progress changed signal won't be emitted */
415         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
416                 return FALSE;
417
418         /* Set new status */
419         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
420         
421         /* Cancel the mail operation. We need to wrap it between this
422            start/stop operations to allow following calls to the
423            account */
424         g_return_val_if_fail (priv->account, FALSE);
425         tny_account_cancel (priv->account);
426
427         return canceled;
428 }
429
430 guint 
431 modest_mail_operation_get_task_done (ModestMailOperation *self)
432 {
433         ModestMailOperationPrivate *priv;
434
435         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
436
437         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
438         return priv->done;
439 }
440
441 guint 
442 modest_mail_operation_get_task_total (ModestMailOperation *self)
443 {
444         ModestMailOperationPrivate *priv;
445
446         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
447
448         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
449         return priv->total;
450 }
451
452 gboolean
453 modest_mail_operation_is_finished (ModestMailOperation *self)
454 {
455         ModestMailOperationPrivate *priv;
456         gboolean retval = FALSE;
457
458         if (!MODEST_IS_MAIL_OPERATION (self)) {
459                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
460                 return retval;
461         }
462
463         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
464
465         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
466             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
467             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
468             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
469                 retval = TRUE;
470         } else {
471                 retval = FALSE;
472         }
473
474         return retval;
475 }
476
477 guint
478 modest_mail_operation_get_id (ModestMailOperation *self)
479 {
480         ModestMailOperationPrivate *priv;
481
482         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
483
484         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
485         return priv->done;
486 }
487
488 guint 
489 modest_mail_operation_set_id (ModestMailOperation *self,
490                               guint id)
491 {
492         ModestMailOperationPrivate *priv;
493
494         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
495
496         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
497         return priv->done;
498 }
499
500 /*
501  * Creates an image of the current state of a mail operation, the
502  * caller must free it
503  */
504 static ModestMailOperationState *
505 modest_mail_operation_clone_state (ModestMailOperation *self)
506 {
507         ModestMailOperationState *state;
508         ModestMailOperationPrivate *priv;
509
510         /* FIXME: this should be fixed properly
511          * 
512          * in some cases, priv was NULL, so checking here to
513          * make sure.
514          */
515         g_return_val_if_fail (self, NULL);
516         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
517         g_return_val_if_fail (priv, NULL);
518
519         if (!priv)
520                 return NULL;
521
522         state = g_slice_new (ModestMailOperationState);
523
524         state->status = priv->status;
525         state->op_type = priv->op_type;
526         state->done = priv->done;
527         state->total = priv->total;
528         state->finished = modest_mail_operation_is_finished (self);
529         state->bytes_done = 0;
530         state->bytes_total = 0;
531
532         return state;
533 }
534
535 /* ******************************************************************* */
536 /* ************************** SEND   ACTIONS ************************* */
537 /* ******************************************************************* */
538
539 void
540 modest_mail_operation_send_mail (ModestMailOperation *self,
541                                  TnyTransportAccount *transport_account,
542                                  TnyMsg* msg)
543 {
544         TnySendQueue *send_queue = NULL;
545         ModestMailOperationPrivate *priv;
546         
547         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
548         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
549         g_return_if_fail (TNY_IS_MSG (msg));
550         
551         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
552
553         /* Get account and set it into mail_operation */
554         priv->account = g_object_ref (transport_account);
555         priv->done = 1;
556         priv->total = 1;
557
558         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
559         if (!TNY_IS_SEND_QUEUE(send_queue)) {
560                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
561                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
562                              "modest: could not find send queue for account\n");
563                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
564
565         } else {
566                 /* TODO: connect to the msg-sent in order to know when
567                    the mail operation is finished */
568
569 /*              tny_send_queue_add (send_queue, msg, &(priv->error)); */
570                 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue), 
571                                            msg, 
572                                            &(priv->error));
573
574                 /* TODO: we're setting always success, do the check in
575                    the handler */
576                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
577         }
578
579         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)
580                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_outbox_waiting_to_be_sent"));
581
582         /* TODO: do this in the handler of the "msg-sent"
583            signal.Notify about operation end */
584         modest_mail_operation_notify_end (self);
585 }
586
587 static gboolean
588 idle_create_msg_cb (gpointer idle_data)
589 {
590         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
591
592         /* This is a GDK lock because we are an idle callback and
593          * info->callback can contain Gtk+ code */
594
595         gdk_threads_enter (); /* CHECKED */
596         info->callback (info->mail_op, info->msg, info->userdata);
597
598         g_object_unref (info->mail_op);
599         if (info->msg)
600                 g_object_unref (info->msg);
601         g_slice_free (CreateMsgIdleInfo, info);
602         gdk_threads_leave (); /* CHECKED */
603
604         return FALSE;
605 }
606
607 static gpointer 
608 create_msg_thread (gpointer thread_data)
609 {
610         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
611         TnyMsg *new_msg = NULL;
612         ModestMailOperationPrivate *priv;
613
614         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
615         if (info->html_body == NULL) {
616                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
617                                               info->bcc, info->subject, info->plain_body, 
618                                               info->attachments_list);
619         } else {
620                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
621                                                          info->bcc, info->subject, info->html_body,
622                                                          info->plain_body, info->attachments_list,
623                                                          info->images_list);
624         }
625
626         if (new_msg) {
627                 TnyHeader *header;
628                 TnyHeaderFlags flags = 0;
629
630                 /* Set priority flags in message */
631                 header = tny_msg_get_header (new_msg);
632                 if (info->priority_flags != 0)
633                         flags |= info->priority_flags;
634
635                 /* Set attachment flags in message */
636                 if (info->attachments_list != NULL)
637                         flags |= TNY_HEADER_FLAG_ATTACHMENTS;
638
639                 tny_header_set_flags (header, flags);
640                 g_object_unref (G_OBJECT(header));
641         } else {
642                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
643                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
644                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
645                              "modest: failed to create a new msg\n");
646         }
647
648
649         g_free (info->to);
650         g_free (info->from);
651         g_free (info->cc);
652         g_free (info->bcc);
653         g_free (info->plain_body);
654         g_free (info->html_body);
655         g_free (info->subject);
656         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
657         g_list_free (info->attachments_list);
658         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
659         g_list_free (info->images_list);
660
661         if (info->callback) {
662                 CreateMsgIdleInfo *idle_info;
663                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
664                 idle_info->mail_op = info->mail_op;
665                 g_object_ref (info->mail_op);
666                 idle_info->msg = new_msg;
667                 if (new_msg)
668                         g_object_ref (new_msg);
669                 idle_info->callback = info->callback;
670                 idle_info->userdata = info->userdata;
671                 g_idle_add (idle_create_msg_cb, idle_info);
672         } else {
673                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
674         }
675
676         g_object_unref (info->mail_op);
677         g_slice_free (CreateMsgInfo, info);
678         return NULL;
679 }
680
681 void
682 modest_mail_operation_create_msg (ModestMailOperation *self,
683                                   const gchar *from, const gchar *to,
684                                   const gchar *cc, const gchar *bcc,
685                                   const gchar *subject, const gchar *plain_body,
686                                   const gchar *html_body,
687                                   const GList *attachments_list,
688                                   const GList *images_list,
689                                   TnyHeaderFlags priority_flags,
690                                   ModestMailOperationCreateMsgCallback callback,
691                                   gpointer userdata)
692 {
693         CreateMsgInfo *info = NULL;
694
695         info = g_slice_new0 (CreateMsgInfo);
696         info->mail_op = self;
697         g_object_ref (self);
698
699         info->from = g_strdup (from);
700         info->to = g_strdup (to);
701         info->cc = g_strdup (cc);
702         info->bcc  = g_strdup (bcc);
703         info->subject = g_strdup (subject);
704         info->plain_body = g_strdup (plain_body);
705         info->html_body = g_strdup (html_body);
706         info->attachments_list = g_list_copy ((GList *) attachments_list);
707         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
708         info->images_list = g_list_copy ((GList *) images_list);
709         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
710         info->priority_flags = priority_flags;
711
712         info->callback = callback;
713         info->userdata = userdata;
714
715         g_thread_create (create_msg_thread, info, FALSE, NULL);
716 }
717
718 typedef struct
719 {
720         TnyTransportAccount *transport_account;
721         TnyMsg *draft_msg;
722 } SendNewMailInfo;
723
724 static void
725 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
726                                         TnyMsg *msg,
727                                         gpointer userdata)
728 {
729         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
730         TnyFolder *draft_folder = NULL;
731         TnyFolder *outbox_folder = NULL;
732         TnyHeader *header;
733         GError *err = NULL;
734
735         if (!msg) {
736                 goto end;
737         }
738
739         /* Call mail operation */
740         modest_mail_operation_send_mail (self, info->transport_account, msg);
741
742         /* Remove old mail from its source folder */
743         draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
744         outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
745         if (info->draft_msg != NULL) {
746                 TnyFolder *folder = NULL;
747                 TnyFolder *src_folder = NULL;
748                 TnyFolderType folder_type;              
749                 folder = tny_msg_get_folder (info->draft_msg);          
750                 if (folder == NULL) goto end;
751                 folder_type = modest_tny_folder_guess_folder_type (folder);
752                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
753                         src_folder = outbox_folder;
754                 else 
755                         src_folder = draft_folder;
756
757                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
758                  * because this function requires it to have a UID. */          
759                 header = tny_msg_get_header (info->draft_msg);
760                 tny_folder_remove_msg (src_folder, header, NULL);
761
762                 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
763 /*              tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);  /\* expunge *\/ */
764                 
765                 g_object_unref (header);
766                 g_object_unref (folder);
767         }
768
769 end:
770         if (err != NULL)
771                 g_error_free(err);      
772         if (info->draft_msg)
773                 g_object_unref (info->draft_msg);
774         if (draft_folder)
775                 g_object_unref (draft_folder);
776         if (outbox_folder)
777                 g_object_unref (outbox_folder);
778         if (info->transport_account)
779                 g_object_unref (info->transport_account);
780         g_slice_free (SendNewMailInfo, info);
781         modest_mail_operation_notify_end (self);
782 }
783
784 void
785 modest_mail_operation_send_new_mail (ModestMailOperation *self,
786                                      TnyTransportAccount *transport_account,
787                                      TnyMsg *draft_msg,
788                                      const gchar *from,  const gchar *to,
789                                      const gchar *cc,  const gchar *bcc,
790                                      const gchar *subject, const gchar *plain_body,
791                                      const gchar *html_body,
792                                      const GList *attachments_list,
793                                      const GList *images_list,
794                                      TnyHeaderFlags priority_flags)
795 {
796         ModestMailOperationPrivate *priv = NULL;
797         SendNewMailInfo *info;
798
799         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
800         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
801
802         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
803
804         /* Check parametters */
805         if (to == NULL) {
806                 /* Set status failed and set an error */
807                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
808                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
809                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
810                              _("Error trying to send a mail. You need to set at least one recipient"));
811                 return;
812         }
813         info = g_slice_new0 (SendNewMailInfo);
814         info->transport_account = transport_account;
815         if (transport_account)
816                 g_object_ref (transport_account);
817         info->draft_msg = draft_msg;
818         if (draft_msg)
819                 g_object_ref (draft_msg);
820         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
821                                           attachments_list, images_list, priority_flags,
822                                           modest_mail_operation_send_new_mail_cb, info);
823
824 }
825
826 typedef struct
827 {
828         TnyTransportAccount *transport_account;
829         TnyMsg *draft_msg;
830         ModestMsgEditWindow *edit_window;
831 } SaveToDraftsInfo;
832
833 static void
834 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
835                                          TnyMsg *msg,
836                                          gpointer userdata)
837 {
838         TnyFolder *src_folder = NULL;
839         TnyFolder *drafts = NULL;
840         TnyHeader *header = NULL;
841         ModestMailOperationPrivate *priv = NULL;
842         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
843
844         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
845         if (!msg) {
846                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
847                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
848                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
849                              "modest: failed to create a new msg\n");
850                 goto end;
851         }
852
853         drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), 
854                                                         TNY_FOLDER_TYPE_DRAFTS);
855         if (!drafts) {
856                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
857                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
858                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
859                              "modest: failed to create a new msg\n");
860                 goto end;
861         }
862
863         if (!priv->error)
864                 tny_folder_add_msg (drafts, msg, &(priv->error));
865
866         if ((!priv->error) && (info->draft_msg != NULL)) {
867                 header = tny_msg_get_header (info->draft_msg);
868                 src_folder = tny_header_get_folder (header); 
869
870                 /* Remove the old draft */
871                 tny_folder_remove_msg (src_folder, header, NULL);
872
873                 /* Synchronize to expunge and to update the msg counts */
874                 tny_folder_sync_async (drafts, TRUE, NULL, NULL, NULL);
875                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
876
877                 g_object_unref (header);
878         }
879         
880         if (!priv->error)
881                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
882         else
883                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
884
885         if (info->edit_window)
886                 modest_msg_edit_window_set_draft (info->edit_window, msg);
887
888
889 end:
890         if (drafts)
891                 g_object_unref (G_OBJECT(drafts));
892         if (src_folder)
893                 g_object_unref (G_OBJECT(src_folder));
894         if (info->edit_window)
895                 g_object_unref (G_OBJECT(info->edit_window));
896         if (info->draft_msg)
897                 g_object_unref (G_OBJECT (info->draft_msg));
898         if (info->transport_account)
899                 g_object_unref (G_OBJECT(info->transport_account));
900         g_slice_free (SaveToDraftsInfo, info);
901
902         modest_mail_operation_notify_end (self);
903 }
904
905 void
906 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
907                                       TnyTransportAccount *transport_account,
908                                       TnyMsg *draft_msg,
909                                       ModestMsgEditWindow *edit_window,
910                                       const gchar *from,  const gchar *to,
911                                       const gchar *cc,  const gchar *bcc,
912                                       const gchar *subject, const gchar *plain_body,
913                                       const gchar *html_body,
914                                       const GList *attachments_list,
915                                       const GList *images_list,
916                                       TnyHeaderFlags priority_flags)
917 {
918         ModestMailOperationPrivate *priv = NULL;
919         SaveToDraftsInfo *info = NULL;
920
921         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
922         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
923
924         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
925
926         /* Get account and set it into mail_operation */
927         priv->account = g_object_ref (transport_account);
928
929         info = g_slice_new0 (SaveToDraftsInfo);
930         info->transport_account = g_object_ref (transport_account);
931         info->draft_msg = draft_msg;
932         if (draft_msg)
933                 g_object_ref (draft_msg);
934         info->edit_window = edit_window;
935         if (edit_window)
936                 g_object_ref (edit_window);
937
938         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
939                                           attachments_list, images_list, priority_flags,
940                                           modest_mail_operation_save_to_drafts_cb, info);
941
942 }
943
944 typedef struct 
945 {
946         ModestMailOperation *mail_op;
947         TnyStoreAccount *account;
948         TnyTransportAccount *transport_account;
949         gint max_size;
950         gint retrieve_limit;
951         gchar *retrieve_type;
952         gchar *account_name;
953         UpdateAccountCallback callback;
954         gpointer user_data;
955         TnyList *new_headers;
956 } UpdateAccountInfo;
957
958 typedef struct
959 {
960         ModestMailOperation *mail_op;
961         TnyMimePart *mime_part;
962         gssize size;
963         GetMimePartSizeCallback callback;
964         gpointer userdata;
965 } GetMimePartSizeInfo;
966
967 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
968 /* We use this folder observer to track the headers that have been
969  * added to a folder */
970 typedef struct {
971         GObject parent;
972         TnyList *new_headers;
973 } InternalFolderObserver;
974
975 typedef struct {
976         GObjectClass parent;
977 } InternalFolderObserverClass;
978
979 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
980
981 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
982                          internal_folder_observer,
983                          G_TYPE_OBJECT,
984                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
985
986
987 static void
988 foreach_add_item (gpointer header, gpointer user_data)
989 {
990         tny_list_prepend (TNY_LIST (user_data), 
991                           g_object_ref (G_OBJECT (header)));
992 }
993
994 /* This is the method that looks for new messages in a folder */
995 static void
996 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
997 {
998         InternalFolderObserver *derived = (InternalFolderObserver *)self;
999         
1000         TnyFolderChangeChanged changed;
1001
1002         changed = tny_folder_change_get_changed (change);
1003
1004         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1005                 TnyList *list;
1006
1007                 /* Get added headers */
1008                 list = tny_simple_list_new ();
1009                 tny_folder_change_get_added_headers (change, list);
1010
1011                 /* Add them to the folder observer */
1012                 tny_list_foreach (list, foreach_add_item, 
1013                                   derived->new_headers);
1014
1015                 g_object_unref (G_OBJECT (list));
1016         }
1017 }
1018
1019 static void
1020 internal_folder_observer_init (InternalFolderObserver *self) 
1021 {
1022         self->new_headers = tny_simple_list_new ();
1023 }
1024 static void
1025 internal_folder_observer_finalize (GObject *object) 
1026 {
1027         InternalFolderObserver *self;
1028
1029         self = (InternalFolderObserver *) object;
1030         g_object_unref (self->new_headers);
1031
1032         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1033 }
1034 static void
1035 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1036 {
1037         iface->update_func = internal_folder_observer_update;
1038 }
1039 static void
1040 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1041 {
1042         GObjectClass *object_class;
1043
1044         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1045         object_class = (GObjectClass*) klass;
1046         object_class->finalize = internal_folder_observer_finalize;
1047 }
1048
1049 /*****************/
1050
1051 static void
1052 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1053 {
1054         TnyIterator *iter;
1055         TnyList *folders = tny_simple_list_new ();
1056
1057         tny_folder_store_get_folders (store, folders, query, NULL);
1058         iter = tny_list_create_iterator (folders);
1059
1060         while (!tny_iterator_is_done (iter)) {
1061
1062                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1063                 if (folder) {
1064                         tny_list_prepend (all_folders, G_OBJECT (folder));
1065                         recurse_folders (folder, query, all_folders);    
1066                         g_object_unref (G_OBJECT (folder));
1067                 }
1068
1069                 tny_iterator_next (iter);
1070         }
1071          g_object_unref (G_OBJECT (iter));
1072          g_object_unref (G_OBJECT (folders));
1073 }
1074
1075 /* 
1076  * Issues the "progress-changed" signal. The timer won't be removed,
1077  * so you must call g_source_remove to stop the signal emission
1078  */
1079 static gboolean
1080 idle_notify_progress (gpointer data)
1081 {
1082         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1083         ModestMailOperationState *state;
1084
1085         state = modest_mail_operation_clone_state (mail_op);
1086
1087         /* This is a GDK lock because we are an idle callback and
1088          * the handlers of this signal can contain Gtk+ code */
1089
1090         gdk_threads_enter (); /* CHECKED */
1091         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1092         gdk_threads_leave (); /* CHECKED */
1093
1094         g_slice_free (ModestMailOperationState, state);
1095         
1096         return TRUE;
1097 }
1098
1099 /* 
1100  * Issues the "progress-changed" signal and removes the timer. It uses
1101  * a lock to ensure that the progress information of the mail
1102  * operation is not modified while there are notifications pending
1103  */
1104 static gboolean
1105 idle_notify_progress_once (gpointer data)
1106 {
1107         ModestPair *pair;
1108
1109         pair = (ModestPair *) data;
1110
1111         /* This is a GDK lock because we are an idle callback and
1112          * the handlers of this signal can contain Gtk+ code */
1113
1114         gdk_threads_enter (); /* CHECKED */
1115         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1116         gdk_threads_leave (); /* CHECKED */
1117
1118         /* Free the state and the reference to the mail operation */
1119         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1120         g_object_unref (pair->first);
1121
1122         return FALSE;
1123 }
1124
1125 /* 
1126  * Used to notify the queue from the main
1127  * loop. We call it inside an idle call to achieve that
1128  */
1129 static gboolean
1130 idle_notify_queue (gpointer data)
1131 {
1132         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1133
1134         /* Do not need to block, the notify end will do it for us */    
1135         modest_mail_operation_notify_end (mail_op);
1136         g_object_unref (mail_op);
1137
1138         return FALSE;
1139 }
1140
1141 static int
1142 compare_headers_by_date (gconstpointer a, 
1143                          gconstpointer b)
1144 {
1145         TnyHeader **header1, **header2;
1146         time_t sent1, sent2;
1147
1148         header1 = (TnyHeader **) a;
1149         header2 = (TnyHeader **) b;
1150
1151         sent1 = tny_header_get_date_sent (*header1);
1152         sent2 = tny_header_get_date_sent (*header2);
1153
1154         /* We want the most recent ones (greater time_t) at the
1155            beginning */
1156         if (sent1 < sent2)
1157                 return 1;
1158         else
1159                 return -1;
1160 }
1161
1162 static gboolean 
1163 set_last_updated_idle (gpointer data)
1164 {
1165
1166         /* This is a GDK lock because we are an idle callback and
1167          * modest_account_mgr_set_last_updated can issue Gtk+ code */
1168
1169         gdk_threads_enter (); /* CHECKED - please recheck */
1170
1171         /* It does not matter if the time is not exactly the same than
1172            the time when this idle was called, it's just an
1173            approximation and it won't be very different */
1174
1175         modest_account_mgr_set_last_updated (modest_runtime_get_account_mgr (), 
1176                                              (gchar *) data, 
1177                                              time (NULL));
1178
1179         gdk_threads_leave (); /* CHECKED - please recheck */
1180
1181         return FALSE;
1182 }
1183
1184 static gboolean
1185 idle_update_account_cb (gpointer data)
1186 {
1187         UpdateAccountInfo *idle_info;
1188
1189         idle_info = (UpdateAccountInfo *) data;
1190
1191         /* This is a GDK lock because we are an idle callback and
1192          * idle_info->callback can contain Gtk+ code */
1193
1194         gdk_threads_enter (); /* CHECKED */
1195         idle_info->callback (idle_info->mail_op,
1196                              idle_info->new_headers,
1197                              idle_info->user_data);
1198         gdk_threads_leave (); /* CHECKED */
1199
1200         /* Frees */
1201         g_object_unref (idle_info->mail_op);
1202         if (idle_info->new_headers)
1203                 g_object_unref (idle_info->new_headers);
1204         g_free (idle_info);
1205
1206         return FALSE;
1207 }
1208
1209 static TnyList *
1210 get_all_folders_from_account (TnyStoreAccount *account,
1211                               GError **error)
1212 {
1213         TnyList *all_folders = NULL;
1214         TnyIterator *iter = NULL;
1215         TnyFolderStoreQuery *query = NULL;
1216
1217         all_folders = tny_simple_list_new ();
1218         query = tny_folder_store_query_new ();
1219         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1220         tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
1221                                       all_folders,
1222                                       query,
1223                                       error);
1224
1225         if (*error) {
1226                 if (all_folders)
1227                         g_object_unref (all_folders);
1228                 return NULL;
1229         }
1230
1231         iter = tny_list_create_iterator (all_folders);
1232         while (!tny_iterator_is_done (iter)) {
1233                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1234                 if (folder) {
1235                         recurse_folders (folder, query, all_folders);
1236                         g_object_unref (folder);
1237                 }
1238                 tny_iterator_next (iter);
1239         }
1240         g_object_unref (G_OBJECT (iter));
1241
1242         return all_folders;
1243 }
1244
1245
1246 static gpointer
1247 update_account_thread (gpointer thr_user_data)
1248 {
1249         static gboolean first_time = TRUE;
1250         UpdateAccountInfo *info = NULL;
1251         TnyList *all_folders = NULL, *new_headers = NULL;
1252         GPtrArray *new_headers_array = NULL;
1253         TnyIterator *iter = NULL;
1254         ModestMailOperationPrivate *priv = NULL;
1255         ModestTnySendQueue *send_queue = NULL;
1256         gint i = 0, timeout = 0;
1257
1258         info = (UpdateAccountInfo *) thr_user_data;
1259         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1260
1261         /* Get account and set it into mail_operation */
1262         priv->account = g_object_ref (info->account);
1263
1264         /* Get all the folders. We can do it synchronously because
1265            we're already running in a different thread than the UI */
1266         all_folders = get_all_folders_from_account (info->account, &(priv->error));
1267         if (!all_folders) {
1268                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1269                 goto out;
1270         }
1271
1272         /* Update status and notify. We need to call the notification
1273            with a source function in order to call it from the main
1274            loop. We need that in order not to get into trouble with
1275            Gtk+. We use a timeout in order to provide more status
1276            information, because the sync tinymail call does not
1277            provide it for the moment */
1278         timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1279
1280         new_headers_array = g_ptr_array_new ();
1281         iter = tny_list_create_iterator (all_folders);
1282
1283         while (!tny_iterator_is_done (iter) && !priv->error && 
1284                priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1285
1286                 TnyFolderType folder_type;
1287                 TnyFolder *folder = NULL;
1288
1289                 folder = TNY_FOLDER (tny_iterator_get_current (iter));
1290                 folder_type = tny_folder_get_folder_type (folder);
1291
1292                 /* Refresh it only if it's the INBOX */
1293                 if (folder_type == TNY_FOLDER_TYPE_INBOX) {
1294                         InternalFolderObserver *observer = NULL;
1295                         TnyIterator *new_headers_iter = NULL;
1296
1297                         /* Refresh the folder. Our observer receives
1298                          * the new emails during folder refreshes, so
1299                          * we can use observer->new_headers
1300                          */
1301                         observer = g_object_new (internal_folder_observer_get_type (), NULL);
1302                         tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1303                 
1304                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1305
1306                         new_headers_iter = tny_list_create_iterator (observer->new_headers);
1307                         while (!tny_iterator_is_done (new_headers_iter)) {
1308                                 TnyHeader *header = NULL;
1309
1310                                 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1311                                 /* Apply per-message size limits */
1312                                 if (tny_header_get_message_size (header) < info->max_size)
1313                                         g_ptr_array_add (new_headers_array, g_object_ref (header));
1314                                 
1315                                 g_object_unref (header);
1316                                 tny_iterator_next (new_headers_iter);
1317                         }
1318                         g_object_unref (new_headers_iter);
1319
1320                         tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1321                         g_object_unref (observer);
1322                 } else {
1323                         /* We no not need to do it the first time,
1324                            because it's automatically done by the tree
1325                            model */
1326                         if (G_LIKELY (!first_time))
1327                                 tny_folder_poke_status (folder);
1328                 }
1329                 g_object_unref (folder);
1330
1331                 tny_iterator_next (iter);
1332         }
1333         g_object_unref (G_OBJECT (iter));
1334         g_source_remove (timeout);
1335
1336         if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED && 
1337             priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1338             new_headers_array->len > 0) {
1339                 gint msg_num = 0;
1340
1341                 /* Order by date */
1342                 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1343
1344                 /* TODO: Ask the user, instead of just failing,
1345                  * showing mail_nc_msg_count_limit_exceeded, with 'Get
1346                  * all' and 'Newest only' buttons. */
1347                 if (new_headers_array->len > info->retrieve_limit) {
1348                         /* TODO */
1349                 }
1350
1351                 /* Should be get only the headers or the message as well? */
1352                 if (g_ascii_strcasecmp (info->retrieve_type, 
1353                                         MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {     
1354                         priv->done = 0;
1355                         priv->total = MIN (new_headers_array->len, info->retrieve_limit);
1356                         while (msg_num < priv->total) {
1357
1358                                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1359                                 TnyFolder *folder = tny_header_get_folder (header);
1360                                 TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1361                                 ModestMailOperationState *state;
1362                                 ModestPair* pair;
1363
1364                                 priv->done++;
1365                                 /* We can not just use the mail operation because the
1366                                    values of done and total could change before the
1367                                    idle is called */
1368                                 state = modest_mail_operation_clone_state (info->mail_op);
1369                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1370                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1371                                                  pair, (GDestroyNotify) modest_pair_free);
1372
1373                                 g_object_unref (msg);
1374                                 g_object_unref (folder);
1375
1376                                 msg_num++;
1377                         }
1378                 }
1379         }
1380
1381         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1382                 goto out;
1383
1384         /* Copy the headers to a list and free the array */
1385         new_headers = tny_simple_list_new ();
1386         for (i=0; i < new_headers_array->len; i++) {
1387                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1388                 tny_list_append (new_headers, G_OBJECT (header));
1389         }
1390         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1391         g_ptr_array_free (new_headers_array, FALSE);
1392         
1393
1394         /* Perform send (if operation was not cancelled) */
1395         priv->done = 0;
1396         priv->total = 0;
1397         if (priv->account != NULL) 
1398                 g_object_unref (priv->account);
1399         priv->account = g_object_ref (info->transport_account);
1400         
1401         send_queue = modest_runtime_get_send_queue (info->transport_account);
1402         if (send_queue) {
1403                 modest_tny_send_queue_try_to_send (send_queue);
1404         } else {
1405                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1406                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1407                              "cannot create a send queue for %s\n", 
1408                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1409                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1410         }
1411         
1412         /* Check if the operation was a success */
1413         if (!priv->error) {
1414                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1415
1416                 /* Update the last updated key */
1417                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1418                                  set_last_updated_idle, 
1419                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1420                                  (GDestroyNotify) g_free);
1421         }
1422
1423  out:
1424
1425         if (info->callback) {
1426                 UpdateAccountInfo *idle_info;
1427
1428                 /* This thread is not in the main lock */
1429                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1430                 idle_info->mail_op = g_object_ref (info->mail_op);
1431                 idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL;
1432                 idle_info->callback = info->callback;
1433                 idle_info->user_data = info->user_data;
1434                 g_idle_add (idle_update_account_cb, idle_info);
1435         }
1436
1437         /* Notify about operation end. Note that the info could be
1438            freed before this idle happens, but the mail operation will
1439            be still alive */
1440         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1441
1442         /* Frees */
1443         if (new_headers)
1444                 g_object_unref (new_headers);
1445         if (all_folders)
1446                 g_object_unref (all_folders);
1447         g_object_unref (info->account);
1448         g_object_unref (info->transport_account);
1449         g_free (info->retrieve_type);
1450         g_slice_free (UpdateAccountInfo, info);
1451
1452         first_time = FALSE;
1453
1454         return NULL;
1455 }
1456
1457 gboolean
1458 modest_mail_operation_update_account (ModestMailOperation *self,
1459                                       const gchar *account_name,
1460                                       UpdateAccountCallback callback,
1461                                       gpointer user_data)
1462 {
1463         GThread *thread = NULL;
1464         UpdateAccountInfo *info = NULL;
1465         ModestMailOperationPrivate *priv = NULL;
1466         ModestAccountMgr *mgr = NULL;
1467         TnyStoreAccount *store_account = NULL;
1468         TnyTransportAccount *transport_account = NULL;
1469
1470         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1471         g_return_val_if_fail (account_name, FALSE);
1472
1473         /* Init mail operation. Set total and done to 0, and do not
1474            update them, this way the progress objects will know that
1475            we have no clue about the number of the objects */
1476         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1477         priv->total = 0;
1478         priv->done  = 0;
1479         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1480
1481         /* Get the store account */
1482         store_account = (TnyStoreAccount *)
1483                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1484                                                                      account_name,
1485                                                                      TNY_ACCOUNT_TYPE_STORE);
1486                                                                      
1487         if (!store_account) {
1488                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1489                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1490                              "cannot get tny store account for %s\n", account_name);
1491                 goto error;
1492         }
1493
1494         
1495         /* Get the transport account, we can not do it in the thread
1496            due to some problems with dbus */
1497         transport_account = (TnyTransportAccount *)
1498                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1499                                                                                     account_name);
1500         if (!transport_account) {
1501                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1502                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1503                              "cannot get tny transport account for %s\n", account_name);
1504                 goto error;
1505         }
1506
1507         /* Create the helper object */
1508         info = g_slice_new (UpdateAccountInfo);
1509         info->mail_op = self;
1510         info->account = store_account;
1511         info->transport_account = transport_account;
1512         info->callback = callback;
1513         info->user_data = user_data;
1514
1515         /* Get the message size limit */
1516         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1517                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1518         if (info->max_size == 0)
1519                 info->max_size = G_MAXINT;
1520         else
1521                 info->max_size = info->max_size * KB;
1522
1523         /* Get per-account retrieval type */
1524         mgr = modest_runtime_get_account_mgr ();
1525         info->retrieve_type = modest_account_mgr_get_retrieve_type (mgr, account_name);
1526
1527         /* Get per-account message amount retrieval limit */
1528         info->retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, account_name);
1529         if (info->retrieve_limit == 0)
1530                 info->retrieve_limit = G_MAXINT;
1531                 
1532         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1533
1534         /* Set account busy */
1535         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1536         priv->account_name = g_strdup(account_name);
1537         
1538         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1539
1540         return TRUE;
1541
1542  error:
1543         if (store_account)
1544                 g_object_unref (store_account);
1545         if (transport_account)
1546                 g_object_unref (transport_account);
1547         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1548         if (callback) {
1549                 callback (self, NULL, user_data);
1550         }
1551         modest_mail_operation_notify_end (self);
1552         return FALSE;
1553 }
1554
1555 /* ******************************************************************* */
1556 /* ************************** STORE  ACTIONS ************************* */
1557 /* ******************************************************************* */
1558
1559
1560 TnyFolder *
1561 modest_mail_operation_create_folder (ModestMailOperation *self,
1562                                      TnyFolderStore *parent,
1563                                      const gchar *name)
1564 {
1565         ModestMailOperationPrivate *priv;
1566         TnyFolder *new_folder = NULL;
1567
1568         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1569         g_return_val_if_fail (name, NULL);
1570         
1571         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1572
1573         /* Check for already existing folder */
1574         if (modest_tny_folder_has_subfolder_with_name (parent, name)) {
1575                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1576                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1577                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1578                              _CS("ckdg_ib_folder_already_exists"));
1579         }
1580
1581         /* Check parent */
1582         if (TNY_IS_FOLDER (parent)) {
1583                 /* Check folder rules */
1584                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1585                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1586                         /* Set status failed and set an error */
1587                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1588                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1589                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1590                                      _("mail_in_ui_folder_create_error"));
1591                 }
1592         }
1593
1594         if (!strcmp (name, " ") || strchr (name, '/')) {
1595                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1596                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1597                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1598                              _("mail_in_ui_folder_create_error"));
1599         }
1600
1601         if (!priv->error) {
1602                 /* Create the folder */
1603                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1604                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1605                 if (!priv->error)
1606                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1607         }
1608
1609         /* Notify about operation end */
1610         modest_mail_operation_notify_end (self);
1611
1612         return new_folder;
1613 }
1614
1615 void
1616 modest_mail_operation_remove_folder (ModestMailOperation *self,
1617                                      TnyFolder           *folder,
1618                                      gboolean             remove_to_trash)
1619 {
1620         TnyAccount *account;
1621         ModestMailOperationPrivate *priv;
1622         ModestTnyFolderRules rules;
1623
1624         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1625         g_return_if_fail (TNY_IS_FOLDER (folder));
1626         
1627         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1628         
1629         /* Check folder rules */
1630         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1631         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1632                 /* Set status failed and set an error */
1633                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1634                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1635                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1636                              _("mail_in_ui_folder_delete_error"));
1637                 goto end;
1638         }
1639
1640         /* Get the account */
1641         account = modest_tny_folder_get_account (folder);
1642         priv->account = g_object_ref(account);
1643
1644         /* Delete folder or move to trash */
1645         if (remove_to_trash) {
1646                 TnyFolder *trash_folder = NULL;
1647                 trash_folder = modest_tny_account_get_special_folder (account,
1648                                                                       TNY_FOLDER_TYPE_TRASH);
1649                 /* TODO: error_handling */
1650                 if (trash_folder) {
1651                         modest_mail_operation_xfer_folder (self, folder,
1652                                                     TNY_FOLDER_STORE (trash_folder), 
1653                                                     TRUE, NULL, NULL);
1654                         g_object_unref (trash_folder);
1655                 }
1656         } else {
1657                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1658
1659                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1660                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1661
1662                 if (!priv->error)
1663                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1664
1665                 if (parent)
1666                         g_object_unref (G_OBJECT (parent));
1667         }
1668         g_object_unref (G_OBJECT (account));
1669
1670  end:
1671         /* Notify about operation end */
1672         modest_mail_operation_notify_end (self);
1673 }
1674
1675 static void
1676 transfer_folder_status_cb (GObject *obj,
1677                            TnyStatus *status,
1678                            gpointer user_data)
1679 {
1680         ModestMailOperation *self;
1681         ModestMailOperationPrivate *priv;
1682         ModestMailOperationState *state;
1683         XFerMsgAsyncHelper *helper;
1684
1685         g_return_if_fail (status != NULL);
1686         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1687
1688         helper = (XFerMsgAsyncHelper *) user_data;
1689         g_return_if_fail (helper != NULL);
1690
1691         self = helper->mail_op;
1692         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1693
1694         priv->done = status->position;
1695         priv->total = status->of_total;
1696
1697         state = modest_mail_operation_clone_state (self);
1698
1699         /* This is not a GDK lock because we are a Tinymail callback
1700          * which is already GDK locked by Tinymail */
1701
1702         /* no gdk_threads_enter (), CHECKED */
1703
1704         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1705
1706         /* no gdk_threads_leave (), CHECKED */
1707
1708         g_slice_free (ModestMailOperationState, state);
1709 }
1710
1711
1712 static void
1713 transfer_folder_cb (TnyFolder *folder, 
1714                     gboolean cancelled, 
1715                     TnyFolderStore *into, 
1716                     TnyFolder *new_folder, 
1717                     GError *err, 
1718                     gpointer user_data)
1719 {
1720         XFerMsgAsyncHelper *helper;
1721         ModestMailOperation *self = NULL;
1722         ModestMailOperationPrivate *priv = NULL;
1723
1724         helper = (XFerMsgAsyncHelper *) user_data;
1725         g_return_if_fail (helper != NULL);       
1726
1727         self = helper->mail_op;
1728         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1729
1730         if (err) {
1731                 priv->error = g_error_copy (err);
1732                 priv->done = 0;
1733                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1734         } else if (cancelled) {
1735                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1736                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1737                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1738                              _("Transference of %s was cancelled."),
1739                              tny_folder_get_name (folder));
1740         } else {
1741                 priv->done = 1;
1742                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1743         }
1744                 
1745         /* Notify about operation end */
1746         modest_mail_operation_notify_end (self);
1747
1748         /* If user defined callback function was defined, call it */
1749         if (helper->user_callback) {
1750
1751                 /* This is not a GDK lock because we are a Tinymail callback
1752                  * which is already GDK locked by Tinymail */
1753
1754                 /* no gdk_threads_enter (), CHECKED */
1755                 helper->user_callback (self, helper->user_data);
1756                 /* no gdk_threads_leave () , CHECKED */
1757         }
1758
1759         /* Free */
1760         g_object_unref (helper->mail_op);
1761         g_slice_free   (XFerMsgAsyncHelper, helper);
1762 }
1763
1764 /**
1765  *
1766  * This function checks if the new name is a valid name for our local
1767  * folders account. The new name could not be the same than then name
1768  * of any of the mandatory local folders
1769  *
1770  * We can not rely on tinymail because tinymail does not check the
1771  * name of the virtual folders that the account could have in the case
1772  * that we're doing a rename (because it directly calls Camel which
1773  * knows nothing about our virtual folders). 
1774  *
1775  * In the case of an actual copy/move (i.e. move/copy a folder between
1776  * accounts) tinymail uses the tny_folder_store_create_account which
1777  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1778  * checks the new name of the folder, so this call in that case
1779  * wouldn't be needed. *But* NOTE that if tinymail changes its
1780  * implementation (if folder transfers within the same account is no
1781  * longer implemented as a rename) this call will allow Modest to work
1782  * perfectly
1783  *
1784  * If the new name is not valid, this function will set the status to
1785  * failed and will set also an error in the mail operation
1786  */
1787 static gboolean
1788 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1789                                  TnyFolderStore *into,
1790                                  const gchar *new_name)
1791 {
1792         if (TNY_IS_ACCOUNT (into) && 
1793             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1794             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1795                                                                  new_name)) {
1796                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1797                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1798                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1799                              _("ckdg_ib_folder_already_exists"));
1800                 return FALSE;
1801         } else
1802                 return TRUE;
1803 }
1804
1805 void
1806 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1807                                    TnyFolder *folder,
1808                                    TnyFolderStore *parent,
1809                                    gboolean delete_original,
1810                                    XferAsyncUserCallback user_callback,
1811                                    gpointer user_data)
1812 {
1813         ModestMailOperationPrivate *priv = NULL;
1814         ModestTnyFolderRules parent_rules = 0, rules; 
1815         XFerMsgAsyncHelper *helper = NULL;
1816         const gchar *folder_name = NULL;
1817         const gchar *error_msg;
1818
1819         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1820         g_return_if_fail (TNY_IS_FOLDER (folder));
1821         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1822
1823         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1824         folder_name = tny_folder_get_name (folder);
1825
1826         /* Set the error msg */
1827         error_msg = _("mail_in_ui_folder_move_target_error");
1828
1829         /* Get account and set it into mail_operation */
1830         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1831         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1832
1833         /* Get folder rules */
1834         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1835         if (TNY_IS_FOLDER (parent))
1836                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1837         
1838         /* Apply operation constraints */
1839         if ((gpointer) parent == (gpointer) folder ||
1840             (!TNY_IS_FOLDER_STORE (parent)) || 
1841             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1842                 /* Folder rules */
1843                 goto error;
1844         } else if (TNY_IS_FOLDER (parent) && 
1845                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1846                 /* Folder rules */
1847                 goto error;
1848
1849         } else if (TNY_IS_FOLDER (parent) &&
1850                    TNY_IS_FOLDER_STORE (folder) &&
1851                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
1852                                                   TNY_FOLDER_STORE (folder))) {
1853                 /* Do not move a parent into a child */
1854                 goto error;
1855         } else if (TNY_IS_FOLDER_STORE (parent) &&
1856                    modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1857                 /* Check that the new folder name is not used by any
1858                    parent subfolder */
1859                 goto error;     
1860         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1861                 /* Check that the new folder name is not used by any
1862                    special local folder */
1863                 goto error;
1864         } else {
1865                 /* Create the helper */
1866                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1867                 helper->mail_op = g_object_ref (self);
1868                 helper->dest_folder = NULL;
1869                 helper->headers = NULL;
1870                 helper->user_callback = user_callback;
1871                 helper->user_data = user_data;
1872                 
1873                 /* Move/Copy folder */
1874                 tny_folder_copy_async (folder,
1875                                        parent,
1876                                        tny_folder_get_name (folder),
1877                                        delete_original,
1878                                        transfer_folder_cb,
1879                                        transfer_folder_status_cb,
1880                                        helper);
1881                 return;
1882         }
1883
1884  error:
1885         /* Set status failed and set an error */
1886         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1887         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1888                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1889                      error_msg);
1890
1891         /* Call the user callback if exists */
1892         if (user_callback)
1893                 user_callback (self, user_data);
1894
1895         /* Notify the queue */
1896         modest_mail_operation_notify_end (self);
1897 }
1898
1899 void
1900 modest_mail_operation_rename_folder (ModestMailOperation *self,
1901                                      TnyFolder *folder,
1902                                      const gchar *name)
1903 {
1904         ModestMailOperationPrivate *priv;
1905         ModestTnyFolderRules rules;
1906         XFerMsgAsyncHelper *helper;
1907
1908         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1909         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1910         g_return_if_fail (name);
1911         
1912         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1913
1914         /* Get account and set it into mail_operation */
1915         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1916
1917         /* Check folder rules */
1918         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1919         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1920                 /* Set status failed and set an error */
1921                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1922                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1923                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1924                              _("FIXME: unable to rename"));
1925
1926                 /* Notify about operation end */
1927                 modest_mail_operation_notify_end (self);
1928         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1929                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1930                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1931                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1932                              _("FIXME: unable to rename"));
1933                 /* Notify about operation end */
1934                 modest_mail_operation_notify_end (self);
1935         } else {
1936                 TnyFolderStore *into;
1937
1938                 into = tny_folder_get_folder_store (folder);    
1939
1940                 /* Check that the new folder name is not used by any
1941                    special local folder */
1942                 if (new_name_valid_if_local_account (priv, into, name)) {
1943                         /* Create the helper */
1944                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1945                         helper->mail_op = g_object_ref(self);
1946                         helper->dest_folder = NULL;
1947                         helper->headers = NULL;
1948                         helper->user_callback = NULL;
1949                         helper->user_data = NULL;
1950                 
1951                         /* Rename. Camel handles folder subscription/unsubscription */
1952                         tny_folder_copy_async (folder, into, name, TRUE,
1953                                                transfer_folder_cb,
1954                                                transfer_folder_status_cb,
1955                                                helper);
1956                 } else {
1957                         modest_mail_operation_notify_end (self);
1958                 }
1959                 g_object_unref (into);
1960         }
1961 }
1962
1963 /* ******************************************************************* */
1964 /* **************************  MSG  ACTIONS  ************************* */
1965 /* ******************************************************************* */
1966
1967 void modest_mail_operation_get_msg (ModestMailOperation *self,
1968                                     TnyHeader *header,
1969                                     GetMsgAsyncUserCallback user_callback,
1970                                     gpointer user_data)
1971 {
1972         GetMsgAsyncHelper *helper = NULL;
1973         TnyFolder *folder;
1974         ModestMailOperationPrivate *priv;
1975         
1976         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1977         g_return_if_fail (TNY_IS_HEADER (header));
1978         
1979         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1980         folder = tny_header_get_folder (header);
1981
1982         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1983
1984         /* Get message from folder */
1985         if (folder) {
1986                 /* Get account and set it into mail_operation */
1987                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1988                 
1989                 /* Check for cached messages */
1990                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
1991                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1992                 else 
1993                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1994
1995                 helper = g_slice_new0 (GetMsgAsyncHelper);
1996                 helper->mail_op = self;
1997                 helper->user_callback = user_callback;
1998                 helper->user_data = user_data;
1999                 helper->header = g_object_ref (header);
2000
2001                 // The callback's reference so that the mail op is not
2002                 // finalized until the async operation is completed even if
2003                 // the user canceled the request meanwhile.
2004                 g_object_ref (G_OBJECT (helper->mail_op));
2005
2006                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2007
2008                 g_object_unref (G_OBJECT (folder));
2009         } else {
2010                 /* Set status failed and set an error */
2011                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2012                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2013                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2014                              _("Error trying to get a message. No folder found for header"));
2015
2016                 /* Notify the queue */
2017                 modest_mail_operation_notify_end (self);
2018         }
2019 }
2020
2021 static void
2022 get_msg_cb (TnyFolder *folder, 
2023             gboolean cancelled, 
2024             TnyMsg *msg, 
2025             GError *error, 
2026             gpointer user_data)
2027 {
2028         GetMsgAsyncHelper *helper = NULL;
2029         ModestMailOperation *self = NULL;
2030         ModestMailOperationPrivate *priv = NULL;
2031
2032         helper = (GetMsgAsyncHelper *) user_data;
2033         g_return_if_fail (helper != NULL);       
2034         self = helper->mail_op;
2035         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2036         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2037
2038         /* Check errors and cancel */
2039         if (error) {
2040                 priv->error = g_error_copy (error);
2041                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2042         } else if (cancelled) {
2043                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2044                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2045                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2046                              _("Error trying to refresh the contents of %s"),
2047                              tny_folder_get_name (folder));
2048         } else {
2049                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2050         }
2051
2052         /* If user defined callback function was defined, call it even
2053            if the operation failed*/
2054         if (helper->user_callback) {
2055                 /* This is not a GDK lock because we are a Tinymail callback
2056                  * which is already GDK locked by Tinymail */
2057
2058                 /* no gdk_threads_enter (), CHECKED */
2059                 helper->user_callback (self, helper->header, msg, helper->user_data);
2060                 /* no gdk_threads_leave (), CHECKED */
2061         }
2062
2063         /* Notify about operation end */
2064         modest_mail_operation_notify_end (self);
2065         /* Free */
2066         g_object_unref (helper->mail_op);
2067         g_object_unref (helper->header);
2068         g_slice_free (GetMsgAsyncHelper, helper);
2069                 
2070 }
2071
2072 static void     
2073 get_msg_status_cb (GObject *obj,
2074                    TnyStatus *status,  
2075                    gpointer user_data)
2076 {
2077         GetMsgAsyncHelper *helper = NULL;
2078         ModestMailOperation *self;
2079         ModestMailOperationPrivate *priv;
2080         ModestMailOperationState *state;
2081
2082         g_return_if_fail (status != NULL);
2083         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2084
2085         helper = (GetMsgAsyncHelper *) user_data;
2086         g_return_if_fail (helper != NULL);       
2087
2088         self = helper->mail_op;
2089         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2090
2091         priv->done = 1;
2092         priv->total = 1;
2093
2094         state = modest_mail_operation_clone_state (self);
2095         state->bytes_done = status->position;
2096         state->bytes_total = status->of_total;
2097
2098         /* This is not a GDK lock because we are a Tinymail callback
2099          * which is already GDK locked by Tinymail */
2100
2101         /* no gdk_threads_enter (), CHECKED */
2102         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2103         /* no gdk_threads_leave (), CHECKED */
2104
2105         g_slice_free (ModestMailOperationState, state);
2106 }
2107
2108 /****************************************************/
2109 typedef struct {
2110         ModestMailOperation *mail_op;
2111         TnyList *headers;
2112         GetMsgAsyncUserCallback user_callback;
2113         gpointer user_data;
2114         GDestroyNotify notify;
2115 } GetFullMsgsInfo;
2116
2117 typedef struct {
2118         GetMsgAsyncUserCallback user_callback;
2119         TnyHeader *header;
2120         TnyMsg *msg;
2121         gpointer user_data;
2122         ModestMailOperation *mail_op;
2123 } NotifyGetMsgsInfo;
2124
2125
2126 /* 
2127  * Used by get_msgs_full_thread to call the user_callback for each
2128  * message that has been read
2129  */
2130 static gboolean
2131 notify_get_msgs_full (gpointer data)
2132 {
2133         NotifyGetMsgsInfo *info;
2134
2135         info = (NotifyGetMsgsInfo *) data;      
2136
2137         /* This is a GDK lock because we are an idle callback and
2138          * because info->user_callback can contain Gtk+ code */
2139
2140         gdk_threads_enter (); /* CHECKED */
2141         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2142         gdk_threads_leave (); /* CHECKED */
2143
2144         g_slice_free (NotifyGetMsgsInfo, info);
2145
2146         return FALSE;
2147 }
2148
2149 /* 
2150  * Used by get_msgs_full_thread to free al the thread resources and to
2151  * call the destroy function for the passed user_data
2152  */
2153 static gboolean
2154 get_msgs_full_destroyer (gpointer data)
2155 {
2156         GetFullMsgsInfo *info;
2157
2158         info = (GetFullMsgsInfo *) data;
2159
2160         if (info->notify) {
2161
2162                 /* This is a GDK lock because we are an idle callback and
2163                  * because info->notify can contain Gtk+ code */
2164
2165                 gdk_threads_enter (); /* CHECKED */
2166                 info->notify (info->user_data);
2167                 gdk_threads_leave (); /* CHECKED */
2168         }
2169
2170         /* free */
2171         g_object_unref (info->headers);
2172         g_slice_free (GetFullMsgsInfo, info);
2173
2174         return FALSE;
2175 }
2176
2177 static gpointer
2178 get_msgs_full_thread (gpointer thr_user_data)
2179 {
2180         GetFullMsgsInfo *info;
2181         ModestMailOperationPrivate *priv = NULL;
2182         TnyIterator *iter = NULL;
2183         
2184         info = (GetFullMsgsInfo *) thr_user_data;       
2185         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2186
2187         iter = tny_list_create_iterator (info->headers);
2188         while (!tny_iterator_is_done (iter)) { 
2189                 TnyHeader *header;
2190                 TnyFolder *folder;
2191                 
2192                 header = TNY_HEADER (tny_iterator_get_current (iter));
2193                 folder = tny_header_get_folder (header);
2194                                 
2195                 /* Check for cached messages */
2196                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2197                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2198                 else 
2199                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2200
2201                 /* Get message from folder */
2202                 if (folder) {
2203                         TnyMsg *msg;
2204                         /* The callback will call it per each header */
2205                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2206
2207                         if (msg) {
2208                                 ModestMailOperationState *state;
2209                                 ModestPair *pair;
2210
2211                                 priv->done++;
2212
2213                                 /* notify progress */
2214                                 state = modest_mail_operation_clone_state (info->mail_op);
2215                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2216                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2217                                                  pair, (GDestroyNotify) modest_pair_free);
2218
2219                                 /* The callback is the responsible for
2220                                    freeing the message */
2221                                 if (info->user_callback) {
2222                                         NotifyGetMsgsInfo *info_notify;
2223                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2224                                         info_notify->user_callback = info->user_callback;
2225                                         info_notify->mail_op = info->mail_op;
2226                                         info_notify->header = g_object_ref (header);
2227                                         info_notify->msg = g_object_ref (msg);
2228                                         info_notify->user_data = info->user_data;
2229                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2230                                                          notify_get_msgs_full, 
2231                                                          info_notify, NULL);
2232                                 }
2233                                 g_object_unref (msg);
2234                         } 
2235                 } else {
2236                         /* Set status failed and set an error */
2237                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2238                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2239                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2240                                      "Error trying to get a message. No folder found for header");
2241                 }
2242
2243                 if (header)
2244                         g_object_unref (header);
2245                 
2246                 tny_iterator_next (iter);
2247         }
2248
2249         /* Set operation status */
2250         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2251                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2252
2253         /* Notify about operation end */
2254         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2255
2256         /* Free thread resources. Will be called after all previous idles */
2257         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2258
2259         return NULL;
2260 }
2261
2262 void 
2263 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2264                                      TnyList *header_list, 
2265                                      GetMsgAsyncUserCallback user_callback,
2266                                      gpointer user_data,
2267                                      GDestroyNotify notify)
2268 {
2269         TnyHeader *header = NULL;
2270         TnyFolder *folder = NULL;
2271         GThread *thread;
2272         ModestMailOperationPrivate *priv = NULL;
2273         GetFullMsgsInfo *info = NULL;
2274         gboolean size_ok = TRUE;
2275         gint max_size;
2276         TnyIterator *iter = NULL;
2277         
2278         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2279         
2280         /* Init mail operation */
2281         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2282         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2283         priv->done = 0;
2284         priv->total = tny_list_get_length(header_list);
2285
2286         /* Get account and set it into mail_operation */
2287         if (tny_list_get_length (header_list) >= 1) {
2288                 iter = tny_list_create_iterator (header_list);
2289                 header = TNY_HEADER (tny_iterator_get_current (iter));
2290                 if (header) {
2291                         folder = tny_header_get_folder (header);
2292                         if (folder) {           
2293                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2294
2295                                 g_object_unref (folder);
2296                         }
2297
2298                         g_object_unref (header);
2299                 }
2300
2301                 if (tny_list_get_length (header_list) == 1) {
2302                         g_object_unref (iter);
2303                         iter = NULL;
2304                 }
2305         }
2306
2307         /* Get msg size limit */
2308         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2309                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2310                                          &(priv->error));
2311         if (priv->error) {
2312                 g_clear_error (&(priv->error));
2313                 max_size = G_MAXINT;
2314         } else {
2315                 max_size = max_size * KB;
2316         }
2317
2318         /* Check message size limits. If there is only one message
2319            always retrieve it */
2320         if (iter != NULL) {
2321                 while (!tny_iterator_is_done (iter) && size_ok) {
2322                         header = TNY_HEADER (tny_iterator_get_current (iter));
2323                         if (header) {
2324                                 if (tny_header_get_message_size (header) >= max_size)
2325                                         size_ok = FALSE;
2326                                 g_object_unref (header);
2327                         }
2328
2329                         tny_iterator_next (iter);
2330                 }
2331                 g_object_unref (iter);
2332         }
2333
2334         if (size_ok) {
2335                 /* Create the info */
2336                 info = g_slice_new0 (GetFullMsgsInfo);
2337                 info->mail_op = self;
2338                 info->user_callback = user_callback;
2339                 info->user_data = user_data;
2340                 info->headers = g_object_ref (header_list);
2341                 info->notify = notify;
2342
2343                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2344         } else {
2345                 /* Set status failed and set an error */
2346                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2347                 /* FIXME: the error msg is different for pop */
2348                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2349                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2350                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2351                 /* Remove from queue and free resources */
2352                 modest_mail_operation_notify_end (self);
2353                 if (notify)
2354                         notify (user_data);
2355         }
2356 }
2357
2358
2359 void 
2360 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2361                                   TnyHeader *header,
2362                                   gboolean remove_to_trash /*ignored*/)
2363 {
2364         TnyFolder *folder;
2365         ModestMailOperationPrivate *priv;
2366
2367         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2368         g_return_if_fail (TNY_IS_HEADER (header));
2369
2370         if (remove_to_trash)
2371                 g_warning ("remove to trash is not implemented");
2372
2373         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2374         folder = tny_header_get_folder (header);
2375
2376         /* Get account and set it into mail_operation */
2377         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2378
2379         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2380
2381         /* remove message from folder */
2382         tny_folder_remove_msg (folder, header, &(priv->error));
2383         if (!priv->error) {
2384                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2385                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2386
2387                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2388 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2389                         tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2390                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2391 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2392                         tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2393                 else
2394                         /* local folders */
2395 /*                      tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2396                         tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2397         }
2398         
2399         
2400         /* Set status */
2401         if (!priv->error)
2402                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2403         else
2404                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2405
2406         /* Free */
2407         g_object_unref (G_OBJECT (folder));
2408
2409         /* Notify about operation end */
2410         modest_mail_operation_notify_end (self);
2411 }
2412
2413 void 
2414 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2415                                    TnyList *headers,
2416                                   gboolean remove_to_trash /*ignored*/)
2417 {
2418         TnyFolder *folder;
2419         ModestMailOperationPrivate *priv;
2420         TnyIterator *iter = NULL;
2421         TnyHeader *header = NULL;
2422
2423         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2424         g_return_if_fail (TNY_IS_LIST (headers));
2425
2426         if (remove_to_trash)
2427                 g_warning ("remove to trash is not implemented");
2428
2429         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2430
2431         /* Get folder from first header and sync it */
2432         iter = tny_list_create_iterator (headers);
2433         header = TNY_HEADER (tny_iterator_get_current (iter));
2434         folder = tny_header_get_folder (header);
2435         
2436         /* Get account and set it into mail_operation */
2437         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2438
2439         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2440
2441         /* remove message from folder */
2442         tny_folder_remove_msgs (folder, headers, &(priv->error));
2443         if (!priv->error) {
2444                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) || 
2445                     TNY_IS_CAMEL_POP_FOLDER (folder))
2446                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */ 
2447                 else
2448                         /* local folders */
2449                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2450         }
2451         
2452         
2453         /* Set status */
2454         if (!priv->error)
2455                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2456         else
2457                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2458
2459         /* Free */
2460         g_object_unref (header);
2461         g_object_unref (iter);
2462         g_object_unref (G_OBJECT (folder));
2463
2464         /* Notify about operation end */
2465         modest_mail_operation_notify_end (self);
2466 }
2467
2468
2469 static void
2470 transfer_msgs_status_cb (GObject *obj,
2471                          TnyStatus *status,  
2472                          gpointer user_data)
2473 {
2474         XFerMsgAsyncHelper *helper = NULL;
2475         ModestMailOperation *self;
2476         ModestMailOperationPrivate *priv;
2477         ModestMailOperationState *state;
2478
2479
2480         g_return_if_fail (status != NULL);
2481         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2482
2483         helper = (XFerMsgAsyncHelper *) user_data;
2484         g_return_if_fail (helper != NULL);       
2485
2486         self = helper->mail_op;
2487         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2488
2489         priv->done = status->position;
2490         priv->total = status->of_total;
2491
2492         state = modest_mail_operation_clone_state (self);
2493
2494         /* This is not a GDK lock because we are a Tinymail callback and
2495          * Tinymail already acquires the Gdk lock */
2496
2497         /* no gdk_threads_enter (), CHECKED */
2498
2499         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2500
2501         /* no gdk_threads_leave (), CHECKED */
2502
2503         g_slice_free (ModestMailOperationState, state);
2504 }
2505
2506
2507 static void
2508 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2509 {
2510         XFerMsgAsyncHelper *helper;
2511         ModestMailOperation *self;
2512         ModestMailOperationPrivate *priv;
2513         TnyIterator *iter = NULL;
2514         TnyHeader *header = NULL;
2515
2516         helper = (XFerMsgAsyncHelper *) user_data;
2517         self = helper->mail_op;
2518
2519         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2520
2521         if (err) {
2522                 priv->error = g_error_copy (err);
2523                 priv->done = 0;
2524                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2525         } else if (cancelled) {
2526                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2527                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2528                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2529                              _("Error trying to refresh the contents of %s"),
2530                              tny_folder_get_name (folder));
2531         } else {
2532                 priv->done = 1;
2533                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2534
2535                 /* Update folder counts */
2536                 tny_folder_poke_status (folder);                
2537                 tny_folder_poke_status (helper->dest_folder);           
2538         }
2539
2540         
2541         /* Mark headers as deleted and seen */
2542         if ((helper->delete) && 
2543             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2544                 iter = tny_list_create_iterator (helper->headers);
2545                 while (!tny_iterator_is_done (iter)) {
2546                         header = TNY_HEADER (tny_iterator_get_current (iter));
2547                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2548                         tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2549                         g_object_unref (header);
2550
2551                         tny_iterator_next (iter);
2552                 }
2553
2554         }
2555                 
2556
2557         /* Notify about operation end */
2558         modest_mail_operation_notify_end (self);
2559
2560         /* If user defined callback function was defined, call it */
2561         if (helper->user_callback) {
2562                 /* This is not a GDK lock because we are a Tinymail callback and
2563                  * Tinymail already acquires the Gdk lock */
2564
2565                 /* no gdk_threads_enter (), CHECKED */
2566                 helper->user_callback (self, helper->user_data);
2567                 /* no gdk_threads_leave (), CHECKED */
2568         }
2569
2570         /* Free */
2571         if (helper->headers)
2572                 g_object_unref (helper->headers);
2573         if (helper->dest_folder)
2574                 g_object_unref (helper->dest_folder);
2575         if (helper->mail_op)
2576                 g_object_unref (helper->mail_op);
2577         if (folder)
2578                 g_object_unref (folder);
2579         if (iter)
2580                 g_object_unref (iter);
2581         g_slice_free (XFerMsgAsyncHelper, helper);
2582 }
2583
2584 void
2585 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2586                                  TnyList *headers, 
2587                                  TnyFolder *folder, 
2588                                  gboolean delete_original,
2589                                  XferAsyncUserCallback user_callback,
2590                                  gpointer user_data)
2591 {
2592         ModestMailOperationPrivate *priv = NULL;
2593         TnyIterator *iter = NULL;
2594         TnyFolder *src_folder = NULL;
2595         XFerMsgAsyncHelper *helper = NULL;
2596         TnyHeader *header = NULL;
2597         ModestTnyFolderRules rules = 0;
2598
2599         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2600         g_return_if_fail (TNY_IS_LIST (headers));
2601         g_return_if_fail (TNY_IS_FOLDER (folder));
2602
2603         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2604         priv->total = 1;
2605         priv->done = 0;
2606         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2607
2608         /* Apply folder rules */
2609         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2610         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2611                 /* Set status failed and set an error */
2612                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2613                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2614                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2615                              _CS("ckct_ib_unable_to_paste_here"));
2616                 /* Notify the queue */
2617                 modest_mail_operation_notify_end (self);
2618                 return;
2619         }
2620                 
2621         /* Get source folder */
2622         iter = tny_list_create_iterator (headers);
2623         header = TNY_HEADER (tny_iterator_get_current (iter));
2624         if (header) {
2625                 src_folder = tny_header_get_folder (header);
2626                 g_object_unref (header);
2627         }
2628
2629         g_object_unref (iter);
2630
2631         /* Check folder source and destination */
2632         if (src_folder == folder) {
2633                 /* Set status failed and set an error */
2634                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2635                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2636                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2637                              _("mcen_ib_unable_to_copy_samefolder"));
2638                 
2639                 /* Notify the queue */
2640                 modest_mail_operation_notify_end (self);
2641                 
2642                 /* Free */
2643                 g_object_unref (src_folder);            
2644                 return;
2645         }
2646
2647         /* Create the helper */
2648         helper = g_slice_new0 (XFerMsgAsyncHelper);
2649         helper->mail_op = g_object_ref(self);
2650         helper->dest_folder = g_object_ref(folder);
2651         helper->headers = g_object_ref(headers);
2652         helper->user_callback = user_callback;
2653         helper->user_data = user_data;
2654         helper->delete = delete_original;
2655
2656         /* Get account and set it into mail_operation */
2657         priv->account = modest_tny_folder_get_account (src_folder);
2658
2659         /* Transfer messages */
2660         tny_folder_transfer_msgs_async (src_folder, 
2661                                         headers, 
2662                                         folder, 
2663                                         delete_original, 
2664                                         transfer_msgs_cb, 
2665                                         transfer_msgs_status_cb,
2666                                         helper);
2667 }
2668
2669
2670 static void
2671 on_refresh_folder (TnyFolder   *folder, 
2672                    gboolean     cancelled, 
2673                    GError     *error,
2674                    gpointer     user_data)
2675 {
2676         RefreshAsyncHelper *helper = NULL;
2677         ModestMailOperation *self = NULL;
2678         ModestMailOperationPrivate *priv = NULL;
2679
2680         helper = (RefreshAsyncHelper *) user_data;
2681         self = helper->mail_op;
2682         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2683
2684         g_return_if_fail(priv!=NULL);
2685
2686         if (error) {
2687                 priv->error = g_error_copy (error);
2688                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2689                 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2690                        error->message);
2691                 goto out;
2692         }
2693
2694         if (cancelled) {
2695                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2696                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2697                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2698                              _("Error trying to refresh the contents of %s"),
2699                              tny_folder_get_name (folder));
2700                 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2701                 goto out;
2702         }
2703
2704         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2705
2706         /* Call user defined callback, if it exists */
2707         if (helper->user_callback) {
2708
2709                 /* This is not a GDK lock because we are a Tinymail callback and
2710                  * Tinymail already acquires the Gdk lock */
2711                 helper->user_callback (self, folder, helper->user_data);
2712         }
2713
2714  out:
2715         /* Free */
2716         g_slice_free   (RefreshAsyncHelper, helper);
2717
2718         /* Notify about operation end */
2719         modest_mail_operation_notify_end (self);
2720         g_object_unref(self);
2721 }
2722
2723 static void
2724 on_refresh_folder_status_update (GObject *obj,
2725                                  TnyStatus *status,
2726                                  gpointer user_data)
2727 {
2728         RefreshAsyncHelper *helper = NULL;
2729         ModestMailOperation *self = NULL;
2730         ModestMailOperationPrivate *priv = NULL;
2731         ModestMailOperationState *state;
2732
2733         g_return_if_fail (user_data != NULL);
2734         g_return_if_fail (status != NULL);
2735         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2736
2737         helper = (RefreshAsyncHelper *) user_data;
2738         self = helper->mail_op;
2739         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2740
2741         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2742
2743         priv->done = status->position;
2744         priv->total = status->of_total;
2745
2746         state = modest_mail_operation_clone_state (self);
2747
2748         /* This is not a GDK lock because we are a Tinymail callback and
2749          * Tinymail already acquires the Gdk lock */
2750         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2751
2752         g_slice_free (ModestMailOperationState, state);
2753 }
2754
2755 void 
2756 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2757                                        TnyFolder *folder,
2758                                        RefreshAsyncUserCallback user_callback,
2759                                        gpointer user_data)
2760 {
2761         ModestMailOperationPrivate *priv = NULL;
2762         RefreshAsyncHelper *helper = NULL;
2763
2764         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2765
2766         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2767
2768         /* Get account and set it into mail_operation */
2769         priv->account = modest_tny_folder_get_account  (folder);
2770
2771         /* Create the helper */
2772         helper = g_slice_new0 (RefreshAsyncHelper);
2773         helper->mail_op = g_object_ref(self);
2774         helper->user_callback = user_callback;
2775         helper->user_data = user_data;
2776
2777         /* Refresh the folder. TODO: tinymail could issue a status
2778            updates before the callback call then this could happen. We
2779            must review the design */
2780         tny_folder_refresh_async (folder,
2781                                   on_refresh_folder,
2782                                   on_refresh_folder_status_update,
2783                                   helper);
2784 }
2785
2786 /**
2787  *
2788  * It's used by the mail operation queue to notify the observers
2789  * attached to that signal that the operation finished. We need to use
2790  * that because tinymail does not give us the progress of a given
2791  * operation when it finishes (it directly calls the operation
2792  * callback).
2793  */
2794 static void
2795 modest_mail_operation_notify_end (ModestMailOperation *self)
2796 {
2797         ModestMailOperationState *state;
2798         ModestMailOperationPrivate *priv = NULL;
2799
2800         g_return_if_fail (self);
2801
2802         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2803
2804         /* Set the account back to not busy */
2805         if (priv->account_name) {
2806                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2807                                                      priv->account_name, FALSE);
2808                 g_free(priv->account_name);
2809                 priv->account_name = NULL;
2810         }
2811         
2812         /* Notify the observers about the mail operation end */
2813         /* We do not wrapp this emission because we assume that this
2814            function is always called from within the main lock */
2815         state = modest_mail_operation_clone_state (self);
2816         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2817         g_slice_free (ModestMailOperationState, state);
2818 }