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