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