Fixes FwNULL 5/16
[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                                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1447                                                                                   info->interactive);
1448                         }
1449                 }
1450         }
1451 }
1452
1453 static void
1454 update_account_get_msg_async_cb (TnyFolder *folder, 
1455                                  gboolean canceled, 
1456                                  TnyMsg *msg, 
1457                                  GError *err, 
1458                                  gpointer user_data)
1459 {
1460         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1461         ModestMailOperationPrivate *priv;
1462
1463         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (msg_info->mail_op);
1464         priv->done++;
1465
1466         if (TNY_IS_MSG (msg)) {
1467                 TnyHeader *header = tny_msg_get_header (msg);
1468
1469                 if (header) {
1470                         ModestMailOperationState *state;
1471                         state = modest_mail_operation_clone_state (msg_info->mail_op);
1472                         msg_info->sum_total_bytes += tny_header_get_message_size (header);
1473                         state->bytes_done = msg_info->sum_total_bytes;
1474                         state->bytes_total = msg_info->total_bytes;
1475
1476                         /* Notify the status change. Only notify about changes
1477                            referred to bytes */
1478                         g_signal_emit (G_OBJECT (msg_info->mail_op), 
1479                                        signals[PROGRESS_CHANGED_SIGNAL], 
1480                                        0, state, NULL);
1481
1482                         g_object_unref (header);
1483                         g_slice_free (ModestMailOperationState, state);
1484                 }
1485         }
1486
1487         if (priv->done == priv->total) {
1488                 TnyList *new_headers;
1489                 UpdateAccountInfo *info;
1490
1491                 /* After getting all the messages send the ones in the
1492                    outboxes */
1493                 info = (UpdateAccountInfo *) msg_info->user_data;
1494                 update_account_send_mail (info);
1495
1496                 /* Check if the operation was a success */
1497                 if (!priv->error)
1498                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1499                 
1500                 /* Call the user callback and free */
1501                 new_headers = tny_iterator_get_list (msg_info->more_msgs);
1502                 update_account_notify_user_and_free (info, new_headers);
1503                 g_object_unref (new_headers);
1504
1505                 /* Delete the helper */
1506                 g_object_unref (msg_info->more_msgs);
1507                 g_object_unref (msg_info->mail_op);
1508                 g_slice_free (GetMsgInfo, msg_info);
1509         }
1510 }
1511
1512 static void
1513 update_account_notify_user_and_free (UpdateAccountInfo *info, 
1514                                      TnyList *new_headers)
1515 {
1516         /* Set the account back to not busy */
1517         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), 
1518                                              info->account_name, FALSE);
1519         
1520         /* User callback */
1521         if (info->callback)
1522                 info->callback (info->mail_op, new_headers, info->user_data);
1523         
1524         /* Mail operation end */
1525         modest_mail_operation_notify_end (info->mail_op);
1526
1527         /* Frees */
1528         if (new_headers)
1529                 g_object_unref (new_headers);
1530         destroy_update_account_info (info);
1531 }
1532
1533 static void
1534 inbox_refreshed_cb (TnyFolder *inbox, 
1535                     gboolean canceled, 
1536                     GError *err, 
1537                     gpointer user_data)
1538 {       
1539         UpdateAccountInfo *info;
1540         ModestMailOperationPrivate *priv;
1541         TnyIterator *new_headers_iter;
1542         GPtrArray *new_headers_array = NULL;
1543         gint max_size, retrieve_limit, i;
1544         ModestAccountMgr *mgr;
1545         ModestAccountRetrieveType retrieve_type;
1546         TnyList *new_headers = NULL;
1547         gboolean headers_only, ignore_limit;
1548
1549         info = (UpdateAccountInfo *) user_data;
1550         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1551         mgr = modest_runtime_get_account_mgr ();
1552
1553         if (canceled || err) {
1554                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1555                 if (err)
1556                         priv->error = g_error_copy (err);
1557                 else
1558                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1559                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1560                                      "canceled");
1561
1562                 if (inbox)
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         if (new_folder) {
2230                 tny_folder_refresh_async (new_folder, NULL, NULL, NULL);
2231                 tny_folder_poke_status (new_folder);
2232         }
2233
2234         /* Notify about operation end */
2235         modest_mail_operation_notify_end (self);
2236
2237         /* If user defined callback function was defined, call it */
2238         if (helper->user_callback) {
2239
2240                 /* This is not a GDK lock because we are a Tinymail callback
2241                  * which is already GDK locked by Tinymail */
2242
2243                 /* no gdk_threads_enter (), CHECKED */
2244                 helper->user_callback (self, new_folder, helper->user_data);
2245                 /* no gdk_threads_leave () , CHECKED */
2246         }
2247
2248         /* Free */
2249         g_object_unref (helper->mail_op);
2250         g_slice_free   (XFerFolderAsyncHelper, helper);
2251 }
2252
2253 /**
2254  *
2255  * This function checks if the new name is a valid name for our local
2256  * folders account. The new name could not be the same than then name
2257  * of any of the mandatory local folders
2258  *
2259  * We can not rely on tinymail because tinymail does not check the
2260  * name of the virtual folders that the account could have in the case
2261  * that we're doing a rename (because it directly calls Camel which
2262  * knows nothing about our virtual folders). 
2263  *
2264  * In the case of an actual copy/move (i.e. move/copy a folder between
2265  * accounts) tinymail uses the tny_folder_store_create_account which
2266  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2267  * checks the new name of the folder, so this call in that case
2268  * wouldn't be needed. *But* NOTE that if tinymail changes its
2269  * implementation (if folder transfers within the same account is no
2270  * longer implemented as a rename) this call will allow Modest to work
2271  * perfectly
2272  *
2273  * If the new name is not valid, this function will set the status to
2274  * failed and will set also an error in the mail operation
2275  */
2276 static gboolean
2277 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2278                                  TnyFolderStore *into,
2279                                  const gchar *new_name)
2280 {
2281         if (TNY_IS_ACCOUNT (into) && 
2282             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2283             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2284                                                                  new_name)) {
2285                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2286                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2287                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2288                              _CS("ckdg_ib_folder_already_exists"));
2289                 return FALSE;
2290         } else
2291                 return TRUE;
2292 }
2293
2294 void
2295 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2296                                    TnyFolder *folder,
2297                                    TnyFolderStore *parent,
2298                                    gboolean delete_original,
2299                                    XferFolderAsyncUserCallback user_callback,
2300                                    gpointer user_data)
2301 {
2302         ModestMailOperationPrivate *priv = NULL;
2303         ModestTnyFolderRules parent_rules = 0, rules; 
2304         XFerFolderAsyncHelper *helper = NULL;
2305         const gchar *folder_name = NULL;
2306         const gchar *error_msg;
2307
2308         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2309         g_return_if_fail (TNY_IS_FOLDER (folder));
2310         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2311
2312         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2313         folder_name = tny_folder_get_name (folder);
2314
2315         /* Set the error msg */
2316         error_msg = _("mail_in_ui_folder_move_target_error");
2317
2318         /* Get account and set it into mail_operation */
2319         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2320         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2321         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2322
2323         /* Get folder rules */
2324         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2325         if (TNY_IS_FOLDER (parent))
2326                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2327         
2328         /* Apply operation constraints */
2329         if ((gpointer) parent == (gpointer) folder ||
2330             (!TNY_IS_FOLDER_STORE (parent)) || 
2331             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2332                 /* Folder rules */
2333                 goto error;
2334         } else if (TNY_IS_FOLDER (parent) && 
2335                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2336                 /* Folder rules */
2337                 goto error;
2338
2339         } else if (TNY_IS_FOLDER (parent) &&
2340                    TNY_IS_FOLDER_STORE (folder) &&
2341                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2342                                                   TNY_FOLDER_STORE (folder))) {
2343                 /* Do not move a parent into a child */
2344                 goto error;
2345         } else if (TNY_IS_FOLDER_STORE (parent) &&
2346                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2347                 /* Check that the new folder name is not used by any
2348                    parent subfolder */
2349                 goto error;     
2350         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2351                 /* Check that the new folder name is not used by any
2352                    special local folder */
2353                 goto error;
2354         } else {
2355                 /* Create the helper */
2356                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2357                 helper->mail_op = g_object_ref (self);
2358                 helper->user_callback = user_callback;
2359                 helper->user_data = user_data;
2360                 
2361                 /* Move/Copy folder */
2362                 modest_mail_operation_notify_start (self);
2363                 tny_folder_copy_async (folder,
2364                                        parent,
2365                                        tny_folder_get_name (folder),
2366                                        delete_original,
2367                                        transfer_folder_cb,
2368                                        transfer_folder_status_cb,
2369                                        helper);
2370                 return;
2371         }
2372
2373  error:
2374         /* Set status failed and set an error */
2375         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2376         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2377                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2378                      error_msg);
2379
2380         /* Call the user callback if exists */
2381         if (user_callback)
2382                 user_callback (self, NULL, user_data);
2383
2384         /* Notify the queue */
2385         modest_mail_operation_notify_end (self);
2386 }
2387
2388 void
2389 modest_mail_operation_rename_folder (ModestMailOperation *self,
2390                                      TnyFolder *folder,
2391                                      const gchar *name,
2392                                      XferFolderAsyncUserCallback user_callback,
2393                                      gpointer user_data)
2394 {
2395         ModestMailOperationPrivate *priv;
2396         ModestTnyFolderRules rules;
2397         XFerFolderAsyncHelper *helper;
2398
2399         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2400         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2401         g_return_if_fail (name);
2402         
2403         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2404
2405         /* Get account and set it into mail_operation */
2406         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2407         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2408
2409         /* Check folder rules */
2410         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2411         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2412                 goto error;
2413         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2414                 goto error;
2415         } else {
2416                 TnyFolderStore *into;
2417
2418                 into = tny_folder_get_folder_store (folder);    
2419
2420                 /* Check that the new folder name is not used by any
2421                    special local folder */
2422                 if (new_name_valid_if_local_account (priv, into, name)) {
2423                         /* Create the helper */
2424                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2425                         helper->mail_op = g_object_ref(self);
2426                         helper->user_callback = user_callback;
2427                         helper->user_data = user_data;
2428                 
2429                         /* Rename. Camel handles folder subscription/unsubscription */
2430                         modest_mail_operation_notify_start (self);
2431                         tny_folder_copy_async (folder, into, name, TRUE,
2432                                                transfer_folder_cb,
2433                                                transfer_folder_status_cb,
2434                                                helper);
2435                         g_object_unref (into);
2436                 } else {
2437                         g_object_unref (into);
2438                         goto error;
2439                 }
2440
2441                 return;
2442         }
2443  error:
2444         /* Set status failed and set an error */
2445         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2446         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2447                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2448                      _("FIXME: unable to rename"));
2449         
2450         if (user_callback)
2451                 user_callback (self, NULL, user_data);
2452
2453         /* Notify about operation end */
2454         modest_mail_operation_notify_end (self);
2455 }
2456
2457 /* ******************************************************************* */
2458 /* **************************  MSG  ACTIONS  ************************* */
2459 /* ******************************************************************* */
2460
2461 void 
2462 modest_mail_operation_get_msg (ModestMailOperation *self,
2463                                TnyHeader *header,
2464                                gboolean progress_feedback,
2465                                GetMsgAsyncUserCallback user_callback,
2466                                gpointer user_data)
2467 {
2468         GetMsgInfo *helper = NULL;
2469         TnyFolder *folder;
2470         ModestMailOperationPrivate *priv;
2471         
2472         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2473         g_return_if_fail (TNY_IS_HEADER (header));
2474         
2475         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2476         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2477         priv->total = 1;
2478         priv->done = 0;
2479
2480         /* Check memory low */
2481         if (_check_memory_low (self)) {
2482                 if (user_callback)
2483                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2484                 modest_mail_operation_notify_end (self);
2485                 return;
2486         }
2487
2488         /* Get account and set it into mail_operation */
2489         folder = tny_header_get_folder (header);
2490         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2491         
2492         /* Check for cached messages */
2493         if (progress_feedback) {
2494                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2495                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2496                 else 
2497                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2498         } else {
2499                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2500         }
2501         
2502         /* Create the helper */
2503         helper = g_slice_new0 (GetMsgInfo);
2504         helper->header = g_object_ref (header);
2505         helper->mail_op = g_object_ref (self);
2506         helper->user_callback = user_callback;
2507         helper->user_data = user_data;
2508         helper->destroy_notify = NULL;
2509         helper->last_total_bytes = 0;
2510         helper->sum_total_bytes = 0;
2511         helper->total_bytes = tny_header_get_message_size (header);
2512         helper->more_msgs = NULL;
2513
2514         modest_mail_operation_notify_start (self);
2515         
2516         /* notify about the start of the operation */ 
2517         ModestMailOperationState *state;
2518         state = modest_mail_operation_clone_state (self);
2519         state->done = 0;
2520         state->total = 0;
2521         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2522                                 0, state, NULL);
2523         g_slice_free (ModestMailOperationState, state);
2524         
2525         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2526
2527         g_object_unref (G_OBJECT (folder));
2528 }
2529
2530 static void     
2531 get_msg_status_cb (GObject *obj,
2532                    TnyStatus *status,  
2533                    gpointer user_data)
2534 {
2535         GetMsgInfo *helper = NULL;
2536
2537         g_return_if_fail (status != NULL);
2538
2539         /* Show only the status information we want */
2540         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2541                 return;
2542
2543         helper = (GetMsgInfo *) user_data;
2544         g_return_if_fail (helper != NULL);       
2545
2546         /* Notify progress */
2547         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2548                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2549 }
2550
2551 static void
2552 get_msg_async_cb (TnyFolder *folder, 
2553                   gboolean canceled, 
2554                   TnyMsg *msg, 
2555                   GError *err, 
2556                   gpointer user_data)
2557 {
2558         GetMsgInfo *info = NULL;
2559         ModestMailOperationPrivate *priv = NULL;
2560         gboolean finished;
2561
2562         info = (GetMsgInfo *) user_data;
2563
2564         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2565         priv->done++;
2566
2567         if (info->more_msgs) {
2568                 tny_iterator_next (info->more_msgs);
2569                 finished = (tny_iterator_is_done (info->more_msgs));
2570         } else {
2571                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2572         }
2573
2574         /* If canceled by the user, ignore the error given by Tinymail */
2575         if (canceled) {
2576                 canceled = TRUE;
2577                 finished = TRUE;
2578                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2579         } else if (err) {
2580                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2581                 if (err) {
2582                         priv->error = g_error_copy ((const GError *) err);
2583                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2584                 }
2585                 if (!priv->error) {
2586                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2587                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2588                                      err->message);
2589                 }
2590         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2591                 /* Set the success status before calling the user callback */
2592                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2593         }
2594
2595
2596         /* Call the user callback */
2597         if (info->user_callback)
2598                 info->user_callback (info->mail_op, info->header, canceled, 
2599                                      msg, err, info->user_data);
2600
2601         /* Notify about operation end if this is the last callback */
2602         if (finished) {
2603                 /* Free user data */
2604                 if (info->destroy_notify)
2605                         info->destroy_notify (info->user_data);
2606
2607                 /* Notify about operation end */
2608                 modest_mail_operation_notify_end (info->mail_op);
2609
2610                 /* Clean */
2611                 if (info->more_msgs)
2612                         g_object_unref (info->more_msgs);
2613                 g_object_unref (info->header);
2614                 g_object_unref (info->mail_op);
2615                 g_slice_free (GetMsgInfo, info);
2616         } else if (info->more_msgs) {
2617                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
2618                 TnyFolder *folder = tny_header_get_folder (header);
2619
2620                 g_object_unref (info->header);
2621                 info->header = g_object_ref (header);
2622
2623                 /* Retrieve the next message */
2624                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
2625
2626                 g_object_unref (header);
2627                 g_object_unref (folder);
2628         } else {
2629                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
2630         }
2631 }
2632
2633 void 
2634 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2635                                      TnyList *header_list, 
2636                                      GetMsgAsyncUserCallback user_callback,
2637                                      gpointer user_data,
2638                                      GDestroyNotify notify)
2639 {
2640         ModestMailOperationPrivate *priv = NULL;
2641         gint msg_list_size;
2642         TnyIterator *iter = NULL;
2643         gboolean has_uncached_messages;
2644         
2645         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2646
2647         /* Init mail operation */
2648         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2649         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2650         priv->done = 0;
2651         priv->total = tny_list_get_length(header_list);
2652
2653         /* Check memory low */
2654         if (_check_memory_low (self)) {
2655                 if (user_callback) {
2656                         TnyHeader *header = NULL;
2657                         TnyIterator *iter;
2658
2659                         if (tny_list_get_length (header_list) > 0) {
2660                                 iter = tny_list_create_iterator (header_list);
2661                                 header = (TnyHeader *) tny_iterator_get_current (iter);
2662                                 g_object_unref (iter);
2663                         }
2664                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2665                         if (header)
2666                                 g_object_unref (header);
2667                 }
2668                 if (notify)
2669                         notify (user_data);
2670                 /* Notify about operation end */
2671                 modest_mail_operation_notify_end (self);
2672                 return;
2673         }
2674
2675         /* Check uncached messages */
2676         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
2677              !has_uncached_messages && !tny_iterator_is_done (iter); 
2678              tny_iterator_next (iter)) {
2679                 TnyHeader *header;
2680
2681                 header = (TnyHeader *) tny_iterator_get_current (iter);
2682                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
2683                         has_uncached_messages = TRUE;
2684                 g_object_unref (header);
2685         }       
2686         g_object_unref (iter);
2687         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
2688
2689         /* Get account and set it into mail_operation */
2690         if (tny_list_get_length (header_list) >= 1) {
2691                 TnyIterator *iterator = tny_list_create_iterator (header_list);
2692                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
2693                 if (header) {
2694                         TnyFolder *folder = tny_header_get_folder (header);
2695                         if (folder) {           
2696                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2697                                 g_object_unref (folder);
2698                         }
2699                         g_object_unref (header);
2700                 }
2701                 g_object_unref (iterator);
2702         }
2703
2704         msg_list_size = compute_message_list_size (header_list, 0);
2705
2706         modest_mail_operation_notify_start (self);
2707         iter = tny_list_create_iterator (header_list);
2708         if (!tny_iterator_is_done (iter)) {
2709                 /* notify about the start of the operation */
2710                 ModestMailOperationState *state;
2711                 state = modest_mail_operation_clone_state (self);
2712                 state->done = 0;
2713                 state->total = 0;
2714                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2715                                0, state, NULL);
2716
2717                 GetMsgInfo *msg_info = NULL;
2718                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2719                 TnyFolder *folder = tny_header_get_folder (header);
2720
2721                 /* Create the message info */
2722                 msg_info = g_slice_new0 (GetMsgInfo);
2723                 msg_info->mail_op = g_object_ref (self);
2724                 msg_info->header = g_object_ref (header);
2725                 msg_info->more_msgs = g_object_ref (iter);
2726                 msg_info->user_callback = user_callback;
2727                 msg_info->user_data = user_data;
2728                 msg_info->destroy_notify = notify;
2729                 msg_info->last_total_bytes = 0;
2730                 msg_info->sum_total_bytes = 0;
2731                 msg_info->total_bytes = msg_list_size;
2732
2733                 /* The callback will call it per each header */
2734                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2735
2736                 /* Free and go on */
2737                 g_object_unref (header);
2738                 g_object_unref (folder);
2739                 g_slice_free (ModestMailOperationState, state);
2740         }
2741         g_object_unref (iter);
2742 }
2743
2744
2745 static void
2746 remove_msgs_async_cb (TnyFolder *folder, 
2747                       gboolean canceled, 
2748                       GError *err, 
2749                       gpointer user_data)
2750 {
2751         gboolean expunge, leave_on_server;
2752         const gchar *account_name;
2753         TnyAccount *account;
2754         ModestProtocolType account_proto = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
2755         ModestMailOperation *self;
2756         ModestMailOperationPrivate *priv;
2757         ModestProtocolRegistry *protocol_registry;
2758
2759         self = (ModestMailOperation *) user_data;
2760         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2761         protocol_registry = modest_runtime_get_protocol_registry ();
2762
2763         if (canceled || err) {
2764                 /* If canceled by the user, ignore the error given by Tinymail */
2765                 if (canceled) {
2766                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2767                 } else if (err) {
2768                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2769                         priv->error = g_error_copy ((const GError *) err);
2770                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2771                 }
2772                 /* Exit */
2773                 modest_mail_operation_notify_end (self);
2774                 g_object_unref (self);
2775                 return;
2776         }
2777
2778         account = tny_folder_get_account (folder);
2779         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2780         leave_on_server =
2781                 modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2782                                                         account_name);  
2783         account_proto = modest_tny_account_get_protocol_type (account);
2784         g_object_unref (account);
2785
2786         if ((modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_proto) && 
2787              !leave_on_server) ||
2788             !modest_tny_folder_is_remote_folder (folder))
2789                 expunge = TRUE;
2790         else
2791                 expunge = FALSE;
2792
2793         /* Sync folder */
2794         tny_folder_sync_async(folder, expunge, sync_folder_finish_callback, 
2795                               NULL, self);
2796 }
2797
2798 void 
2799 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2800                                    TnyList *headers,
2801                                    gboolean remove_to_trash /*ignored*/)
2802 {
2803         TnyFolder *folder = NULL;
2804         ModestMailOperationPrivate *priv;
2805         TnyIterator *iter = NULL;
2806         TnyHeader *header = NULL;
2807         TnyList *remove_headers = NULL;
2808         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2809
2810         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2811         g_return_if_fail (TNY_IS_LIST (headers));
2812
2813         if (remove_to_trash)
2814                 g_warning ("remove to trash is not implemented");
2815
2816         if (tny_list_get_length(headers) == 0) {
2817                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
2818                 goto cleanup; /* nothing to do */
2819         }
2820         
2821         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2822         remove_headers = g_object_ref(headers);
2823
2824         /* Get folder from first header and sync it */
2825         iter = tny_list_create_iterator (headers);      
2826         header = TNY_HEADER (tny_iterator_get_current (iter));
2827
2828         folder = tny_header_get_folder (header);        
2829         if (!TNY_IS_FOLDER(folder)) {
2830                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
2831                 goto cleanup;
2832         }
2833
2834         /* Don't remove messages that are being sent */
2835         if (modest_tny_folder_is_local_folder (folder)) {
2836                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2837         }
2838         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2839                 TnyTransportAccount *traccount = NULL;
2840                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2841                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2842                 if (traccount) {
2843                         ModestTnySendQueueStatus status;
2844                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount, TRUE);
2845
2846                         if (TNY_IS_SEND_QUEUE (send_queue)) {
2847                                 TnyIterator *iter = tny_list_create_iterator(headers);
2848                                 g_object_unref(remove_headers);
2849                                 remove_headers = TNY_LIST(tny_simple_list_new());
2850                                 while (!tny_iterator_is_done(iter)) {
2851                                         char *msg_id;
2852                                         TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2853                                         msg_id = modest_tny_send_queue_get_msg_id (hdr);
2854                                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2855                                         if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2856                                                 tny_list_append(remove_headers, G_OBJECT(hdr));
2857                                         }
2858                                         g_object_unref(hdr);
2859                                         g_free(msg_id);
2860                                         tny_iterator_next(iter);
2861                                 }
2862                                 g_object_unref(iter);
2863                         }
2864                         g_object_unref(traccount);
2865                 }
2866         }
2867
2868         /* Get account and set it into mail_operation */
2869         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2870         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2871         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2872
2873         /* remove message from folder */
2874         modest_mail_operation_notify_start (self);
2875         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
2876                                       NULL, g_object_ref (self));
2877
2878 cleanup:
2879         if (remove_headers)
2880                 g_object_unref (remove_headers);
2881         if (header)
2882                 g_object_unref (header);
2883         if (iter)
2884                 g_object_unref (iter);
2885         if (folder)
2886                 g_object_unref (folder);
2887 }
2888
2889 static void
2890 notify_progress_of_multiple_messages (ModestMailOperation *self,
2891                                       TnyStatus *status,
2892                                       gint *last_total_bytes,
2893                                       gint *sum_total_bytes,
2894                                       gint total_bytes, 
2895                                       gboolean increment_done)
2896 {
2897         ModestMailOperationPrivate *priv;
2898         ModestMailOperationState *state;
2899         gboolean is_num_bytes = FALSE;
2900
2901         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2902
2903         /* We know that tinymail sends us information about
2904          *  transferred bytes with this particular message
2905          */
2906         if (status->message)
2907                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2908
2909         state = modest_mail_operation_clone_state (self);
2910         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2911                 /* We know that we're in a different message when the
2912                    total number of bytes to transfer is different. Of
2913                    course it could fail if we're transferring messages
2914                    of the same size, but this is a workarround */
2915                 if (status->of_total != *last_total_bytes) {
2916                         /* We need to increment the done when there is
2917                            no information about each individual
2918                            message, we need to do this in message
2919                            transfers, and we don't do it for getting
2920                            messages */
2921                         if (increment_done)
2922                                 priv->done++;
2923                         *sum_total_bytes += *last_total_bytes;
2924                         *last_total_bytes = status->of_total;
2925                 }
2926                 state->bytes_done += status->position + *sum_total_bytes;
2927                 state->bytes_total = total_bytes;
2928
2929                 /* Notify the status change. Only notify about changes
2930                    referred to bytes */
2931                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2932                                0, state, NULL);
2933         }
2934
2935         g_slice_free (ModestMailOperationState, state);
2936 }
2937
2938 static void
2939 transfer_msgs_status_cb (GObject *obj,
2940                          TnyStatus *status,  
2941                          gpointer user_data)
2942 {
2943         XFerMsgsAsyncHelper *helper;
2944
2945         g_return_if_fail (status != NULL);
2946
2947         /* Show only the status information we want */
2948         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
2949                 return;
2950
2951         helper = (XFerMsgsAsyncHelper *) user_data;
2952         g_return_if_fail (helper != NULL);       
2953
2954         /* Notify progress */
2955         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2956                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2957 }
2958
2959 static void
2960 transfer_msgs_sync_folder_cb (TnyFolder *self, 
2961                               gboolean cancelled, 
2962                               GError *err, 
2963                               gpointer user_data)
2964 {
2965         XFerMsgsAsyncHelper *helper;
2966         /* We don't care here about the results of the
2967            synchronization */
2968         helper = (XFerMsgsAsyncHelper *) user_data;
2969
2970         /* Notify about operation end */
2971         modest_mail_operation_notify_end (helper->mail_op);
2972
2973         /* If user defined callback function was defined, call it */
2974         if (helper->user_callback)
2975                 helper->user_callback (helper->mail_op, helper->user_data);
2976         
2977         /* Free */
2978         if (helper->more_msgs)
2979                 g_object_unref (helper->more_msgs);
2980         if (helper->headers)
2981                 g_object_unref (helper->headers);
2982         if (helper->dest_folder)
2983                 g_object_unref (helper->dest_folder);
2984         if (helper->mail_op)
2985                 g_object_unref (helper->mail_op);
2986         g_slice_free (XFerMsgsAsyncHelper, helper);
2987 }
2988
2989 static void
2990 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2991 {
2992         XFerMsgsAsyncHelper *helper;
2993         ModestMailOperation *self;
2994         ModestMailOperationPrivate *priv;
2995         gboolean finished = TRUE;
2996
2997         helper = (XFerMsgsAsyncHelper *) user_data;
2998         self = helper->mail_op;
2999
3000         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3001
3002         if (cancelled) {
3003                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3004         } else if (err) {
3005                 priv->error = g_error_copy (err);
3006                 priv->done = 0;
3007                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
3008         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
3009                 if (helper->more_msgs) {
3010                         /* We'll transfer the next message in the list */
3011                         tny_iterator_next (helper->more_msgs);
3012                         if (!tny_iterator_is_done (helper->more_msgs)) {
3013                                 GObject *next_header;
3014                                 g_object_unref (helper->headers);
3015                                 helper->headers = tny_simple_list_new ();
3016                                 next_header = tny_iterator_get_current (helper->more_msgs);
3017                                 tny_list_append (helper->headers, next_header);
3018                                 g_object_unref (next_header);
3019                                 finished = FALSE;
3020                         }
3021                 }
3022                 if (finished) {
3023                         priv->done = 1;
3024                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3025                 }
3026         }
3027
3028         if (finished) {
3029                 /* Synchronize the source folder contents. This should
3030                    be done by tinymail but the camel_folder_sync it's
3031                    actually disabled in transfer_msgs_thread_clean
3032                    because it's supposed to cause hangs */
3033                 tny_folder_sync_async (folder, helper->delete, 
3034                                        transfer_msgs_sync_folder_cb, 
3035                                        NULL, helper);
3036         } else {
3037                 /* Transfer more messages */
3038                 tny_folder_transfer_msgs_async (folder,
3039                                                 helper->headers,
3040                                                 helper->dest_folder,
3041                                                 helper->delete,
3042                                                 transfer_msgs_cb,
3043                                                 transfer_msgs_status_cb,
3044                                                 helper);
3045         }
3046 }
3047
3048 /* Computes the size of the messages the headers in the list belongs
3049    to. If num_elements is different from 0 then it only takes into
3050    account the first num_elements for the calculation */
3051 static guint
3052 compute_message_list_size (TnyList *headers, 
3053                            guint num_elements)
3054 {
3055         TnyIterator *iter;
3056         guint size = 0, element = 0;
3057
3058         /* If num_elements is not valid then take all into account */
3059         if ((num_elements <= 0) || (num_elements > tny_list_get_length (headers)))
3060                 num_elements = tny_list_get_length (headers);
3061
3062         iter = tny_list_create_iterator (headers);
3063         while (!tny_iterator_is_done (iter) && element < num_elements) {
3064                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3065                 size += tny_header_get_message_size (header);
3066                 g_object_unref (header);
3067                 tny_iterator_next (iter);
3068                 element++;
3069         }
3070         g_object_unref (iter);
3071
3072         return size;
3073 }
3074
3075 void
3076 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
3077                                  TnyList *headers, 
3078                                  TnyFolder *folder, 
3079                                  gboolean delete_original,
3080                                  XferMsgsAsyncUserCallback user_callback,
3081                                  gpointer user_data)
3082 {
3083         ModestMailOperationPrivate *priv = NULL;
3084         TnyIterator *iter = NULL;
3085         TnyFolder *src_folder = NULL;
3086         XFerMsgsAsyncHelper *helper = NULL;
3087         TnyHeader *header = NULL;
3088         ModestTnyFolderRules rules = 0;
3089         TnyAccount *dst_account = NULL;
3090         gboolean leave_on_server;
3091         ModestMailOperationState *state;
3092         ModestProtocolRegistry *protocol_registry;
3093         ModestProtocolType account_protocol;
3094
3095         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
3096         g_return_if_fail (headers && TNY_IS_LIST (headers));
3097         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
3098
3099         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3100         protocol_registry = modest_runtime_get_protocol_registry ();
3101
3102         priv->total = tny_list_get_length (headers);
3103         priv->done = 0;
3104         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3105         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3106
3107         /* Apply folder rules */
3108         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3109         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3110                 /* Set status failed and set an error */
3111                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3112                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3113                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
3114                              _CS("ckct_ib_unable_to_paste_here"));
3115                 /* Notify the queue */
3116                 modest_mail_operation_notify_end (self);
3117                 return;
3118         }
3119                 
3120         /* Get source folder */
3121         iter = tny_list_create_iterator (headers);
3122         header = TNY_HEADER (tny_iterator_get_current (iter));
3123         if (header) {
3124                 src_folder = tny_header_get_folder (header);
3125                 g_object_unref (header);
3126         }
3127         g_object_unref (iter);
3128
3129         if (src_folder == NULL) {
3130                 /* Notify the queue */
3131                 modest_mail_operation_notify_end (self);
3132
3133                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
3134                 return;
3135         }
3136
3137         
3138         /* Check folder source and destination */
3139         if (src_folder == folder) {
3140                 /* Set status failed and set an error */
3141                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3142                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3143                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
3144                              _("mail_in_ui_folder_copy_target_error"));
3145                 
3146                 /* Notify the queue */
3147                 modest_mail_operation_notify_end (self);
3148                 
3149                 /* Free */
3150                 g_object_unref (src_folder);            
3151                 return;
3152         }
3153
3154         /* Create the helper */
3155         helper = g_slice_new0 (XFerMsgsAsyncHelper);
3156         helper->mail_op = g_object_ref(self);
3157         helper->dest_folder = g_object_ref(folder);
3158         helper->user_callback = user_callback;
3159         helper->user_data = user_data;
3160         helper->last_total_bytes = 0;
3161         helper->sum_total_bytes = 0;
3162         helper->total_bytes = compute_message_list_size (headers, 0);
3163
3164         /* Get account and set it into mail_operation */
3165         priv->account = modest_tny_folder_get_account (src_folder);
3166         dst_account = modest_tny_folder_get_account (folder);
3167
3168         if (priv->account == dst_account) {
3169                 /* Transfer all messages at once using the fast
3170                  * method. Note that depending on the server this
3171                  * might not be that fast, and might not be
3172                  * user-cancellable either */
3173                 helper->headers = g_object_ref (headers);
3174                 helper->more_msgs = NULL;
3175         } else {
3176                 /* Transfer messages one by one so the user can cancel
3177                  * the operation */
3178                 GObject *hdr;
3179                 helper->headers = tny_simple_list_new ();
3180                 helper->more_msgs = tny_list_create_iterator (headers);
3181                 hdr = tny_iterator_get_current (helper->more_msgs);
3182                 tny_list_append (helper->headers, hdr);
3183                 g_object_unref (hdr);
3184         }
3185
3186         /* If leave_on_server is set to TRUE then don't use
3187            delete_original, we always pass FALSE. This is because
3188            otherwise tinymail will try to sync the source folder and
3189            this could cause an error if we're offline while
3190            transferring an already downloaded message from a POP
3191            account */
3192         account_protocol = modest_tny_account_get_protocol_type (priv->account);
3193         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_protocol)) {
3194                 const gchar *account_name;
3195
3196                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
3197                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3198                                                                           account_name);
3199         } else {
3200                 leave_on_server = FALSE;
3201         }
3202
3203         /* Do not delete messages if leave on server is TRUE */
3204         helper->delete = (leave_on_server) ? FALSE : delete_original;
3205
3206         modest_mail_operation_notify_start (self);
3207
3208         /* Start notifying progress */
3209         state = modest_mail_operation_clone_state (self);
3210         state->done = 0;
3211         state->total = 0;
3212         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3213         g_slice_free (ModestMailOperationState, state);
3214
3215         tny_folder_transfer_msgs_async (src_folder, 
3216                                         helper->headers, 
3217                                         folder, 
3218                                         helper->delete, 
3219                                         transfer_msgs_cb, 
3220                                         transfer_msgs_status_cb,
3221                                         helper);
3222         g_object_unref (src_folder);
3223         g_object_unref (dst_account);
3224 }
3225
3226
3227 static void
3228 on_refresh_folder (TnyFolder   *folder, 
3229                    gboolean     cancelled, 
3230                    GError     *error,
3231                    gpointer     user_data)
3232 {
3233         RefreshAsyncHelper *helper = NULL;
3234         ModestMailOperation *self = NULL;
3235         ModestMailOperationPrivate *priv = NULL;
3236
3237         helper = (RefreshAsyncHelper *) user_data;
3238         self = helper->mail_op;
3239         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3240
3241         g_return_if_fail(priv!=NULL);
3242
3243         if (error) {
3244                 priv->error = g_error_copy (error);
3245                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3246                 goto out;
3247         }
3248
3249         if (cancelled) {
3250                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3251                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3252                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3253                              _("Error trying to refresh the contents of %s"),
3254                              tny_folder_get_name (folder));
3255                 goto out;
3256         }
3257
3258         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3259  out:
3260
3261         /* Call user defined callback, if it exists */
3262         if (helper->user_callback) {
3263
3264                 /* This is not a GDK lock because we are a Tinymail callback and
3265                  * Tinymail already acquires the Gdk lock */
3266                 helper->user_callback (self, folder, helper->user_data);
3267         }
3268
3269         /* Free */
3270         g_slice_free (RefreshAsyncHelper, helper);
3271
3272         /* Notify about operation end */
3273         modest_mail_operation_notify_end (self);
3274         g_object_unref(self);
3275 }
3276
3277 static void
3278 on_refresh_folder_status_update (GObject *obj,
3279                                  TnyStatus *status,
3280                                  gpointer user_data)
3281 {
3282         RefreshAsyncHelper *helper = NULL;
3283         ModestMailOperation *self = NULL;
3284         ModestMailOperationPrivate *priv = NULL;
3285         ModestMailOperationState *state;
3286
3287         g_return_if_fail (user_data != NULL);
3288         g_return_if_fail (status != NULL);
3289
3290         /* Show only the status information we want */
3291         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
3292                 return;
3293
3294         helper = (RefreshAsyncHelper *) user_data;
3295         self = helper->mail_op;
3296         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3297
3298         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3299
3300         priv->done = status->position;
3301         priv->total = status->of_total;
3302
3303         state = modest_mail_operation_clone_state (self);
3304
3305         /* This is not a GDK lock because we are a Tinymail callback and
3306          * Tinymail already acquires the Gdk lock */
3307         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3308
3309         g_slice_free (ModestMailOperationState, state);
3310 }
3311
3312 void 
3313 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3314                                        TnyFolder *folder,
3315                                        RefreshAsyncUserCallback user_callback,
3316                                        gpointer user_data)
3317 {
3318         ModestMailOperationPrivate *priv = NULL;
3319         RefreshAsyncHelper *helper = NULL;
3320
3321         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3322
3323         /* Check memory low */
3324         if (_check_memory_low (self)) {
3325                 if (user_callback)
3326                         user_callback (self, folder, user_data);
3327                 /* Notify about operation end */
3328                 modest_mail_operation_notify_end (self);
3329                 return;
3330         }
3331
3332         /* Get account and set it into mail_operation */
3333         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3334         priv->account = modest_tny_folder_get_account  (folder);
3335         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3336
3337         /* Create the helper */
3338         helper = g_slice_new0 (RefreshAsyncHelper);
3339         helper->mail_op = g_object_ref(self);
3340         helper->user_callback = user_callback;
3341         helper->user_data = user_data;
3342
3343         modest_mail_operation_notify_start (self);
3344         
3345         /* notify that the operation was started */
3346         ModestMailOperationState *state;
3347         state = modest_mail_operation_clone_state (self);
3348         state->done = 0;
3349         state->total = 0;
3350         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3351                         0, state, NULL);
3352         g_slice_free (ModestMailOperationState, state);
3353         
3354         tny_folder_refresh_async (folder,
3355                                   on_refresh_folder,
3356                                   on_refresh_folder_status_update,
3357                                   helper);
3358 }
3359
3360 static void
3361 run_queue_notify_and_destroy (RunQueueHelper *helper,
3362                               ModestMailOperationStatus status)
3363 {
3364         ModestMailOperationPrivate *priv;
3365
3366         /* Disconnect */
3367         if (helper->error_handler &&
3368             g_signal_handler_is_connected (helper->queue, helper->error_handler))
3369                 g_signal_handler_disconnect (helper->queue, helper->error_handler);
3370         if (helper->start_handler &&
3371             g_signal_handler_is_connected (helper->queue, helper->start_handler))
3372                 g_signal_handler_disconnect (helper->queue, helper->start_handler);
3373         if (helper->stop_handler &&
3374             g_signal_handler_is_connected (helper->queue, helper->stop_handler))
3375                 g_signal_handler_disconnect (helper->queue, helper->stop_handler);
3376
3377         /* Set status */
3378         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
3379         priv->status = status;
3380
3381         /* Notify end */
3382         modest_mail_operation_notify_end (helper->self);
3383
3384         /* Free data */
3385         g_object_unref (helper->queue);
3386         g_object_unref (helper->self);
3387         g_slice_free (RunQueueHelper, helper);
3388 }
3389
3390 static void
3391 run_queue_stop (ModestTnySendQueue *queue,
3392                 gpointer user_data)
3393 {
3394         RunQueueHelper *helper;
3395
3396         g_debug ("%s sending queue stopped", __FUNCTION__);
3397
3398         helper = (RunQueueHelper *) user_data;
3399         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
3400 }
3401
3402 void
3403 modest_mail_operation_run_queue (ModestMailOperation *self,
3404                                  ModestTnySendQueue *queue)
3405 {
3406         ModestMailOperationPrivate *priv;
3407         RunQueueHelper *helper;
3408
3409         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3410         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3411         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3412
3413         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3414         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3415         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3416
3417         /* Create the helper */
3418         helper = g_slice_new0 (RunQueueHelper);
3419         helper->queue = g_object_ref (queue);
3420         helper->self = g_object_ref (self);
3421         helper->stop_handler = g_signal_connect (queue, "queue-stop", 
3422                                                  G_CALLBACK (run_queue_stop), 
3423                                                  helper);
3424
3425         /* Notify operation has started */
3426         modest_mail_operation_notify_start (self);
3427         g_debug ("%s, run queue started", __FUNCTION__);
3428 }
3429
3430 static void
3431 queue_wakeup_callback (ModestTnySendQueue *queue,
3432                        gboolean cancelled,
3433                        GError *error,
3434                        gpointer userdata)
3435 {
3436         ModestMailOperation *mail_op;
3437         ModestMailOperationPrivate *priv;
3438
3439         mail_op = (ModestMailOperation *) userdata;
3440         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3441
3442         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3443         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (queue));
3444
3445         /* Notify end */
3446         modest_mail_operation_notify_end (mail_op);
3447         g_object_unref (mail_op);
3448 }
3449
3450 void
3451 modest_mail_operation_queue_wakeup (ModestMailOperation *self,
3452                                     ModestTnySendQueue *queue)
3453 {
3454         ModestMailOperationPrivate *priv;
3455
3456         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3457         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3458         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3459
3460         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3461         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3462         priv->op_type = MODEST_MAIL_OPERATION_TYPE_QUEUE_WAKEUP;
3463
3464         g_object_ref (self);
3465
3466         modest_tny_send_queue_wakeup (queue, queue_wakeup_callback, self);
3467         modest_mail_operation_notify_start (self);
3468 }
3469
3470 static void
3471 shutdown_callback (ModestTnyAccountStore *account_store, gpointer userdata)
3472 {
3473         ModestMailOperation *self = (ModestMailOperation *) userdata;
3474         ModestMailOperationPrivate *priv;
3475
3476         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3477         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3478         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3479
3480         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3481
3482         modest_mail_operation_notify_end (self);
3483         g_object_unref (self);
3484 }
3485
3486 void
3487 modest_mail_operation_shutdown (ModestMailOperation *self, ModestTnyAccountStore *account_store)
3488 {
3489         ModestMailOperationPrivate *priv;
3490
3491         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3492         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3493         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3494
3495         modest_mail_operation_queue_set_running_shutdown (modest_runtime_get_mail_operation_queue ());
3496
3497         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3498         priv->account = NULL;
3499         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SHUTDOWN;
3500
3501         modest_mail_operation_notify_start (self);
3502         g_object_ref (self);
3503         modest_tny_account_store_shutdown (account_store, shutdown_callback, self);
3504 }
3505
3506 static void
3507 sync_folder_finish_callback (TnyFolder *self, 
3508                              gboolean cancelled, 
3509                              GError *err, 
3510                              gpointer user_data)
3511
3512 {
3513         ModestMailOperation *mail_op;
3514         ModestMailOperationPrivate *priv;
3515
3516         mail_op = (ModestMailOperation *) user_data;
3517         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3518
3519         /* If canceled by the user, ignore the error given by Tinymail */
3520         if (cancelled) {
3521                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3522         } else if (err) {
3523                 /* If the operation was a sync then the status is
3524                    failed, but if it's part of another operation then
3525                    just set it as finished with errors */
3526                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
3527                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3528                 else
3529                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3530                 priv->error = g_error_copy ((const GError *) err);
3531                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3532         } else {
3533                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3534         }
3535
3536         modest_mail_operation_notify_end (mail_op);
3537         g_object_unref (mail_op);
3538 }
3539
3540 void
3541 modest_mail_operation_sync_folder (ModestMailOperation *self,
3542                                    TnyFolder *folder, gboolean expunge)
3543 {
3544         ModestMailOperationPrivate *priv;
3545
3546         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3547         g_return_if_fail (TNY_IS_FOLDER (folder));
3548         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3549
3550         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3551         priv->account = modest_tny_folder_get_account (folder);
3552         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3553
3554         modest_mail_operation_notify_start (self);
3555         g_object_ref (self);
3556         tny_folder_sync_async (folder, expunge, 
3557                                (TnyFolderCallback) sync_folder_finish_callback, 
3558                                NULL, self);
3559 }
3560
3561 static void
3562 modest_mail_operation_notify_start (ModestMailOperation *self)
3563 {
3564         ModestMailOperationPrivate *priv = NULL;
3565
3566         g_return_if_fail (self);
3567
3568         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3569
3570         /* Ensure that all the fields are filled correctly */
3571         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3572
3573         /* Notify the observers about the mail operation. We do not
3574            wrapp this emission because we assume that this function is
3575            always called from within the main lock */
3576         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3577 }
3578
3579 /**
3580  *
3581  * It's used by the mail operation queue to notify the observers
3582  * attached to that signal that the operation finished. We need to use
3583  * that because tinymail does not give us the progress of a given
3584  * operation when it finishes (it directly calls the operation
3585  * callback).
3586  */
3587 static void
3588 modest_mail_operation_notify_end (ModestMailOperation *self)
3589 {
3590         ModestMailOperationPrivate *priv = NULL;
3591
3592         g_return_if_fail (self);
3593
3594         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3595
3596         /* Notify the observers about the mail operation end. We do
3597            not wrapp this emission because we assume that this
3598            function is always called from within the main lock */
3599         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3600
3601         /* Remove the error user data */
3602         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3603                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3604 }
3605
3606 TnyAccount *
3607 modest_mail_operation_get_account (ModestMailOperation *self)
3608 {
3609         ModestMailOperationPrivate *priv = NULL;
3610
3611         g_return_val_if_fail (self, NULL);
3612
3613         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3614
3615         return (priv->account) ? g_object_ref (priv->account) : NULL;
3616 }
3617
3618 void
3619 modest_mail_operation_noop (ModestMailOperation *self)
3620 {
3621         ModestMailOperationPrivate *priv = NULL;
3622
3623         g_return_if_fail (self);
3624
3625         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3626         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3627         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3628         priv->done = 0;
3629         priv->total = 0;
3630
3631         /* This mail operation does nothing actually */
3632         modest_mail_operation_notify_start (self);
3633         modest_mail_operation_notify_end (self);
3634 }
3635
3636
3637 gchar*
3638 modest_mail_operation_to_string (ModestMailOperation *self)
3639 {
3640         const gchar *type, *status, *account_id;
3641         ModestMailOperationPrivate *priv = NULL;
3642         
3643         g_return_val_if_fail (self, NULL);
3644
3645         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3646
3647         /* new operations don't have anything interesting */
3648         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3649                 return g_strdup_printf ("%p <new operation>", self);
3650         
3651         switch (priv->op_type) {
3652         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3653         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3654         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3655         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3656         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3657         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
3658         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
3659         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3660         default: type = "UNEXPECTED"; break;
3661         }
3662
3663         switch (priv->status) {
3664         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3665         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3666         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3667         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3668         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3669         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3670         default:                                                status= "UNEXPECTED"; break;
3671         } 
3672
3673         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3674
3675         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3676                                 priv->done, priv->total,
3677                                 priv->error && priv->error->message ? priv->error->message : "");
3678 }
3679
3680 /* 
3681  * Once the mail operations were objects this will be no longer
3682  * needed. I don't like it, but we need it for the moment
3683  */
3684 static gboolean
3685 _check_memory_low (ModestMailOperation *mail_op)
3686 {
3687         if (modest_platform_check_memory_low (NULL, FALSE)) {
3688                 ModestMailOperationPrivate *priv;
3689
3690                 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3691                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3692                 g_set_error (&(priv->error),
3693                              MODEST_MAIL_OPERATION_ERROR,
3694                              MODEST_MAIL_OPERATION_ERROR_LOW_MEMORY,
3695                              "Not enough memory to complete the operation");
3696                 return TRUE;
3697         } else {
3698                 return FALSE;
3699         }
3700 }