Fixes leak 22/26
[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                 tny_folder_remove_observer (inbox, info->inbox_observer);
1563                 g_object_unref (info->inbox_observer);
1564                 info->inbox_observer = NULL;
1565
1566                 /* Notify the user about the error and then exit */
1567                 update_account_notify_user_and_free (info, NULL);
1568                 return;
1569         }
1570
1571         if (!inbox) {
1572                 /* Try to send anyway */
1573                 goto send_mail;
1574         }
1575
1576         /* Set the last updated as the current time */
1577         modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1578
1579         /* Get the message max size */
1580         max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1581                                          MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1582         if (max_size == 0)
1583                 max_size = G_MAXINT;
1584         else
1585                 max_size = max_size * KB;
1586
1587         /* Create the new headers array. We need it to sort the
1588            new headers by date */
1589         new_headers_array = g_ptr_array_new ();
1590         if (info->inbox_observer) {
1591                 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1592                 while (!tny_iterator_is_done (new_headers_iter)) {
1593                         TnyHeader *header = NULL;
1594
1595                         header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1596                         /* Apply per-message size limits */
1597                         if (tny_header_get_message_size (header) < max_size)
1598                                 g_ptr_array_add (new_headers_array, g_object_ref (header));
1599                         
1600                         g_object_unref (header);
1601                         tny_iterator_next (new_headers_iter);
1602                 }
1603                 g_object_unref (new_headers_iter);
1604
1605                 tny_folder_remove_observer (inbox, info->inbox_observer);
1606                 g_object_unref (info->inbox_observer);
1607                 info->inbox_observer = NULL;
1608         }
1609
1610         if (new_headers_array->len == 0) {
1611                 g_ptr_array_free (new_headers_array, FALSE);
1612                 goto send_mail;
1613         }
1614
1615         /* Get per-account message amount retrieval limit */
1616         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1617         if (retrieve_limit == 0)
1618                 retrieve_limit = G_MAXINT;
1619         
1620         /* Get per-account retrieval type */
1621         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1622         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1623
1624         /* Order by date */
1625         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1626         
1627         /* Ask the users if they want to retrieve all the messages
1628            even though the limit was exceeded */
1629         ignore_limit = FALSE;
1630         if (new_headers_array->len > retrieve_limit) {
1631                 /* Ask the user if a callback has been specified and
1632                    if the mail operation has a source (this means that
1633                    was invoked by the user and not automatically by a
1634                    D-Bus method) */
1635                 if (info->retrieve_all_cb && priv->source)
1636                         ignore_limit = info->retrieve_all_cb (priv->source,
1637                                                               new_headers_array->len,
1638                                                               retrieve_limit);
1639         }
1640
1641         /* Copy the headers to a list and free the array */
1642         new_headers = tny_simple_list_new ();
1643         for (i=0; i < new_headers_array->len; i++) {
1644                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1645                 tny_list_append (new_headers, G_OBJECT (header));
1646         }
1647         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1648         g_ptr_array_free (new_headers_array, FALSE);
1649         
1650         if (!headers_only && (tny_list_get_length (new_headers) > 0)) {
1651                 gint msg_num = 0;
1652                 TnyIterator *iter;
1653                 GetMsgInfo *msg_info;
1654
1655                 priv->done = 0;
1656                 if (ignore_limit)
1657                         priv->total = tny_list_get_length (new_headers);
1658                 else
1659                         priv->total = MIN (tny_list_get_length (new_headers), retrieve_limit);
1660
1661                 iter = tny_list_create_iterator (new_headers);
1662
1663                 /* Create the message info */
1664                 msg_info = g_slice_new0 (GetMsgInfo);
1665                 msg_info->mail_op = g_object_ref (info->mail_op);
1666                 msg_info->total_bytes = compute_message_list_size (new_headers, priv->total);
1667                 msg_info->more_msgs = g_object_ref (iter);
1668                 msg_info->user_data = info;
1669
1670                 while ((msg_num < priv->total ) && !tny_iterator_is_done (iter)) {              
1671                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1672                         TnyFolder *folder = tny_header_get_folder (header);
1673
1674                         /* Get message in an async way */
1675                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb, 
1676                                                   NULL, msg_info);
1677
1678                         g_object_unref (folder);
1679                         
1680                         msg_num++;
1681                         tny_iterator_next (iter);
1682                 }
1683                 g_object_unref (iter);
1684
1685                 /* The mail operation will finish when the last
1686                    message is retrieved */
1687                 return;
1688         }
1689  send_mail:
1690         /* If we don't have to retrieve the new messages then
1691            simply send mail */
1692         update_account_send_mail (info);
1693         
1694         /* Check if the operation was a success */
1695         if (!priv->error)
1696                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1697         
1698         /* Call the user callback and free */
1699         update_account_notify_user_and_free (info, new_headers);
1700 }
1701
1702 static void
1703 inbox_refresh_status_update (GObject *obj,
1704                              TnyStatus *status,
1705                              gpointer user_data)
1706 {
1707         UpdateAccountInfo *info = NULL;
1708         ModestMailOperation *self = NULL;
1709         ModestMailOperationPrivate *priv = NULL;
1710         ModestMailOperationState *state;
1711
1712         g_return_if_fail (user_data != NULL);
1713         g_return_if_fail (status != NULL);
1714
1715         /* Show only the status information we want */
1716         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
1717                 return;
1718
1719         info = (UpdateAccountInfo *) user_data;
1720         self = info->mail_op;
1721         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1722
1723         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1724
1725         priv->done = status->position;
1726         priv->total = status->of_total;
1727
1728         state = modest_mail_operation_clone_state (self);
1729
1730         /* This is not a GDK lock because we are a Tinymail callback and
1731          * Tinymail already acquires the Gdk lock */
1732         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1733
1734         g_slice_free (ModestMailOperationState, state);
1735 }
1736
1737 static void 
1738 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1739                           gboolean canceled,
1740                           TnyList *list, 
1741                           GError *err, 
1742                           gpointer user_data)
1743 {
1744         UpdateAccountInfo *info;
1745         ModestMailOperationPrivate *priv;
1746     
1747         info = (UpdateAccountInfo *) user_data;
1748         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1749
1750         if (err || canceled) {
1751                 /* If the error was previosly set by another callback
1752                    don't set it again */
1753                 if (!priv->error) {
1754                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1755                         if (err)
1756                                 priv->error = g_error_copy (err);
1757                         else
1758                                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1759                                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1760                                              "canceled");
1761                 }
1762         } else { 
1763                 /* We're not getting INBOX children if we don't want to poke all */
1764                 TnyIterator *iter = tny_list_create_iterator (list);
1765                 while (!tny_iterator_is_done (iter)) {
1766                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1767
1768                         /* Add to the list of all folders */
1769                         tny_list_append (info->folders, (GObject *) folder);
1770                         
1771                         if (info->poke_all) {
1772                                 TnyList *folders = tny_simple_list_new ();
1773                                 /* Add pending call */
1774                                 info->pending_calls++;
1775                                 
1776                                 tny_folder_store_get_folders_async (folder, folders, NULL, FALSE,
1777                                                                     recurse_folders_async_cb, 
1778                                                                     NULL, info);
1779                                 g_object_unref (folders);
1780                         }
1781                         
1782                         g_object_unref (G_OBJECT (folder));
1783                         
1784                         tny_iterator_next (iter);           
1785                 }
1786                 g_object_unref (G_OBJECT (iter));
1787         }
1788
1789         /* Remove my own pending call */
1790         info->pending_calls--;
1791
1792         /* This means that we have all the folders */
1793         if (info->pending_calls == 0) {
1794                 TnyIterator *iter_all_folders;
1795                 TnyFolder *inbox = NULL;
1796
1797                 /* If there was any error do not continue */
1798                 if (priv->error) {
1799                         update_account_notify_user_and_free (info, NULL);
1800                         return;
1801                 }
1802
1803                 iter_all_folders = tny_list_create_iterator (info->folders);
1804
1805                 /* Do a poke status over all folders */
1806                 while (!tny_iterator_is_done (iter_all_folders) &&
1807                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1808                         TnyFolder *folder = NULL;
1809
1810                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1811
1812                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1813                                 /* Get a reference to the INBOX */
1814                                 inbox = g_object_ref (folder);
1815                         } else {
1816                                 /* Issue a poke status over the folder */
1817                                 if (info->poke_all)
1818                                         tny_folder_poke_status (folder);
1819                         }
1820
1821                         /* Free and go to next */
1822                         g_object_unref (folder);
1823                         tny_iterator_next (iter_all_folders);
1824                 }
1825                 g_object_unref (iter_all_folders);
1826
1827                 /* Refresh the INBOX */
1828                 if (inbox) {
1829                         /* Refresh the folder. Our observer receives
1830                          * the new emails during folder refreshes, so
1831                          * we can use observer->new_headers
1832                          */
1833                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1834                         tny_folder_add_observer (inbox, info->inbox_observer);
1835
1836                         /* Refresh the INBOX */
1837                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, inbox_refresh_status_update, info);
1838                         g_object_unref (inbox);
1839                 } else {
1840                         /* We could not perform the inbox refresh but
1841                            we'll try to send mails anyway */
1842                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1843                 }
1844         }
1845 }
1846
1847 void
1848 modest_mail_operation_update_account (ModestMailOperation *self,
1849                                       const gchar *account_name,
1850                                       gboolean poke_all,
1851                                       gboolean interactive,
1852                                       RetrieveAllCallback retrieve_all_cb,
1853                                       UpdateAccountCallback callback,
1854                                       gpointer user_data)
1855 {
1856         UpdateAccountInfo *info = NULL;
1857         ModestMailOperationPrivate *priv = NULL;
1858         ModestTnyAccountStore *account_store = NULL;
1859         TnyList *folders;
1860         ModestMailOperationState *state;
1861
1862         /* Init mail operation */
1863         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1864         priv->total = 0;
1865         priv->done  = 0;
1866         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1867         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
1868
1869         /* Get the store account */
1870         account_store = modest_runtime_get_account_store ();
1871         priv->account =
1872                 modest_tny_account_store_get_server_account (account_store,
1873                                                              account_name,
1874                                                              TNY_ACCOUNT_TYPE_STORE);
1875
1876         /* The above function could return NULL */
1877         if (!priv->account) {
1878                 /* Check if the operation was a success */
1879                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1880                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1881                              "no account");
1882                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1883
1884                 /* Call the user callback */
1885                 if (callback)
1886                         callback (self, NULL, user_data);
1887
1888                 /* Notify about operation end */
1889                 modest_mail_operation_notify_end (self);
1890
1891                 return;
1892         }
1893         
1894         /* We have once seen priv->account getting finalized during this code,
1895          * therefore adding a reference (bug #82296) */
1896         
1897         g_object_ref (priv->account);
1898
1899         /* Create the helper object */
1900         info = g_slice_new0 (UpdateAccountInfo);
1901         info->pending_calls = 1;
1902         info->folders = tny_simple_list_new ();
1903         info->mail_op = g_object_ref (self);
1904         info->poke_all = poke_all;
1905         info->interactive = interactive;
1906         info->account_name = g_strdup (account_name);
1907         info->callback = callback;
1908         info->user_data = user_data;
1909         info->retrieve_all_cb = retrieve_all_cb;
1910
1911         /* Set account busy */
1912         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1913         modest_mail_operation_notify_start (self);
1914
1915         /* notify about the start of the operation */ 
1916         state = modest_mail_operation_clone_state (self);
1917         state->done = 0;
1918         state->total = 0;
1919
1920         /* Start notifying progress */
1921         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1922         g_slice_free (ModestMailOperationState, state);
1923         
1924         /* Get all folders and continue in the callback */ 
1925         folders = tny_simple_list_new ();
1926         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
1927                                             folders, NULL, FALSE,
1928                                             recurse_folders_async_cb, 
1929                                             NULL, info);
1930         g_object_unref (folders);
1931         
1932         g_object_unref (priv->account);
1933         
1934 }
1935
1936 /*
1937  * Used to notify the queue from the main
1938  * loop. We call it inside an idle call to achieve that
1939  */
1940 static gboolean
1941 idle_notify_queue (gpointer data)
1942 {
1943         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1944
1945         gdk_threads_enter ();
1946         modest_mail_operation_notify_end (mail_op);
1947         gdk_threads_leave ();
1948         g_object_unref (mail_op);
1949
1950         return FALSE;
1951 }
1952
1953 static int
1954 compare_headers_by_date (gconstpointer a,
1955                          gconstpointer b)
1956 {
1957         TnyHeader **header1, **header2;
1958         time_t sent1, sent2;
1959
1960         header1 = (TnyHeader **) a;
1961         header2 = (TnyHeader **) b;
1962
1963         sent1 = tny_header_get_date_sent (*header1);
1964         sent2 = tny_header_get_date_sent (*header2);
1965
1966         /* We want the most recent ones (greater time_t) at the
1967            beginning */
1968         if (sent1 < sent2)
1969                 return 1;
1970         else
1971                 return -1;
1972 }
1973
1974
1975 /* ******************************************************************* */
1976 /* ************************** STORE  ACTIONS ************************* */
1977 /* ******************************************************************* */
1978
1979 typedef struct {
1980         ModestMailOperation *mail_op;
1981         CreateFolderUserCallback callback;
1982         gpointer user_data;
1983 } CreateFolderInfo;
1984
1985
1986 static void
1987 create_folder_cb (TnyFolderStore *parent_folder, 
1988                   gboolean canceled, 
1989                   TnyFolder *new_folder, 
1990                   GError *err, 
1991                   gpointer user_data)
1992 {
1993         ModestMailOperationPrivate *priv;
1994         CreateFolderInfo *info;
1995
1996         info = (CreateFolderInfo *) user_data;
1997         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1998
1999         if (canceled || err) {
2000                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2001                 if (err)
2002                         priv->error = g_error_copy (err);
2003                 else
2004                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2005                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2006                                      "canceled");               
2007         } else {
2008                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2009         }
2010
2011         /* The user will unref the new_folder */
2012         if (info->callback)
2013                 info->callback (info->mail_op, parent_folder, 
2014                                 new_folder, info->user_data);
2015         
2016         /* Notify about operation end */
2017         modest_mail_operation_notify_end (info->mail_op);
2018
2019         /* Frees */
2020         g_object_unref (info->mail_op);
2021         g_slice_free (CreateFolderInfo, info);
2022 }
2023
2024 void
2025 modest_mail_operation_create_folder (ModestMailOperation *self,
2026                                      TnyFolderStore *parent,
2027                                      const gchar *name,
2028                                      CreateFolderUserCallback callback,
2029                                      gpointer user_data)
2030 {
2031         ModestMailOperationPrivate *priv;
2032
2033         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2034         g_return_if_fail (name);
2035         
2036         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2037         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2038         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
2039                 g_object_ref (parent) : 
2040                 modest_tny_folder_get_account (TNY_FOLDER (parent));
2041
2042         /* Check for already existing folder */
2043         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
2044                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2045                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2046                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2047                              _CS("ckdg_ib_folder_already_exists"));
2048         }
2049
2050         /* Check parent */
2051         if (TNY_IS_FOLDER (parent)) {
2052                 /* Check folder rules */
2053                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2054                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2055                         /* Set status failed and set an error */
2056                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2057                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2058                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2059                                      _("mail_in_ui_folder_create_error"));
2060                 }
2061         }
2062
2063         if (!strcmp (name, " ") || strchr (name, '/')) {
2064                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2065                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2066                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2067                              _("mail_in_ui_folder_create_error"));
2068         }
2069
2070         if (!priv->error) {
2071                 CreateFolderInfo *info;
2072
2073                 info = g_slice_new0 (CreateFolderInfo);
2074                 info->mail_op = g_object_ref (self);
2075                 info->callback = callback;
2076                 info->user_data = user_data;
2077
2078                 modest_mail_operation_notify_start (self);
2079
2080                 /* Create the folder */
2081                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
2082                                                       NULL, info);
2083         } else {
2084                 /* Call the user callback anyway */
2085                 if (callback)
2086                         callback (self, parent, NULL, user_data);
2087                 /* Notify about operation end */
2088                 modest_mail_operation_notify_end (self);
2089         }
2090 }
2091
2092 void
2093 modest_mail_operation_remove_folder (ModestMailOperation *self,
2094                                      TnyFolder           *folder,
2095                                      gboolean             remove_to_trash)
2096 {
2097         ModestMailOperationPrivate *priv;
2098         ModestTnyFolderRules rules;
2099
2100         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2101         g_return_if_fail (TNY_IS_FOLDER (folder));
2102         
2103         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2104         
2105         /* Check folder rules */
2106         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2107         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
2108                 /* Set status failed and set an error */
2109                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2110                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2111                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2112                              _("mail_in_ui_folder_delete_error"));
2113                 goto end;
2114         }
2115
2116         /* Get the account */
2117         priv->account = modest_tny_folder_get_account (folder);
2118         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2119
2120         /* Delete folder or move to trash */
2121         if (remove_to_trash) {
2122                 TnyFolder *trash_folder = NULL;
2123                 trash_folder = modest_tny_account_get_special_folder (priv->account,
2124                                                                       TNY_FOLDER_TYPE_TRASH);
2125                 /* TODO: error_handling */
2126                 if (trash_folder) {
2127                         modest_mail_operation_notify_start (self);
2128                         modest_mail_operation_xfer_folder (self, folder,
2129                                                     TNY_FOLDER_STORE (trash_folder), 
2130                                                     TRUE, NULL, NULL);
2131                         g_object_unref (trash_folder);
2132                 } else {
2133                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
2134                 }
2135         } else {
2136                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
2137                 if (parent) {
2138                         modest_mail_operation_notify_start (self);
2139                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
2140                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
2141                         
2142                         if (!priv->error)
2143                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2144
2145                         g_object_unref (parent);
2146                 } else
2147                         g_warning ("%s: could not get parent folder", __FUNCTION__);
2148         }
2149
2150  end:
2151         /* Notify about operation end */
2152         modest_mail_operation_notify_end (self);
2153 }
2154
2155 static void
2156 transfer_folder_status_cb (GObject *obj,
2157                            TnyStatus *status,
2158                            gpointer user_data)
2159 {
2160         ModestMailOperation *self;
2161         ModestMailOperationPrivate *priv;
2162         ModestMailOperationState *state;
2163         XFerFolderAsyncHelper *helper;
2164
2165         g_return_if_fail (status != NULL);
2166
2167         /* Show only the status information we want */
2168         if (status->code != TNY_FOLDER_STATUS_CODE_COPY_FOLDER)
2169                 return;
2170
2171         helper = (XFerFolderAsyncHelper *) user_data;
2172         g_return_if_fail (helper != NULL);
2173
2174         self = helper->mail_op;
2175         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2176
2177         priv->done = status->position;
2178         priv->total = status->of_total;
2179
2180         state = modest_mail_operation_clone_state (self);
2181
2182         /* This is not a GDK lock because we are a Tinymail callback
2183          * which is already GDK locked by Tinymail */
2184
2185         /* no gdk_threads_enter (), CHECKED */
2186
2187         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
2188
2189         /* no gdk_threads_leave (), CHECKED */
2190
2191         g_slice_free (ModestMailOperationState, state);
2192 }
2193
2194 static void
2195 transfer_folder_cb (TnyFolder *folder, 
2196                     gboolean cancelled, 
2197                     TnyFolderStore *into, 
2198                     TnyFolder *new_folder, 
2199                     GError *err, 
2200                     gpointer user_data)
2201 {
2202         XFerFolderAsyncHelper *helper;
2203         ModestMailOperation *self = NULL;
2204         ModestMailOperationPrivate *priv = NULL;
2205
2206         helper = (XFerFolderAsyncHelper *) user_data;
2207         g_return_if_fail (helper != NULL);
2208
2209         self = helper->mail_op;
2210         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2211
2212         if (err) {
2213                 priv->error = g_error_copy (err);
2214                 priv->done = 0;
2215                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2216         } else if (cancelled) {
2217                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2218                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2219                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2220                              _("Transference of %s was cancelled."),
2221                              tny_folder_get_name (folder));
2222         } else {
2223                 priv->done = 1;
2224                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2225         }
2226
2227         /* Update state of new folder */
2228         if (new_folder) {
2229                 tny_folder_refresh_async (new_folder, NULL, NULL, NULL);
2230                 tny_folder_poke_status (new_folder);
2231         }
2232
2233         /* Notify about operation end */
2234         modest_mail_operation_notify_end (self);
2235
2236         /* If user defined callback function was defined, call it */
2237         if (helper->user_callback) {
2238
2239                 /* This is not a GDK lock because we are a Tinymail callback
2240                  * which is already GDK locked by Tinymail */
2241
2242                 /* no gdk_threads_enter (), CHECKED */
2243                 helper->user_callback (self, new_folder, helper->user_data);
2244                 /* no gdk_threads_leave () , CHECKED */
2245         }
2246
2247         /* Free */
2248         g_object_unref (helper->mail_op);
2249         g_slice_free   (XFerFolderAsyncHelper, helper);
2250 }
2251
2252 /**
2253  *
2254  * This function checks if the new name is a valid name for our local
2255  * folders account. The new name could not be the same than then name
2256  * of any of the mandatory local folders
2257  *
2258  * We can not rely on tinymail because tinymail does not check the
2259  * name of the virtual folders that the account could have in the case
2260  * that we're doing a rename (because it directly calls Camel which
2261  * knows nothing about our virtual folders). 
2262  *
2263  * In the case of an actual copy/move (i.e. move/copy a folder between
2264  * accounts) tinymail uses the tny_folder_store_create_account which
2265  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2266  * checks the new name of the folder, so this call in that case
2267  * wouldn't be needed. *But* NOTE that if tinymail changes its
2268  * implementation (if folder transfers within the same account is no
2269  * longer implemented as a rename) this call will allow Modest to work
2270  * perfectly
2271  *
2272  * If the new name is not valid, this function will set the status to
2273  * failed and will set also an error in the mail operation
2274  */
2275 static gboolean
2276 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2277                                  TnyFolderStore *into,
2278                                  const gchar *new_name)
2279 {
2280         if (TNY_IS_ACCOUNT (into) && 
2281             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2282             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2283                                                                  new_name)) {
2284                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2285                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2286                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2287                              _CS("ckdg_ib_folder_already_exists"));
2288                 return FALSE;
2289         } else
2290                 return TRUE;
2291 }
2292
2293 void
2294 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2295                                    TnyFolder *folder,
2296                                    TnyFolderStore *parent,
2297                                    gboolean delete_original,
2298                                    XferFolderAsyncUserCallback user_callback,
2299                                    gpointer user_data)
2300 {
2301         ModestMailOperationPrivate *priv = NULL;
2302         ModestTnyFolderRules parent_rules = 0, rules; 
2303         XFerFolderAsyncHelper *helper = NULL;
2304         const gchar *folder_name = NULL;
2305         const gchar *error_msg;
2306
2307         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2308         g_return_if_fail (TNY_IS_FOLDER (folder));
2309         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2310
2311         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2312         folder_name = tny_folder_get_name (folder);
2313
2314         /* Set the error msg */
2315         error_msg = _("mail_in_ui_folder_move_target_error");
2316
2317         /* Get account and set it into mail_operation */
2318         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2319         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2320         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2321
2322         /* Get folder rules */
2323         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2324         if (TNY_IS_FOLDER (parent))
2325                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2326         
2327         /* Apply operation constraints */
2328         if ((gpointer) parent == (gpointer) folder ||
2329             (!TNY_IS_FOLDER_STORE (parent)) || 
2330             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2331                 /* Folder rules */
2332                 goto error;
2333         } else if (TNY_IS_FOLDER (parent) && 
2334                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2335                 /* Folder rules */
2336                 goto error;
2337
2338         } else if (TNY_IS_FOLDER (parent) &&
2339                    TNY_IS_FOLDER_STORE (folder) &&
2340                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2341                                                   TNY_FOLDER_STORE (folder))) {
2342                 /* Do not move a parent into a child */
2343                 goto error;
2344         } else if (TNY_IS_FOLDER_STORE (parent) &&
2345                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2346                 /* Check that the new folder name is not used by any
2347                    parent subfolder */
2348                 goto error;     
2349         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2350                 /* Check that the new folder name is not used by any
2351                    special local folder */
2352                 goto error;
2353         } else {
2354                 /* Create the helper */
2355                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2356                 helper->mail_op = g_object_ref (self);
2357                 helper->user_callback = user_callback;
2358                 helper->user_data = user_data;
2359                 
2360                 /* Move/Copy folder */
2361                 modest_mail_operation_notify_start (self);
2362                 tny_folder_copy_async (folder,
2363                                        parent,
2364                                        tny_folder_get_name (folder),
2365                                        delete_original,
2366                                        transfer_folder_cb,
2367                                        transfer_folder_status_cb,
2368                                        helper);
2369                 return;
2370         }
2371
2372  error:
2373         /* Set status failed and set an error */
2374         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2375         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2376                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2377                      error_msg);
2378
2379         /* Call the user callback if exists */
2380         if (user_callback)
2381                 user_callback (self, NULL, user_data);
2382
2383         /* Notify the queue */
2384         modest_mail_operation_notify_end (self);
2385 }
2386
2387 void
2388 modest_mail_operation_rename_folder (ModestMailOperation *self,
2389                                      TnyFolder *folder,
2390                                      const gchar *name,
2391                                      XferFolderAsyncUserCallback user_callback,
2392                                      gpointer user_data)
2393 {
2394         ModestMailOperationPrivate *priv;
2395         ModestTnyFolderRules rules;
2396         XFerFolderAsyncHelper *helper;
2397
2398         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2399         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2400         g_return_if_fail (name);
2401         
2402         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2403
2404         /* Get account and set it into mail_operation */
2405         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2406         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2407
2408         /* Check folder rules */
2409         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2410         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2411                 goto error;
2412         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2413                 goto error;
2414         } else {
2415                 TnyFolderStore *into;
2416
2417                 into = tny_folder_get_folder_store (folder);    
2418
2419                 /* Check that the new folder name is not used by any
2420                    special local folder */
2421                 if (new_name_valid_if_local_account (priv, into, name)) {
2422                         /* Create the helper */
2423                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2424                         helper->mail_op = g_object_ref(self);
2425                         helper->user_callback = user_callback;
2426                         helper->user_data = user_data;
2427                 
2428                         /* Rename. Camel handles folder subscription/unsubscription */
2429                         modest_mail_operation_notify_start (self);
2430                         tny_folder_copy_async (folder, into, name, TRUE,
2431                                                transfer_folder_cb,
2432                                                transfer_folder_status_cb,
2433                                                helper);
2434                         g_object_unref (into);
2435                 } else {
2436                         g_object_unref (into);
2437                         goto error;
2438                 }
2439
2440                 return;
2441         }
2442  error:
2443         /* Set status failed and set an error */
2444         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2445         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2446                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2447                      _("FIXME: unable to rename"));
2448         
2449         if (user_callback)
2450                 user_callback (self, NULL, user_data);
2451
2452         /* Notify about operation end */
2453         modest_mail_operation_notify_end (self);
2454 }
2455
2456 /* ******************************************************************* */
2457 /* **************************  MSG  ACTIONS  ************************* */
2458 /* ******************************************************************* */
2459
2460 void 
2461 modest_mail_operation_get_msg (ModestMailOperation *self,
2462                                TnyHeader *header,
2463                                gboolean progress_feedback,
2464                                GetMsgAsyncUserCallback user_callback,
2465                                gpointer user_data)
2466 {
2467         GetMsgInfo *helper = NULL;
2468         TnyFolder *folder;
2469         ModestMailOperationPrivate *priv;
2470         
2471         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2472         g_return_if_fail (TNY_IS_HEADER (header));
2473         
2474         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2475         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2476         priv->total = 1;
2477         priv->done = 0;
2478
2479         /* Check memory low */
2480         if (_check_memory_low (self)) {
2481                 if (user_callback)
2482                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2483                 modest_mail_operation_notify_end (self);
2484                 return;
2485         }
2486
2487         /* Get account and set it into mail_operation */
2488         folder = tny_header_get_folder (header);
2489         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2490         
2491         /* Check for cached messages */
2492         if (progress_feedback) {
2493                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2494                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2495                 else 
2496                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2497         } else {
2498                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2499         }
2500         
2501         /* Create the helper */
2502         helper = g_slice_new0 (GetMsgInfo);
2503         helper->header = g_object_ref (header);
2504         helper->mail_op = g_object_ref (self);
2505         helper->user_callback = user_callback;
2506         helper->user_data = user_data;
2507         helper->destroy_notify = NULL;
2508         helper->last_total_bytes = 0;
2509         helper->sum_total_bytes = 0;
2510         helper->total_bytes = tny_header_get_message_size (header);
2511         helper->more_msgs = NULL;
2512
2513         modest_mail_operation_notify_start (self);
2514         
2515         /* notify about the start of the operation */ 
2516         ModestMailOperationState *state;
2517         state = modest_mail_operation_clone_state (self);
2518         state->done = 0;
2519         state->total = 0;
2520         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2521                                 0, state, NULL);
2522         g_slice_free (ModestMailOperationState, state);
2523         
2524         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2525
2526         g_object_unref (G_OBJECT (folder));
2527 }
2528
2529 static void     
2530 get_msg_status_cb (GObject *obj,
2531                    TnyStatus *status,  
2532                    gpointer user_data)
2533 {
2534         GetMsgInfo *helper = NULL;
2535
2536         g_return_if_fail (status != NULL);
2537
2538         /* Show only the status information we want */
2539         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2540                 return;
2541
2542         helper = (GetMsgInfo *) user_data;
2543         g_return_if_fail (helper != NULL);       
2544
2545         /* Notify progress */
2546         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2547                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2548 }
2549
2550 static void
2551 get_msg_async_cb (TnyFolder *folder, 
2552                   gboolean canceled, 
2553                   TnyMsg *msg, 
2554                   GError *err, 
2555                   gpointer user_data)
2556 {
2557         GetMsgInfo *info = NULL;
2558         ModestMailOperationPrivate *priv = NULL;
2559         gboolean finished;
2560
2561         info = (GetMsgInfo *) user_data;
2562
2563         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2564         priv->done++;
2565
2566         if (info->more_msgs) {
2567                 tny_iterator_next (info->more_msgs);
2568                 finished = (tny_iterator_is_done (info->more_msgs));
2569         } else {
2570                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2571         }
2572
2573         /* If canceled by the user, ignore the error given by Tinymail */
2574         if (canceled) {
2575                 canceled = TRUE;
2576                 finished = TRUE;
2577                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2578         } else if (err) {
2579                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2580                 if (err) {
2581                         priv->error = g_error_copy ((const GError *) err);
2582                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2583                 }
2584                 if (!priv->error) {
2585                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2586                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2587                                      err->message);
2588                 }
2589         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2590                 /* Set the success status before calling the user callback */
2591                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2592         }
2593
2594
2595         /* Call the user callback */
2596         if (info->user_callback)
2597                 info->user_callback (info->mail_op, info->header, canceled, 
2598                                      msg, err, info->user_data);
2599
2600         /* Notify about operation end if this is the last callback */
2601         if (finished) {
2602                 /* Free user data */
2603                 if (info->destroy_notify)
2604                         info->destroy_notify (info->user_data);
2605
2606                 /* Notify about operation end */
2607                 modest_mail_operation_notify_end (info->mail_op);
2608
2609                 /* Clean */
2610                 if (info->more_msgs)
2611                         g_object_unref (info->more_msgs);
2612                 g_object_unref (info->header);
2613                 g_object_unref (info->mail_op);
2614                 g_slice_free (GetMsgInfo, info);
2615         } else if (info->more_msgs) {
2616                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
2617                 TnyFolder *folder = tny_header_get_folder (header);
2618
2619                 g_object_unref (info->header);
2620                 info->header = g_object_ref (header);
2621
2622                 /* Retrieve the next message */
2623                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
2624
2625                 g_object_unref (header);
2626                 g_object_unref (folder);
2627         } else {
2628                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
2629         }
2630 }
2631
2632 void 
2633 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2634                                      TnyList *header_list, 
2635                                      GetMsgAsyncUserCallback user_callback,
2636                                      gpointer user_data,
2637                                      GDestroyNotify notify)
2638 {
2639         ModestMailOperationPrivate *priv = NULL;
2640         gint msg_list_size;
2641         TnyIterator *iter = NULL;
2642         gboolean has_uncached_messages;
2643         
2644         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2645
2646         /* Init mail operation */
2647         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2648         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2649         priv->done = 0;
2650         priv->total = tny_list_get_length(header_list);
2651
2652         /* Check memory low */
2653         if (_check_memory_low (self)) {
2654                 if (user_callback) {
2655                         TnyHeader *header = NULL;
2656                         TnyIterator *iter;
2657
2658                         if (tny_list_get_length (header_list) > 0) {
2659                                 iter = tny_list_create_iterator (header_list);
2660                                 header = (TnyHeader *) tny_iterator_get_current (iter);
2661                                 g_object_unref (iter);
2662                         }
2663                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2664                         if (header)
2665                                 g_object_unref (header);
2666                 }
2667                 if (notify)
2668                         notify (user_data);
2669                 /* Notify about operation end */
2670                 modest_mail_operation_notify_end (self);
2671                 return;
2672         }
2673
2674         /* Check uncached messages */
2675         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
2676              !has_uncached_messages && !tny_iterator_is_done (iter); 
2677              tny_iterator_next (iter)) {
2678                 TnyHeader *header;
2679
2680                 header = (TnyHeader *) tny_iterator_get_current (iter);
2681                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
2682                         has_uncached_messages = TRUE;
2683                 g_object_unref (header);
2684         }       
2685         g_object_unref (iter);
2686         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
2687
2688         /* Get account and set it into mail_operation */
2689         if (tny_list_get_length (header_list) >= 1) {
2690                 TnyIterator *iterator = tny_list_create_iterator (header_list);
2691                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
2692                 if (header) {
2693                         TnyFolder *folder = tny_header_get_folder (header);
2694                         if (folder) {           
2695                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2696                                 g_object_unref (folder);
2697                         }
2698                         g_object_unref (header);
2699                 }
2700                 g_object_unref (iterator);
2701         }
2702
2703         msg_list_size = compute_message_list_size (header_list, 0);
2704
2705         modest_mail_operation_notify_start (self);
2706         iter = tny_list_create_iterator (header_list);
2707         if (!tny_iterator_is_done (iter)) {
2708                 /* notify about the start of the operation */
2709                 ModestMailOperationState *state;
2710                 state = modest_mail_operation_clone_state (self);
2711                 state->done = 0;
2712                 state->total = 0;
2713                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2714                                0, state, NULL);
2715
2716                 GetMsgInfo *msg_info = NULL;
2717                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2718                 TnyFolder *folder = tny_header_get_folder (header);
2719
2720                 /* Create the message info */
2721                 msg_info = g_slice_new0 (GetMsgInfo);
2722                 msg_info->mail_op = g_object_ref (self);
2723                 msg_info->header = g_object_ref (header);
2724                 msg_info->more_msgs = g_object_ref (iter);
2725                 msg_info->user_callback = user_callback;
2726                 msg_info->user_data = user_data;
2727                 msg_info->destroy_notify = notify;
2728                 msg_info->last_total_bytes = 0;
2729                 msg_info->sum_total_bytes = 0;
2730                 msg_info->total_bytes = msg_list_size;
2731
2732                 /* The callback will call it per each header */
2733                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2734
2735                 /* Free and go on */
2736                 g_object_unref (header);
2737                 g_object_unref (folder);
2738                 g_slice_free (ModestMailOperationState, state);
2739         }
2740         g_object_unref (iter);
2741 }
2742
2743
2744 static void
2745 remove_msgs_async_cb (TnyFolder *folder, 
2746                       gboolean canceled, 
2747                       GError *err, 
2748                       gpointer user_data)
2749 {
2750         gboolean expunge, leave_on_server;
2751         const gchar *account_name;
2752         TnyAccount *account;
2753         ModestProtocolType account_proto = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
2754         ModestMailOperation *self;
2755         ModestMailOperationPrivate *priv;
2756         ModestProtocolRegistry *protocol_registry;
2757
2758         self = (ModestMailOperation *) user_data;
2759         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2760         protocol_registry = modest_runtime_get_protocol_registry ();
2761
2762         if (canceled || err) {
2763                 /* If canceled by the user, ignore the error given by Tinymail */
2764                 if (canceled) {
2765                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2766                 } else if (err) {
2767                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2768                         priv->error = g_error_copy ((const GError *) err);
2769                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2770                 }
2771                 /* Exit */
2772                 modest_mail_operation_notify_end (self);
2773                 g_object_unref (self);
2774                 return;
2775         }
2776
2777         account = tny_folder_get_account (folder);
2778         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2779         leave_on_server =
2780                 modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2781                                                         account_name);  
2782         account_proto = modest_tny_account_get_protocol_type (account);
2783         g_object_unref (account);
2784
2785         if ((modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_proto) && 
2786              !leave_on_server) ||
2787             !modest_tny_folder_is_remote_folder (folder))
2788                 expunge = TRUE;
2789         else
2790                 expunge = FALSE;
2791
2792         /* Sync folder */
2793         tny_folder_sync_async(folder, expunge, sync_folder_finish_callback, 
2794                               NULL, self);
2795 }
2796
2797 void 
2798 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2799                                    TnyList *headers,
2800                                    gboolean remove_to_trash /*ignored*/)
2801 {
2802         TnyFolder *folder = NULL;
2803         ModestMailOperationPrivate *priv;
2804         TnyIterator *iter = NULL;
2805         TnyHeader *header = NULL;
2806         TnyList *remove_headers = NULL;
2807         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2808
2809         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2810         g_return_if_fail (TNY_IS_LIST (headers));
2811
2812         if (remove_to_trash)
2813                 g_warning ("remove to trash is not implemented");
2814
2815         if (tny_list_get_length(headers) == 0) {
2816                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
2817                 goto cleanup; /* nothing to do */
2818         }
2819         
2820         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2821         remove_headers = g_object_ref(headers);
2822
2823         /* Get folder from first header and sync it */
2824         iter = tny_list_create_iterator (headers);      
2825         header = TNY_HEADER (tny_iterator_get_current (iter));
2826
2827         folder = tny_header_get_folder (header);        
2828         if (!TNY_IS_FOLDER(folder)) {
2829                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
2830                 goto cleanup;
2831         }
2832
2833         /* Don't remove messages that are being sent */
2834         if (modest_tny_folder_is_local_folder (folder)) {
2835                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2836         }
2837         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2838                 TnyTransportAccount *traccount = NULL;
2839                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2840                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2841                 if (traccount) {
2842                         ModestTnySendQueueStatus status;
2843                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount, TRUE);
2844
2845                         if (TNY_IS_SEND_QUEUE (send_queue)) {
2846                                 TnyIterator *iter = tny_list_create_iterator(headers);
2847                                 g_object_unref(remove_headers);
2848                                 remove_headers = TNY_LIST(tny_simple_list_new());
2849                                 while (!tny_iterator_is_done(iter)) {
2850                                         char *msg_id;
2851                                         TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2852                                         msg_id = modest_tny_send_queue_get_msg_id (hdr);
2853                                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2854                                         if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2855                                                 tny_list_append(remove_headers, G_OBJECT(hdr));
2856                                         }
2857                                         g_object_unref(hdr);
2858                                         g_free(msg_id);
2859                                         tny_iterator_next(iter);
2860                                 }
2861                                 g_object_unref(iter);
2862                         }
2863                         g_object_unref(traccount);
2864                 }
2865         }
2866
2867         /* Get account and set it into mail_operation */
2868         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2869         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2870         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2871
2872         /* remove message from folder */
2873         modest_mail_operation_notify_start (self);
2874         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
2875                                       NULL, g_object_ref (self));
2876
2877 cleanup:
2878         if (remove_headers)
2879                 g_object_unref (remove_headers);
2880         if (header)
2881                 g_object_unref (header);
2882         if (iter)
2883                 g_object_unref (iter);
2884         if (folder)
2885                 g_object_unref (folder);
2886 }
2887
2888 static void
2889 notify_progress_of_multiple_messages (ModestMailOperation *self,
2890                                       TnyStatus *status,
2891                                       gint *last_total_bytes,
2892                                       gint *sum_total_bytes,
2893                                       gint total_bytes, 
2894                                       gboolean increment_done)
2895 {
2896         ModestMailOperationPrivate *priv;
2897         ModestMailOperationState *state;
2898         gboolean is_num_bytes = FALSE;
2899
2900         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2901
2902         /* We know that tinymail sends us information about
2903          *  transferred bytes with this particular message
2904          */
2905         if (status->message)
2906                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2907
2908         state = modest_mail_operation_clone_state (self);
2909         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2910                 /* We know that we're in a different message when the
2911                    total number of bytes to transfer is different. Of
2912                    course it could fail if we're transferring messages
2913                    of the same size, but this is a workarround */
2914                 if (status->of_total != *last_total_bytes) {
2915                         /* We need to increment the done when there is
2916                            no information about each individual
2917                            message, we need to do this in message
2918                            transfers, and we don't do it for getting
2919                            messages */
2920                         if (increment_done)
2921                                 priv->done++;
2922                         *sum_total_bytes += *last_total_bytes;
2923                         *last_total_bytes = status->of_total;
2924                 }
2925                 state->bytes_done += status->position + *sum_total_bytes;
2926                 state->bytes_total = total_bytes;
2927
2928                 /* Notify the status change. Only notify about changes
2929                    referred to bytes */
2930                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2931                                0, state, NULL);
2932         }
2933
2934         g_slice_free (ModestMailOperationState, state);
2935 }
2936
2937 static void
2938 transfer_msgs_status_cb (GObject *obj,
2939                          TnyStatus *status,  
2940                          gpointer user_data)
2941 {
2942         XFerMsgsAsyncHelper *helper;
2943
2944         g_return_if_fail (status != NULL);
2945
2946         /* Show only the status information we want */
2947         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
2948                 return;
2949
2950         helper = (XFerMsgsAsyncHelper *) user_data;
2951         g_return_if_fail (helper != NULL);       
2952
2953         /* Notify progress */
2954         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2955                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2956 }
2957
2958 static void
2959 transfer_msgs_sync_folder_cb (TnyFolder *self, 
2960                               gboolean cancelled, 
2961                               GError *err, 
2962                               gpointer user_data)
2963 {
2964         XFerMsgsAsyncHelper *helper;
2965         /* We don't care here about the results of the
2966            synchronization */
2967         helper = (XFerMsgsAsyncHelper *) user_data;
2968
2969         /* Notify about operation end */
2970         modest_mail_operation_notify_end (helper->mail_op);
2971
2972         /* If user defined callback function was defined, call it */
2973         if (helper->user_callback)
2974                 helper->user_callback (helper->mail_op, helper->user_data);
2975         
2976         /* Free */
2977         if (helper->more_msgs)
2978                 g_object_unref (helper->more_msgs);
2979         if (helper->headers)
2980                 g_object_unref (helper->headers);
2981         if (helper->dest_folder)
2982                 g_object_unref (helper->dest_folder);
2983         if (helper->mail_op)
2984                 g_object_unref (helper->mail_op);
2985         g_slice_free (XFerMsgsAsyncHelper, helper);
2986 }
2987
2988 static void
2989 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2990 {
2991         XFerMsgsAsyncHelper *helper;
2992         ModestMailOperation *self;
2993         ModestMailOperationPrivate *priv;
2994         gboolean finished = TRUE;
2995
2996         helper = (XFerMsgsAsyncHelper *) user_data;
2997         self = helper->mail_op;
2998
2999         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3000
3001         if (cancelled) {
3002                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3003         } else if (err) {
3004                 priv->error = g_error_copy (err);
3005                 priv->done = 0;
3006                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
3007         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
3008                 if (helper->more_msgs) {
3009                         /* We'll transfer the next message in the list */
3010                         tny_iterator_next (helper->more_msgs);
3011                         if (!tny_iterator_is_done (helper->more_msgs)) {
3012                                 GObject *next_header;
3013                                 g_object_unref (helper->headers);
3014                                 helper->headers = tny_simple_list_new ();
3015                                 next_header = tny_iterator_get_current (helper->more_msgs);
3016                                 tny_list_append (helper->headers, next_header);
3017                                 g_object_unref (next_header);
3018                                 finished = FALSE;
3019                         }
3020                 }
3021                 if (finished) {
3022                         priv->done = 1;
3023                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3024                 }
3025         }
3026
3027         if (finished) {
3028                 /* Synchronize the source folder contents. This should
3029                    be done by tinymail but the camel_folder_sync it's
3030                    actually disabled in transfer_msgs_thread_clean
3031                    because it's supposed to cause hangs */
3032                 tny_folder_sync_async (folder, helper->delete, 
3033                                        transfer_msgs_sync_folder_cb, 
3034                                        NULL, helper);
3035         } else {
3036                 /* Transfer more messages */
3037                 tny_folder_transfer_msgs_async (folder,
3038                                                 helper->headers,
3039                                                 helper->dest_folder,
3040                                                 helper->delete,
3041                                                 transfer_msgs_cb,
3042                                                 transfer_msgs_status_cb,
3043                                                 helper);
3044         }
3045 }
3046
3047 /* Computes the size of the messages the headers in the list belongs
3048    to. If num_elements is different from 0 then it only takes into
3049    account the first num_elements for the calculation */
3050 static guint
3051 compute_message_list_size (TnyList *headers, 
3052                            guint num_elements)
3053 {
3054         TnyIterator *iter;
3055         guint size = 0, element = 0;
3056
3057         /* If num_elements is not valid then take all into account */
3058         if ((num_elements <= 0) || (num_elements > tny_list_get_length (headers)))
3059                 num_elements = tny_list_get_length (headers);
3060
3061         iter = tny_list_create_iterator (headers);
3062         while (!tny_iterator_is_done (iter) && element < num_elements) {
3063                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3064                 size += tny_header_get_message_size (header);
3065                 g_object_unref (header);
3066                 tny_iterator_next (iter);
3067                 element++;
3068         }
3069         g_object_unref (iter);
3070
3071         return size;
3072 }
3073
3074 void
3075 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
3076                                  TnyList *headers, 
3077                                  TnyFolder *folder, 
3078                                  gboolean delete_original,
3079                                  XferMsgsAsyncUserCallback user_callback,
3080                                  gpointer user_data)
3081 {
3082         ModestMailOperationPrivate *priv = NULL;
3083         TnyIterator *iter = NULL;
3084         TnyFolder *src_folder = NULL;
3085         XFerMsgsAsyncHelper *helper = NULL;
3086         TnyHeader *header = NULL;
3087         ModestTnyFolderRules rules = 0;
3088         TnyAccount *dst_account = NULL;
3089         gboolean leave_on_server;
3090         ModestMailOperationState *state;
3091         ModestProtocolRegistry *protocol_registry;
3092         ModestProtocolType account_protocol;
3093
3094         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
3095         g_return_if_fail (headers && TNY_IS_LIST (headers));
3096         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
3097
3098         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3099         protocol_registry = modest_runtime_get_protocol_registry ();
3100
3101         priv->total = tny_list_get_length (headers);
3102         priv->done = 0;
3103         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3104         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3105
3106         /* Apply folder rules */
3107         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3108         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3109                 /* Set status failed and set an error */
3110                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3111                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3112                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
3113                              _CS("ckct_ib_unable_to_paste_here"));
3114                 /* Notify the queue */
3115                 modest_mail_operation_notify_end (self);
3116                 return;
3117         }
3118                 
3119         /* Get source folder */
3120         iter = tny_list_create_iterator (headers);
3121         header = TNY_HEADER (tny_iterator_get_current (iter));
3122         if (header) {
3123                 src_folder = tny_header_get_folder (header);
3124                 g_object_unref (header);
3125         }
3126         g_object_unref (iter);
3127
3128         if (src_folder == NULL) {
3129                 /* Notify the queue */
3130                 modest_mail_operation_notify_end (self);
3131
3132                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
3133                 return;
3134         }
3135
3136         
3137         /* Check folder source and destination */
3138         if (src_folder == folder) {
3139                 /* Set status failed and set an error */
3140                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3141                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3142                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
3143                              _("mail_in_ui_folder_copy_target_error"));
3144                 
3145                 /* Notify the queue */
3146                 modest_mail_operation_notify_end (self);
3147                 
3148                 /* Free */
3149                 g_object_unref (src_folder);            
3150                 return;
3151         }
3152
3153         /* Create the helper */
3154         helper = g_slice_new0 (XFerMsgsAsyncHelper);
3155         helper->mail_op = g_object_ref(self);
3156         helper->dest_folder = g_object_ref(folder);
3157         helper->user_callback = user_callback;
3158         helper->user_data = user_data;
3159         helper->last_total_bytes = 0;
3160         helper->sum_total_bytes = 0;
3161         helper->total_bytes = compute_message_list_size (headers, 0);
3162
3163         /* Get account and set it into mail_operation */
3164         priv->account = modest_tny_folder_get_account (src_folder);
3165         dst_account = modest_tny_folder_get_account (folder);
3166
3167         if (priv->account == dst_account) {
3168                 /* Transfer all messages at once using the fast
3169                  * method. Note that depending on the server this
3170                  * might not be that fast, and might not be
3171                  * user-cancellable either */
3172                 helper->headers = g_object_ref (headers);
3173                 helper->more_msgs = NULL;
3174         } else {
3175                 /* Transfer messages one by one so the user can cancel
3176                  * the operation */
3177                 GObject *hdr;
3178                 helper->headers = tny_simple_list_new ();
3179                 helper->more_msgs = tny_list_create_iterator (headers);
3180                 hdr = tny_iterator_get_current (helper->more_msgs);
3181                 tny_list_append (helper->headers, hdr);
3182                 g_object_unref (hdr);
3183         }
3184
3185         /* If leave_on_server is set to TRUE then don't use
3186            delete_original, we always pass FALSE. This is because
3187            otherwise tinymail will try to sync the source folder and
3188            this could cause an error if we're offline while
3189            transferring an already downloaded message from a POP
3190            account */
3191         account_protocol = modest_tny_account_get_protocol_type (priv->account);
3192         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_protocol)) {
3193                 const gchar *account_name;
3194
3195                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
3196                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3197                                                                           account_name);
3198         } else {
3199                 leave_on_server = FALSE;
3200         }
3201
3202         /* Do not delete messages if leave on server is TRUE */
3203         helper->delete = (leave_on_server) ? FALSE : delete_original;
3204
3205         modest_mail_operation_notify_start (self);
3206
3207         /* Start notifying progress */
3208         state = modest_mail_operation_clone_state (self);
3209         state->done = 0;
3210         state->total = 0;
3211         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3212         g_slice_free (ModestMailOperationState, state);
3213
3214         tny_folder_transfer_msgs_async (src_folder, 
3215                                         helper->headers, 
3216                                         folder, 
3217                                         helper->delete, 
3218                                         transfer_msgs_cb, 
3219                                         transfer_msgs_status_cb,
3220                                         helper);
3221         g_object_unref (src_folder);
3222         g_object_unref (dst_account);
3223 }
3224
3225
3226 static void
3227 on_refresh_folder (TnyFolder   *folder, 
3228                    gboolean     cancelled, 
3229                    GError     *error,
3230                    gpointer     user_data)
3231 {
3232         RefreshAsyncHelper *helper = NULL;
3233         ModestMailOperation *self = NULL;
3234         ModestMailOperationPrivate *priv = NULL;
3235
3236         helper = (RefreshAsyncHelper *) user_data;
3237         self = helper->mail_op;
3238         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3239
3240         g_return_if_fail(priv!=NULL);
3241
3242         if (error) {
3243                 priv->error = g_error_copy (error);
3244                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3245                 goto out;
3246         }
3247
3248         if (cancelled) {
3249                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3250                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3251                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3252                              _("Error trying to refresh the contents of %s"),
3253                              tny_folder_get_name (folder));
3254                 goto out;
3255         }
3256
3257         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3258  out:
3259
3260         /* Call user defined callback, if it exists */
3261         if (helper->user_callback) {
3262
3263                 /* This is not a GDK lock because we are a Tinymail callback and
3264                  * Tinymail already acquires the Gdk lock */
3265                 helper->user_callback (self, folder, helper->user_data);
3266         }
3267
3268         /* Free */
3269         g_slice_free (RefreshAsyncHelper, helper);
3270
3271         /* Notify about operation end */
3272         modest_mail_operation_notify_end (self);
3273         g_object_unref(self);
3274 }
3275
3276 static void
3277 on_refresh_folder_status_update (GObject *obj,
3278                                  TnyStatus *status,
3279                                  gpointer user_data)
3280 {
3281         RefreshAsyncHelper *helper = NULL;
3282         ModestMailOperation *self = NULL;
3283         ModestMailOperationPrivate *priv = NULL;
3284         ModestMailOperationState *state;
3285
3286         g_return_if_fail (user_data != NULL);
3287         g_return_if_fail (status != NULL);
3288
3289         /* Show only the status information we want */
3290         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
3291                 return;
3292
3293         helper = (RefreshAsyncHelper *) user_data;
3294         self = helper->mail_op;
3295         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3296
3297         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3298
3299         priv->done = status->position;
3300         priv->total = status->of_total;
3301
3302         state = modest_mail_operation_clone_state (self);
3303
3304         /* This is not a GDK lock because we are a Tinymail callback and
3305          * Tinymail already acquires the Gdk lock */
3306         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3307
3308         g_slice_free (ModestMailOperationState, state);
3309 }
3310
3311 void 
3312 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3313                                        TnyFolder *folder,
3314                                        RefreshAsyncUserCallback user_callback,
3315                                        gpointer user_data)
3316 {
3317         ModestMailOperationPrivate *priv = NULL;
3318         RefreshAsyncHelper *helper = NULL;
3319
3320         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3321
3322         /* Check memory low */
3323         if (_check_memory_low (self)) {
3324                 if (user_callback)
3325                         user_callback (self, folder, user_data);
3326                 /* Notify about operation end */
3327                 modest_mail_operation_notify_end (self);
3328                 return;
3329         }
3330
3331         /* Get account and set it into mail_operation */
3332         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3333         priv->account = modest_tny_folder_get_account  (folder);
3334         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3335
3336         /* Create the helper */
3337         helper = g_slice_new0 (RefreshAsyncHelper);
3338         helper->mail_op = g_object_ref(self);
3339         helper->user_callback = user_callback;
3340         helper->user_data = user_data;
3341
3342         modest_mail_operation_notify_start (self);
3343         
3344         /* notify that the operation was started */
3345         ModestMailOperationState *state;
3346         state = modest_mail_operation_clone_state (self);
3347         state->done = 0;
3348         state->total = 0;
3349         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3350                         0, state, NULL);
3351         g_slice_free (ModestMailOperationState, state);
3352         
3353         tny_folder_refresh_async (folder,
3354                                   on_refresh_folder,
3355                                   on_refresh_folder_status_update,
3356                                   helper);
3357 }
3358
3359 static void
3360 run_queue_notify_and_destroy (RunQueueHelper *helper,
3361                               ModestMailOperationStatus status)
3362 {
3363         ModestMailOperationPrivate *priv;
3364
3365         /* Disconnect */
3366         if (helper->error_handler &&
3367             g_signal_handler_is_connected (helper->queue, helper->error_handler))
3368                 g_signal_handler_disconnect (helper->queue, helper->error_handler);
3369         if (helper->start_handler &&
3370             g_signal_handler_is_connected (helper->queue, helper->start_handler))
3371                 g_signal_handler_disconnect (helper->queue, helper->start_handler);
3372         if (helper->stop_handler &&
3373             g_signal_handler_is_connected (helper->queue, helper->stop_handler))
3374                 g_signal_handler_disconnect (helper->queue, helper->stop_handler);
3375
3376         /* Set status */
3377         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
3378         priv->status = status;
3379
3380         /* Notify end */
3381         modest_mail_operation_notify_end (helper->self);
3382
3383         /* Free data */
3384         g_object_unref (helper->queue);
3385         g_object_unref (helper->self);
3386         g_slice_free (RunQueueHelper, helper);
3387 }
3388
3389 static void
3390 run_queue_stop (ModestTnySendQueue *queue,
3391                 gpointer user_data)
3392 {
3393         RunQueueHelper *helper;
3394
3395         g_debug ("%s sending queue stopped", __FUNCTION__);
3396
3397         helper = (RunQueueHelper *) user_data;
3398         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
3399 }
3400
3401 void
3402 modest_mail_operation_run_queue (ModestMailOperation *self,
3403                                  ModestTnySendQueue *queue)
3404 {
3405         ModestMailOperationPrivate *priv;
3406         RunQueueHelper *helper;
3407
3408         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3409         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3410         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3411
3412         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3413         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3414         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3415
3416         /* Create the helper */
3417         helper = g_slice_new0 (RunQueueHelper);
3418         helper->queue = g_object_ref (queue);
3419         helper->self = g_object_ref (self);
3420         helper->stop_handler = g_signal_connect (queue, "queue-stop", 
3421                                                  G_CALLBACK (run_queue_stop), 
3422                                                  helper);
3423
3424         /* Notify operation has started */
3425         modest_mail_operation_notify_start (self);
3426         g_debug ("%s, run queue started", __FUNCTION__);
3427 }
3428
3429 static void
3430 queue_wakeup_callback (ModestTnySendQueue *queue,
3431                        gboolean cancelled,
3432                        GError *error,
3433                        gpointer userdata)
3434 {
3435         ModestMailOperation *mail_op;
3436         ModestMailOperationPrivate *priv;
3437
3438         mail_op = (ModestMailOperation *) userdata;
3439         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3440
3441         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3442         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (queue));
3443
3444         /* Notify end */
3445         modest_mail_operation_notify_end (mail_op);
3446         g_object_unref (mail_op);
3447 }
3448
3449 void
3450 modest_mail_operation_queue_wakeup (ModestMailOperation *self,
3451                                     ModestTnySendQueue *queue)
3452 {
3453         ModestMailOperationPrivate *priv;
3454
3455         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3456         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3457         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3458
3459         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3460         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3461         priv->op_type = MODEST_MAIL_OPERATION_TYPE_QUEUE_WAKEUP;
3462
3463         g_object_ref (self);
3464
3465         modest_tny_send_queue_wakeup (queue, queue_wakeup_callback, self);
3466         modest_mail_operation_notify_start (self);
3467 }
3468
3469 static void
3470 shutdown_callback (ModestTnyAccountStore *account_store, gpointer userdata)
3471 {
3472         ModestMailOperation *self = (ModestMailOperation *) userdata;
3473         ModestMailOperationPrivate *priv;
3474
3475         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3476         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3477         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3478
3479         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3480
3481         modest_mail_operation_notify_end (self);
3482         g_object_unref (self);
3483 }
3484
3485 void
3486 modest_mail_operation_shutdown (ModestMailOperation *self, ModestTnyAccountStore *account_store)
3487 {
3488         ModestMailOperationPrivate *priv;
3489
3490         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3491         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3492         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3493
3494         modest_mail_operation_queue_set_running_shutdown (modest_runtime_get_mail_operation_queue ());
3495
3496         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3497         priv->account = NULL;
3498         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SHUTDOWN;
3499
3500         modest_mail_operation_notify_start (self);
3501         g_object_ref (self);
3502         modest_tny_account_store_shutdown (account_store, shutdown_callback, self);
3503 }
3504
3505 static void
3506 sync_folder_finish_callback (TnyFolder *self, 
3507                              gboolean cancelled, 
3508                              GError *err, 
3509                              gpointer user_data)
3510
3511 {
3512         ModestMailOperation *mail_op;
3513         ModestMailOperationPrivate *priv;
3514
3515         mail_op = (ModestMailOperation *) user_data;
3516         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3517
3518         /* If canceled by the user, ignore the error given by Tinymail */
3519         if (cancelled) {
3520                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3521         } else if (err) {
3522                 /* If the operation was a sync then the status is
3523                    failed, but if it's part of another operation then
3524                    just set it as finished with errors */
3525                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
3526                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3527                 else
3528                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3529                 priv->error = g_error_copy ((const GError *) err);
3530                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3531         } else {
3532                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3533         }
3534
3535         modest_mail_operation_notify_end (mail_op);
3536         g_object_unref (mail_op);
3537 }
3538
3539 void
3540 modest_mail_operation_sync_folder (ModestMailOperation *self,
3541                                    TnyFolder *folder, gboolean expunge)
3542 {
3543         ModestMailOperationPrivate *priv;
3544
3545         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3546         g_return_if_fail (TNY_IS_FOLDER (folder));
3547         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3548
3549         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3550         priv->account = modest_tny_folder_get_account (folder);
3551         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3552
3553         modest_mail_operation_notify_start (self);
3554         g_object_ref (self);
3555         tny_folder_sync_async (folder, expunge, 
3556                                (TnyFolderCallback) sync_folder_finish_callback, 
3557                                NULL, self);
3558 }
3559
3560 static void
3561 modest_mail_operation_notify_start (ModestMailOperation *self)
3562 {
3563         ModestMailOperationPrivate *priv = NULL;
3564
3565         g_return_if_fail (self);
3566
3567         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3568
3569         /* Ensure that all the fields are filled correctly */
3570         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3571
3572         /* Notify the observers about the mail operation. We do not
3573            wrapp this emission because we assume that this function is
3574            always called from within the main lock */
3575         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3576 }
3577
3578 /**
3579  *
3580  * It's used by the mail operation queue to notify the observers
3581  * attached to that signal that the operation finished. We need to use
3582  * that because tinymail does not give us the progress of a given
3583  * operation when it finishes (it directly calls the operation
3584  * callback).
3585  */
3586 static void
3587 modest_mail_operation_notify_end (ModestMailOperation *self)
3588 {
3589         ModestMailOperationPrivate *priv = NULL;
3590
3591         g_return_if_fail (self);
3592
3593         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3594
3595         /* Notify the observers about the mail operation end. We do
3596            not wrapp this emission because we assume that this
3597            function is always called from within the main lock */
3598         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3599
3600         /* Remove the error user data */
3601         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3602                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3603 }
3604
3605 TnyAccount *
3606 modest_mail_operation_get_account (ModestMailOperation *self)
3607 {
3608         ModestMailOperationPrivate *priv = NULL;
3609
3610         g_return_val_if_fail (self, NULL);
3611
3612         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3613
3614         return (priv->account) ? g_object_ref (priv->account) : NULL;
3615 }
3616
3617 void
3618 modest_mail_operation_noop (ModestMailOperation *self)
3619 {
3620         ModestMailOperationPrivate *priv = NULL;
3621
3622         g_return_if_fail (self);
3623
3624         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3625         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3626         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3627         priv->done = 0;
3628         priv->total = 0;
3629
3630         /* This mail operation does nothing actually */
3631         modest_mail_operation_notify_start (self);
3632         modest_mail_operation_notify_end (self);
3633 }
3634
3635
3636 gchar*
3637 modest_mail_operation_to_string (ModestMailOperation *self)
3638 {
3639         const gchar *type, *status, *account_id;
3640         ModestMailOperationPrivate *priv = NULL;
3641         
3642         g_return_val_if_fail (self, NULL);
3643
3644         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3645
3646         /* new operations don't have anything interesting */
3647         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3648                 return g_strdup_printf ("%p <new operation>", self);
3649         
3650         switch (priv->op_type) {
3651         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3652         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3653         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3654         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3655         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3656         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
3657         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
3658         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3659         default: type = "UNEXPECTED"; break;
3660         }
3661
3662         switch (priv->status) {
3663         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3664         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3665         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3666         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3667         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3668         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3669         default:                                                status= "UNEXPECTED"; break;
3670         } 
3671
3672         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3673
3674         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3675                                 priv->done, priv->total,
3676                                 priv->error && priv->error->message ? priv->error->message : "");
3677 }
3678
3679 /* 
3680  * Once the mail operations were objects this will be no longer
3681  * needed. I don't like it, but we need it for the moment
3682  */
3683 static gboolean
3684 _check_memory_low (ModestMailOperation *mail_op)
3685 {
3686         if (modest_platform_check_memory_low (NULL, FALSE)) {
3687                 ModestMailOperationPrivate *priv;
3688
3689                 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3690                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3691                 g_set_error (&(priv->error),
3692                              MODEST_MAIL_OPERATION_ERROR,
3693                              MODEST_MAIL_OPERATION_ERROR_LOW_MEMORY,
3694                              "Not enough memory to complete the operation");
3695                 return TRUE;
3696         } else {
3697                 return FALSE;
3698         }
3699 }