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