9a38bcf0ce0ccc4fd88cd1ec5f285f8ef47ef2b7
[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         /* Notify messages are "read" */
3347         iter = tny_list_create_iterator (remove_headers);
3348         while (!tny_iterator_is_done (iter)) {
3349                 gchar *msg_uid;
3350                 TnyHeader *header;
3351
3352                 header = TNY_HEADER (tny_iterator_get_current (iter));
3353                 msg_uid =  modest_tny_folder_get_header_unique_id (header);
3354                 if (msg_uid) {
3355                         modest_platform_emit_msg_read_changed_signal (msg_uid, TRUE);
3356                         g_free (msg_uid);
3357                 }
3358                 g_object_unref (header);
3359                 tny_iterator_next (iter);
3360         }
3361         g_object_unref (iter);
3362
3363         /* remove message from folder */
3364         modest_mail_operation_notify_start (self);
3365         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
3366                                       NULL, g_object_ref (self));
3367
3368 cleanup:
3369         if (remove_headers)
3370                 g_object_unref (remove_headers);
3371         if (header)
3372                 g_object_unref (header);
3373         if (folder)
3374                 g_object_unref (folder);
3375 }
3376
3377 static void
3378 notify_progress_of_multiple_messages (ModestMailOperation *self,
3379                                       TnyStatus *status,
3380                                       gint *last_total_bytes,
3381                                       gint *sum_total_bytes,
3382                                       gint total_bytes, 
3383                                       gboolean increment_done)
3384 {
3385         ModestMailOperationPrivate *priv;
3386         ModestMailOperationState *state;
3387         gboolean is_num_bytes = FALSE;
3388
3389         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3390
3391         /* We know that tinymail sends us information about
3392          *  transferred bytes with this particular message
3393          */
3394         if (status->message)
3395                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
3396
3397         state = modest_mail_operation_clone_state (self);
3398         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
3399                 /* We know that we're in a different message when the
3400                    total number of bytes to transfer is different. Of
3401                    course it could fail if we're transferring messages
3402                    of the same size, but this is a workarround */
3403                 if (status->of_total != *last_total_bytes) {
3404                         /* We need to increment the done when there is
3405                            no information about each individual
3406                            message, we need to do this in message
3407                            transfers, and we don't do it for getting
3408                            messages */
3409                         if (increment_done)
3410                                 priv->done++;
3411                         *sum_total_bytes += *last_total_bytes;
3412                         *last_total_bytes = status->of_total;
3413                 }
3414                 state->bytes_done += status->position + *sum_total_bytes;
3415                 state->bytes_total = total_bytes;
3416
3417                 /* Notify the status change. Only notify about changes
3418                    referred to bytes */
3419                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3420                                0, state, NULL);
3421         }
3422
3423         g_slice_free (ModestMailOperationState, state);
3424 }
3425
3426 static void
3427 transfer_msgs_status_cb (GObject *obj,
3428                          TnyStatus *status,  
3429                          gpointer user_data)
3430 {
3431         XFerMsgsAsyncHelper *helper;
3432
3433         g_return_if_fail (status != NULL);
3434
3435         /* Show only the status information we want */
3436         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
3437                 return;
3438
3439         helper = (XFerMsgsAsyncHelper *) user_data;
3440         g_return_if_fail (helper != NULL);       
3441
3442         /* Notify progress */
3443         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
3444                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
3445 }
3446
3447 static void
3448 transfer_msgs_sync_folder_cb (TnyFolder *self, 
3449                               gboolean cancelled, 
3450                               GError *err, 
3451                               gpointer user_data)
3452 {
3453         XFerMsgsAsyncHelper *helper;
3454         /* We don't care here about the results of the
3455            synchronization */
3456         helper = (XFerMsgsAsyncHelper *) user_data;
3457
3458         /* Notify about operation end */
3459         modest_mail_operation_notify_end (helper->mail_op);
3460
3461         /* If user defined callback function was defined, call it */
3462         if (helper->user_callback)
3463                 helper->user_callback (helper->mail_op, helper->user_data);
3464         
3465         /* Free */
3466         if (helper->more_msgs)
3467                 g_object_unref (helper->more_msgs);
3468         if (helper->headers)
3469                 g_object_unref (helper->headers);
3470         if (helper->dest_folder)
3471                 g_object_unref (helper->dest_folder);
3472         if (helper->mail_op)
3473                 g_object_unref (helper->mail_op);
3474         g_slice_free (XFerMsgsAsyncHelper, helper);
3475 }
3476
3477 static void
3478 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
3479 {
3480         XFerMsgsAsyncHelper *helper;
3481         ModestMailOperation *self;
3482         ModestMailOperationPrivate *priv;
3483         gboolean finished = TRUE;
3484
3485         helper = (XFerMsgsAsyncHelper *) user_data;
3486         self = helper->mail_op;
3487
3488         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3489
3490         if (cancelled) {
3491                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3492         } else if (err) {
3493                 priv->error = g_error_copy (err);
3494                 priv->done = 0;
3495                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
3496         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
3497                 if (helper->more_msgs) {
3498                         /* We'll transfer the next message in the list */
3499                         tny_iterator_next (helper->more_msgs);
3500                         if (!tny_iterator_is_done (helper->more_msgs)) {
3501                                 GObject *next_header;
3502                                 g_object_unref (helper->headers);
3503                                 helper->headers = tny_simple_list_new ();
3504                                 next_header = tny_iterator_get_current (helper->more_msgs);
3505                                 tny_list_append (helper->headers, next_header);
3506                                 g_object_unref (next_header);
3507                                 finished = FALSE;
3508                         }
3509                 }
3510                 if (finished) {
3511                         priv->done = 1;
3512                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3513                 }
3514         }
3515
3516         if (finished) {
3517                 /* Synchronize the source folder contents. This should
3518                    be done by tinymail but the camel_folder_sync it's
3519                    actually disabled in transfer_msgs_thread_clean
3520                    because it's supposed to cause hangs */
3521                 tny_folder_sync_async (folder, helper->delete, 
3522                                        transfer_msgs_sync_folder_cb, 
3523                                        NULL, helper);
3524         } else {
3525                 /* Transfer more messages */
3526                 tny_folder_transfer_msgs_async (folder,
3527                                                 helper->headers,
3528                                                 helper->dest_folder,
3529                                                 helper->delete,
3530                                                 transfer_msgs_cb,
3531                                                 transfer_msgs_status_cb,
3532                                                 helper);
3533         }
3534 }
3535
3536 /* Computes the size of the messages the headers in the list belongs
3537    to. If num_elements is different from 0 then it only takes into
3538    account the first num_elements for the calculation */
3539 static guint
3540 compute_message_list_size (TnyList *headers, 
3541                            guint num_elements)
3542 {
3543         TnyIterator *iter;
3544         guint size = 0, element = 0;
3545
3546         /* If num_elements is not valid then take all into account */
3547         if ((num_elements <= 0) || (num_elements > tny_list_get_length (headers)))
3548                 num_elements = tny_list_get_length (headers);
3549
3550         iter = tny_list_create_iterator (headers);
3551         while (!tny_iterator_is_done (iter) && element < num_elements) {
3552                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3553                 size += tny_header_get_message_size (header);
3554                 g_object_unref (header);
3555                 tny_iterator_next (iter);
3556                 element++;
3557         }
3558         g_object_unref (iter);
3559
3560         return size;
3561 }
3562
3563 void
3564 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
3565                                  TnyList *headers, 
3566                                  TnyFolder *folder, 
3567                                  gboolean delete_original,
3568                                  XferMsgsAsyncUserCallback user_callback,
3569                                  gpointer user_data)
3570 {
3571         ModestMailOperationPrivate *priv = NULL;
3572         TnyIterator *iter = NULL;
3573         TnyFolder *src_folder = NULL;
3574         XFerMsgsAsyncHelper *helper = NULL;
3575         TnyHeader *header = NULL;
3576         ModestTnyFolderRules rules = 0;
3577         TnyAccount *dst_account = NULL;
3578         gboolean leave_on_server;
3579         ModestMailOperationState *state;
3580         ModestProtocolRegistry *protocol_registry;
3581         ModestProtocolType account_protocol;
3582
3583         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
3584         g_return_if_fail (headers && TNY_IS_LIST (headers));
3585         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
3586
3587         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3588         protocol_registry = modest_runtime_get_protocol_registry ();
3589
3590         priv->total = tny_list_get_length (headers);
3591         priv->done = 0;
3592         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3593         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3594
3595         /* Apply folder rules */
3596         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3597         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3598                 /* Set status failed and set an error */
3599                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3600                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3601                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
3602                              _CS("ckct_ib_unable_to_paste_here"));
3603                 /* Notify the queue */
3604                 modest_mail_operation_notify_end (self);
3605                 return;
3606         }
3607                 
3608         /* Get source folder */
3609         iter = tny_list_create_iterator (headers);
3610         header = TNY_HEADER (tny_iterator_get_current (iter));
3611         if (header) {
3612                 src_folder = tny_header_get_folder (header);
3613                 g_object_unref (header);
3614         }
3615         g_object_unref (iter);
3616
3617         if (src_folder == NULL) {
3618                 /* Notify the queue */
3619                 modest_mail_operation_notify_end (self);
3620
3621                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
3622                 return;
3623         }
3624
3625         
3626         /* Check folder source and destination */
3627         if (src_folder == folder) {
3628                 /* Set status failed and set an error */
3629                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3630                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3631                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
3632                              _("mail_in_ui_folder_copy_target_error"));
3633                 
3634                 /* Notify the queue */
3635                 modest_mail_operation_notify_end (self);
3636                 
3637                 /* Free */
3638                 g_object_unref (src_folder);            
3639                 return;
3640         }
3641
3642         /* Create the helper */
3643         helper = g_slice_new0 (XFerMsgsAsyncHelper);
3644         helper->mail_op = g_object_ref(self);
3645         helper->dest_folder = g_object_ref(folder);
3646         helper->user_callback = user_callback;
3647         helper->user_data = user_data;
3648         helper->last_total_bytes = 0;
3649         helper->sum_total_bytes = 0;
3650         helper->total_bytes = compute_message_list_size (headers, 0);
3651
3652         /* Get account and set it into mail_operation */
3653         priv->account = modest_tny_folder_get_account (src_folder);
3654         dst_account = modest_tny_folder_get_account (folder);
3655
3656         if (priv->account == dst_account) {
3657                 /* Transfer all messages at once using the fast
3658                  * method. Note that depending on the server this
3659                  * might not be that fast, and might not be
3660                  * user-cancellable either */
3661                 helper->headers = g_object_ref (headers);
3662                 helper->more_msgs = NULL;
3663         } else {
3664                 /* Transfer messages one by one so the user can cancel
3665                  * the operation */
3666                 GObject *hdr;
3667                 helper->headers = tny_simple_list_new ();
3668                 helper->more_msgs = tny_list_create_iterator (headers);
3669                 hdr = tny_iterator_get_current (helper->more_msgs);
3670                 tny_list_append (helper->headers, hdr);
3671                 g_object_unref (hdr);
3672         }
3673
3674         /* If leave_on_server is set to TRUE then don't use
3675            delete_original, we always pass FALSE. This is because
3676            otherwise tinymail will try to sync the source folder and
3677            this could cause an error if we're offline while
3678            transferring an already downloaded message from a POP
3679            account */
3680         account_protocol = modest_tny_account_get_protocol_type (priv->account);
3681         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_protocol)) {
3682                 const gchar *account_name;
3683
3684                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
3685                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3686                                                                           account_name);
3687         } else {
3688                 leave_on_server = FALSE;
3689         }
3690
3691         /* Do not delete messages if leave on server is TRUE */
3692         helper->delete = (leave_on_server) ? FALSE : delete_original;
3693
3694         modest_mail_operation_notify_start (self);
3695
3696         /* Start notifying progress */
3697         state = modest_mail_operation_clone_state (self);
3698         state->done = 0;
3699         state->total = 0;
3700         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3701         g_slice_free (ModestMailOperationState, state);
3702
3703         tny_folder_transfer_msgs_async (src_folder, 
3704                                         helper->headers, 
3705                                         folder, 
3706                                         helper->delete, 
3707                                         transfer_msgs_cb, 
3708                                         transfer_msgs_status_cb,
3709                                         helper);
3710         g_object_unref (src_folder);
3711         g_object_unref (dst_account);
3712 }
3713
3714
3715 static void
3716 on_refresh_folder (TnyFolder   *folder, 
3717                    gboolean     cancelled, 
3718                    GError     *error,
3719                    gpointer     user_data)
3720 {
3721         RefreshAsyncHelper *helper = NULL;
3722         ModestMailOperation *self = NULL;
3723         ModestMailOperationPrivate *priv = NULL;
3724
3725         helper = (RefreshAsyncHelper *) user_data;
3726         self = helper->mail_op;
3727         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3728
3729         g_return_if_fail(priv!=NULL);
3730
3731         if (error) {
3732                 priv->error = g_error_copy (error);
3733                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3734                 goto out;
3735         }
3736
3737         if (cancelled) {
3738                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3739                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3740                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3741                              _("Error trying to refresh the contents of %s"),
3742                              tny_folder_get_name (folder));
3743                 goto out;
3744         }
3745
3746         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3747  out:
3748
3749         /* Call user defined callback, if it exists */
3750         if (helper->user_callback) {
3751
3752                 /* This is not a GDK lock because we are a Tinymail callback and
3753                  * Tinymail already acquires the Gdk lock */
3754                 helper->user_callback (self, folder, helper->user_data);
3755         }
3756
3757         /* Free */
3758         g_slice_free (RefreshAsyncHelper, helper);
3759
3760         /* Notify about operation end */
3761         modest_mail_operation_notify_end (self);
3762         g_object_unref(self);
3763 }
3764
3765 static void
3766 on_refresh_folder_status_update (GObject *obj,
3767                                  TnyStatus *status,
3768                                  gpointer user_data)
3769 {
3770         RefreshAsyncHelper *helper = NULL;
3771         ModestMailOperation *self = NULL;
3772         ModestMailOperationPrivate *priv = NULL;
3773         ModestMailOperationState *state;
3774
3775         g_return_if_fail (user_data != NULL);
3776         g_return_if_fail (status != NULL);
3777
3778         /* Show only the status information we want */
3779         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
3780                 return;
3781
3782         helper = (RefreshAsyncHelper *) user_data;
3783         self = helper->mail_op;
3784         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3785
3786         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3787
3788         priv->done = status->position;
3789         priv->total = status->of_total;
3790
3791         state = modest_mail_operation_clone_state (self);
3792
3793         /* This is not a GDK lock because we are a Tinymail callback and
3794          * Tinymail already acquires the Gdk lock */
3795         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3796
3797         g_slice_free (ModestMailOperationState, state);
3798 }
3799
3800 void 
3801 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3802                                        TnyFolder *folder,
3803                                        RefreshAsyncUserCallback user_callback,
3804                                        gpointer user_data)
3805 {
3806         ModestMailOperationPrivate *priv = NULL;
3807         RefreshAsyncHelper *helper = NULL;
3808
3809         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3810
3811         /* Check memory low */
3812         if (_check_memory_low (self)) {
3813                 if (user_callback)
3814                         user_callback (self, folder, user_data);
3815                 /* Notify about operation end */
3816                 modest_mail_operation_notify_end (self);
3817                 return;
3818         }
3819
3820         /* Get account and set it into mail_operation */
3821         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3822         priv->account = modest_tny_folder_get_account  (folder);
3823         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3824
3825         /* Create the helper */
3826         helper = g_slice_new0 (RefreshAsyncHelper);
3827         helper->mail_op = g_object_ref(self);
3828         helper->user_callback = user_callback;
3829         helper->user_data = user_data;
3830
3831         modest_mail_operation_notify_start (self);
3832         
3833         /* notify that the operation was started */
3834         ModestMailOperationState *state;
3835         state = modest_mail_operation_clone_state (self);
3836         state->done = 0;
3837         state->total = 0;
3838         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3839                         0, state, NULL);
3840         g_slice_free (ModestMailOperationState, state);
3841         
3842         tny_folder_refresh_async (folder,
3843                                   on_refresh_folder,
3844                                   on_refresh_folder_status_update,
3845                                   helper);
3846 }
3847
3848 static void
3849 run_queue_notify_and_destroy (RunQueueHelper *helper,
3850                               ModestMailOperationStatus status)
3851 {
3852         ModestMailOperationPrivate *priv;
3853
3854         /* Disconnect */
3855         if (helper->error_handler &&
3856             g_signal_handler_is_connected (helper->queue, helper->error_handler))
3857                 g_signal_handler_disconnect (helper->queue, helper->error_handler);
3858         if (helper->start_handler &&
3859             g_signal_handler_is_connected (helper->queue, helper->start_handler))
3860                 g_signal_handler_disconnect (helper->queue, helper->start_handler);
3861         if (helper->stop_handler &&
3862             g_signal_handler_is_connected (helper->queue, helper->stop_handler))
3863                 g_signal_handler_disconnect (helper->queue, helper->stop_handler);
3864
3865         /* Set status */
3866         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
3867         priv->status = status;
3868
3869         /* Notify end */
3870         modest_mail_operation_notify_end (helper->self);
3871
3872         /* Free data */
3873         g_object_unref (helper->queue);
3874         g_object_unref (helper->self);
3875         g_slice_free (RunQueueHelper, helper);
3876 }
3877
3878 static void
3879 run_queue_stop (ModestTnySendQueue *queue,
3880                 gpointer user_data)
3881 {
3882         RunQueueHelper *helper;
3883
3884         g_debug ("%s sending queue stopped", __FUNCTION__);
3885
3886         helper = (RunQueueHelper *) user_data;
3887         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
3888 }
3889
3890 void
3891 modest_mail_operation_run_queue (ModestMailOperation *self,
3892                                  ModestTnySendQueue *queue)
3893 {
3894         ModestMailOperationPrivate *priv;
3895         RunQueueHelper *helper;
3896
3897         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3898         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3899         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3900
3901         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3902         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3903         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3904
3905         /* Create the helper */
3906         helper = g_slice_new0 (RunQueueHelper);
3907         helper->queue = g_object_ref (queue);
3908         helper->self = g_object_ref (self);
3909         helper->stop_handler = g_signal_connect (queue, "queue-stop", 
3910                                                  G_CALLBACK (run_queue_stop), 
3911                                                  helper);
3912
3913         /* Notify operation has started */
3914         modest_mail_operation_notify_start (self);
3915         g_debug ("%s, run queue started", __FUNCTION__);
3916 }
3917
3918 static void
3919 queue_wakeup_callback (ModestTnySendQueue *queue,
3920                        gboolean cancelled,
3921                        GError *error,
3922                        gpointer userdata)
3923 {
3924         ModestMailOperation *mail_op;
3925         ModestMailOperationPrivate *priv;
3926
3927         mail_op = (ModestMailOperation *) userdata;
3928         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3929
3930         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3931         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (queue));
3932
3933         /* Notify end */
3934         modest_mail_operation_notify_end (mail_op);
3935         g_object_unref (mail_op);
3936 }
3937
3938 void
3939 modest_mail_operation_queue_wakeup (ModestMailOperation *self,
3940                                     ModestTnySendQueue *queue)
3941 {
3942         ModestMailOperationPrivate *priv;
3943
3944         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3945         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3946         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3947
3948         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3949         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3950         priv->op_type = MODEST_MAIL_OPERATION_TYPE_QUEUE_WAKEUP;
3951
3952         g_object_ref (self);
3953
3954         modest_tny_send_queue_wakeup (queue, queue_wakeup_callback, self);
3955         modest_mail_operation_notify_start (self);
3956 }
3957
3958 static void
3959 shutdown_callback (ModestTnyAccountStore *account_store, gpointer userdata)
3960 {
3961         ModestMailOperation *self = (ModestMailOperation *) userdata;
3962         ModestMailOperationPrivate *priv;
3963
3964         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3965         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3966         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3967
3968         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3969
3970         modest_mail_operation_notify_end (self);
3971         g_object_unref (self);
3972 }
3973
3974 void
3975 modest_mail_operation_shutdown (ModestMailOperation *self, ModestTnyAccountStore *account_store)
3976 {
3977         ModestMailOperationPrivate *priv;
3978
3979         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3980         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3981         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3982
3983         modest_mail_operation_queue_set_running_shutdown (modest_runtime_get_mail_operation_queue ());
3984
3985         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3986         priv->account = NULL;
3987         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SHUTDOWN;
3988
3989         modest_mail_operation_notify_start (self);
3990         g_object_ref (self);
3991         modest_tny_account_store_shutdown (account_store, shutdown_callback, self);
3992 }
3993
3994 static void
3995 sync_folder_finish_callback (TnyFolder *self,
3996                              gboolean cancelled,
3997                              GError *err,
3998                              gpointer user_data)
3999
4000 {
4001         ModestMailOperationPrivate *priv;
4002         SyncFolderHelper *helper;
4003
4004         helper = (SyncFolderHelper *) user_data;
4005         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->mail_op);
4006
4007         /* If canceled by the user, ignore the error given by Tinymail */
4008         if (cancelled) {
4009                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
4010         } else if (err) {
4011                 /* If the operation was a sync then the status is
4012                    failed, but if it's part of another operation then
4013                    just set it as finished with errors */
4014                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
4015                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
4016                 else
4017                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
4018                 priv->error = g_error_copy ((const GError *) err);
4019                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
4020         } else {
4021                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
4022         }
4023
4024         /* User callback */
4025         if (helper->user_callback)
4026                 helper->user_callback (helper->mail_op, self, helper->user_data);
4027
4028         modest_mail_operation_notify_end (helper->mail_op);
4029
4030         /* Frees */
4031         g_object_unref (helper->mail_op);
4032         g_slice_free (SyncFolderHelper, helper);
4033 }
4034
4035 void
4036 modest_mail_operation_sync_folder (ModestMailOperation *self,
4037                                    TnyFolder *folder,
4038                                    gboolean expunge,
4039                                    SyncFolderCallback callback,
4040                                    gpointer user_data)
4041 {
4042         ModestMailOperationPrivate *priv;
4043         SyncFolderHelper *helper;
4044
4045         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
4046         g_return_if_fail (TNY_IS_FOLDER (folder));
4047         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
4048
4049         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
4050         priv->account = modest_tny_folder_get_account (folder);
4051         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
4052
4053         /* Create helper */
4054         helper = g_slice_new0 (SyncFolderHelper);
4055         helper->mail_op = g_object_ref (self);
4056         helper->user_callback = callback;
4057         helper->user_data = user_data;
4058
4059         modest_mail_operation_notify_start (self);
4060         tny_folder_sync_async (folder, expunge,
4061                                (TnyFolderCallback) sync_folder_finish_callback,
4062                                NULL, helper);
4063 }
4064
4065 static void
4066 modest_mail_operation_notify_start (ModestMailOperation *self)
4067 {
4068         ModestMailOperationPrivate *priv = NULL;
4069
4070         g_return_if_fail (self);
4071
4072         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4073
4074         /* Ensure that all the fields are filled correctly */
4075         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
4076
4077         /* Notify the observers about the mail operation. We do not
4078            wrapp this emission because we assume that this function is
4079            always called from within the main lock */
4080         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
4081 }
4082
4083 /**
4084  *
4085  * It's used by the mail operation queue to notify the observers
4086  * attached to that signal that the operation finished. We need to use
4087  * that because tinymail does not give us the progress of a given
4088  * operation when it finishes (it directly calls the operation
4089  * callback).
4090  */
4091 static void
4092 modest_mail_operation_notify_end (ModestMailOperation *self)
4093 {
4094         ModestMailOperationPrivate *priv = NULL;
4095
4096         g_return_if_fail (self);
4097
4098         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4099
4100         /* Notify the observers about the mail operation end. We do
4101            not wrapp this emission because we assume that this
4102            function is always called from within the main lock */
4103         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
4104
4105         /* Remove the error user data */
4106         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
4107                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
4108 }
4109
4110 TnyAccount *
4111 modest_mail_operation_get_account (ModestMailOperation *self)
4112 {
4113         ModestMailOperationPrivate *priv = NULL;
4114
4115         g_return_val_if_fail (self, NULL);
4116
4117         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4118
4119         return (priv->account) ? g_object_ref (priv->account) : NULL;
4120 }
4121
4122 void
4123 modest_mail_operation_noop (ModestMailOperation *self)
4124 {
4125         ModestMailOperationPrivate *priv = NULL;
4126
4127         g_return_if_fail (self);
4128
4129         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4130         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
4131         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
4132         priv->done = 0;
4133         priv->total = 0;
4134
4135         /* This mail operation does nothing actually */
4136         modest_mail_operation_notify_start (self);
4137         modest_mail_operation_notify_end (self);
4138 }
4139
4140
4141 gchar*
4142 modest_mail_operation_to_string (ModestMailOperation *self)
4143 {
4144         const gchar *type, *status, *account_id;
4145         ModestMailOperationPrivate *priv = NULL;
4146         
4147         g_return_val_if_fail (self, NULL);
4148
4149         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4150
4151         /* new operations don't have anything interesting */
4152         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
4153                 return g_strdup_printf ("%p <new operation>", self);
4154         
4155         switch (priv->op_type) {
4156         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
4157         case MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE:    type= "SEND-AND-RECEIVE";    break;
4158         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
4159         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
4160         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
4161         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
4162         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
4163         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
4164         case MODEST_MAIL_OPERATION_TYPE_SHUTDOWN: type= "SHUTDOWN"; break;
4165         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
4166         default: type = "UNEXPECTED"; break;
4167         }
4168
4169         switch (priv->status) {
4170         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
4171         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
4172         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
4173         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
4174         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
4175         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
4176         default:                                                status= "UNEXPECTED"; break;
4177         } 
4178
4179         account_id = priv->account ? tny_account_get_id (priv->account) : "";
4180
4181         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
4182                                 priv->done, priv->total,
4183                                 priv->error && priv->error->message ? priv->error->message : "");
4184 }
4185
4186 /* 
4187  * Once the mail operations were objects this will be no longer
4188  * needed. I don't like it, but we need it for the moment
4189  */
4190 static gboolean
4191 _check_memory_low (ModestMailOperation *mail_op)
4192 {
4193         if (modest_platform_check_memory_low (NULL, FALSE)) {
4194                 ModestMailOperationPrivate *priv;
4195
4196                 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
4197                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
4198                 g_set_error (&(priv->error),
4199                              MODEST_MAIL_OPERATION_ERROR,
4200                              MODEST_MAIL_OPERATION_ERROR_LOW_MEMORY,
4201                              "Not enough memory to complete the operation");
4202                 return TRUE;
4203         } else {
4204                 return FALSE;
4205         }
4206 }