Emit the folder updated signal on message move
[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-error.h>
45 #include <tny-folder-observer.h>
46 #include <camel/camel-stream-mem.h>
47 #include <glib/gi18n.h>
48 #include <modest-defs.h>
49 #include "modest-platform.h"
50 #include "modest-account-mgr-helpers.h"
51 #include <modest-tny-account.h>
52 #include <modest-tny-send-queue.h>
53 #include <modest-runtime.h>
54 #include "modest-text-utils.h"
55 #include "modest-tny-msg.h"
56 #include "modest-tny-folder.h"
57 #include "modest-tny-account-store.h"
58 #include "modest-tny-platform-factory.h"
59 #include "modest-marshal.h"
60 #include "modest-error.h"
61 #include "modest-mail-operation.h"
62 #include <modest-count-stream.h>
63 #include <libgnomevfs/gnome-vfs.h>
64 #include "modest-utils.h"
65 #include "modest-debug.h"
66 #ifdef MODEST_USE_LIBTIME
67 #include <clockd/libtime.h>
68 #endif
69 #include "modest-account-protocol.h"
70 #include <camel/camel-stream-null.h>
71 #include <widgets/modest-msg-view-window.h>
72
73 #define KB 1024
74
75 /* 
76  * Remove all these #ifdef stuff when the tinymail's idle calls become
77  * locked
78  */
79 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
80
81 /* 'private'/'protected' functions */
82 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
83 static void modest_mail_operation_init       (ModestMailOperation *obj);
84 static void modest_mail_operation_finalize   (GObject *obj);
85
86 static void     get_msg_async_cb (TnyFolder *folder, 
87                                   gboolean cancelled, 
88                                   TnyMsg *msg, 
89                                   GError *rr, 
90                                   gpointer user_data);
91
92 static void     get_msg_status_cb (GObject *obj,
93                                    TnyStatus *status,  
94                                    gpointer user_data);
95
96 static void     modest_mail_operation_notify_start (ModestMailOperation *self);
97 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
98
99 static void     notify_progress_of_multiple_messages (ModestMailOperation *self,
100                                                       TnyStatus *status,
101                                                       gint *last_total_bytes,
102                                                       gint *sum_total_bytes,
103                                                       gint total_bytes,
104                                                       gboolean increment_done);
105
106 static guint    compute_message_list_size (TnyList *headers, guint num_elements);
107
108 static int      compare_headers_by_date   (gconstpointer a,
109                                            gconstpointer b);
110
111 static void     sync_folder_finish_callback (TnyFolder *self, 
112                                              gboolean cancelled, 
113                                              GError *err, 
114                                              gpointer user_data);
115
116 static gboolean _check_memory_low         (ModestMailOperation *mail_op);
117
118
119 typedef struct {
120         ModestTnySendQueue *queue;
121         ModestMailOperation *self;
122         guint error_handler;
123         guint start_handler;
124         guint stop_handler;
125 } RunQueueHelper;
126
127 static void run_queue_notify_and_destroy (RunQueueHelper *helper,
128                                           ModestMailOperationStatus status);
129
130 /* Helpers for the update account operation (send & receive)*/
131 typedef struct 
132 {
133         ModestMailOperation *mail_op;
134         gchar *account_name;
135         UpdateAccountCallback callback;
136         gpointer user_data;
137         TnyList *folders;
138         gint pending_calls;
139         gboolean poke_all;
140         TnyFolderObserver *inbox_observer;
141         gboolean interactive;
142         gboolean msg_readed;
143         gboolean update_folder_counts;
144 } UpdateAccountInfo;
145
146 static void destroy_update_account_info         (UpdateAccountInfo *info);
147
148 static void update_account_send_mail            (UpdateAccountInfo *info);
149
150 static void update_account_get_msg_async_cb     (TnyFolder *folder, 
151                                                  gboolean canceled, 
152                                                  TnyMsg *msg, 
153                                                  GError *err, 
154                                                  gpointer user_data);
155
156 static void update_account_notify_user_and_free (UpdateAccountInfo *info, 
157                                                  TnyList *new_headers);
158
159 enum _ModestMailOperationSignals 
160 {
161         PROGRESS_CHANGED_SIGNAL,
162         OPERATION_STARTED_SIGNAL,
163         OPERATION_FINISHED_SIGNAL,
164         NUM_SIGNALS
165 };
166
167 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
168 struct _ModestMailOperationPrivate {
169         TnyAccount                *account;
170         guint                      done;
171         guint                      total;
172         GObject                   *source;
173         GError                    *error;
174         ErrorCheckingUserCallback  error_checking;
175         gpointer                   error_checking_user_data;
176         ErrorCheckingUserDataDestroyer error_checking_user_data_destroyer;
177         ModestMailOperationStatus  status;      
178         ModestMailOperationTypeOperation op_type;
179 };
180
181 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
182                                                    MODEST_TYPE_MAIL_OPERATION, \
183                                                    ModestMailOperationPrivate))
184
185 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
186                                                    priv->status = new_status;\
187                                                }
188
189
190 typedef struct {
191         GetMsgAsyncUserCallback user_callback;
192         TnyHeader *header;
193         TnyIterator *more_msgs;
194         gpointer user_data;
195         ModestMailOperation *mail_op;
196         GDestroyNotify destroy_notify;
197         gint last_total_bytes;
198         gint sum_total_bytes;
199         gint total_bytes;
200         TnyIterator *get_parts;
201         TnyMsg *msg;
202 } GetMsgInfo;
203
204 typedef struct _RefreshAsyncHelper {    
205         ModestMailOperation *mail_op;
206         RefreshAsyncUserCallback user_callback; 
207         gpointer user_data;
208 } RefreshAsyncHelper;
209
210 typedef struct _XFerMsgsAsyncHelper
211 {
212         ModestMailOperation *mail_op;
213         TnyList *headers;
214         TnyIterator *more_msgs;
215         TnyFolder *dest_folder;
216         XferMsgsAsyncUserCallback user_callback;        
217         gboolean delete;
218         gpointer user_data;
219         gint last_total_bytes;
220         gint sum_total_bytes;
221         gint total_bytes;
222 } XFerMsgsAsyncHelper;
223
224 typedef struct _XFerFolderAsyncHelper
225 {
226         ModestMailOperation *mail_op;
227         XferFolderAsyncUserCallback user_callback;      
228         gpointer user_data;
229 } XFerFolderAsyncHelper;
230
231 typedef struct _SyncFolderHelper {
232         ModestMailOperation *mail_op;
233         SyncFolderCallback user_callback;
234         gpointer user_data;
235 } SyncFolderHelper;
236
237 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
238                                                       TnyMsg *msg,
239                                                       gpointer userdata);
240
241 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
242                                                        const gchar *from, const gchar *to,
243                                                        const gchar *cc, const gchar *bcc,
244                                                        const gchar *subject, const gchar *plain_body,
245                                                        const gchar *html_body, const GList *attachments_list,
246                                                        const GList *images_list,
247                                                        TnyHeaderFlags priority_flags,
248                                                        const gchar *references, const gchar *in_reply_to,
249                                                        TnyList *header_pairs,
250                                                        ModestMailOperationCreateMsgCallback callback,
251                                                        gpointer userdata);
252
253 static gboolean      idle_notify_queue (gpointer data);
254 typedef struct
255 {
256         ModestMailOperation *mail_op;
257         gchar *from;
258         gchar *to;
259         gchar *cc;
260         gchar *bcc;
261         gchar *subject;
262         gchar *references;
263         gchar *in_reply_to;
264         gchar *plain_body;
265         gchar *html_body;
266         GList *attachments_list;
267         GList *images_list;
268         TnyHeaderFlags priority_flags;
269         TnyList *header_pairs;
270         ModestMailOperationCreateMsgCallback callback;
271         gpointer userdata;
272 } CreateMsgInfo;
273
274 typedef struct
275 {
276         ModestMailOperation *mail_op;
277         TnyMsg *msg;
278         ModestMailOperationCreateMsgCallback callback;
279         gpointer userdata;
280 } CreateMsgIdleInfo;
281
282 /* globals */
283 static GObjectClass *parent_class = NULL;
284
285 static guint signals[NUM_SIGNALS] = {0};
286
287 GType
288 modest_mail_operation_get_type (void)
289 {
290         static GType my_type = 0;
291         if (!my_type) {
292                 static const GTypeInfo my_info = {
293                         sizeof(ModestMailOperationClass),
294                         NULL,           /* base init */
295                         NULL,           /* base finalize */
296                         (GClassInitFunc) modest_mail_operation_class_init,
297                         NULL,           /* class finalize */
298                         NULL,           /* class data */
299                         sizeof(ModestMailOperation),
300                         1,              /* n_preallocs */
301                         (GInstanceInitFunc) modest_mail_operation_init,
302                         NULL
303                 };
304                 my_type = g_type_register_static (G_TYPE_OBJECT,
305                                                   "ModestMailOperation",
306                                                   &my_info, 0);
307         }
308         return my_type;
309 }
310
311 static void
312 modest_mail_operation_class_init (ModestMailOperationClass *klass)
313 {
314         GObjectClass *gobject_class;
315         gobject_class = (GObjectClass*) klass;
316
317         parent_class            = g_type_class_peek_parent (klass);
318         gobject_class->finalize = modest_mail_operation_finalize;
319
320         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
321
322         /**
323          * ModestMailOperation::progress-changed
324          * @self: the #MailOperation that emits the signal
325          * @user_data: user data set when the signal handler was connected
326          *
327          * Emitted when the progress of a mail operation changes
328          */
329         signals[PROGRESS_CHANGED_SIGNAL] = 
330                 g_signal_new ("progress-changed",
331                               G_TYPE_FROM_CLASS (gobject_class),
332                               G_SIGNAL_RUN_FIRST,
333                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
334                               NULL, NULL,
335                               g_cclosure_marshal_VOID__POINTER,
336                               G_TYPE_NONE, 1, G_TYPE_POINTER);
337         /**
338          * operation-started
339          *
340          * This signal is issued whenever a mail operation starts, and
341          * starts mean when the tinymail operation is issued. This
342          * means that it could happen that something wrong happens and
343          * the tinymail function is never called. In this situation a
344          * operation-finished will be issued without any
345          * operation-started
346          */
347         signals[OPERATION_STARTED_SIGNAL] =
348                 g_signal_new ("operation-started",
349                               G_TYPE_FROM_CLASS (gobject_class),
350                               G_SIGNAL_RUN_FIRST,
351                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_started),
352                               NULL, NULL,
353                               g_cclosure_marshal_VOID__VOID,
354                               G_TYPE_NONE, 0);
355         /**
356          * operation-started
357          *
358          * This signal is issued whenever a mail operation
359          * finishes. Note that this signal could be issued without any
360          * previous "operation-started" signal, because this last one
361          * is only issued when the tinymail operation is successfully
362          * started
363          */
364         signals[OPERATION_FINISHED_SIGNAL] =
365                 g_signal_new ("operation-finished",
366                               G_TYPE_FROM_CLASS (gobject_class),
367                               G_SIGNAL_RUN_FIRST,
368                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_finished),
369                               NULL, NULL,
370                               g_cclosure_marshal_VOID__VOID,
371                               G_TYPE_NONE, 0);
372 }
373
374 static void
375 modest_mail_operation_init (ModestMailOperation *obj)
376 {
377         ModestMailOperationPrivate *priv;
378
379         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
380
381         priv->account        = NULL;
382         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
383         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
384         priv->error          = NULL;
385         priv->done           = 0;
386         priv->total          = 0;
387         priv->source         = NULL;
388         priv->error_checking = NULL;
389         priv->error_checking_user_data = NULL;
390 }
391
392 static void
393 modest_mail_operation_finalize (GObject *obj)
394 {
395         ModestMailOperationPrivate *priv;
396
397         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
398
399         
400         
401         if (priv->error) {
402                 g_error_free (priv->error);
403                 priv->error = NULL;
404         }
405         if (priv->source) {
406                 g_object_unref (priv->source);
407                 priv->source = NULL;
408         }
409         if (priv->account) {
410                 g_object_unref (priv->account);
411                 priv->account = NULL;
412         }
413
414
415         G_OBJECT_CLASS(parent_class)->finalize (obj);
416 }
417
418 ModestMailOperation*
419 modest_mail_operation_new (GObject *source)
420 {
421         ModestMailOperation *obj;
422         ModestMailOperationPrivate *priv;
423                 
424         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
425         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
426
427         if (source != NULL)
428                 priv->source = g_object_ref(source);
429
430         return obj;
431 }
432
433 ModestMailOperation*
434 modest_mail_operation_new_with_error_handling (GObject *source,
435                                                ErrorCheckingUserCallback error_handler,
436                                                gpointer user_data,
437                                                ErrorCheckingUserDataDestroyer error_handler_destroyer)
438 {
439         ModestMailOperation *obj;
440         ModestMailOperationPrivate *priv;
441                 
442         obj = modest_mail_operation_new (source);
443         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
444         
445         g_return_val_if_fail (error_handler != NULL, obj);
446         priv->error_checking = error_handler;
447         priv->error_checking_user_data = user_data;
448         priv->error_checking_user_data_destroyer = error_handler_destroyer;
449
450         return obj;
451 }
452
453 void
454 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
455 {
456         ModestMailOperationPrivate *priv;
457
458         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION(self));
459         
460         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
461         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
462
463         /* Call the user callback */
464         if (priv->error_checking != NULL)
465                 priv->error_checking (self, priv->error_checking_user_data);
466 }
467
468
469 ModestMailOperationTypeOperation
470 modest_mail_operation_get_type_operation (ModestMailOperation *self)
471 {
472         ModestMailOperationPrivate *priv;
473
474         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
475                               MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
476         
477         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
478         
479         return priv->op_type;
480 }
481
482 gboolean 
483 modest_mail_operation_is_mine (ModestMailOperation *self, 
484                                GObject *me)
485 {
486         ModestMailOperationPrivate *priv;
487
488         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
489                               FALSE);
490         
491         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
492         if (priv->source == NULL) return FALSE;
493
494         return priv->source == me;
495 }
496
497 GObject *
498 modest_mail_operation_get_source (ModestMailOperation *self)
499 {
500         ModestMailOperationPrivate *priv;
501
502         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
503                               NULL);
504         
505         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
506         if (!priv) {
507                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
508                 return NULL;
509         }
510         
511         return (priv->source) ? g_object_ref (priv->source) : NULL;
512 }
513
514 ModestMailOperationStatus
515 modest_mail_operation_get_status (ModestMailOperation *self)
516 {
517         ModestMailOperationPrivate *priv;
518
519         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
520         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
521                               MODEST_MAIL_OPERATION_STATUS_INVALID);
522
523         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
524         if (!priv) {
525                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
526                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
527         }
528         
529         return priv->status;
530 }
531
532 const GError *
533 modest_mail_operation_get_error (ModestMailOperation *self)
534 {
535         ModestMailOperationPrivate *priv;
536
537         g_return_val_if_fail (self, NULL);
538         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
539
540         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
541
542         if (!priv) {
543                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
544                 return NULL;
545         }
546
547         return priv->error;
548 }
549
550 gboolean 
551 modest_mail_operation_cancel (ModestMailOperation *self)
552 {
553         ModestMailOperationPrivate *priv;
554         gboolean canceled = FALSE;
555         
556         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION (self), FALSE);
557
558         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
559
560         /* Set new status */
561         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
562         
563         /* Cancel the mail operation */
564         g_return_val_if_fail (priv->account, FALSE);
565         tny_account_cancel (priv->account);
566
567         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SEND) {
568                 ModestTnySendQueue *queue;
569                 queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (priv->account),
570                                                        TRUE);
571
572                 /* Cancel the sending of the following next messages */
573                 if (TNY_IS_SEND_QUEUE (queue))
574                         tny_send_queue_cancel (TNY_SEND_QUEUE (queue), TNY_SEND_QUEUE_CANCEL_ACTION_SUSPEND, NULL);
575         }
576         
577         return canceled;
578 }
579
580 guint 
581 modest_mail_operation_get_task_done (ModestMailOperation *self)
582 {
583         ModestMailOperationPrivate *priv;
584
585         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
586                               0);
587         
588         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
589         return priv->done;
590 }
591
592 guint 
593 modest_mail_operation_get_task_total (ModestMailOperation *self)
594 {
595         ModestMailOperationPrivate *priv;
596
597         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
598                               0);
599
600         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
601         return priv->total;
602 }
603
604 gboolean
605 modest_mail_operation_is_finished (ModestMailOperation *self)
606 {
607         ModestMailOperationPrivate *priv;
608         gboolean retval = FALSE;
609
610         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
611                               FALSE);
612         
613         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
614
615         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
616             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
617             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
618             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
619                 retval = TRUE;
620         } else {
621                 retval = FALSE;
622         }
623
624         return retval;
625 }
626
627 /*
628  * Creates an image of the current state of a mail operation, the
629  * caller must free it
630  */
631 static ModestMailOperationState *
632 modest_mail_operation_clone_state (ModestMailOperation *self)
633 {
634         ModestMailOperationState *state;
635         ModestMailOperationPrivate *priv;
636
637         g_return_val_if_fail (self, NULL);
638         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
639         g_return_val_if_fail (priv, NULL);
640
641         if (!priv)
642                 return NULL;
643
644         state = g_slice_new (ModestMailOperationState);
645
646         state->status = priv->status;
647         state->op_type = priv->op_type;
648         state->done = priv->done;
649         state->total = priv->total;
650         state->finished = modest_mail_operation_is_finished (self);
651         state->bytes_done = 0;
652         state->bytes_total = 0;
653
654         return state;
655 }
656
657 /* ******************************************************************* */
658 /* ************************** SEND   ACTIONS ************************* */
659 /* ******************************************************************* */
660
661 typedef struct 
662 {
663         ModestMailOperation *mail_op;
664         gboolean notify;
665 } SendNewMailHelper;
666
667 static void
668 send_mail_on_sync_async_cb (TnyFolder *folder, 
669                             gboolean cancelled, 
670                             GError *err, 
671                             gpointer user_data)
672 {
673         ModestMailOperationPrivate *priv;
674         ModestMailOperation *self;
675         SendNewMailHelper *helper;
676
677         helper = (SendNewMailHelper *) user_data;
678         self = helper->mail_op;
679         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
680
681         if (cancelled)
682                 goto end;
683
684         if (err) {
685                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
686                              MODEST_MAIL_OPERATION_ERROR_SEND_QUEUE_ADD_ERROR,
687                              "Error adding a msg to the send queue\n");
688                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
689         } else {
690                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
691         }
692
693  end:
694         if (helper->notify)
695                 modest_mail_operation_notify_end (self);
696
697         g_object_unref (helper->mail_op);
698         g_slice_free (SendNewMailHelper, helper);
699 }
700
701 static void
702 run_queue_start (TnySendQueue *self,
703                  gpointer user_data)
704 {
705         RunQueueHelper *helper = (RunQueueHelper *) user_data;
706         ModestMailOperation *mail_op;
707                         
708         g_debug ("%s sending queue successfully started", __FUNCTION__);
709
710         /* Wait for the message to be sent */
711         mail_op = modest_mail_operation_new (NULL);
712         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
713                                          mail_op);
714         modest_mail_operation_run_queue (mail_op, helper->queue);
715         g_object_unref (mail_op);
716
717         /* Free the helper and end operation */
718         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
719 }
720
721 static void 
722 run_queue_error_happened (TnySendQueue *queue, 
723                           TnyHeader *header, 
724                           TnyMsg *msg, 
725                           GError *error, 
726                           gpointer user_data)
727 {
728         RunQueueHelper *helper = (RunQueueHelper *) user_data;
729         ModestMailOperationPrivate *priv;
730
731         /* If we are here this means that the send queue could not
732            start to send emails. Shouldn't happen as this means that
733            we could not create the thread */
734         g_debug ("%s sending queue failed to create the thread", __FUNCTION__);
735
736         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
737         priv->error = g_error_copy ((const GError *) error);
738
739         if (error->code != TNY_SYSTEM_ERROR_UNKNOWN) {
740                 /* This code is here for safety reasons. It should
741                    never be called, because that would mean that we
742                    are not controlling some error case */
743                 g_warning ("%s Error %s should not happen", 
744                            __FUNCTION__, error->message);
745         }
746
747         /* Free helper and end operation */
748         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_FAILED);
749 }
750
751 static void
752 send_mail_on_added_to_outbox (TnySendQueue *send_queue, 
753                               gboolean cancelled, 
754                               TnyMsg *msg, 
755                               GError *err,
756                               gpointer user_data)
757 {
758         ModestMailOperationPrivate *priv;
759         ModestMailOperation *self;
760         SendNewMailHelper *helper;
761
762         helper = (SendNewMailHelper *) user_data;
763         self = helper->mail_op;
764         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
765
766         if (cancelled)
767                 goto end;
768
769         if (err) {
770                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
771                              MODEST_MAIL_OPERATION_ERROR_SEND_QUEUE_ADD_ERROR,
772                              "Error adding a msg to the send queue\n");
773                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
774         } else {
775                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
776         }
777
778  end:
779         if (helper->notify) {
780                 TnyTransportAccount *trans_account;
781                 ModestTnySendQueue *queue;
782
783                 trans_account = (TnyTransportAccount *) modest_mail_operation_get_account (self);
784                 if (trans_account) {
785                         queue = modest_runtime_get_send_queue (trans_account, TRUE);
786                         if (queue) {
787                                 RunQueueHelper *helper;
788
789                                 /* Create the helper */
790                                 helper = g_slice_new0 (RunQueueHelper);
791                                 helper->queue = g_object_ref (queue);
792                                 helper->self = g_object_ref (self);
793
794                                 /* if sending is ongoing wait for the queue to
795                                    stop. Otherwise wait for the queue-start
796                                    signal. It could happen that the queue
797                                    could not start, then check also the error
798                                    happened signal */
799                                 if (modest_tny_send_queue_sending_in_progress (queue)) {
800                                         run_queue_start (TNY_SEND_QUEUE (queue), helper);
801                                 } else {
802                                         helper->start_handler = g_signal_connect (queue, "queue-start", 
803                                                                                   G_CALLBACK (run_queue_start), 
804                                                                                   helper);
805                                         helper->error_handler = g_signal_connect (queue, "error-happened", 
806                                                                                   G_CALLBACK (run_queue_error_happened), 
807                                                                                   helper);
808                                 }
809                         } else {
810                                 /* Finalize this mail operation */
811                                 modest_mail_operation_notify_end (self);
812                         }
813                         g_object_unref (trans_account);
814                 } else {
815                         g_warning ("No transport account for the operation");
816                 }
817         }
818
819         g_object_unref (helper->mail_op);
820         g_slice_free (SendNewMailHelper, helper);
821 }
822
823 static gboolean
824 idle_create_msg_cb (gpointer idle_data)
825 {
826         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
827
828         /* This is a GDK lock because we are an idle callback and
829          * info->callback can contain Gtk+ code */
830
831         gdk_threads_enter (); /* CHECKED */
832         info->callback (info->mail_op, info->msg, info->userdata);
833
834         g_object_unref (info->mail_op);
835         if (info->msg)
836                 g_object_unref (info->msg);
837         g_slice_free (CreateMsgIdleInfo, info);
838         gdk_threads_leave (); /* CHECKED */
839
840         return FALSE;
841 }
842
843 static gpointer 
844 create_msg_thread (gpointer thread_data)
845 {
846         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
847         TnyMsg *new_msg = NULL;
848         ModestMailOperationPrivate *priv;
849         gint attached = 0;
850
851         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
852         if (info->html_body == NULL) {
853                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
854                                               info->bcc, info->subject, 
855                                               info->references, info->in_reply_to,
856                                               info->plain_body, 
857                                               info->attachments_list, &attached,
858                                               info->header_pairs,
859                                               &(priv->error));
860         } else {
861                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
862                                                          info->bcc, info->subject, 
863                                                          info->references, info->in_reply_to,
864                                                          info->html_body,
865                                                          info->plain_body, info->attachments_list,
866                                                          info->images_list, &attached,
867                                                          info->header_pairs,
868                                                          &(priv->error));
869         }
870
871         if (new_msg) {
872                 TnyHeader *header = tny_msg_get_header (new_msg);
873
874                 /* Set priority flags in message */
875                 if (info->priority_flags != TNY_HEADER_FLAG_NORMAL_PRIORITY)
876                         tny_header_set_flag (header, info->priority_flags);
877
878                 /* Set attachment flags in message */
879                 if (info->attachments_list != NULL && attached > 0)
880                         tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
881
882                 g_object_unref (G_OBJECT(header));
883         } else {
884                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
885                 if (!priv->error)
886                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
887                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
888                                      "modest: failed to create a new msg\n");
889         }
890
891
892         g_free (info->to);
893         g_free (info->from);
894         g_free (info->cc);
895         g_free (info->bcc);
896         g_free (info->plain_body);
897         g_free (info->html_body);
898         g_free (info->subject);
899         g_free (info->references);
900         g_free (info->in_reply_to);
901         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
902         g_list_free (info->attachments_list);
903         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
904         g_list_free (info->images_list);
905         g_object_unref (info->header_pairs);
906
907         if (info->callback) {
908                 CreateMsgIdleInfo *idle_info;
909                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
910                 idle_info->mail_op = g_object_ref (info->mail_op);
911                 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
912                 idle_info->callback = info->callback;
913                 idle_info->userdata = info->userdata;
914                 g_idle_add (idle_create_msg_cb, idle_info);
915         } else {
916                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
917         }
918
919         g_object_unref (info->mail_op);
920         g_slice_free (CreateMsgInfo, info);
921         if (new_msg) g_object_unref(new_msg);
922         return NULL;
923 }
924
925
926 void
927 modest_mail_operation_create_msg (ModestMailOperation *self,
928                                   const gchar *from, const gchar *to,
929                                   const gchar *cc, const gchar *bcc,
930                                   const gchar *subject, const gchar *plain_body,
931                                   const gchar *html_body,
932                                   const GList *attachments_list,
933                                   const GList *images_list,
934                                   TnyHeaderFlags priority_flags,
935                                   const gchar *references,
936                                   const gchar *in_reply_to,
937                                   TnyList *header_pairs,
938                                   ModestMailOperationCreateMsgCallback callback,
939                                   gpointer userdata)
940 {
941         CreateMsgInfo *info = NULL;
942
943         info = g_slice_new0 (CreateMsgInfo);
944         info->mail_op = g_object_ref (self);
945
946         info->from = g_strdup (from);
947         info->to = g_strdup (to);
948         info->cc = g_strdup (cc);
949         info->bcc  = g_strdup (bcc);
950         info->subject = g_strdup (subject);
951         info->plain_body = g_strdup (plain_body);
952         info->html_body = g_strdup (html_body);
953         info->references = g_strdup (references);
954         info->in_reply_to = g_strdup (in_reply_to);
955         info->attachments_list = g_list_copy ((GList *) attachments_list);
956         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
957         info->images_list = g_list_copy ((GList *) images_list);
958         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
959         info->priority_flags = 0 | priority_flags;
960         info->header_pairs = tny_list_copy (header_pairs);
961
962         info->callback = callback;
963         info->userdata = userdata;
964
965         g_thread_create (create_msg_thread, info, FALSE, NULL);
966 }
967
968 typedef struct
969 {
970         TnyTransportAccount *transport_account;
971         TnyMsg *draft_msg;
972 } SendNewMailInfo;
973
974 static void
975 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
976                                         TnyMsg *msg,
977                                         gpointer userdata)
978 {
979         TnySendQueue *send_queue = NULL;
980         ModestMailOperationPrivate *priv = NULL;
981         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
982         TnyFolder *draft_folder = NULL;
983         TnyFolder *outbox_folder = NULL;
984         TnyHeader *header = NULL;
985
986         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
987
988         if (!msg) {
989                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
990                 modest_mail_operation_notify_end (self);
991                 goto end;
992         }
993
994         if (priv->error && priv->error->code != MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
995                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
996                 modest_mail_operation_notify_end (self);
997                 goto end;
998         }
999
1000         /* Add message to send queue */
1001         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (info->transport_account, TRUE));
1002         if (!TNY_IS_SEND_QUEUE(send_queue)) {
1003                 if (priv->error) {
1004                         g_error_free (priv->error);
1005                         priv->error = NULL;
1006                 }
1007                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1008                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1009                              "modest: could not find send queue for account\n");
1010                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1011                 modest_mail_operation_notify_end (self);
1012                 goto end;
1013         } else {
1014                 SendNewMailHelper *helper = g_slice_new (SendNewMailHelper);
1015                 helper->mail_op = g_object_ref (self);
1016                 helper->notify = (info->draft_msg == NULL);
1017
1018                 /* Add the msg to the queue. The callback will free
1019                    the helper */
1020                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1021                                                                   FALSE);
1022                 tny_send_queue_add_async (send_queue, msg, send_mail_on_added_to_outbox, 
1023                                           NULL, helper);
1024         }
1025
1026         if (info->draft_msg != NULL) {
1027                 TnyList *tmp_headers = NULL;
1028                 TnyFolder *folder = NULL;
1029                 TnyFolder *src_folder = NULL;
1030                 TnyFolderType folder_type;              
1031                 TnyTransportAccount *transport_account = NULL;
1032                 SendNewMailHelper *helper = NULL;
1033
1034                 /* To remove the old mail from its source folder, we need to get the
1035                  * transport account of the original draft message (the transport account
1036                  * might have been changed by the user) */
1037                 header = tny_msg_get_header (info->draft_msg);
1038                 transport_account = modest_tny_account_store_get_transport_account_from_outbox_header(
1039                         modest_runtime_get_account_store(), header);
1040                 if (transport_account == NULL)
1041                         transport_account = g_object_ref(info->transport_account);
1042                 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
1043                                                                       TNY_FOLDER_TYPE_DRAFTS);
1044                 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
1045                                                                        TNY_FOLDER_TYPE_OUTBOX);
1046                 g_object_unref(transport_account);
1047
1048                 if (!draft_folder) {
1049                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL drafts folder",
1050                                    __FUNCTION__);
1051                         modest_mail_operation_notify_end (self);
1052                         goto end;
1053                 }
1054                 if (!outbox_folder) {
1055                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL outbox folder",
1056                                    __FUNCTION__);
1057                         modest_mail_operation_notify_end (self);
1058                         goto end;
1059                 }
1060
1061                 folder = tny_msg_get_folder (info->draft_msg);          
1062                 if (folder == NULL) {
1063                         modest_mail_operation_notify_end (self);
1064                         goto end;
1065                 }
1066                 folder_type = modest_tny_folder_guess_folder_type (folder);
1067
1068                 if (folder_type == TNY_FOLDER_TYPE_INVALID)
1069                         g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1070                 
1071                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
1072                         src_folder = outbox_folder;
1073                 else 
1074                         src_folder = draft_folder;
1075
1076                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
1077                  * because this function requires it to have a UID. */
1078                 helper = g_slice_new (SendNewMailHelper);
1079                 helper->mail_op = g_object_ref (self);
1080                 helper->notify = TRUE;
1081
1082                 tmp_headers = tny_simple_list_new ();
1083                 tny_list_append (tmp_headers, (GObject*) header);
1084                 tny_folder_remove_msgs_async (src_folder, tmp_headers, NULL, NULL, NULL);
1085                 g_object_unref (tmp_headers);
1086                 tny_folder_sync_async (src_folder, TRUE, send_mail_on_sync_async_cb, 
1087                                        NULL, helper);
1088                 g_object_unref (folder);
1089         }
1090
1091 end:
1092         if (header)
1093                 g_object_unref (header);
1094         if (info->draft_msg)
1095                 g_object_unref (info->draft_msg);
1096         if (draft_folder)
1097                 g_object_unref (draft_folder);
1098         if (outbox_folder)
1099                 g_object_unref (outbox_folder);
1100         if (info->transport_account)
1101                 g_object_unref (info->transport_account);
1102         g_slice_free (SendNewMailInfo, info);
1103 }
1104
1105 void
1106 modest_mail_operation_send_mail (ModestMailOperation *self,
1107                                  TnyTransportAccount *transport_account,
1108                                  TnyMsg *msg)
1109 {
1110         TnySendQueue *send_queue = NULL;
1111         ModestMailOperationPrivate *priv = NULL;
1112
1113         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1114
1115         if (!msg) {
1116                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1117                 modest_mail_operation_notify_end (self);
1118                 return;
1119         }
1120
1121         if (priv->error && priv->error->code != MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1122                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1123                 modest_mail_operation_notify_end (self);
1124                 return;
1125         }
1126
1127         /* Add message to send queue */
1128         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account, TRUE));
1129         if (!TNY_IS_SEND_QUEUE(send_queue)) {
1130                 if (priv->error) {
1131                         g_error_free (priv->error);
1132                         priv->error = NULL;
1133                 }
1134                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1135                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1136                              "modest: could not find send queue for account\n");
1137                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1138                 modest_mail_operation_notify_end (self);
1139                 return;
1140         } else {
1141                 SendNewMailHelper *helper = g_slice_new (SendNewMailHelper);
1142                 helper->mail_op = g_object_ref (self);
1143                 helper->notify = TRUE;
1144
1145                 /* Add the msg to the queue. The callback will free
1146                    the helper */
1147                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1148                                                                   FALSE);
1149                 tny_send_queue_add_async (send_queue, msg, send_mail_on_added_to_outbox, 
1150                                           NULL, helper);
1151         }
1152
1153 }
1154
1155 void
1156 modest_mail_operation_send_new_mail (ModestMailOperation *self,
1157                                      TnyTransportAccount *transport_account,
1158                                      TnyMsg *draft_msg,
1159                                      const gchar *from,  const gchar *to,
1160                                      const gchar *cc,  const gchar *bcc,
1161                                      const gchar *subject, const gchar *plain_body,
1162                                      const gchar *html_body,
1163                                      const GList *attachments_list,
1164                                      const GList *images_list,
1165                                      const gchar *references,
1166                                      const gchar *in_reply_to,
1167                                      TnyHeaderFlags priority_flags,
1168                                      TnyList *header_pairs)
1169 {
1170         ModestMailOperationPrivate *priv = NULL;
1171         SendNewMailInfo *info;
1172
1173         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1174         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1175
1176         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1177         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1178         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
1179         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1180
1181         modest_mail_operation_notify_start (self);
1182
1183         /* Check parametters */
1184         if (to == NULL) {
1185                 /* Set status failed and set an error */
1186                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1187                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1188                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1189                              _("Error trying to send a mail. You need to set at least one recipient"));
1190                 modest_mail_operation_notify_end (self);
1191                 return;
1192         }
1193         info = g_slice_new0 (SendNewMailInfo);
1194         info->transport_account = transport_account;
1195         if (transport_account)
1196                 g_object_ref (transport_account);
1197         info->draft_msg = draft_msg;
1198         if (draft_msg)
1199                 g_object_ref (draft_msg);
1200
1201
1202         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1203                                           attachments_list, images_list, priority_flags,
1204                                           references, in_reply_to,
1205                                           header_pairs,
1206                                           modest_mail_operation_send_new_mail_cb, info);
1207
1208 }
1209
1210 typedef struct
1211 {
1212         ModestMailOperation *mailop;
1213         TnyMsg *msg;
1214         SaveToDraftstCallback callback;
1215         gpointer userdata;
1216 } FinishSaveRemoteDraftInfo;
1217
1218 static void
1219 finish_save_remote_draft (ModestAccountProtocol *protocol,
1220                           GError *err,
1221                           const gchar *account_id,
1222                           TnyMsg *new_remote_msg,
1223                           TnyMsg *new_msg,
1224                           TnyMsg *old_msg,
1225                           gpointer userdata)
1226 {
1227         FinishSaveRemoteDraftInfo *info = (FinishSaveRemoteDraftInfo *) userdata;
1228         ModestMailOperationPrivate *priv = NULL;
1229
1230         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1231
1232         if (!priv->error && err != NULL) {
1233                 /* Priority for errors in save to local stage */
1234                 priv->error = g_error_copy (err);
1235                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1236         }
1237
1238         if (info->callback)
1239                 info->callback (info->mailop, info->msg, info->userdata);
1240
1241         if (info->msg)
1242                 g_object_unref (info->msg);
1243
1244         modest_mail_operation_notify_end (info->mailop);
1245         g_object_unref (info->mailop);
1246
1247         g_slice_free (FinishSaveRemoteDraftInfo, info);
1248 }
1249
1250 typedef struct
1251 {
1252         TnyTransportAccount *transport_account;
1253         TnyMsg *draft_msg;
1254         SaveToDraftstCallback callback;
1255         gpointer user_data;
1256         TnyFolder *drafts;
1257         TnyMsg *msg;
1258         ModestMailOperation *mailop;
1259 } SaveToDraftsAddMsgInfo;
1260
1261 static void
1262 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
1263                                                 gboolean canceled,
1264                                                 GError *err,
1265                                                 gpointer userdata)
1266 {
1267         ModestMailOperationPrivate *priv = NULL;
1268         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
1269         GError *io_error = NULL;
1270         gboolean callback_called = FALSE;
1271
1272         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1273
1274         if (priv->error && priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1275                 io_error = priv->error;
1276                 priv->error = NULL;
1277         }
1278         if (priv->error) {
1279                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
1280                 g_error_free(priv->error);
1281         }
1282
1283         priv->error = (err == NULL) ? NULL : g_error_copy(err);
1284
1285         if ((!priv->error) && (info->draft_msg != NULL)) {
1286                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
1287                 TnyFolder *src_folder = tny_header_get_folder (header);
1288
1289                 g_debug ("--- REMOVE AND SYNC");
1290                 /* Remove the old draft */
1291                 tny_folder_remove_msg (src_folder, header, NULL);
1292
1293                 /* Synchronize to expunge and to update the msg counts */
1294                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1295                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1296                 g_debug ("--- REMOVED - SYNCED");
1297
1298                 g_object_unref (G_OBJECT(header));
1299                 g_object_unref (G_OBJECT(src_folder));
1300         }
1301
1302         if (priv->error) {
1303                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1304                 if (io_error) {
1305                         g_error_free (io_error);
1306                         io_error = NULL;
1307                 }
1308         } else if (io_error) {
1309                 priv->error = io_error;
1310                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
1311         } else {
1312                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1313         }
1314
1315         if (info->transport_account) {
1316                 ModestProtocolType transport_protocol_type;
1317                 ModestProtocol *transport_protocol;
1318
1319                 transport_protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (info->transport_account));
1320
1321                 transport_protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1322                                                                                     transport_protocol_type);
1323                 if (transport_protocol && MODEST_IS_ACCOUNT_PROTOCOL (transport_protocol)) {
1324                         FinishSaveRemoteDraftInfo *srd_info = g_slice_new (FinishSaveRemoteDraftInfo);
1325                         srd_info->mailop = info->mailop?g_object_ref (info->mailop):NULL;
1326                         srd_info->msg = info->msg?g_object_ref (info->msg):NULL;
1327                         srd_info->callback = info->callback;
1328                         srd_info->userdata = info->user_data;
1329                         modest_account_protocol_save_remote_draft (MODEST_ACCOUNT_PROTOCOL (transport_protocol), 
1330                                                                    tny_account_get_id (TNY_ACCOUNT (info->transport_account)),
1331                                                                    info->msg, info->draft_msg,
1332                                                                    finish_save_remote_draft,
1333                                                                    srd_info);
1334                                                                    
1335                         callback_called = TRUE;
1336                 }
1337         }
1338
1339         /* Call the user callback */
1340         if (!callback_called && info->callback)
1341                 info->callback (info->mailop, info->msg, info->user_data);
1342
1343         if (info->transport_account)
1344                 g_object_unref (G_OBJECT(info->transport_account));
1345         if (info->draft_msg)
1346                 g_object_unref (G_OBJECT (info->draft_msg));
1347         if (info->drafts)
1348                 g_object_unref (G_OBJECT(info->drafts));
1349         if (info->msg)
1350                 g_object_unref (G_OBJECT (info->msg));
1351
1352         if (!callback_called)
1353                 modest_mail_operation_notify_end (info->mailop);
1354         if (info->mailop)
1355                 g_object_unref(info->mailop);
1356         g_slice_free (SaveToDraftsAddMsgInfo, info);
1357 }
1358
1359 typedef struct
1360 {
1361         TnyTransportAccount *transport_account;
1362         TnyMsg *draft_msg;
1363         SaveToDraftstCallback callback;
1364         gpointer user_data;
1365 } SaveToDraftsInfo;
1366
1367 static void
1368 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1369                                          TnyMsg *msg,
1370                                          gpointer userdata)
1371 {
1372         TnyFolder *drafts = NULL;
1373         ModestMailOperationPrivate *priv = NULL;
1374         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1375
1376         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1377
1378         if (!msg) {
1379                 if (!(priv->error)) {
1380                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1381                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1382                                      "modest: failed to create a new msg\n");
1383                 }
1384         } else {
1385                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1386                                                                 TNY_FOLDER_TYPE_DRAFTS);
1387                 if (!drafts && !(priv->error)) {
1388                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1389                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1390                                      "modest: failed to create a new msg\n");
1391                 }
1392         }
1393
1394         if (!priv->error || priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1395                 if (drafts) {
1396                         SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1397                         cb_info->transport_account = g_object_ref(info->transport_account);
1398                         cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1399                         cb_info->callback = info->callback;
1400                         cb_info->user_data = info->user_data;
1401                         cb_info->drafts = g_object_ref(drafts);
1402                         cb_info->msg = g_object_ref(msg);
1403                         cb_info->mailop = g_object_ref(self);
1404                         tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1405                                                  NULL, cb_info);
1406                 }
1407         } else {
1408                 /* Call the user callback */
1409                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1410                 if (info->callback)
1411                         info->callback (self, msg, info->user_data);
1412                 modest_mail_operation_notify_end (self);
1413         }
1414
1415         if (drafts)
1416                 g_object_unref (G_OBJECT(drafts));
1417         if (info->draft_msg)
1418                 g_object_unref (G_OBJECT (info->draft_msg));
1419         if (info->transport_account)
1420                 g_object_unref (G_OBJECT(info->transport_account));
1421         g_slice_free (SaveToDraftsInfo, info);
1422 }
1423
1424 void
1425 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1426                                       TnyTransportAccount *transport_account,
1427                                       TnyMsg *draft_msg,
1428                                       const gchar *from,  const gchar *to,
1429                                       const gchar *cc,  const gchar *bcc,
1430                                       const gchar *subject, const gchar *plain_body,
1431                                       const gchar *html_body,
1432                                       const GList *attachments_list,
1433                                       const GList *images_list,
1434                                       TnyHeaderFlags priority_flags,
1435                                       const gchar *references,
1436                                       const gchar *in_reply_to,
1437                                       TnyList *header_pairs,
1438                                       SaveToDraftstCallback callback,
1439                                       gpointer user_data)
1440 {
1441         ModestMailOperationPrivate *priv = NULL;
1442         SaveToDraftsInfo *info = NULL;
1443
1444         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1445         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1446
1447         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1448
1449         /* Get account and set it into mail_operation */
1450         priv->account = g_object_ref (transport_account);
1451         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1452
1453         info = g_slice_new0 (SaveToDraftsInfo);
1454         info->transport_account = g_object_ref (transport_account);
1455         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1456         info->callback = callback;
1457         info->user_data = user_data;
1458
1459         g_debug ("--- CREATE MESSAGE");
1460         modest_mail_operation_notify_start (self);
1461         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1462                                           attachments_list, images_list, priority_flags,
1463                                           references, in_reply_to,
1464                                           header_pairs,
1465                                           modest_mail_operation_save_to_drafts_cb, info);
1466 }
1467
1468 typedef struct
1469 {
1470         ModestMailOperation *mail_op;
1471         TnyMimePart *mime_part;
1472         gssize size;
1473         GetMimePartSizeCallback callback;
1474         gpointer userdata;
1475 } GetMimePartSizeInfo;
1476
1477 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1478 /* We use this folder observer to track the headers that have been
1479  * added to a folder */
1480 typedef struct {
1481         GObject parent;
1482         TnyList *new_headers;
1483 } InternalFolderObserver;
1484
1485 typedef struct {
1486         GObjectClass parent;
1487 } InternalFolderObserverClass;
1488
1489 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1490
1491 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1492                          internal_folder_observer,
1493                          G_TYPE_OBJECT,
1494                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1495
1496
1497 static void
1498 foreach_add_item (gpointer header, gpointer user_data)
1499 {
1500         tny_list_append (TNY_LIST (user_data), G_OBJECT (header));
1501 }
1502
1503 /* This is the method that looks for new messages in a folder */
1504 static void
1505 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1506 {
1507         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1508         
1509         TnyFolderChangeChanged changed;
1510
1511         changed = tny_folder_change_get_changed (change);
1512
1513         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1514                 TnyList *list;
1515
1516                 /* Get added headers */
1517                 list = tny_simple_list_new ();
1518                 tny_folder_change_get_added_headers (change, list);
1519
1520                 /* Add them to the folder observer */
1521                 tny_list_foreach (list, foreach_add_item, 
1522                                   derived->new_headers);
1523
1524                 g_object_unref (G_OBJECT (list));
1525         }
1526 }
1527
1528 static void
1529 internal_folder_observer_init (InternalFolderObserver *self) 
1530 {
1531         self->new_headers = tny_simple_list_new ();
1532 }
1533 static void
1534 internal_folder_observer_finalize (GObject *object) 
1535 {
1536         InternalFolderObserver *self;
1537
1538         self = (InternalFolderObserver *) object;
1539         g_object_unref (self->new_headers);
1540
1541         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1542 }
1543 static void
1544 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1545 {
1546         iface->update = internal_folder_observer_update;
1547 }
1548 static void
1549 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1550 {
1551         GObjectClass *object_class;
1552
1553         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1554         object_class = (GObjectClass*) klass;
1555         object_class->finalize = internal_folder_observer_finalize;
1556 }
1557
1558 static void
1559 destroy_update_account_info (UpdateAccountInfo *info)
1560 {
1561         g_free (info->account_name);
1562         g_object_unref (info->folders);
1563         g_object_unref (info->mail_op);
1564         g_slice_free (UpdateAccountInfo, info);
1565 }
1566
1567
1568 static void
1569 update_account_send_mail (UpdateAccountInfo *info)
1570 {
1571         TnyTransportAccount *transport_account = NULL;
1572         ModestTnyAccountStore *account_store;
1573
1574         if (info->update_folder_counts)
1575                 return;
1576
1577         account_store = modest_runtime_get_account_store ();
1578
1579         /* We don't try to send messages while sending mails is blocked */
1580         if (modest_tny_account_store_is_send_mail_blocked (account_store))
1581                 return;
1582
1583         /* Get the transport account */
1584         transport_account = (TnyTransportAccount *) 
1585                 modest_tny_account_store_get_server_account (account_store, info->account_name, 
1586                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1587
1588         if (transport_account) {
1589                 ModestTnySendQueue *send_queue;
1590                 TnyFolder *outbox;
1591                 guint num_messages;
1592
1593                 send_queue = modest_runtime_get_send_queue (transport_account, TRUE);
1594                 g_object_unref (transport_account);
1595
1596                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1597                         /* Get outbox folder */
1598                         outbox = tny_send_queue_get_outbox (TNY_SEND_QUEUE (send_queue));
1599                         if (outbox) { /* this could fail in some cases */
1600                                 num_messages = tny_folder_get_all_count (outbox);
1601                                 g_object_unref (outbox);
1602                         } else {
1603                                 g_warning ("%s: could not get outbox", __FUNCTION__);
1604                                 num_messages = 0;
1605                         }
1606
1607                         if (num_messages != 0) {
1608                                 ModestMailOperation *mail_op;
1609                                 /* Reenable suspended items */
1610                                 mail_op = modest_mail_operation_new (NULL);
1611                                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1612                                                                  mail_op);
1613                                 modest_mail_operation_queue_wakeup (mail_op, MODEST_TNY_SEND_QUEUE (send_queue));
1614
1615                                 /* Try to send */
1616                                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1617                                                                                   info->interactive);
1618                         }
1619                 }
1620         }
1621 }
1622
1623 static void
1624 update_account_get_msg_async_cb (TnyFolder *folder, 
1625                                  gboolean canceled, 
1626                                  TnyMsg *msg, 
1627                                  GError *err, 
1628                                  gpointer user_data)
1629 {
1630         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1631         ModestMailOperationPrivate *priv;
1632
1633         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (msg_info->mail_op);
1634         priv->done++;
1635
1636         if (TNY_IS_MSG (msg)) {
1637                 TnyHeader *header = tny_msg_get_header (msg);
1638
1639                 if (header) {
1640                         ModestMailOperationState *state;
1641                         state = modest_mail_operation_clone_state (msg_info->mail_op);
1642                         msg_info->sum_total_bytes += tny_header_get_message_size (header);
1643                         state->bytes_done = msg_info->sum_total_bytes;
1644                         state->bytes_total = msg_info->total_bytes;
1645
1646                         /* Notify the status change. Only notify about changes
1647                            referred to bytes */
1648                         g_signal_emit (G_OBJECT (msg_info->mail_op), 
1649                                        signals[PROGRESS_CHANGED_SIGNAL], 
1650                                        0, state, NULL);
1651
1652                         g_object_unref (header);
1653                         g_slice_free (ModestMailOperationState, state);
1654                 }
1655         }
1656
1657         if (priv->done == priv->total) {
1658                 TnyList *new_headers;
1659                 UpdateAccountInfo *info;
1660
1661                 /* After getting all the messages send the ones in the
1662                    outboxes */
1663                 info = (UpdateAccountInfo *) msg_info->user_data;
1664                 update_account_send_mail (info);
1665
1666                 /* Check if the operation was a success */
1667                 if (!priv->error)
1668                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1669                 
1670                 /* Call the user callback and free */
1671                 new_headers = tny_iterator_get_list (msg_info->more_msgs);
1672                 update_account_notify_user_and_free (info, new_headers);
1673
1674                 /* Delete the helper */
1675                 if (msg_info->msg)
1676                         g_object_unref (msg_info->msg);
1677                 g_object_unref (msg_info->more_msgs);
1678                 g_object_unref (msg_info->mail_op);
1679                 g_slice_free (GetMsgInfo, msg_info);
1680         }
1681 }
1682
1683 static void
1684 update_account_notify_user_and_free (UpdateAccountInfo *info, 
1685                                      TnyList *new_headers)
1686 {
1687         /* Set the account back to not busy */
1688         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), 
1689                                              info->account_name, FALSE);
1690         
1691         /* User callback */
1692         if (info->callback)
1693                 info->callback (info->mail_op, new_headers, info->user_data);
1694         
1695         /* Mail operation end */
1696         modest_mail_operation_notify_end (info->mail_op);
1697
1698         /* Frees */
1699         if (new_headers)
1700                 g_object_unref (new_headers);
1701         destroy_update_account_info (info);
1702 }
1703
1704 static void
1705 inbox_refreshed_cb (TnyFolder *inbox, 
1706                     gboolean canceled, 
1707                     GError *err, 
1708                     gpointer user_data)
1709 {       
1710         UpdateAccountInfo *info;
1711         ModestMailOperationPrivate *priv;
1712         TnyIterator *new_headers_iter;
1713         GPtrArray *new_headers_array = NULL;
1714         gint max_size = G_MAXINT, retrieve_limit, i;
1715         ModestAccountMgr *mgr;
1716         ModestAccountRetrieveType retrieve_type;
1717         TnyList *new_headers = NULL;
1718         gboolean headers_only;
1719         time_t time_to_store;
1720
1721         info = (UpdateAccountInfo *) user_data;
1722         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1723         mgr = modest_runtime_get_account_mgr ();
1724
1725         if (canceled || err) {
1726                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1727                 if (err)
1728                         priv->error = g_error_copy (err);
1729                 else
1730                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1731                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1732                                      "canceled");
1733
1734                 if (inbox)
1735                         tny_folder_remove_observer (inbox, info->inbox_observer);
1736                 g_object_unref (info->inbox_observer);
1737                 info->inbox_observer = NULL;
1738
1739                 /* Notify the user about the error and then exit */
1740                 update_account_notify_user_and_free (info, NULL);
1741                 return;
1742         }
1743
1744         if (!inbox) {
1745                 /* Try to send anyway */
1746                 goto send_mail;
1747         }
1748
1749         if (!info->update_folder_counts) {
1750                 /* Set the last updated as the current time */
1751 #ifdef MODEST_USE_LIBTIME
1752                 struct tm utc_tm;
1753                 time_get_utc (&utc_tm);
1754                 time_to_store = time_mktime (&utc_tm, "GMT");
1755 #else
1756                 time_to_store = time (NULL);
1757 #endif
1758                 modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time_to_store);
1759
1760                 /* Get the message max size */
1761                 max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1762                                                  MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1763                 if (max_size == 0)
1764                         max_size = G_MAXINT;
1765                 else
1766                         max_size = max_size * KB;
1767         }
1768
1769         /* Create the new headers array. We need it to sort the
1770            new headers by date */
1771         new_headers_array = g_ptr_array_new ();
1772         if (info->inbox_observer) {
1773                 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1774                 if (!tny_iterator_is_done (new_headers_iter)) {
1775                         modest_platform_emit_folder_updated_signal (info->account_name, tny_folder_get_id (TNY_FOLDER (inbox)));
1776                         modest_account_mgr_set_has_new_mails (modest_runtime_get_account_mgr (),
1777                                                               info->account_name, TRUE);
1778                 }
1779                 while (!tny_iterator_is_done (new_headers_iter)) {
1780                         TnyHeader *header = NULL;
1781
1782                         header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1783                         /* Apply per-message size limits */
1784                         if (tny_header_get_message_size (header) < max_size)
1785                                 g_ptr_array_add (new_headers_array, g_object_ref (header));
1786
1787                         g_object_unref (header);
1788                         tny_iterator_next (new_headers_iter);
1789                 }
1790                 g_object_unref (new_headers_iter);
1791
1792                 tny_folder_remove_observer (inbox, info->inbox_observer);
1793                 g_object_unref (info->inbox_observer);
1794                 info->inbox_observer = NULL;
1795         }
1796
1797         if (new_headers_array->len == 0) {
1798                 g_ptr_array_free (new_headers_array, FALSE);
1799                 goto send_mail;
1800         }
1801
1802         /* Get per-account message amount retrieval limit */
1803         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1804         if (retrieve_limit == 0)
1805                 retrieve_limit = G_MAXINT;
1806
1807         /* Get per-account retrieval type */
1808         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1809         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1810
1811         /* Order by date */
1812         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1813
1814         /* Copy the headers to a list and free the array */
1815         new_headers = tny_simple_list_new ();
1816         for (i=0; i < new_headers_array->len; i++) {
1817                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1818                 /* We want the first element to be the most recent
1819                    one, that's why we reverse the list */
1820                 tny_list_prepend (new_headers, G_OBJECT (header));
1821         }
1822         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1823         g_ptr_array_free (new_headers_array, FALSE);
1824
1825         if (!headers_only && (tny_list_get_length (new_headers) > 0)) {
1826                 gint msg_num = 0;
1827                 TnyIterator *iter;
1828                 GetMsgInfo *msg_info;
1829
1830                 priv->done = 0;
1831                 priv->total = MIN (tny_list_get_length (new_headers), retrieve_limit);
1832
1833                 iter = tny_list_create_iterator (new_headers);
1834
1835                 /* Create the message info */
1836                 msg_info = g_slice_new0 (GetMsgInfo);
1837                 msg_info->mail_op = g_object_ref (info->mail_op);
1838                 msg_info->total_bytes = compute_message_list_size (new_headers, priv->total);
1839                 msg_info->more_msgs = g_object_ref (iter);
1840                 msg_info->msg = NULL;
1841                 msg_info->user_data = info;
1842
1843                 while ((msg_num < priv->total ) && !tny_iterator_is_done (iter)) {
1844                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1845                         TnyFolder *folder = tny_header_get_folder (header);
1846
1847                         /* Get message in an async way */
1848                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb,
1849                                                   NULL, msg_info);
1850
1851                         g_object_unref (folder);
1852                         g_object_unref (header);
1853
1854                         msg_num++;
1855                         tny_iterator_next (iter);
1856                 }
1857                 g_object_unref (iter);
1858                 g_object_unref (new_headers);
1859
1860                 /* The mail operation will finish when the last
1861                    message is retrieved */
1862                 return;
1863         }
1864  send_mail:
1865         /* If we don't have to retrieve the new messages then
1866            simply send mail */
1867         update_account_send_mail (info);
1868
1869         /* Check if the operation was a success */
1870         if (!priv->error)
1871                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1872
1873         /* Call the user callback and free */
1874         update_account_notify_user_and_free (info, new_headers);
1875 }
1876
1877 static void
1878 inbox_refresh_status_update (GObject *obj,
1879                              TnyStatus *status,
1880                              gpointer user_data)
1881 {
1882         UpdateAccountInfo *info = NULL;
1883         ModestMailOperation *self = NULL;
1884         ModestMailOperationPrivate *priv = NULL;
1885         ModestMailOperationState *state;
1886
1887         g_return_if_fail (user_data != NULL);
1888         g_return_if_fail (status != NULL);
1889
1890         /* Show only the status information we want */
1891         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
1892                 return;
1893
1894         info = (UpdateAccountInfo *) user_data;
1895         self = info->mail_op;
1896         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1897
1898         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1899
1900         priv->done = status->position;
1901         priv->total = status->of_total;
1902
1903         state = modest_mail_operation_clone_state (self);
1904
1905         /* This is not a GDK lock because we are a Tinymail callback and
1906          * Tinymail already acquires the Gdk lock */
1907         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1908
1909         g_slice_free (ModestMailOperationState, state);
1910 }
1911
1912 static void 
1913 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1914                           gboolean canceled,
1915                           TnyList *list, 
1916                           GError *err, 
1917                           gpointer user_data)
1918 {
1919         UpdateAccountInfo *info;
1920         ModestMailOperationPrivate *priv;
1921     
1922         info = (UpdateAccountInfo *) user_data;
1923         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1924
1925         if (err || canceled) {
1926                 /* If the error was previosly set by another callback
1927                    don't set it again */
1928                 if (!priv->error) {
1929                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1930                         if (err)
1931                                 priv->error = g_error_copy (err);
1932                         else
1933                                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1934                                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1935                                              "canceled");
1936                 }
1937         } else { 
1938                 /* We're not getting INBOX children if we don't want to poke all */
1939                 TnyIterator *iter = tny_list_create_iterator (list);
1940                 while (!tny_iterator_is_done (iter)) {
1941                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1942
1943                         /* Add to the list of all folders */
1944                         tny_list_append (info->folders, (GObject *) folder);
1945                         
1946                         if (info->poke_all) {
1947                                 TnyList *folders = tny_simple_list_new ();
1948                                 /* Add pending call */
1949                                 info->pending_calls++;
1950                                 
1951                                 tny_folder_store_get_folders_async (folder, folders, NULL, FALSE,
1952                                                                     recurse_folders_async_cb, 
1953                                                                     NULL, info);
1954                                 g_object_unref (folders);
1955                         }
1956                         
1957                         g_object_unref (G_OBJECT (folder));
1958                         
1959                         tny_iterator_next (iter);           
1960                 }
1961                 g_object_unref (G_OBJECT (iter));
1962         }
1963
1964         /* Remove my own pending call */
1965         info->pending_calls--;
1966
1967         /* This means that we have all the folders */
1968         if (info->pending_calls == 0) {
1969                 TnyIterator *iter_all_folders;
1970                 TnyFolder *inbox = NULL;
1971
1972                 /* If there was any error do not continue */
1973                 if (priv->error) {
1974                         update_account_notify_user_and_free (info, NULL);
1975                         return;
1976                 }
1977
1978                 iter_all_folders = tny_list_create_iterator (info->folders);
1979
1980                 /* Do a poke status over all folders */
1981                 while (!tny_iterator_is_done (iter_all_folders) &&
1982                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1983                         TnyFolder *folder = NULL;
1984
1985                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1986
1987                         if (!info->update_folder_counts && tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1988                                 /* Get a reference to the INBOX */
1989                                 inbox = g_object_ref (folder);
1990                         } else {
1991                                 /* Issue a poke status over the folder */
1992                                 if (info->poke_all)
1993                                         tny_folder_poke_status (folder);
1994                         }
1995
1996                         /* Free and go to next */
1997                         g_object_unref (folder);
1998                         tny_iterator_next (iter_all_folders);
1999                 }
2000                 g_object_unref (iter_all_folders);
2001
2002                 /* Refresh the INBOX */
2003                 if (inbox) {
2004                         /* Refresh the folder. Our observer receives
2005                          * the new emails during folder refreshes, so
2006                          * we can use observer->new_headers
2007                          */
2008                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
2009                         tny_folder_add_observer (inbox, info->inbox_observer);
2010
2011                         /* Refresh the INBOX */
2012                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, inbox_refresh_status_update, info);
2013                         g_object_unref (inbox);
2014                 } else {
2015                         /* We could not perform the inbox refresh but
2016                            we'll try to send mails anyway */
2017                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
2018                 }
2019         }
2020 }
2021
2022 void
2023 modest_mail_operation_update_account (ModestMailOperation *self,
2024                                       const gchar *account_name,
2025                                       gboolean poke_all,
2026                                       gboolean interactive,
2027                                       UpdateAccountCallback callback,
2028                                       gpointer user_data)
2029 {
2030         UpdateAccountInfo *info = NULL;
2031         ModestMailOperationPrivate *priv = NULL;
2032         ModestTnyAccountStore *account_store = NULL;
2033         TnyList *folders;
2034         ModestMailOperationState *state;
2035
2036         /* Init mail operation */
2037         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2038         priv->total = 0;
2039         priv->done  = 0;
2040         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2041         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
2042
2043         /* Get the store account */
2044         account_store = modest_runtime_get_account_store ();
2045         priv->account =
2046                 modest_tny_account_store_get_server_account (account_store,
2047                                                              account_name,
2048                                                              TNY_ACCOUNT_TYPE_STORE);
2049
2050         /* The above function could return NULL */
2051         if (!priv->account) {
2052                 /* Check if the operation was a success */
2053                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2054                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2055                              "no account");
2056                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2057
2058                 /* Call the user callback */
2059                 if (callback)
2060                         callback (self, NULL, user_data);
2061
2062                 /* Notify about operation end */
2063                 modest_mail_operation_notify_end (self);
2064
2065                 return;
2066         }
2067         
2068         /* We have once seen priv->account getting finalized during this code,
2069          * therefore adding a reference (bug #82296) */
2070         
2071         g_object_ref (priv->account);
2072
2073         /* Create the helper object */
2074         info = g_slice_new0 (UpdateAccountInfo);
2075         info->pending_calls = 1;
2076         info->folders = tny_simple_list_new ();
2077         info->mail_op = g_object_ref (self);
2078         info->poke_all = poke_all;
2079         info->interactive = interactive;
2080         info->update_folder_counts = FALSE;
2081         info->account_name = g_strdup (account_name);
2082         info->callback = callback;
2083         info->user_data = user_data;
2084
2085         /* Set account busy */
2086         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2087         modest_mail_operation_notify_start (self);
2088
2089         /* notify about the start of the operation */ 
2090         state = modest_mail_operation_clone_state (self);
2091         state->done = 0;
2092         state->total = 0;
2093
2094         /* Start notifying progress */
2095         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2096         g_slice_free (ModestMailOperationState, state);
2097         
2098         /* Get all folders and continue in the callback */ 
2099         folders = tny_simple_list_new ();
2100         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2101                                             folders, NULL, TRUE,
2102                                             recurse_folders_async_cb, 
2103                                             NULL, info);
2104         g_object_unref (folders);
2105         
2106         g_object_unref (priv->account);
2107         
2108 }
2109
2110 void
2111 modest_mail_operation_update_folder_counts (ModestMailOperation *self,
2112                                       const gchar *account_name)
2113 {
2114         UpdateAccountInfo *info = NULL;
2115         ModestMailOperationPrivate *priv = NULL;
2116         ModestTnyAccountStore *account_store = NULL;
2117         TnyList *folders;
2118         ModestMailOperationState *state;
2119
2120         /* Init mail operation */
2121         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2122         priv->total = 0;
2123         priv->done  = 0;
2124         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2125         priv->op_type = MODEST_MAIL_OPERATION_TYPE_UPDATE_FOLDER_COUNTS;
2126
2127         /* Get the store account */
2128         account_store = modest_runtime_get_account_store ();
2129         priv->account =
2130                 modest_tny_account_store_get_server_account (account_store,
2131                                                              account_name,
2132                                                              TNY_ACCOUNT_TYPE_STORE);
2133
2134         /* The above function could return NULL */
2135         if (!priv->account) {
2136                 /* Check if the operation was a success */
2137                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2138                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2139                              "no account");
2140                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2141
2142                 /* Notify about operation end */
2143                 modest_mail_operation_notify_end (self);
2144
2145                 return;
2146         }
2147         
2148         /* We have once seen priv->account getting finalized during this code,
2149          * therefore adding a reference (bug #82296) */
2150         
2151         g_object_ref (priv->account);
2152
2153         /* Create the helper object */
2154         info = g_slice_new0 (UpdateAccountInfo);
2155         info->pending_calls = 1;
2156         info->folders = tny_simple_list_new ();
2157         info->mail_op = g_object_ref (self);
2158         info->poke_all = TRUE;
2159         info->interactive = FALSE;
2160         info->update_folder_counts = TRUE;
2161         info->account_name = g_strdup (account_name);
2162         info->callback = NULL;
2163         info->user_data = NULL;
2164
2165         /* Set account busy */
2166         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2167         modest_mail_operation_notify_start (self);
2168
2169         /* notify about the start of the operation */ 
2170         state = modest_mail_operation_clone_state (self);
2171         state->done = 0;
2172         state->total = 0;
2173
2174         /* Start notifying progress */
2175         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2176         g_slice_free (ModestMailOperationState, state);
2177         
2178         /* Get all folders and continue in the callback */ 
2179         folders = tny_simple_list_new ();
2180         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2181                                             folders, NULL, TRUE,
2182                                             recurse_folders_async_cb, 
2183                                             NULL, info);
2184         g_object_unref (folders);
2185         
2186         g_object_unref (priv->account);
2187         
2188 }
2189
2190 /*
2191  * Used to notify the queue from the main
2192  * loop. We call it inside an idle call to achieve that
2193  */
2194 static gboolean
2195 idle_notify_queue (gpointer data)
2196 {
2197         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
2198
2199         gdk_threads_enter ();
2200         modest_mail_operation_notify_end (mail_op);
2201         gdk_threads_leave ();
2202         g_object_unref (mail_op);
2203
2204         return FALSE;
2205 }
2206
2207 static int
2208 compare_headers_by_date (gconstpointer a,
2209                          gconstpointer b)
2210 {
2211         TnyHeader **header1, **header2;
2212         time_t sent1, sent2;
2213
2214         header1 = (TnyHeader **) a;
2215         header2 = (TnyHeader **) b;
2216
2217         sent1 = tny_header_get_date_sent (*header1);
2218         sent2 = tny_header_get_date_sent (*header2);
2219
2220         /* We want the most recent ones (greater time_t) at the
2221            beginning */
2222         if (sent1 < sent2)
2223                 return -1;
2224         else
2225                 return 1;
2226 }
2227
2228
2229 /* ******************************************************************* */
2230 /* ************************** STORE  ACTIONS ************************* */
2231 /* ******************************************************************* */
2232
2233 typedef struct {
2234         ModestMailOperation *mail_op;
2235         CreateFolderUserCallback callback;
2236         gpointer user_data;
2237 } CreateFolderInfo;
2238
2239
2240 static void
2241 create_folder_cb (TnyFolderStore *parent_folder, 
2242                   gboolean canceled, 
2243                   TnyFolder *new_folder, 
2244                   GError *err, 
2245                   gpointer user_data)
2246 {
2247         ModestMailOperationPrivate *priv;
2248         CreateFolderInfo *info;
2249
2250         info = (CreateFolderInfo *) user_data;
2251         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2252
2253         if (canceled || err) {
2254                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2255                 if (err)
2256                         priv->error = g_error_copy (err);
2257                 else
2258                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2259                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2260                                      "canceled");               
2261         } else {
2262                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2263         }
2264
2265         /* The user will unref the new_folder */
2266         if (info->callback)
2267                 info->callback (info->mail_op, parent_folder, 
2268                                 new_folder, info->user_data);
2269         
2270         /* Notify about operation end */
2271         modest_mail_operation_notify_end (info->mail_op);
2272
2273         /* Frees */
2274         g_object_unref (info->mail_op);
2275         g_slice_free (CreateFolderInfo, info);
2276 }
2277
2278 void
2279 modest_mail_operation_create_folder (ModestMailOperation *self,
2280                                      TnyFolderStore *parent,
2281                                      const gchar *name,
2282                                      CreateFolderUserCallback callback,
2283                                      gpointer user_data)
2284 {
2285         ModestMailOperationPrivate *priv;
2286
2287         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2288         g_return_if_fail (name);
2289         
2290         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2291         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2292         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
2293                 g_object_ref (parent) : 
2294                 modest_tny_folder_get_account (TNY_FOLDER (parent));
2295
2296         /* Check for already existing folder */
2297         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
2298                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2299                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2300                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2301                              _CS_FOLDER_ALREADY_EXISTS);
2302         }
2303
2304         /* Check parent */
2305         if (TNY_IS_FOLDER (parent)) {
2306                 /* Check folder rules */
2307                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2308                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2309                         /* Set status failed and set an error */
2310                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2311                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2312                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2313                                      _("mail_in_ui_folder_create_error"));
2314                 }
2315         }
2316
2317         if (!priv->error && (!strcmp (name, " ") || strchr (name, '/'))) {
2318                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2319                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2320                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2321                              _("mail_in_ui_folder_create_error"));
2322         }
2323
2324         if (!priv->error) {
2325                 CreateFolderInfo *info;
2326
2327                 info = g_slice_new0 (CreateFolderInfo);
2328                 info->mail_op = g_object_ref (self);
2329                 info->callback = callback;
2330                 info->user_data = user_data;
2331
2332                 modest_mail_operation_notify_start (self);
2333
2334                 /* Create the folder */
2335                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
2336                                                       NULL, info);
2337         } else {
2338                 /* Call the user callback anyway */
2339                 if (callback)
2340                         callback (self, parent, NULL, user_data);
2341                 /* Notify about operation end */
2342                 modest_mail_operation_notify_end (self);
2343         }
2344 }
2345
2346 void
2347 modest_mail_operation_remove_folder (ModestMailOperation *self,
2348                                      TnyFolder           *folder,
2349                                      gboolean             remove_to_trash)
2350 {
2351         ModestMailOperationPrivate *priv;
2352         ModestTnyFolderRules rules;
2353
2354         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2355         g_return_if_fail (TNY_IS_FOLDER (folder));
2356         
2357         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2358         
2359         /* Check folder rules */
2360         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2361         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
2362                 /* Set status failed and set an error */
2363                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2364                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2365                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2366                              _("mail_in_ui_folder_delete_error"));
2367                 goto end;
2368         }
2369
2370         /* Get the account */
2371         priv->account = modest_tny_folder_get_account (folder);
2372         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2373
2374         /* Delete folder or move to trash */
2375         if (remove_to_trash) {
2376                 TnyFolder *trash_folder = NULL;
2377                 trash_folder = modest_tny_account_get_special_folder (priv->account,
2378                                                                       TNY_FOLDER_TYPE_TRASH);
2379                 /* TODO: error_handling */
2380                 if (trash_folder) {
2381                         modest_mail_operation_notify_start (self);
2382                         modest_mail_operation_xfer_folder (self, folder,
2383                                                     TNY_FOLDER_STORE (trash_folder), 
2384                                                     TRUE, NULL, NULL);
2385                         g_object_unref (trash_folder);
2386                 } else {
2387                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
2388                 }
2389         } else {
2390                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
2391                 if (parent) {
2392                         modest_mail_operation_notify_start (self);
2393                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
2394                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
2395                         
2396                         if (!priv->error)
2397                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2398
2399                         g_object_unref (parent);
2400                 } else
2401                         g_warning ("%s: could not get parent folder", __FUNCTION__);
2402         }
2403
2404  end:
2405         /* Notify about operation end */
2406         modest_mail_operation_notify_end (self);
2407 }
2408
2409 static void
2410 transfer_folder_status_cb (GObject *obj,
2411                            TnyStatus *status,
2412                            gpointer user_data)
2413 {
2414         ModestMailOperation *self;
2415         ModestMailOperationPrivate *priv;
2416         ModestMailOperationState *state;
2417         XFerFolderAsyncHelper *helper;
2418
2419         g_return_if_fail (status != NULL);
2420
2421         /* Show only the status information we want */
2422         if (status->code != TNY_FOLDER_STATUS_CODE_COPY_FOLDER)
2423                 return;
2424
2425         helper = (XFerFolderAsyncHelper *) user_data;
2426         g_return_if_fail (helper != NULL);
2427
2428         self = helper->mail_op;
2429         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2430
2431         priv->done = status->position;
2432         priv->total = status->of_total;
2433
2434         state = modest_mail_operation_clone_state (self);
2435
2436         /* This is not a GDK lock because we are a Tinymail callback
2437          * which is already GDK locked by Tinymail */
2438
2439         /* no gdk_threads_enter (), CHECKED */
2440
2441         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
2442
2443         /* no gdk_threads_leave (), CHECKED */
2444
2445         g_slice_free (ModestMailOperationState, state);
2446 }
2447
2448 static void
2449 transfer_folder_cb (TnyFolder *folder, 
2450                     gboolean cancelled, 
2451                     TnyFolderStore *into, 
2452                     TnyFolder *new_folder, 
2453                     GError *err, 
2454                     gpointer user_data)
2455 {
2456         XFerFolderAsyncHelper *helper;
2457         ModestMailOperation *self = NULL;
2458         ModestMailOperationPrivate *priv = NULL;
2459
2460         helper = (XFerFolderAsyncHelper *) user_data;
2461         g_return_if_fail (helper != NULL);
2462
2463         self = helper->mail_op;
2464         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2465
2466         if (err) {
2467                 priv->error = g_error_copy (err);
2468                 priv->done = 0;
2469                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2470         } else if (cancelled) {
2471                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2472                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2473                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2474                              _("Transference of %s was cancelled."),
2475                              tny_folder_get_name (folder));
2476         } else {
2477                 priv->done = 1;
2478                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2479         }
2480
2481         /* Update state of new folder */
2482         if (new_folder) {
2483                 tny_folder_refresh_async (new_folder, NULL, NULL, NULL);
2484                 tny_folder_poke_status (new_folder);
2485         }
2486
2487         /* Notify about operation end */
2488         modest_mail_operation_notify_end (self);
2489
2490         /* If user defined callback function was defined, call it */
2491         if (helper->user_callback) {
2492
2493                 /* This is not a GDK lock because we are a Tinymail callback
2494                  * which is already GDK locked by Tinymail */
2495
2496                 /* no gdk_threads_enter (), CHECKED */
2497                 helper->user_callback (self, new_folder, helper->user_data);
2498                 /* no gdk_threads_leave () , CHECKED */
2499         }
2500
2501         /* Free */
2502         g_object_unref (helper->mail_op);
2503         g_slice_free   (XFerFolderAsyncHelper, helper);
2504 }
2505
2506 /**
2507  *
2508  * This function checks if the new name is a valid name for our local
2509  * folders account. The new name could not be the same than then name
2510  * of any of the mandatory local folders
2511  *
2512  * We can not rely on tinymail because tinymail does not check the
2513  * name of the virtual folders that the account could have in the case
2514  * that we're doing a rename (because it directly calls Camel which
2515  * knows nothing about our virtual folders). 
2516  *
2517  * In the case of an actual copy/move (i.e. move/copy a folder between
2518  * accounts) tinymail uses the tny_folder_store_create_account which
2519  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2520  * checks the new name of the folder, so this call in that case
2521  * wouldn't be needed. *But* NOTE that if tinymail changes its
2522  * implementation (if folder transfers within the same account is no
2523  * longer implemented as a rename) this call will allow Modest to work
2524  * perfectly
2525  *
2526  * If the new name is not valid, this function will set the status to
2527  * failed and will set also an error in the mail operation
2528  */
2529 static gboolean
2530 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2531                                  TnyFolderStore *into,
2532                                  const gchar *new_name)
2533 {
2534         if (TNY_IS_ACCOUNT (into) && 
2535             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2536             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2537                                                                  new_name)) {
2538                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2539                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2540                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2541                              _CS_FOLDER_ALREADY_EXISTS);
2542                 return FALSE;
2543         } else
2544                 return TRUE;
2545 }
2546
2547 void
2548 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2549                                    TnyFolder *folder,
2550                                    TnyFolderStore *parent,
2551                                    gboolean delete_original,
2552                                    XferFolderAsyncUserCallback user_callback,
2553                                    gpointer user_data)
2554 {
2555         ModestMailOperationPrivate *priv = NULL;
2556         ModestTnyFolderRules parent_rules = 0, rules; 
2557         XFerFolderAsyncHelper *helper = NULL;
2558         const gchar *folder_name = NULL;
2559         const gchar *error_msg;
2560
2561         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2562         g_return_if_fail (TNY_IS_FOLDER (folder));
2563         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2564
2565         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2566         folder_name = tny_folder_get_name (folder);
2567
2568         /* Set the error msg */
2569         error_msg = _("mail_in_ui_folder_move_target_error");
2570
2571         /* Get account and set it into mail_operation */
2572         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2573         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2574         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2575
2576         /* Get folder rules */
2577         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2578         if (TNY_IS_FOLDER (parent))
2579                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2580         
2581         /* Apply operation constraints */
2582         if ((gpointer) parent == (gpointer) folder ||
2583             (!TNY_IS_FOLDER_STORE (parent)) || 
2584             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2585                 /* Folder rules */
2586                 goto error;
2587         } else if (TNY_IS_FOLDER (parent) && 
2588                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2589                 /* Folder rules */
2590                 goto error;
2591
2592         } else if (TNY_IS_FOLDER (parent) &&
2593                    TNY_IS_FOLDER_STORE (folder) &&
2594                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2595                                                   TNY_FOLDER_STORE (folder))) {
2596                 /* Do not move a parent into a child */
2597                 goto error;
2598         } else if (TNY_IS_FOLDER_STORE (parent) &&
2599                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2600                 /* Check that the new folder name is not used by any
2601                    parent subfolder */
2602                 goto error;     
2603         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2604                 /* Check that the new folder name is not used by any
2605                    special local folder */
2606                 goto error;
2607         } else {
2608                 /* Create the helper */
2609                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2610                 helper->mail_op = g_object_ref (self);
2611                 helper->user_callback = user_callback;
2612                 helper->user_data = user_data;
2613                 
2614                 /* Move/Copy folder */
2615                 modest_mail_operation_notify_start (self);
2616                 tny_folder_copy_async (folder,
2617                                        parent,
2618                                        tny_folder_get_name (folder),
2619                                        delete_original,
2620                                        transfer_folder_cb,
2621                                        transfer_folder_status_cb,
2622                                        helper);
2623                 return;
2624         }
2625
2626  error:
2627         /* Set status failed and set an error */
2628         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2629         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2630                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2631                      "%s", error_msg);
2632
2633         /* Call the user callback if exists */
2634         if (user_callback)
2635                 user_callback (self, NULL, user_data);
2636
2637         /* Notify the queue */
2638         modest_mail_operation_notify_end (self);
2639 }
2640
2641 void
2642 modest_mail_operation_rename_folder (ModestMailOperation *self,
2643                                      TnyFolder *folder,
2644                                      const gchar *name,
2645                                      XferFolderAsyncUserCallback user_callback,
2646                                      gpointer user_data)
2647 {
2648         ModestMailOperationPrivate *priv;
2649         ModestTnyFolderRules rules;
2650         XFerFolderAsyncHelper *helper;
2651
2652         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2653         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2654         g_return_if_fail (name);
2655         
2656         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2657
2658         /* Get account and set it into mail_operation */
2659         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2660         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2661
2662         /* Check folder rules */
2663         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2664         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2665                 goto error;
2666         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2667                 goto error;
2668         } else {
2669                 TnyFolderStore *into;
2670
2671                 into = tny_folder_get_folder_store (folder);    
2672
2673                 /* Check that the new folder name is not used by any
2674                    special local folder */
2675                 if (new_name_valid_if_local_account (priv, into, name)) {
2676                         /* Create the helper */
2677                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2678                         helper->mail_op = g_object_ref(self);
2679                         helper->user_callback = user_callback;
2680                         helper->user_data = user_data;
2681                 
2682                         /* Rename. Camel handles folder subscription/unsubscription */
2683                         modest_mail_operation_notify_start (self);
2684                         tny_folder_copy_async (folder, into, name, TRUE,
2685                                                transfer_folder_cb,
2686                                                transfer_folder_status_cb,
2687                                                helper);
2688                         g_object_unref (into);
2689                 } else {
2690                         g_object_unref (into);
2691                         goto error;
2692                 }
2693
2694                 return;
2695         }
2696  error:
2697         /* Set status failed and set an error */
2698         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2699         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2700                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2701                      _("FIXME: unable to rename"));
2702         
2703         if (user_callback)
2704                 user_callback (self, NULL, user_data);
2705
2706         /* Notify about operation end */
2707         modest_mail_operation_notify_end (self);
2708 }
2709
2710 /* ******************************************************************* */
2711 /* **************************  MSG  ACTIONS  ************************* */
2712 /* ******************************************************************* */
2713
2714 void 
2715 modest_mail_operation_find_msg (ModestMailOperation *self,
2716                                 TnyFolder *folder,
2717                                 const gchar *msg_uid,
2718                                 gboolean progress_feedback,
2719                                 GetMsgAsyncUserCallback user_callback,
2720                                 gpointer user_data)
2721 {
2722         GetMsgInfo *helper = NULL;
2723         ModestMailOperationPrivate *priv;
2724         
2725         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2726         g_return_if_fail (msg_uid != NULL);
2727         
2728         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2729         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2730         priv->total = 1;
2731         priv->done = 0;
2732
2733         /* Check memory low */
2734         if (_check_memory_low (self)) {
2735                 if (user_callback)
2736                         user_callback (self, NULL, FALSE, NULL, priv->error, user_data);
2737                 modest_mail_operation_notify_end (self);
2738                 return;
2739         }
2740
2741         /* Get account and set it into mail_operation */
2742         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2743         
2744         /* Check for cached messages */
2745         if (progress_feedback) {
2746                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2747         } else {
2748                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2749         }
2750         
2751         /* Create the helper */
2752         helper = g_slice_new0 (GetMsgInfo);
2753         helper->header = NULL;
2754         helper->mail_op = g_object_ref (self);
2755         helper->user_callback = user_callback;
2756         helper->user_data = user_data;
2757         helper->destroy_notify = NULL;
2758         helper->last_total_bytes = 0;
2759         helper->sum_total_bytes = 0;
2760         helper->total_bytes = 0;
2761         helper->more_msgs = NULL;
2762         helper->get_parts = NULL;
2763         helper->msg = NULL;
2764
2765         modest_mail_operation_notify_start (self);
2766         
2767         /* notify about the start of the operation */ 
2768         ModestMailOperationState *state;
2769         state = modest_mail_operation_clone_state (self);
2770         state->done = 0;
2771         state->total = 0;
2772         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2773                                 0, state, NULL);
2774         g_slice_free (ModestMailOperationState, state);
2775         
2776         tny_folder_find_msg_async (folder, msg_uid, get_msg_async_cb, get_msg_status_cb, helper);
2777 }
2778
2779 void 
2780 modest_mail_operation_get_msg (ModestMailOperation *self,
2781                                TnyHeader *header,
2782                                gboolean progress_feedback,
2783                                GetMsgAsyncUserCallback user_callback,
2784                                gpointer user_data)
2785 {
2786         GetMsgInfo *helper = NULL;
2787         TnyFolder *folder;
2788         ModestMailOperationPrivate *priv;
2789         
2790         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2791         g_return_if_fail (TNY_IS_HEADER (header));
2792         
2793         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2794         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2795         priv->total = 1;
2796         priv->done = 0;
2797
2798         /* Check memory low */
2799         if (_check_memory_low (self)) {
2800                 if (user_callback)
2801                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2802                 modest_mail_operation_notify_end (self);
2803                 return;
2804         }
2805
2806         /* Get account and set it into mail_operation */
2807         folder = tny_header_get_folder (header);
2808         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2809         
2810         /* Check for cached messages */
2811         if (progress_feedback) {
2812                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2813                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2814                 else 
2815                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2816         } else {
2817                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2818         }
2819         
2820         /* Create the helper */
2821         helper = g_slice_new0 (GetMsgInfo);
2822         helper->header = g_object_ref (header);
2823         helper->mail_op = g_object_ref (self);
2824         helper->user_callback = user_callback;
2825         helper->user_data = user_data;
2826         helper->destroy_notify = NULL;
2827         helper->last_total_bytes = 0;
2828         helper->sum_total_bytes = 0;
2829         helper->total_bytes = tny_header_get_message_size (header);
2830         helper->more_msgs = NULL;
2831         helper->get_parts = NULL;
2832         helper->msg = NULL;
2833
2834         modest_mail_operation_notify_start (self);
2835         
2836         /* notify about the start of the operation */ 
2837         ModestMailOperationState *state;
2838         state = modest_mail_operation_clone_state (self);
2839         state->done = 0;
2840         state->total = 0;
2841         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2842                                 0, state, NULL);
2843         g_slice_free (ModestMailOperationState, state);
2844         
2845         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2846
2847         g_object_unref (G_OBJECT (folder));
2848 }
2849
2850 void 
2851 modest_mail_operation_get_msg_and_parts (ModestMailOperation *self,
2852                                          TnyHeader *header,
2853                                          TnyList *parts,
2854                                          gboolean progress_feedback,
2855                                          GetMsgAsyncUserCallback user_callback,
2856                                          gpointer user_data)
2857 {
2858         GetMsgInfo *helper = NULL;
2859         TnyFolder *folder;
2860         ModestMailOperationPrivate *priv;
2861         
2862         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2863         g_return_if_fail (TNY_IS_HEADER (header));
2864         
2865         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2866         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2867         priv->total = 1;
2868         priv->done = 0;
2869
2870         /* Check memory low */
2871         if (_check_memory_low (self)) {
2872                 if (user_callback)
2873                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2874                 modest_mail_operation_notify_end (self);
2875                 return;
2876         }
2877
2878         /* Get account and set it into mail_operation */
2879         folder = tny_header_get_folder (header);
2880         if (folder == NULL && MODEST_IS_MSG_VIEW_WINDOW (priv->source)) {
2881                 const gchar *acc_name;
2882                 acc_name = modest_window_get_active_account (MODEST_WINDOW (priv->source));
2883                 priv->account = modest_tny_account_store_get_server_account
2884                         (modest_runtime_get_account_store (),
2885                          acc_name,
2886                          TNY_ACCOUNT_TYPE_STORE);
2887                 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (priv->account), 
2888                                                                        modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (priv->source)));
2889         } else {
2890                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2891         }
2892         
2893         /* Check for cached messages */
2894         if (progress_feedback) {
2895                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2896                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2897                 else 
2898                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2899         } else {
2900                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2901         }
2902         
2903         /* Create the helper */
2904         helper = g_slice_new0 (GetMsgInfo);
2905         helper->header = g_object_ref (header);
2906         helper->mail_op = g_object_ref (self);
2907         helper->user_callback = user_callback;
2908         helper->user_data = user_data;
2909         helper->destroy_notify = NULL;
2910         helper->last_total_bytes = 0;
2911         helper->sum_total_bytes = 0;
2912         helper->total_bytes = tny_header_get_message_size (header);
2913         helper->more_msgs = NULL;
2914         helper->get_parts = tny_list_create_iterator (parts);
2915         helper->msg = NULL;
2916
2917         modest_mail_operation_notify_start (self);
2918         
2919         /* notify about the start of the operation */ 
2920         ModestMailOperationState *state;
2921         state = modest_mail_operation_clone_state (self);
2922         state->done = 0;
2923         state->total = 0;
2924         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2925                                 0, state, NULL);
2926         g_slice_free (ModestMailOperationState, state);
2927         
2928         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2929
2930         g_object_unref (G_OBJECT (folder));
2931 }
2932
2933 static void     
2934 get_msg_status_cb (GObject *obj,
2935                    TnyStatus *status,  
2936                    gpointer user_data)
2937 {
2938         GetMsgInfo *helper = NULL;
2939
2940         g_return_if_fail (status != NULL);
2941
2942         /* Show only the status information we want */
2943         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2944                 return;
2945
2946         helper = (GetMsgInfo *) user_data;
2947         g_return_if_fail (helper != NULL);       
2948
2949         /* Notify progress */
2950         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2951                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2952 }
2953
2954 static void
2955 get_msg_async_get_part_cb (TnyMimePart *self, gboolean cancelled, TnyStream *stream, GError *err, gpointer user_data)
2956 {
2957         GetMsgInfo *helper;
2958         TnyFolder *folder = NULL;
2959
2960         helper = (GetMsgInfo *) user_data;
2961
2962         if (helper->header) {
2963                 folder = tny_header_get_folder (helper->header);
2964         }
2965
2966         get_msg_async_cb (folder, cancelled, helper->msg, err, user_data);
2967
2968         if (folder) g_object_unref (folder);
2969 }
2970
2971 static void
2972 get_msg_async_cb (TnyFolder *folder, 
2973                   gboolean canceled, 
2974                   TnyMsg *msg, 
2975                   GError *err, 
2976                   gpointer user_data)
2977 {
2978         GetMsgInfo *info = NULL;
2979         ModestMailOperationPrivate *priv = NULL;
2980         gboolean finished;
2981
2982         info = (GetMsgInfo *) user_data;
2983
2984         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2985         priv->done++;
2986
2987         if (info->more_msgs) {
2988                 tny_iterator_next (info->more_msgs);
2989                 finished = (tny_iterator_is_done (info->more_msgs));
2990         } else if (info->get_parts) {
2991                 tny_iterator_next (info->get_parts);
2992                 finished = (tny_iterator_is_done (info->get_parts));
2993         } else {
2994                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2995         }
2996
2997         /* If canceled by the user, ignore the error given by Tinymail */
2998         if (canceled) {
2999                 canceled = TRUE;
3000                 finished = TRUE;
3001                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3002         } else if (err) {
3003                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3004                 priv->error = g_error_copy ((const GError *) err);
3005                 if (priv->error) {
3006                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3007                 } else {
3008                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3009                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3010                                      "%s", err->message);
3011                 }
3012         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
3013                 /* Set the success status before calling the user callback */
3014                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3015         }
3016
3017         if (info->header == NULL && msg)
3018                 info->header = tny_msg_get_header (msg);
3019
3020         /* Call the user callback */
3021         if (info->user_callback && (finished || (info->get_parts == NULL)))
3022                 info->user_callback (info->mail_op, info->header, canceled, 
3023                                      msg, err, info->user_data);
3024
3025         /* Notify about operation end if this is the last callback */
3026         if (finished) {
3027                 /* Free user data */
3028                 if (info->destroy_notify)
3029                         info->destroy_notify (info->user_data);
3030
3031                 /* Notify about operation end */
3032                 modest_mail_operation_notify_end (info->mail_op);
3033
3034                 /* Clean */
3035                 if (info->msg)
3036                         g_object_unref (info->msg);
3037                 if (info->more_msgs)
3038                         g_object_unref (info->more_msgs);
3039                 if (info->header)
3040                         g_object_unref (info->header);
3041                 g_object_unref (info->mail_op);
3042                 g_slice_free (GetMsgInfo, info);
3043         } else if (info->get_parts) {
3044                 CamelStream *null_stream;
3045                 TnyStream *tny_null_stream;
3046                 TnyMimePart *part;
3047
3048                 if (info->msg == NULL && msg != NULL)
3049                         info->msg = g_object_ref (msg);
3050
3051                 null_stream = camel_stream_null_new ();
3052                 tny_null_stream = tny_camel_stream_new (null_stream);
3053                 
3054                 part = TNY_MIME_PART (tny_iterator_get_current (info->get_parts));
3055                 tny_mime_part_decode_to_stream_async (part, tny_null_stream, 
3056                                                       get_msg_async_get_part_cb,
3057                                                       get_msg_status_cb,
3058                                                       info);
3059                 g_object_unref (tny_null_stream);
3060                 g_object_unref (part);
3061
3062         } else if (info->more_msgs) {
3063                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
3064                 TnyFolder *folder = tny_header_get_folder (header);
3065
3066                 g_object_unref (info->header);
3067                 info->header = g_object_ref (header);
3068
3069                 /* Retrieve the next message */
3070                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
3071
3072                 g_object_unref (header);
3073                 g_object_unref (folder);
3074         } else {
3075                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
3076         }
3077 }
3078
3079 void 
3080 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
3081                                      TnyList *header_list, 
3082                                      GetMsgAsyncUserCallback user_callback,
3083                                      gpointer user_data,
3084                                      GDestroyNotify notify)
3085 {
3086         ModestMailOperationPrivate *priv = NULL;
3087         gint msg_list_size;
3088         TnyIterator *iter = NULL;
3089         gboolean has_uncached_messages;
3090         
3091         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3092
3093         /* Init mail operation */
3094         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3095         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3096         priv->done = 0;
3097         priv->total = tny_list_get_length(header_list);
3098
3099         /* Check memory low */
3100         if (_check_memory_low (self)) {
3101                 if (user_callback) {
3102                         TnyHeader *header = NULL;
3103                         TnyIterator *iter;
3104
3105                         if (tny_list_get_length (header_list) > 0) {
3106                                 iter = tny_list_create_iterator (header_list);
3107                                 header = (TnyHeader *) tny_iterator_get_current (iter);
3108                                 g_object_unref (iter);
3109                         }
3110                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
3111                         if (header)
3112                                 g_object_unref (header);
3113                 }
3114                 if (notify)
3115                         notify (user_data);
3116                 /* Notify about operation end */
3117                 modest_mail_operation_notify_end (self);
3118                 return;
3119         }
3120
3121         /* Check uncached messages */
3122         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
3123              !has_uncached_messages && !tny_iterator_is_done (iter); 
3124              tny_iterator_next (iter)) {
3125                 TnyHeader *header;
3126
3127                 header = (TnyHeader *) tny_iterator_get_current (iter);
3128                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
3129                         has_uncached_messages = TRUE;
3130                 g_object_unref (header);
3131         }       
3132         g_object_unref (iter);
3133         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
3134
3135         /* Get account and set it into mail_operation */
3136         if (tny_list_get_length (header_list) >= 1) {
3137                 TnyIterator *iterator = tny_list_create_iterator (header_list);
3138                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
3139                 if (header) {
3140                         TnyFolder *folder = tny_header_get_folder (header);
3141                         if (folder) {           
3142                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
3143                                 g_object_unref (folder);
3144                         }
3145                         g_object_unref (header);
3146                 }
3147                 g_object_unref (iterator);
3148         }
3149
3150         msg_list_size = compute_message_list_size (header_list, 0);
3151
3152         modest_mail_operation_notify_start (self);
3153         iter = tny_list_create_iterator (header_list);
3154         if (!tny_iterator_is_done (iter)) {
3155                 /* notify about the start of the operation */
3156                 ModestMailOperationState *state;
3157                 state = modest_mail_operation_clone_state (self);
3158                 state->done = 0;
3159                 state->total = 0;
3160                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
3161                                0, state, NULL);
3162
3163                 GetMsgInfo *msg_info = NULL;
3164                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3165                 TnyFolder *folder = tny_header_get_folder (header);
3166
3167                 /* Create the message info */
3168                 msg_info = g_slice_new0 (GetMsgInfo);
3169                 msg_info->mail_op = g_object_ref (self);
3170                 msg_info->header = g_object_ref (header);
3171                 msg_info->more_msgs = g_object_ref (iter);
3172                 msg_info->user_callback = user_callback;
3173                 msg_info->user_data = user_data;
3174                 msg_info->destroy_notify = notify;
3175                 msg_info->last_total_bytes = 0;
3176                 msg_info->sum_total_bytes = 0;
3177                 msg_info->total_bytes = msg_list_size;
3178                 msg_info->msg = NULL;
3179
3180                 /* The callback will call it per each header */
3181                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
3182
3183                 /* Free and go on */
3184                 g_object_unref (header);
3185                 g_object_unref (folder);
3186                 g_slice_free (ModestMailOperationState, state);
3187         }
3188         g_object_unref (iter);
3189 }
3190
3191
3192 static void
3193 remove_msgs_async_cb (TnyFolder *folder, 
3194                       gboolean canceled, 
3195                       GError *err, 
3196                       gpointer user_data)
3197 {
3198         gboolean expunge;
3199         const gchar *account_name;
3200         TnyAccount *account;
3201         ModestProtocolType account_proto = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
3202         ModestMailOperation *self;
3203         ModestMailOperationPrivate *priv;
3204         ModestProtocolRegistry *protocol_registry;
3205         SyncFolderHelper *helper;
3206
3207         self = (ModestMailOperation *) user_data;
3208         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3209         protocol_registry = modest_runtime_get_protocol_registry ();
3210
3211         if (canceled || err) {
3212                 /* If canceled by the user, ignore the error given by Tinymail */
3213                 if (canceled) {
3214                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3215                 } else if (err) {
3216                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3217                         priv->error = g_error_copy ((const GError *) err);
3218                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3219                 }
3220                 /* Exit */
3221                 modest_mail_operation_notify_end (self);
3222                 g_object_unref (self);
3223                 return;
3224         }
3225
3226         account = modest_tny_folder_get_account (folder);
3227         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3228         account_proto = modest_tny_account_get_protocol_type (account);
3229         g_object_unref (account);
3230
3231         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_proto)) {
3232                 if (modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3233                                                             account_name))
3234                         expunge = FALSE;