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