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