Preserver headers of messages in drafts and sending messages.
[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_new_mail (ModestMailOperation *self,
1107                                      TnyTransportAccount *transport_account,
1108                                      TnyMsg *draft_msg,
1109                                      const gchar *from,  const gchar *to,
1110                                      const gchar *cc,  const gchar *bcc,
1111                                      const gchar *subject, const gchar *plain_body,
1112                                      const gchar *html_body,
1113                                      const GList *attachments_list,
1114                                      const GList *images_list,
1115                                      const gchar *references,
1116                                      const gchar *in_reply_to,
1117                                      TnyHeaderFlags priority_flags,
1118                                      TnyList *header_pairs)
1119 {
1120         ModestMailOperationPrivate *priv = NULL;
1121         SendNewMailInfo *info;
1122
1123         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1124         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1125
1126         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1127         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1128         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
1129         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1130
1131         modest_mail_operation_notify_start (self);
1132
1133         /* Check parametters */
1134         if (to == NULL) {
1135                 /* Set status failed and set an error */
1136                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1137                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1138                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1139                              _("Error trying to send a mail. You need to set at least one recipient"));
1140                 modest_mail_operation_notify_end (self);
1141                 return;
1142         }
1143         info = g_slice_new0 (SendNewMailInfo);
1144         info->transport_account = transport_account;
1145         if (transport_account)
1146                 g_object_ref (transport_account);
1147         info->draft_msg = draft_msg;
1148         if (draft_msg)
1149                 g_object_ref (draft_msg);
1150
1151
1152         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1153                                           attachments_list, images_list, priority_flags,
1154                                           references, in_reply_to,
1155                                           header_pairs,
1156                                           modest_mail_operation_send_new_mail_cb, info);
1157
1158 }
1159
1160 typedef struct
1161 {
1162         ModestMailOperation *mailop;
1163         TnyMsg *msg;
1164         SaveToDraftstCallback callback;
1165         gpointer userdata;
1166 } FinishSaveRemoteDraftInfo;
1167
1168 static void
1169 finish_save_remote_draft (ModestAccountProtocol *protocol,
1170                           GError *err,
1171                           const gchar *account_id,
1172                           TnyMsg *new_remote_msg,
1173                           TnyMsg *new_msg,
1174                           TnyMsg *old_msg,
1175                           gpointer userdata)
1176 {
1177         FinishSaveRemoteDraftInfo *info = (FinishSaveRemoteDraftInfo *) userdata;
1178         ModestMailOperationPrivate *priv = NULL;
1179
1180         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1181
1182         if (!priv->error && err != NULL) {
1183                 /* Priority for errors in save to local stage */
1184                 priv->error = g_error_copy (err);
1185                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1186         }
1187
1188         if (info->callback)
1189                 info->callback (info->mailop, info->msg, info->userdata);
1190
1191         if (info->msg)
1192                 g_object_unref (info->msg);
1193
1194         modest_mail_operation_notify_end (info->mailop);
1195         g_object_unref (info->mailop);
1196
1197         g_slice_free (FinishSaveRemoteDraftInfo, info);
1198 }
1199
1200 typedef struct
1201 {
1202         TnyTransportAccount *transport_account;
1203         TnyMsg *draft_msg;
1204         SaveToDraftstCallback callback;
1205         gpointer user_data;
1206         TnyFolder *drafts;
1207         TnyMsg *msg;
1208         ModestMailOperation *mailop;
1209 } SaveToDraftsAddMsgInfo;
1210
1211 static void
1212 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
1213                                                 gboolean canceled,
1214                                                 GError *err,
1215                                                 gpointer userdata)
1216 {
1217         ModestMailOperationPrivate *priv = NULL;
1218         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
1219         GError *io_error = NULL;
1220         gboolean callback_called = FALSE;
1221
1222         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1223
1224         if (priv->error && priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1225                 io_error = priv->error;
1226                 priv->error = NULL;
1227         }
1228         if (priv->error) {
1229                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
1230                 g_error_free(priv->error);
1231         }
1232
1233         priv->error = (err == NULL) ? NULL : g_error_copy(err);
1234
1235         if ((!priv->error) && (info->draft_msg != NULL)) {
1236                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
1237                 TnyFolder *src_folder = tny_header_get_folder (header);
1238
1239                 g_debug ("--- REMOVE AND SYNC");
1240                 /* Remove the old draft */
1241                 tny_folder_remove_msg (src_folder, header, NULL);
1242
1243                 /* Synchronize to expunge and to update the msg counts */
1244                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1245                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1246                 g_debug ("--- REMOVED - SYNCED");
1247
1248                 g_object_unref (G_OBJECT(header));
1249                 g_object_unref (G_OBJECT(src_folder));
1250         }
1251
1252         if (priv->error) {
1253                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1254                 if (io_error) {
1255                         g_error_free (io_error);
1256                         io_error = NULL;
1257                 }
1258         } else if (io_error) {
1259                 priv->error = io_error;
1260                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
1261         } else {
1262                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1263         }
1264
1265         if (info->transport_account) {
1266                 ModestProtocolType transport_protocol_type;
1267                 ModestProtocol *transport_protocol;
1268
1269                 transport_protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (info->transport_account));
1270
1271                 transport_protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1272                                                                                     transport_protocol_type);
1273                 if (transport_protocol && MODEST_IS_ACCOUNT_PROTOCOL (transport_protocol)) {
1274                         FinishSaveRemoteDraftInfo *srd_info = g_slice_new (FinishSaveRemoteDraftInfo);
1275                         srd_info->mailop = info->mailop?g_object_ref (info->mailop):NULL;
1276                         srd_info->msg = info->msg?g_object_ref (info->msg):NULL;
1277                         srd_info->callback = info->callback;
1278                         srd_info->userdata = info->user_data;
1279                         modest_account_protocol_save_remote_draft (MODEST_ACCOUNT_PROTOCOL (transport_protocol), 
1280                                                                    tny_account_get_id (TNY_ACCOUNT (info->transport_account)),
1281                                                                    info->msg, info->draft_msg,
1282                                                                    finish_save_remote_draft,
1283                                                                    srd_info);
1284                                                                    
1285                         callback_called = TRUE;
1286                 }
1287         }
1288
1289         /* Call the user callback */
1290         if (!callback_called && info->callback)
1291                 info->callback (info->mailop, info->msg, info->user_data);
1292
1293         if (info->transport_account)
1294                 g_object_unref (G_OBJECT(info->transport_account));
1295         if (info->draft_msg)
1296                 g_object_unref (G_OBJECT (info->draft_msg));
1297         if (info->drafts)
1298                 g_object_unref (G_OBJECT(info->drafts));
1299         if (info->msg)
1300                 g_object_unref (G_OBJECT (info->msg));
1301
1302         if (!callback_called)
1303                 modest_mail_operation_notify_end (info->mailop);
1304         if (info->mailop)
1305                 g_object_unref(info->mailop);
1306         g_slice_free (SaveToDraftsAddMsgInfo, info);
1307 }
1308
1309 typedef struct
1310 {
1311         TnyTransportAccount *transport_account;
1312         TnyMsg *draft_msg;
1313         SaveToDraftstCallback callback;
1314         gpointer user_data;
1315 } SaveToDraftsInfo;
1316
1317 static void
1318 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1319                                          TnyMsg *msg,
1320                                          gpointer userdata)
1321 {
1322         TnyFolder *drafts = NULL;
1323         ModestMailOperationPrivate *priv = NULL;
1324         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1325
1326         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1327
1328         if (!msg) {
1329                 if (!(priv->error)) {
1330                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1331                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1332                                      "modest: failed to create a new msg\n");
1333                 }
1334         } else {
1335                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1336                                                                 TNY_FOLDER_TYPE_DRAFTS);
1337                 if (!drafts && !(priv->error)) {
1338                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1339                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1340                                      "modest: failed to create a new msg\n");
1341                 }
1342         }
1343
1344         if (!priv->error || priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1345                 if (drafts) {
1346                         SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1347                         cb_info->transport_account = g_object_ref(info->transport_account);
1348                         cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1349                         cb_info->callback = info->callback;
1350                         cb_info->user_data = info->user_data;
1351                         cb_info->drafts = g_object_ref(drafts);
1352                         cb_info->msg = g_object_ref(msg);
1353                         cb_info->mailop = g_object_ref(self);
1354                         tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1355                                                  NULL, cb_info);
1356                 }
1357         } else {
1358                 /* Call the user callback */
1359                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1360                 if (info->callback)
1361                         info->callback (self, msg, info->user_data);
1362                 modest_mail_operation_notify_end (self);
1363         }
1364
1365         if (drafts)
1366                 g_object_unref (G_OBJECT(drafts));
1367         if (info->draft_msg)
1368                 g_object_unref (G_OBJECT (info->draft_msg));
1369         if (info->transport_account)
1370                 g_object_unref (G_OBJECT(info->transport_account));
1371         g_slice_free (SaveToDraftsInfo, info);
1372 }
1373
1374 void
1375 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1376                                       TnyTransportAccount *transport_account,
1377                                       TnyMsg *draft_msg,
1378                                       const gchar *from,  const gchar *to,
1379                                       const gchar *cc,  const gchar *bcc,
1380                                       const gchar *subject, const gchar *plain_body,
1381                                       const gchar *html_body,
1382                                       const GList *attachments_list,
1383                                       const GList *images_list,
1384                                       TnyHeaderFlags priority_flags,
1385                                       const gchar *references,
1386                                       const gchar *in_reply_to,
1387                                       TnyList *header_pairs,
1388                                       SaveToDraftstCallback callback,
1389                                       gpointer user_data)
1390 {
1391         ModestMailOperationPrivate *priv = NULL;
1392         SaveToDraftsInfo *info = NULL;
1393
1394         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1395         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1396
1397         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1398
1399         /* Get account and set it into mail_operation */
1400         priv->account = g_object_ref (transport_account);
1401         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1402
1403         info = g_slice_new0 (SaveToDraftsInfo);
1404         info->transport_account = g_object_ref (transport_account);
1405         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1406         info->callback = callback;
1407         info->user_data = user_data;
1408
1409         g_debug ("--- CREATE MESSAGE");
1410         modest_mail_operation_notify_start (self);
1411         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1412                                           attachments_list, images_list, priority_flags,
1413                                           references, in_reply_to,
1414                                           header_pairs,
1415                                           modest_mail_operation_save_to_drafts_cb, info);
1416 }
1417
1418 typedef struct
1419 {
1420         ModestMailOperation *mail_op;
1421         TnyMimePart *mime_part;
1422         gssize size;
1423         GetMimePartSizeCallback callback;
1424         gpointer userdata;
1425 } GetMimePartSizeInfo;
1426
1427 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1428 /* We use this folder observer to track the headers that have been
1429  * added to a folder */
1430 typedef struct {
1431         GObject parent;
1432         TnyList *new_headers;
1433 } InternalFolderObserver;
1434
1435 typedef struct {
1436         GObjectClass parent;
1437 } InternalFolderObserverClass;
1438
1439 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1440
1441 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1442                          internal_folder_observer,
1443                          G_TYPE_OBJECT,
1444                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1445
1446
1447 static void
1448 foreach_add_item (gpointer header, gpointer user_data)
1449 {
1450         tny_list_append (TNY_LIST (user_data), G_OBJECT (header));
1451 }
1452
1453 /* This is the method that looks for new messages in a folder */
1454 static void
1455 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1456 {
1457         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1458         
1459         TnyFolderChangeChanged changed;
1460
1461         changed = tny_folder_change_get_changed (change);
1462
1463         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1464                 TnyList *list;
1465
1466                 /* Get added headers */
1467                 list = tny_simple_list_new ();
1468                 tny_folder_change_get_added_headers (change, list);
1469
1470                 /* Add them to the folder observer */
1471                 tny_list_foreach (list, foreach_add_item, 
1472                                   derived->new_headers);
1473
1474                 g_object_unref (G_OBJECT (list));
1475         }
1476 }
1477
1478 static void
1479 internal_folder_observer_init (InternalFolderObserver *self) 
1480 {
1481         self->new_headers = tny_simple_list_new ();
1482 }
1483 static void
1484 internal_folder_observer_finalize (GObject *object) 
1485 {
1486         InternalFolderObserver *self;
1487
1488         self = (InternalFolderObserver *) object;
1489         g_object_unref (self->new_headers);
1490
1491         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1492 }
1493 static void
1494 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1495 {
1496         iface->update = internal_folder_observer_update;
1497 }
1498 static void
1499 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1500 {
1501         GObjectClass *object_class;
1502
1503         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1504         object_class = (GObjectClass*) klass;
1505         object_class->finalize = internal_folder_observer_finalize;
1506 }
1507
1508 static void
1509 destroy_update_account_info (UpdateAccountInfo *info)
1510 {
1511         g_free (info->account_name);
1512         g_object_unref (info->folders);
1513         g_object_unref (info->mail_op);
1514         g_slice_free (UpdateAccountInfo, info);
1515 }
1516
1517
1518 static void
1519 update_account_send_mail (UpdateAccountInfo *info)
1520 {
1521         TnyTransportAccount *transport_account = NULL;
1522         ModestTnyAccountStore *account_store;
1523
1524         if (info->update_folder_counts)
1525                 return;
1526
1527         account_store = modest_runtime_get_account_store ();
1528
1529         /* We don't try to send messages while sending mails is blocked */
1530         if (modest_tny_account_store_is_send_mail_blocked (account_store))
1531                 return;
1532
1533         /* Get the transport account */
1534         transport_account = (TnyTransportAccount *) 
1535                 modest_tny_account_store_get_server_account (account_store, info->account_name, 
1536                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1537
1538         if (transport_account) {
1539                 ModestTnySendQueue *send_queue;
1540                 TnyFolder *outbox;
1541                 guint num_messages;
1542
1543                 send_queue = modest_runtime_get_send_queue (transport_account, TRUE);
1544                 g_object_unref (transport_account);
1545
1546                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1547                         /* Get outbox folder */
1548                         outbox = tny_send_queue_get_outbox (TNY_SEND_QUEUE (send_queue));
1549                         if (outbox) { /* this could fail in some cases */
1550                                 num_messages = tny_folder_get_all_count (outbox);
1551                                 g_object_unref (outbox);
1552                         } else {
1553                                 g_warning ("%s: could not get outbox", __FUNCTION__);
1554                                 num_messages = 0;
1555                         }
1556
1557                         if (num_messages != 0) {
1558                                 ModestMailOperation *mail_op;
1559                                 /* Reenable suspended items */
1560                                 mail_op = modest_mail_operation_new (NULL);
1561                                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1562                                                                  mail_op);
1563                                 modest_mail_operation_queue_wakeup (mail_op, MODEST_TNY_SEND_QUEUE (send_queue));
1564
1565                                 /* Try to send */
1566                                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1567                                                                                   info->interactive);
1568                         }
1569                 }
1570         }
1571 }
1572
1573 static void
1574 update_account_get_msg_async_cb (TnyFolder *folder, 
1575                                  gboolean canceled, 
1576                                  TnyMsg *msg, 
1577                                  GError *err, 
1578                                  gpointer user_data)
1579 {
1580         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1581         ModestMailOperationPrivate *priv;
1582
1583         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (msg_info->mail_op);
1584         priv->done++;
1585
1586         if (TNY_IS_MSG (msg)) {
1587                 TnyHeader *header = tny_msg_get_header (msg);
1588
1589                 if (header) {
1590                         ModestMailOperationState *state;
1591                         state = modest_mail_operation_clone_state (msg_info->mail_op);
1592                         msg_info->sum_total_bytes += tny_header_get_message_size (header);
1593                         state->bytes_done = msg_info->sum_total_bytes;
1594                         state->bytes_total = msg_info->total_bytes;
1595
1596                         /* Notify the status change. Only notify about changes
1597                            referred to bytes */
1598                         g_signal_emit (G_OBJECT (msg_info->mail_op), 
1599                                        signals[PROGRESS_CHANGED_SIGNAL], 
1600                                        0, state, NULL);
1601
1602                         g_object_unref (header);
1603                         g_slice_free (ModestMailOperationState, state);
1604                 }
1605         }
1606
1607         if (priv->done == priv->total) {
1608                 TnyList *new_headers;
1609                 UpdateAccountInfo *info;
1610
1611                 /* After getting all the messages send the ones in the
1612                    outboxes */
1613                 info = (UpdateAccountInfo *) msg_info->user_data;
1614                 update_account_send_mail (info);
1615
1616                 /* Check if the operation was a success */
1617                 if (!priv->error)
1618                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1619                 
1620                 /* Call the user callback and free */
1621                 new_headers = tny_iterator_get_list (msg_info->more_msgs);
1622                 update_account_notify_user_and_free (info, new_headers);
1623
1624                 /* Delete the helper */
1625                 if (msg_info->msg)
1626                         g_object_unref (msg_info->msg);
1627                 g_object_unref (msg_info->more_msgs);
1628                 g_object_unref (msg_info->mail_op);
1629                 g_slice_free (GetMsgInfo, msg_info);
1630         }
1631 }
1632
1633 static void
1634 update_account_notify_user_and_free (UpdateAccountInfo *info, 
1635                                      TnyList *new_headers)
1636 {
1637         /* Set the account back to not busy */
1638         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), 
1639                                              info->account_name, FALSE);
1640         
1641         /* User callback */
1642         if (info->callback)
1643                 info->callback (info->mail_op, new_headers, info->user_data);
1644         
1645         /* Mail operation end */
1646         modest_mail_operation_notify_end (info->mail_op);
1647
1648         /* Frees */
1649         if (new_headers)
1650                 g_object_unref (new_headers);
1651         destroy_update_account_info (info);
1652 }
1653
1654 static void
1655 inbox_refreshed_cb (TnyFolder *inbox, 
1656                     gboolean canceled, 
1657                     GError *err, 
1658                     gpointer user_data)
1659 {       
1660         UpdateAccountInfo *info;
1661         ModestMailOperationPrivate *priv;
1662         TnyIterator *new_headers_iter;
1663         GPtrArray *new_headers_array = NULL;
1664         gint max_size = G_MAXINT, retrieve_limit, i;
1665         ModestAccountMgr *mgr;
1666         ModestAccountRetrieveType retrieve_type;
1667         TnyList *new_headers = NULL;
1668         gboolean headers_only;
1669         time_t time_to_store;
1670
1671         info = (UpdateAccountInfo *) user_data;
1672         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1673         mgr = modest_runtime_get_account_mgr ();
1674
1675         if (canceled || err) {
1676                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1677                 if (err)
1678                         priv->error = g_error_copy (err);
1679                 else
1680                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1681                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1682                                      "canceled");
1683
1684                 if (inbox)
1685                         tny_folder_remove_observer (inbox, info->inbox_observer);
1686                 g_object_unref (info->inbox_observer);
1687                 info->inbox_observer = NULL;
1688
1689                 /* Notify the user about the error and then exit */
1690                 update_account_notify_user_and_free (info, NULL);
1691                 return;
1692         }
1693
1694         if (!inbox) {
1695                 /* Try to send anyway */
1696                 goto send_mail;
1697         }
1698
1699         if (!info->update_folder_counts) {
1700                 /* Set the last updated as the current time */
1701 #ifdef MODEST_USE_LIBTIME
1702                 struct tm utc_tm;
1703                 time_get_utc (&utc_tm);
1704                 time_to_store = time_mktime (&utc_tm, "GMT");
1705 #else
1706                 time_to_store = time (NULL);
1707 #endif
1708                 modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time_to_store);
1709
1710                 /* Get the message max size */
1711                 max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1712                                                  MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1713                 if (max_size == 0)
1714                         max_size = G_MAXINT;
1715                 else
1716                         max_size = max_size * KB;
1717         }
1718
1719         /* Create the new headers array. We need it to sort the
1720            new headers by date */
1721         new_headers_array = g_ptr_array_new ();
1722         if (info->inbox_observer) {
1723                 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1724                 if (!tny_iterator_is_done (new_headers_iter)) {
1725                         modest_platform_emit_folder_updated_signal (info->account_name, tny_folder_get_id (TNY_FOLDER (inbox)));
1726                 }
1727                 while (!tny_iterator_is_done (new_headers_iter)) {
1728                         TnyHeader *header = NULL;
1729
1730                         header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1731                         /* Apply per-message size limits */
1732                         if (tny_header_get_message_size (header) < max_size)
1733                                 g_ptr_array_add (new_headers_array, g_object_ref (header));
1734
1735                         g_object_unref (header);
1736                         tny_iterator_next (new_headers_iter);
1737                 }
1738                 g_object_unref (new_headers_iter);
1739
1740                 tny_folder_remove_observer (inbox, info->inbox_observer);
1741                 g_object_unref (info->inbox_observer);
1742                 info->inbox_observer = NULL;
1743         }
1744
1745         if (new_headers_array->len == 0) {
1746                 g_ptr_array_free (new_headers_array, FALSE);
1747                 goto send_mail;
1748         }
1749
1750         /* Get per-account message amount retrieval limit */
1751         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1752         if (retrieve_limit == 0)
1753                 retrieve_limit = G_MAXINT;
1754
1755         /* Get per-account retrieval type */
1756         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1757         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1758
1759         /* Order by date */
1760         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1761
1762         /* Copy the headers to a list and free the array */
1763         new_headers = tny_simple_list_new ();
1764         for (i=0; i < new_headers_array->len; i++) {
1765                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1766                 /* We want the first element to be the most recent
1767                    one, that's why we reverse the list */
1768                 tny_list_prepend (new_headers, G_OBJECT (header));
1769         }
1770         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1771         g_ptr_array_free (new_headers_array, FALSE);
1772
1773         if (!headers_only && (tny_list_get_length (new_headers) > 0)) {
1774                 gint msg_num = 0;
1775                 TnyIterator *iter;
1776                 GetMsgInfo *msg_info;
1777
1778                 priv->done = 0;
1779                 priv->total = MIN (tny_list_get_length (new_headers), retrieve_limit);
1780
1781                 iter = tny_list_create_iterator (new_headers);
1782
1783                 /* Create the message info */
1784                 msg_info = g_slice_new0 (GetMsgInfo);
1785                 msg_info->mail_op = g_object_ref (info->mail_op);
1786                 msg_info->total_bytes = compute_message_list_size (new_headers, priv->total);
1787                 msg_info->more_msgs = g_object_ref (iter);
1788                 msg_info->msg = NULL;
1789                 msg_info->user_data = info;
1790
1791                 while ((msg_num < priv->total ) && !tny_iterator_is_done (iter)) {
1792                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1793                         TnyFolder *folder = tny_header_get_folder (header);
1794
1795                         /* Get message in an async way */
1796                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb,
1797                                                   NULL, msg_info);
1798
1799                         g_object_unref (folder);
1800                         g_object_unref (header);
1801
1802                         msg_num++;
1803                         tny_iterator_next (iter);
1804                 }
1805                 g_object_unref (iter);
1806                 g_object_unref (new_headers);
1807
1808                 /* The mail operation will finish when the last
1809                    message is retrieved */
1810                 return;
1811         }
1812  send_mail:
1813         /* If we don't have to retrieve the new messages then
1814            simply send mail */
1815         update_account_send_mail (info);
1816
1817         /* Check if the operation was a success */
1818         if (!priv->error)
1819                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1820
1821         /* Call the user callback and free */
1822         update_account_notify_user_and_free (info, new_headers);
1823 }
1824
1825 static void
1826 inbox_refresh_status_update (GObject *obj,
1827                              TnyStatus *status,
1828                              gpointer user_data)
1829 {
1830         UpdateAccountInfo *info = NULL;
1831         ModestMailOperation *self = NULL;
1832         ModestMailOperationPrivate *priv = NULL;
1833         ModestMailOperationState *state;
1834
1835         g_return_if_fail (user_data != NULL);
1836         g_return_if_fail (status != NULL);
1837
1838         /* Show only the status information we want */
1839         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
1840                 return;
1841
1842         info = (UpdateAccountInfo *) user_data;
1843         self = info->mail_op;
1844         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1845
1846         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1847
1848         priv->done = status->position;
1849         priv->total = status->of_total;
1850
1851         state = modest_mail_operation_clone_state (self);
1852
1853         /* This is not a GDK lock because we are a Tinymail callback and
1854          * Tinymail already acquires the Gdk lock */
1855         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1856
1857         g_slice_free (ModestMailOperationState, state);
1858 }
1859
1860 static void 
1861 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1862                           gboolean canceled,
1863                           TnyList *list, 
1864                           GError *err, 
1865                           gpointer user_data)
1866 {
1867         UpdateAccountInfo *info;
1868         ModestMailOperationPrivate *priv;
1869     
1870         info = (UpdateAccountInfo *) user_data;
1871         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1872
1873         if (err || canceled) {
1874                 /* If the error was previosly set by another callback
1875                    don't set it again */
1876                 if (!priv->error) {
1877                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1878                         if (err)
1879                                 priv->error = g_error_copy (err);
1880                         else
1881                                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1882                                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1883                                              "canceled");
1884                 }
1885         } else { 
1886                 /* We're not getting INBOX children if we don't want to poke all */
1887                 TnyIterator *iter = tny_list_create_iterator (list);
1888                 while (!tny_iterator_is_done (iter)) {
1889                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1890
1891                         /* Add to the list of all folders */
1892                         tny_list_append (info->folders, (GObject *) folder);
1893                         
1894                         if (info->poke_all) {
1895                                 TnyList *folders = tny_simple_list_new ();
1896                                 /* Add pending call */
1897                                 info->pending_calls++;
1898                                 
1899                                 tny_folder_store_get_folders_async (folder, folders, NULL, FALSE,
1900                                                                     recurse_folders_async_cb, 
1901                                                                     NULL, info);
1902                                 g_object_unref (folders);
1903                         }
1904                         
1905                         g_object_unref (G_OBJECT (folder));
1906                         
1907                         tny_iterator_next (iter);           
1908                 }
1909                 g_object_unref (G_OBJECT (iter));
1910         }
1911
1912         /* Remove my own pending call */
1913         info->pending_calls--;
1914
1915         /* This means that we have all the folders */
1916         if (info->pending_calls == 0) {
1917                 TnyIterator *iter_all_folders;
1918                 TnyFolder *inbox = NULL;
1919
1920                 /* If there was any error do not continue */
1921                 if (priv->error) {
1922                         update_account_notify_user_and_free (info, NULL);
1923                         return;
1924                 }
1925
1926                 iter_all_folders = tny_list_create_iterator (info->folders);
1927
1928                 /* Do a poke status over all folders */
1929                 while (!tny_iterator_is_done (iter_all_folders) &&
1930                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1931                         TnyFolder *folder = NULL;
1932
1933                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1934
1935                         if (!info->update_folder_counts && tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1936                                 /* Get a reference to the INBOX */
1937                                 inbox = g_object_ref (folder);
1938                         } else {
1939                                 /* Issue a poke status over the folder */
1940                                 if (info->poke_all)
1941                                         tny_folder_poke_status (folder);
1942                         }
1943
1944                         /* Free and go to next */
1945                         g_object_unref (folder);
1946                         tny_iterator_next (iter_all_folders);
1947                 }
1948                 g_object_unref (iter_all_folders);
1949
1950                 /* Refresh the INBOX */
1951                 if (inbox) {
1952                         /* Refresh the folder. Our observer receives
1953                          * the new emails during folder refreshes, so
1954                          * we can use observer->new_headers
1955                          */
1956                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1957                         tny_folder_add_observer (inbox, info->inbox_observer);
1958
1959                         /* Refresh the INBOX */
1960                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, inbox_refresh_status_update, info);
1961                         g_object_unref (inbox);
1962                 } else {
1963                         /* We could not perform the inbox refresh but
1964                            we'll try to send mails anyway */
1965                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1966                 }
1967         }
1968 }
1969
1970 void
1971 modest_mail_operation_update_account (ModestMailOperation *self,
1972                                       const gchar *account_name,
1973                                       gboolean poke_all,
1974                                       gboolean interactive,
1975                                       UpdateAccountCallback callback,
1976                                       gpointer user_data)
1977 {
1978         UpdateAccountInfo *info = NULL;
1979         ModestMailOperationPrivate *priv = NULL;
1980         ModestTnyAccountStore *account_store = NULL;
1981         TnyList *folders;
1982         ModestMailOperationState *state;
1983
1984         /* Init mail operation */
1985         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1986         priv->total = 0;
1987         priv->done  = 0;
1988         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1989         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
1990
1991         /* Get the store account */
1992         account_store = modest_runtime_get_account_store ();
1993         priv->account =
1994                 modest_tny_account_store_get_server_account (account_store,
1995                                                              account_name,
1996                                                              TNY_ACCOUNT_TYPE_STORE);
1997
1998         /* The above function could return NULL */
1999         if (!priv->account) {
2000                 /* Check if the operation was a success */
2001                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2002                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2003                              "no account");
2004                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2005
2006                 /* Call the user callback */
2007                 if (callback)
2008                         callback (self, NULL, user_data);
2009
2010                 /* Notify about operation end */
2011                 modest_mail_operation_notify_end (self);
2012
2013                 return;
2014         }
2015         
2016         /* We have once seen priv->account getting finalized during this code,
2017          * therefore adding a reference (bug #82296) */
2018         
2019         g_object_ref (priv->account);
2020
2021         /* Create the helper object */
2022         info = g_slice_new0 (UpdateAccountInfo);
2023         info->pending_calls = 1;
2024         info->folders = tny_simple_list_new ();
2025         info->mail_op = g_object_ref (self);
2026         info->poke_all = poke_all;
2027         info->interactive = interactive;
2028         info->update_folder_counts = FALSE;
2029         info->account_name = g_strdup (account_name);
2030         info->callback = callback;
2031         info->user_data = user_data;
2032
2033         /* Set account busy */
2034         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2035         modest_mail_operation_notify_start (self);
2036
2037         /* notify about the start of the operation */ 
2038         state = modest_mail_operation_clone_state (self);
2039         state->done = 0;
2040         state->total = 0;
2041
2042         /* Start notifying progress */
2043         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2044         g_slice_free (ModestMailOperationState, state);
2045         
2046         /* Get all folders and continue in the callback */ 
2047         folders = tny_simple_list_new ();
2048         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2049                                             folders, NULL, TRUE,
2050                                             recurse_folders_async_cb, 
2051                                             NULL, info);
2052         g_object_unref (folders);
2053         
2054         g_object_unref (priv->account);
2055         
2056 }
2057
2058 void
2059 modest_mail_operation_update_folder_counts (ModestMailOperation *self,
2060                                       const gchar *account_name)
2061 {
2062         UpdateAccountInfo *info = NULL;
2063         ModestMailOperationPrivate *priv = NULL;
2064         ModestTnyAccountStore *account_store = NULL;
2065         TnyList *folders;
2066         ModestMailOperationState *state;
2067
2068         /* Init mail operation */
2069         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2070         priv->total = 0;
2071         priv->done  = 0;
2072         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2073         priv->op_type = MODEST_MAIL_OPERATION_TYPE_UPDATE_FOLDER_COUNTS;
2074
2075         /* Get the store account */
2076         account_store = modest_runtime_get_account_store ();
2077         priv->account =
2078                 modest_tny_account_store_get_server_account (account_store,
2079                                                              account_name,
2080                                                              TNY_ACCOUNT_TYPE_STORE);
2081
2082         /* The above function could return NULL */
2083         if (!priv->account) {
2084                 /* Check if the operation was a success */
2085                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2086                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2087                              "no account");
2088                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2089
2090                 /* Notify about operation end */
2091                 modest_mail_operation_notify_end (self);
2092
2093                 return;
2094         }
2095         
2096         /* We have once seen priv->account getting finalized during this code,
2097          * therefore adding a reference (bug #82296) */
2098         
2099         g_object_ref (priv->account);
2100
2101         /* Create the helper object */
2102         info = g_slice_new0 (UpdateAccountInfo);
2103         info->pending_calls = 1;
2104         info->folders = tny_simple_list_new ();
2105         info->mail_op = g_object_ref (self);
2106         info->poke_all = TRUE;
2107         info->interactive = FALSE;
2108         info->update_folder_counts = TRUE;
2109         info->account_name = g_strdup (account_name);
2110         info->callback = NULL;
2111         info->user_data = NULL;
2112
2113         /* Set account busy */
2114         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2115         modest_mail_operation_notify_start (self);
2116
2117         /* notify about the start of the operation */ 
2118         state = modest_mail_operation_clone_state (self);
2119         state->done = 0;
2120         state->total = 0;
2121
2122         /* Start notifying progress */
2123         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2124         g_slice_free (ModestMailOperationState, state);
2125         
2126         /* Get all folders and continue in the callback */ 
2127         folders = tny_simple_list_new ();
2128         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2129                                             folders, NULL, TRUE,
2130                                             recurse_folders_async_cb, 
2131                                             NULL, info);
2132         g_object_unref (folders);
2133         
2134         g_object_unref (priv->account);
2135         
2136 }
2137
2138 /*
2139  * Used to notify the queue from the main
2140  * loop. We call it inside an idle call to achieve that
2141  */
2142 static gboolean
2143 idle_notify_queue (gpointer data)
2144 {
2145         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
2146
2147         gdk_threads_enter ();
2148         modest_mail_operation_notify_end (mail_op);
2149         gdk_threads_leave ();
2150         g_object_unref (mail_op);
2151
2152         return FALSE;
2153 }
2154
2155 static int
2156 compare_headers_by_date (gconstpointer a,
2157                          gconstpointer b)
2158 {
2159         TnyHeader **header1, **header2;
2160         time_t sent1, sent2;
2161
2162         header1 = (TnyHeader **) a;
2163         header2 = (TnyHeader **) b;
2164
2165         sent1 = tny_header_get_date_sent (*header1);
2166         sent2 = tny_header_get_date_sent (*header2);
2167
2168         /* We want the most recent ones (greater time_t) at the
2169            beginning */
2170         if (sent1 < sent2)
2171                 return -1;
2172         else
2173                 return 1;
2174 }
2175
2176
2177 /* ******************************************************************* */
2178 /* ************************** STORE  ACTIONS ************************* */
2179 /* ******************************************************************* */
2180
2181 typedef struct {
2182         ModestMailOperation *mail_op;
2183         CreateFolderUserCallback callback;
2184         gpointer user_data;
2185 } CreateFolderInfo;
2186
2187
2188 static void
2189 create_folder_cb (TnyFolderStore *parent_folder, 
2190                   gboolean canceled, 
2191                   TnyFolder *new_folder, 
2192                   GError *err, 
2193                   gpointer user_data)
2194 {
2195         ModestMailOperationPrivate *priv;
2196         CreateFolderInfo *info;
2197
2198         info = (CreateFolderInfo *) user_data;
2199         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2200
2201         if (canceled || err) {
2202                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2203                 if (err)
2204                         priv->error = g_error_copy (err);
2205                 else
2206                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2207                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2208                                      "canceled");               
2209         } else {
2210                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2211         }
2212
2213         /* The user will unref the new_folder */
2214         if (info->callback)
2215                 info->callback (info->mail_op, parent_folder, 
2216                                 new_folder, info->user_data);
2217         
2218         /* Notify about operation end */
2219         modest_mail_operation_notify_end (info->mail_op);
2220
2221         /* Frees */
2222         g_object_unref (info->mail_op);
2223         g_slice_free (CreateFolderInfo, info);
2224 }
2225
2226 void
2227 modest_mail_operation_create_folder (ModestMailOperation *self,
2228                                      TnyFolderStore *parent,
2229                                      const gchar *name,
2230                                      CreateFolderUserCallback callback,
2231                                      gpointer user_data)
2232 {
2233         ModestMailOperationPrivate *priv;
2234
2235         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2236         g_return_if_fail (name);
2237         
2238         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2239         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2240         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
2241                 g_object_ref (parent) : 
2242                 modest_tny_folder_get_account (TNY_FOLDER (parent));
2243
2244         /* Check for already existing folder */
2245         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
2246                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2247                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2248                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2249                              _CS("ckdg_ib_folder_already_exists"));
2250         }
2251
2252         /* Check parent */
2253         if (TNY_IS_FOLDER (parent)) {
2254                 /* Check folder rules */
2255                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2256                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2257                         /* Set status failed and set an error */
2258                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2259                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2260                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2261                                      _("mail_in_ui_folder_create_error"));
2262                 }
2263         }
2264
2265         if (!priv->error && (!strcmp (name, " ") || strchr (name, '/'))) {
2266                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2267                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2268                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2269                              _("mail_in_ui_folder_create_error"));
2270         }
2271
2272         if (!priv->error) {
2273                 CreateFolderInfo *info;
2274
2275                 info = g_slice_new0 (CreateFolderInfo);
2276                 info->mail_op = g_object_ref (self);
2277                 info->callback = callback;
2278                 info->user_data = user_data;
2279
2280                 modest_mail_operation_notify_start (self);
2281
2282                 /* Create the folder */
2283                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
2284                                                       NULL, info);
2285         } else {
2286                 /* Call the user callback anyway */
2287                 if (callback)
2288                         callback (self, parent, NULL, user_data);
2289                 /* Notify about operation end */
2290                 modest_mail_operation_notify_end (self);
2291         }
2292 }
2293
2294 void
2295 modest_mail_operation_remove_folder (ModestMailOperation *self,
2296                                      TnyFolder           *folder,
2297                                      gboolean             remove_to_trash)
2298 {
2299         ModestMailOperationPrivate *priv;
2300         ModestTnyFolderRules rules;
2301
2302         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2303         g_return_if_fail (TNY_IS_FOLDER (folder));
2304         
2305         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2306         
2307         /* Check folder rules */
2308         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2309         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
2310                 /* Set status failed and set an error */
2311                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2312                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2313                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2314                              _("mail_in_ui_folder_delete_error"));
2315                 goto end;
2316         }
2317
2318         /* Get the account */
2319         priv->account = modest_tny_folder_get_account (folder);
2320         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2321
2322         /* Delete folder or move to trash */
2323         if (remove_to_trash) {
2324                 TnyFolder *trash_folder = NULL;
2325                 trash_folder = modest_tny_account_get_special_folder (priv->account,
2326                                                                       TNY_FOLDER_TYPE_TRASH);
2327                 /* TODO: error_handling */
2328                 if (trash_folder) {
2329                         modest_mail_operation_notify_start (self);
2330                         modest_mail_operation_xfer_folder (self, folder,
2331                                                     TNY_FOLDER_STORE (trash_folder), 
2332                                                     TRUE, NULL, NULL);
2333                         g_object_unref (trash_folder);
2334                 } else {
2335                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
2336                 }
2337         } else {
2338                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
2339                 if (parent) {
2340                         modest_mail_operation_notify_start (self);
2341                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
2342                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
2343                         
2344                         if (!priv->error)
2345                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2346
2347                         g_object_unref (parent);
2348                 } else
2349                         g_warning ("%s: could not get parent folder", __FUNCTION__);
2350         }
2351
2352  end:
2353         /* Notify about operation end */
2354         modest_mail_operation_notify_end (self);
2355 }
2356
2357 static void
2358 transfer_folder_status_cb (GObject *obj,
2359                            TnyStatus *status,
2360                            gpointer user_data)
2361 {
2362         ModestMailOperation *self;
2363         ModestMailOperationPrivate *priv;
2364         ModestMailOperationState *state;
2365         XFerFolderAsyncHelper *helper;
2366
2367         g_return_if_fail (status != NULL);
2368
2369         /* Show only the status information we want */
2370         if (status->code != TNY_FOLDER_STATUS_CODE_COPY_FOLDER)
2371                 return;
2372
2373         helper = (XFerFolderAsyncHelper *) user_data;
2374         g_return_if_fail (helper != NULL);
2375
2376         self = helper->mail_op;
2377         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2378
2379         priv->done = status->position;
2380         priv->total = status->of_total;
2381
2382         state = modest_mail_operation_clone_state (self);
2383
2384         /* This is not a GDK lock because we are a Tinymail callback
2385          * which is already GDK locked by Tinymail */
2386
2387         /* no gdk_threads_enter (), CHECKED */
2388
2389         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
2390
2391         /* no gdk_threads_leave (), CHECKED */
2392
2393         g_slice_free (ModestMailOperationState, state);
2394 }
2395
2396 static void
2397 transfer_folder_cb (TnyFolder *folder, 
2398                     gboolean cancelled, 
2399                     TnyFolderStore *into, 
2400                     TnyFolder *new_folder, 
2401                     GError *err, 
2402                     gpointer user_data)
2403 {
2404         XFerFolderAsyncHelper *helper;
2405         ModestMailOperation *self = NULL;
2406         ModestMailOperationPrivate *priv = NULL;
2407
2408         helper = (XFerFolderAsyncHelper *) user_data;
2409         g_return_if_fail (helper != NULL);
2410
2411         self = helper->mail_op;
2412         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2413
2414         if (err) {
2415                 priv->error = g_error_copy (err);
2416                 priv->done = 0;
2417                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2418         } else if (cancelled) {
2419                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2420                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2421                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2422                              _("Transference of %s was cancelled."),
2423                              tny_folder_get_name (folder));
2424         } else {
2425                 priv->done = 1;
2426                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2427         }
2428
2429         /* Update state of new folder */
2430         if (new_folder) {
2431                 tny_folder_refresh_async (new_folder, NULL, NULL, NULL);
2432                 tny_folder_poke_status (new_folder);
2433         }
2434
2435         /* Notify about operation end */
2436         modest_mail_operation_notify_end (self);
2437
2438         /* If user defined callback function was defined, call it */
2439         if (helper->user_callback) {
2440
2441                 /* This is not a GDK lock because we are a Tinymail callback
2442                  * which is already GDK locked by Tinymail */
2443
2444                 /* no gdk_threads_enter (), CHECKED */
2445                 helper->user_callback (self, new_folder, helper->user_data);
2446                 /* no gdk_threads_leave () , CHECKED */
2447         }
2448
2449         /* Free */
2450         g_object_unref (helper->mail_op);
2451         g_slice_free   (XFerFolderAsyncHelper, helper);
2452 }
2453
2454 /**
2455  *
2456  * This function checks if the new name is a valid name for our local
2457  * folders account. The new name could not be the same than then name
2458  * of any of the mandatory local folders
2459  *
2460  * We can not rely on tinymail because tinymail does not check the
2461  * name of the virtual folders that the account could have in the case
2462  * that we're doing a rename (because it directly calls Camel which
2463  * knows nothing about our virtual folders). 
2464  *
2465  * In the case of an actual copy/move (i.e. move/copy a folder between
2466  * accounts) tinymail uses the tny_folder_store_create_account which
2467  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2468  * checks the new name of the folder, so this call in that case
2469  * wouldn't be needed. *But* NOTE that if tinymail changes its
2470  * implementation (if folder transfers within the same account is no
2471  * longer implemented as a rename) this call will allow Modest to work
2472  * perfectly
2473  *
2474  * If the new name is not valid, this function will set the status to
2475  * failed and will set also an error in the mail operation
2476  */
2477 static gboolean
2478 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2479                                  TnyFolderStore *into,
2480                                  const gchar *new_name)
2481 {
2482         if (TNY_IS_ACCOUNT (into) && 
2483             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2484             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2485                                                                  new_name)) {
2486                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2487                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2488                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2489                              _CS("ckdg_ib_folder_already_exists"));
2490                 return FALSE;
2491         } else
2492                 return TRUE;
2493 }
2494
2495 void
2496 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2497                                    TnyFolder *folder,
2498                                    TnyFolderStore *parent,
2499                                    gboolean delete_original,
2500                                    XferFolderAsyncUserCallback user_callback,
2501                                    gpointer user_data)
2502 {
2503         ModestMailOperationPrivate *priv = NULL;
2504         ModestTnyFolderRules parent_rules = 0, rules; 
2505         XFerFolderAsyncHelper *helper = NULL;
2506         const gchar *folder_name = NULL;
2507         const gchar *error_msg;
2508
2509         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2510         g_return_if_fail (TNY_IS_FOLDER (folder));
2511         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2512
2513         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2514         folder_name = tny_folder_get_name (folder);
2515
2516         /* Set the error msg */
2517         error_msg = _("mail_in_ui_folder_move_target_error");
2518
2519         /* Get account and set it into mail_operation */
2520         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2521         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2522         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2523
2524         /* Get folder rules */
2525         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2526         if (TNY_IS_FOLDER (parent))
2527                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2528         
2529         /* Apply operation constraints */
2530         if ((gpointer) parent == (gpointer) folder ||
2531             (!TNY_IS_FOLDER_STORE (parent)) || 
2532             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2533                 /* Folder rules */
2534                 goto error;
2535         } else if (TNY_IS_FOLDER (parent) && 
2536                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2537                 /* Folder rules */
2538                 goto error;
2539
2540         } else if (TNY_IS_FOLDER (parent) &&
2541                    TNY_IS_FOLDER_STORE (folder) &&
2542                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2543                                                   TNY_FOLDER_STORE (folder))) {
2544                 /* Do not move a parent into a child */
2545                 goto error;
2546         } else if (TNY_IS_FOLDER_STORE (parent) &&
2547                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2548                 /* Check that the new folder name is not used by any
2549                    parent subfolder */
2550                 goto error;     
2551         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2552                 /* Check that the new folder name is not used by any
2553                    special local folder */
2554                 goto error;
2555         } else {
2556                 /* Create the helper */
2557                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2558                 helper->mail_op = g_object_ref (self);
2559                 helper->user_callback = user_callback;
2560                 helper->user_data = user_data;
2561                 
2562                 /* Move/Copy folder */
2563                 modest_mail_operation_notify_start (self);
2564                 tny_folder_copy_async (folder,
2565                                        parent,
2566                                        tny_folder_get_name (folder),
2567                                        delete_original,
2568                                        transfer_folder_cb,
2569                                        transfer_folder_status_cb,
2570                                        helper);
2571                 return;
2572         }
2573
2574  error:
2575         /* Set status failed and set an error */
2576         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2577         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2578                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2579                      "%s", error_msg);
2580
2581         /* Call the user callback if exists */
2582         if (user_callback)
2583                 user_callback (self, NULL, user_data);
2584
2585         /* Notify the queue */
2586         modest_mail_operation_notify_end (self);
2587 }
2588
2589 void
2590 modest_mail_operation_rename_folder (ModestMailOperation *self,
2591                                      TnyFolder *folder,
2592                                      const gchar *name,
2593                                      XferFolderAsyncUserCallback user_callback,
2594                                      gpointer user_data)
2595 {
2596         ModestMailOperationPrivate *priv;
2597         ModestTnyFolderRules rules;
2598         XFerFolderAsyncHelper *helper;
2599
2600         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2601         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2602         g_return_if_fail (name);
2603         
2604         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2605
2606         /* Get account and set it into mail_operation */
2607         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2608         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2609
2610         /* Check folder rules */
2611         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2612         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2613                 goto error;
2614         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2615                 goto error;
2616         } else {
2617                 TnyFolderStore *into;
2618
2619                 into = tny_folder_get_folder_store (folder);    
2620
2621                 /* Check that the new folder name is not used by any
2622                    special local folder */
2623                 if (new_name_valid_if_local_account (priv, into, name)) {
2624                         /* Create the helper */
2625                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2626                         helper->mail_op = g_object_ref(self);
2627                         helper->user_callback = user_callback;
2628                         helper->user_data = user_data;
2629                 
2630                         /* Rename. Camel handles folder subscription/unsubscription */
2631                         modest_mail_operation_notify_start (self);
2632                         tny_folder_copy_async (folder, into, name, TRUE,
2633                                                transfer_folder_cb,
2634                                                transfer_folder_status_cb,
2635                                                helper);
2636                         g_object_unref (into);
2637                 } else {
2638                         g_object_unref (into);
2639                         goto error;
2640                 }
2641
2642                 return;
2643         }
2644  error:
2645         /* Set status failed and set an error */
2646         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2647         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2648                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2649                      _("FIXME: unable to rename"));
2650         
2651         if (user_callback)
2652                 user_callback (self, NULL, user_data);
2653
2654         /* Notify about operation end */
2655         modest_mail_operation_notify_end (self);
2656 }
2657
2658 /* ******************************************************************* */
2659 /* **************************  MSG  ACTIONS  ************************* */
2660 /* ******************************************************************* */
2661
2662 void 
2663 modest_mail_operation_find_msg (ModestMailOperation *self,
2664                                 TnyFolder *folder,
2665                                 const gchar *msg_uid,
2666                                 gboolean progress_feedback,
2667                                 GetMsgAsyncUserCallback user_callback,
2668                                 gpointer user_data)
2669 {
2670         GetMsgInfo *helper = NULL;
2671         ModestMailOperationPrivate *priv;
2672         
2673         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2674         g_return_if_fail (msg_uid != NULL);
2675         
2676         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2677         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2678         priv->total = 1;
2679         priv->done = 0;
2680
2681         /* Check memory low */
2682         if (_check_memory_low (self)) {
2683                 if (user_callback)
2684                         user_callback (self, NULL, FALSE, NULL, priv->error, user_data);
2685                 modest_mail_operation_notify_end (self);
2686                 return;
2687         }
2688
2689         /* Get account and set it into mail_operation */
2690         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2691         
2692         /* Check for cached messages */
2693         if (progress_feedback) {
2694                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2695         } else {
2696                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2697         }
2698         
2699         /* Create the helper */
2700         helper = g_slice_new0 (GetMsgInfo);
2701         helper->header = NULL;
2702         helper->mail_op = g_object_ref (self);
2703         helper->user_callback = user_callback;
2704         helper->user_data = user_data;
2705         helper->destroy_notify = NULL;
2706         helper->last_total_bytes = 0;
2707         helper->sum_total_bytes = 0;
2708         helper->total_bytes = 0;
2709         helper->more_msgs = NULL;
2710         helper->get_parts = NULL;
2711         helper->msg = NULL;
2712
2713         modest_mail_operation_notify_start (self);
2714         
2715         /* notify about the start of the operation */ 
2716         ModestMailOperationState *state;
2717         state = modest_mail_operation_clone_state (self);
2718         state->done = 0;
2719         state->total = 0;
2720         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2721                                 0, state, NULL);
2722         g_slice_free (ModestMailOperationState, state);
2723         
2724         tny_folder_find_msg_async (folder, msg_uid, get_msg_async_cb, get_msg_status_cb, helper);
2725 }
2726
2727 void 
2728 modest_mail_operation_get_msg (ModestMailOperation *self,
2729                                TnyHeader *header,
2730                                gboolean progress_feedback,
2731                                GetMsgAsyncUserCallback user_callback,
2732                                gpointer user_data)
2733 {
2734         GetMsgInfo *helper = NULL;
2735         TnyFolder *folder;
2736         ModestMailOperationPrivate *priv;
2737         
2738         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2739         g_return_if_fail (TNY_IS_HEADER (header));
2740         
2741         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2742         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2743         priv->total = 1;
2744         priv->done = 0;
2745
2746         /* Check memory low */
2747         if (_check_memory_low (self)) {
2748                 if (user_callback)
2749                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2750                 modest_mail_operation_notify_end (self);
2751                 return;
2752         }
2753
2754         /* Get account and set it into mail_operation */
2755         folder = tny_header_get_folder (header);
2756         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2757         
2758         /* Check for cached messages */
2759         if (progress_feedback) {
2760                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2761                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2762                 else 
2763                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2764         } else {
2765                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2766         }
2767         
2768         /* Create the helper */
2769         helper = g_slice_new0 (GetMsgInfo);
2770         helper->header = g_object_ref (header);
2771         helper->mail_op = g_object_ref (self);
2772         helper->user_callback = user_callback;
2773         helper->user_data = user_data;
2774         helper->destroy_notify = NULL;
2775         helper->last_total_bytes = 0;
2776         helper->sum_total_bytes = 0;
2777         helper->total_bytes = tny_header_get_message_size (header);
2778         helper->more_msgs = NULL;
2779         helper->get_parts = NULL;
2780         helper->msg = NULL;
2781
2782         modest_mail_operation_notify_start (self);
2783         
2784         /* notify about the start of the operation */ 
2785         ModestMailOperationState *state;
2786         state = modest_mail_operation_clone_state (self);
2787         state->done = 0;
2788         state->total = 0;
2789         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2790                                 0, state, NULL);
2791         g_slice_free (ModestMailOperationState, state);
2792         
2793         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2794
2795         g_object_unref (G_OBJECT (folder));
2796 }
2797
2798 void 
2799 modest_mail_operation_get_msg_and_parts (ModestMailOperation *self,
2800                                          TnyHeader *header,
2801                                          TnyList *parts,
2802                                          gboolean progress_feedback,
2803                                          GetMsgAsyncUserCallback user_callback,
2804                                          gpointer user_data)
2805 {
2806         GetMsgInfo *helper = NULL;
2807         TnyFolder *folder;
2808         ModestMailOperationPrivate *priv;
2809         
2810         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2811         g_return_if_fail (TNY_IS_HEADER (header));
2812         
2813         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2814         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2815         priv->total = 1;
2816         priv->done = 0;
2817
2818         /* Check memory low */
2819         if (_check_memory_low (self)) {
2820                 if (user_callback)
2821                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2822                 modest_mail_operation_notify_end (self);
2823                 return;
2824         }
2825
2826         /* Get account and set it into mail_operation */
2827         folder = tny_header_get_folder (header);
2828         if (folder == NULL && MODEST_IS_MSG_VIEW_WINDOW (priv->source)) {
2829                 const gchar *acc_name;
2830                 acc_name = modest_window_get_active_account (MODEST_WINDOW (priv->source));
2831                 priv->account = modest_tny_account_store_get_server_account
2832                         (modest_runtime_get_account_store (),
2833                          acc_name,
2834                          TNY_ACCOUNT_TYPE_STORE);
2835                 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (priv->account), 
2836                                                                        modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (priv->source)));
2837         } else {
2838                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2839         }
2840         
2841         /* Check for cached messages */
2842         if (progress_feedback) {
2843                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2844                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2845                 else 
2846                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2847         } else {
2848                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2849         }
2850         
2851         /* Create the helper */
2852         helper = g_slice_new0 (GetMsgInfo);
2853         helper->header = g_object_ref (header);
2854         helper->mail_op = g_object_ref (self);
2855         helper->user_callback = user_callback;
2856         helper->user_data = user_data;
2857         helper->destroy_notify = NULL;
2858         helper->last_total_bytes = 0;
2859         helper->sum_total_bytes = 0;
2860         helper->total_bytes = tny_header_get_message_size (header);
2861         helper->more_msgs = NULL;
2862         helper->get_parts = tny_list_create_iterator (parts);
2863         helper->msg = NULL;
2864
2865         modest_mail_operation_notify_start (self);
2866         
2867         /* notify about the start of the operation */ 
2868         ModestMailOperationState *state;
2869         state = modest_mail_operation_clone_state (self);
2870         state->done = 0;
2871         state->total = 0;
2872         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2873                                 0, state, NULL);
2874         g_slice_free (ModestMailOperationState, state);
2875         
2876         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2877
2878         g_object_unref (G_OBJECT (folder));
2879 }
2880
2881 static void     
2882 get_msg_status_cb (GObject *obj,
2883                    TnyStatus *status,  
2884                    gpointer user_data)
2885 {
2886         GetMsgInfo *helper = NULL;
2887
2888         g_return_if_fail (status != NULL);
2889
2890         /* Show only the status information we want */
2891         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2892                 return;
2893
2894         helper = (GetMsgInfo *) user_data;
2895         g_return_if_fail (helper != NULL);       
2896
2897         /* Notify progress */
2898         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2899                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2900 }
2901
2902 static void
2903 get_msg_async_get_part_cb (TnyMimePart *self, gboolean cancelled, TnyStream *stream, GError *err, gpointer user_data)
2904 {
2905         GetMsgInfo *helper;
2906         TnyFolder *folder = NULL;
2907
2908         helper = (GetMsgInfo *) user_data;
2909
2910         if (helper->header) {
2911                 folder = tny_header_get_folder (helper->header);
2912         }
2913
2914         get_msg_async_cb (folder, cancelled, helper->msg, err, user_data);
2915
2916         if (folder) g_object_unref (folder);
2917 }
2918
2919 static void
2920 get_msg_async_cb (TnyFolder *folder, 
2921                   gboolean canceled, 
2922                   TnyMsg *msg, 
2923                   GError *err, 
2924                   gpointer user_data)
2925 {
2926         GetMsgInfo *info = NULL;
2927         ModestMailOperationPrivate *priv = NULL;
2928         gboolean finished;
2929
2930         info = (GetMsgInfo *) user_data;
2931
2932         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2933         priv->done++;
2934
2935         if (info->more_msgs) {
2936                 tny_iterator_next (info->more_msgs);
2937                 finished = (tny_iterator_is_done (info->more_msgs));
2938         } else if (info->get_parts) {
2939                 tny_iterator_next (info->get_parts);
2940                 finished = (tny_iterator_is_done (info->get_parts));
2941         } else {
2942                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2943         }
2944
2945         /* If canceled by the user, ignore the error given by Tinymail */
2946         if (canceled) {
2947                 canceled = TRUE;
2948                 finished = TRUE;
2949                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2950         } else if (err) {
2951                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2952                 priv->error = g_error_copy ((const GError *) err);
2953                 if (priv->error) {
2954                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2955                 } else {
2956                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2957                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2958                                      "%s", err->message);
2959                 }
2960         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2961                 /* Set the success status before calling the user callback */
2962                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2963         }
2964
2965         if (info->header == NULL && msg)
2966                 info->header = tny_msg_get_header (msg);
2967
2968         /* Call the user callback */
2969         if (info->user_callback && (finished || (info->get_parts == NULL)))
2970                 info->user_callback (info->mail_op, info->header, canceled, 
2971                                      msg, err, info->user_data);
2972
2973         /* Notify about operation end if this is the last callback */
2974         if (finished) {
2975                 /* Free user data */
2976                 if (info->destroy_notify)
2977                         info->destroy_notify (info->user_data);
2978
2979                 /* Notify about operation end */
2980                 modest_mail_operation_notify_end (info->mail_op);
2981
2982                 /* Clean */
2983                 if (info->msg)
2984                         g_object_unref (info->msg);
2985                 if (info->more_msgs)
2986                         g_object_unref (info->more_msgs);
2987                 if (info->header)
2988                         g_object_unref (info->header);
2989                 g_object_unref (info->mail_op);
2990                 g_slice_free (GetMsgInfo, info);
2991         } else if (info->get_parts) {
2992                 CamelStream *null_stream;
2993                 TnyStream *tny_null_stream;
2994                 TnyMimePart *part;
2995
2996                 if (info->msg == NULL && msg != NULL)
2997                         info->msg = g_object_ref (msg);
2998
2999                 null_stream = camel_stream_null_new ();
3000                 tny_null_stream = tny_camel_stream_new (null_stream);
3001                 
3002                 part = TNY_MIME_PART (tny_iterator_get_current (info->get_parts));
3003                 tny_mime_part_decode_to_stream_async (part, tny_null_stream, 
3004                                                       get_msg_async_get_part_cb,
3005                                                       get_msg_status_cb,
3006                                                       info);
3007                 g_object_unref (tny_null_stream);
3008                 g_object_unref (part);
3009
3010         } else if (info->more_msgs) {
3011                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
3012                 TnyFolder *folder = tny_header_get_folder (header);
3013
3014                 g_object_unref (info->header);
3015                 info->header = g_object_ref (header);
3016
3017                 /* Retrieve the next message */
3018                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
3019
3020                 g_object_unref (header);
3021                 g_object_unref (folder);
3022         } else {
3023                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
3024         }
3025 }
3026
3027 void 
3028 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
3029                                      TnyList *header_list, 
3030                                      GetMsgAsyncUserCallback user_callback,
3031                                      gpointer user_data,
3032                                      GDestroyNotify notify)
3033 {
3034         ModestMailOperationPrivate *priv = NULL;
3035         gint msg_list_size;
3036         TnyIterator *iter = NULL;
3037         gboolean has_uncached_messages;
3038         
3039         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3040
3041         /* Init mail operation */
3042         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3043         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3044         priv->done = 0;
3045         priv->total = tny_list_get_length(header_list);
3046
3047         /* Check memory low */
3048         if (_check_memory_low (self)) {
3049                 if (user_callback) {
3050                         TnyHeader *header = NULL;
3051                         TnyIterator *iter;
3052
3053                         if (tny_list_get_length (header_list) > 0) {
3054                                 iter = tny_list_create_iterator (header_list);
3055                                 header = (TnyHeader *) tny_iterator_get_current (iter);
3056                                 g_object_unref (iter);
3057                         }
3058                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
3059                         if (header)
3060                                 g_object_unref (header);
3061                 }
3062                 if (notify)
3063                         notify (user_data);
3064                 /* Notify about operation end */
3065                 modest_mail_operation_notify_end (self);
3066                 return;
3067         }
3068
3069         /* Check uncached messages */
3070         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
3071              !has_uncached_messages && !tny_iterator_is_done (iter); 
3072              tny_iterator_next (iter)) {
3073                 TnyHeader *header;
3074
3075                 header = (TnyHeader *) tny_iterator_get_current (iter);
3076                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
3077                         has_uncached_messages = TRUE;
3078                 g_object_unref (header);
3079         }       
3080         g_object_unref (iter);
3081         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
3082
3083         /* Get account and set it into mail_operation */
3084         if (tny_list_get_length (header_list) >= 1) {
3085                 TnyIterator *iterator = tny_list_create_iterator (header_list);
3086                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
3087                 if (header) {
3088                         TnyFolder *folder = tny_header_get_folder (header);
3089                         if (folder) {           
3090                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
3091                                 g_object_unref (folder);
3092                         }
3093                         g_object_unref (header);
3094                 }
3095                 g_object_unref (iterator);
3096         }
3097
3098         msg_list_size = compute_message_list_size (header_list, 0);
3099
3100         modest_mail_operation_notify_start (self);
3101         iter = tny_list_create_iterator (header_list);
3102         if (!tny_iterator_is_done (iter)) {
3103                 /* notify about the start of the operation */
3104                 ModestMailOperationState *state;
3105                 state = modest_mail_operation_clone_state (self);
3106                 state->done = 0;
3107                 state->total = 0;
3108                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
3109                                0, state, NULL);
3110
3111                 GetMsgInfo *msg_info = NULL;
3112                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3113                 TnyFolder *folder = tny_header_get_folder (header);
3114
3115                 /* Create the message info */
3116                 msg_info = g_slice_new0 (GetMsgInfo);
3117                 msg_info->mail_op = g_object_ref (self);
3118                 msg_info->header = g_object_ref (header);
3119                 msg_info->more_msgs = g_object_ref (iter);
3120                 msg_info->user_callback = user_callback;
3121                 msg_info->user_data = user_data;
3122                 msg_info->destroy_notify = notify;
3123                 msg_info->last_total_bytes = 0;
3124                 msg_info->sum_total_bytes = 0;
3125                 msg_info->total_bytes = msg_list_size;
3126                 msg_info->msg = NULL;
3127
3128                 /* The callback will call it per each header */
3129                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
3130
3131                 /* Free and go on */
3132                 g_object_unref (header);
3133                 g_object_unref (folder);
3134                 g_slice_free (ModestMailOperationState, state);
3135         }
3136         g_object_unref (iter);
3137 }
3138
3139
3140 static void
3141 remove_msgs_async_cb (TnyFolder *folder, 
3142                       gboolean canceled, 
3143                       GError *err, 
3144                       gpointer user_data)
3145 {
3146         gboolean expunge;
3147         const gchar *account_name;
3148         TnyAccount *account;
3149         ModestProtocolType account_proto = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
3150         ModestMailOperation *self;
3151         ModestMailOperationPrivate *priv;
3152         ModestProtocolRegistry *protocol_registry;
3153         SyncFolderHelper *helper;
3154
3155         self = (ModestMailOperation *) user_data;
3156         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3157         protocol_registry = modest_runtime_get_protocol_registry ();
3158
3159         if (canceled || err) {
3160                 /* If canceled by the user, ignore the error given by Tinymail */
3161                 if (canceled) {
3162                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3163                 } else if (err) {
3164                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3165                         priv->error = g_error_copy ((const GError *) err);
3166                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3167                 }
3168                 /* Exit */
3169                 modest_mail_operation_notify_end (self);
3170                 g_object_unref (self);
3171                 return;
3172         }
3173
3174         account = modest_tny_folder_get_account (folder);
3175         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3176         account_proto = modest_tny_account_get_protocol_type (account);
3177         g_object_unref (account);
3178
3179         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_proto)) {
3180                 if (modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3181                                                             account_name))
3182                         expunge = FALSE;
3183                 else
3184                         expunge = TRUE;
3185         } else {
3186                 expunge = TRUE;
3187         }
3188
3189         /* Create helper */
3190         helper = g_slice_new0 (SyncFolderHelper);
3191         helper->mail_op = g_object_ref (self);
3192         helper->user_callback = NULL;
3193         helper->user_data = NULL;
3194
3195         /* Sync folder */
3196         tny_folder_sync_async(folder, expunge, sync_folder_finish_callback, NULL, helper);
3197
3198         /* Remove the extra reference */
3199         g_object_unref (self);
3200 }
3201
3202 void 
3203 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
3204                                    TnyList *headers,
3205                                    gboolean remove_to_trash /*ignored*/)
3206 {
3207         TnyFolder *folder = NULL;
3208         ModestMailOperationPrivate *priv;
3209         TnyIterator *iter = NULL;
3210         TnyHeader *header = NULL;
3211         TnyList *remove_headers = NULL;
3212         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
3213         ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
3214
3215         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3216         g_return_if_fail (TNY_IS_LIST (headers));
3217
3218         if (remove_to_trash)
3219                 g_warning ("remove to trash is not implemented");
3220
3221         if (tny_list_get_length(headers) == 0) {
3222                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
3223                 goto cleanup; /* nothing to do */
3224         }
3225
3226         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3227
3228         /* Get folder from first header and sync it */
3229         iter = tny_list_create_iterator (headers);
3230         header = TNY_HEADER (tny_iterator_get_current (iter));
3231         g_object_unref (iter);
3232
3233         folder = tny_header_get_folder (header);
3234         if (!TNY_IS_FOLDER(folder)) {
3235                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
3236                 goto cleanup;
3237         }
3238
3239         /* Use the merged folder if we're removing messages from outbox */
3240         if (modest_tny_folder_is_local_folder (folder)) {
3241                 ModestTnyLocalFoldersAccount *local_account;
3242
3243                 local_account = (ModestTnyLocalFoldersAccount *)
3244                         modest_tny_account_store_get_local_folders_account (accstore);
3245                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
3246                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
3247                         g_object_unref (folder);
3248                         folder = modest_tny_local_folders_account_get_merged_outbox (local_account);
3249                 }
3250                 g_object_unref (local_account);
3251         }
3252
3253         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
3254                 TnyIterator *headers_iter = tny_list_create_iterator (headers);
3255
3256                 while (!tny_iterator_is_done (headers_iter)) {
3257                         TnyTransportAccount *traccount = NULL;
3258                         TnyHeader *hdr = NULL;
3259
3260                         hdr = TNY_HEADER (tny_iterator_get_current (headers_iter));
3261                         traccount = modest_tny_account_store_get_transport_account_from_outbox_header (accstore,
3262                                                                                                        header);
3263                         if (traccount) {
3264                                 ModestTnySendQueueStatus status;
3265                                 ModestTnySendQueue *send_queue;
3266
3267                                 send_queue = modest_runtime_get_send_queue(traccount, TRUE);
3268                                 if (TNY_IS_SEND_QUEUE (send_queue)) {
3269                                         char *msg_id;
3270
3271                                         msg_id = modest_tny_send_queue_get_msg_id (hdr);
3272                                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
3273                                         if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
3274                                                 if (G_UNLIKELY (remove_headers == NULL))
3275                                                         remove_headers = tny_simple_list_new ();
3276                                                 tny_list_append(remove_headers, G_OBJECT(hdr));
3277                                         }
3278                                         g_free(msg_id);
3279                                 }
3280                                 g_object_unref(traccount);
3281                         }
3282                         g_object_unref(hdr);
3283                         tny_iterator_next (headers_iter);
3284                 }
3285                 g_object_unref(headers_iter);
3286         }
3287
3288         /* Get account and set it into mail_operation */
3289         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
3290         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
3291         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3292
3293         if (!remove_headers)
3294                 remove_headers = g_object_ref (headers);
3295
3296         /* remove message from folder */
3297         modest_mail_operation_notify_start (self);
3298         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
3299                                       NULL, g_object_ref (self));
3300
3301 cleanup:
3302         if (remove_headers)
3303                 g_object_unref (remove_headers);
3304         if (header)
3305                 g_object_unref (header);
3306         if (folder)
3307                 g_object_unref (folder);
3308 }
3309
3310 static void
3311 notify_progress_of_multiple_messages (ModestMailOperation *self,
3312                                       TnyStatus *status,
3313                                       gint *last_total_bytes,
3314                                       gint *sum_total_bytes,
3315                                       gint total_bytes, 
3316                                       gboolean increment_done)
3317 {
3318         ModestMailOperationPrivate *priv;
3319         ModestMailOperationState *state;
3320         gboolean is_num_bytes = FALSE;
3321
3322         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3323
3324         /* We know that tinymail sends us information about
3325          *  transferred bytes with this particular message
3326          */
3327         if (status->message)
3328                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
3329
3330         state = modest_mail_operation_clone_state (self);
3331         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
3332                 /* We know that we're in a different message when the
3333                    total number of bytes to transfer is different. Of
3334                    course it could fail if we're transferring messages
3335                    of the same size, but this is a workarround */
3336                 if (status->of_total != *last_total_bytes) {
3337                         /* We need to increment the done when there is
3338                            no information about each individual
3339                            message, we need to do this in message
3340                            transfers, and we don't do it for getting
3341                            messages */
3342                         if (increment_done)
3343                                 priv->done++;
3344                         *sum_total_bytes += *last_total_bytes;
3345                         *last_total_bytes = status->of_total;
3346                 }
3347                 state->bytes_done += status->position + *sum_total_bytes;
3348                 state->bytes_total = total_bytes;
3349
3350                 /* Notify the status change. Only notify about changes
3351                    referred to bytes */
3352                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3353                                0, state, NULL);
3354         }
3355
3356         g_slice_free (ModestMailOperationState, state);
3357 }
3358
3359 static void
3360 transfer_msgs_status_cb (GObject *obj,
3361                          TnyStatus *status,  
3362                          gpointer user_data)
3363 {
3364         XFerMsgsAsyncHelper *helper;
3365
3366         g_return_if_fail (status != NULL);
3367
3368         /* Show only the status information we want */
3369         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
3370                 return;
3371
3372         helper = (XFerMsgsAsyncHelper *) user_data;
3373         g_return_if_fail (helper != NULL);       
3374
3375         /* Notify progress */
3376         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
3377                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
3378 }
3379
3380 static void
3381 transfer_msgs_sync_folder_cb (TnyFolder *self, 
3382                               gboolean cancelled, 
3383                               GError *err, 
3384                               gpointer user_data)
3385 {
3386         XFerMsgsAsyncHelper *helper;
3387         /* We don't care here about the results of the
3388            synchronization */
3389         helper = (XFerMsgsAsyncHelper *) user_data;
3390
3391         /* Notify about operation end */
3392         modest_mail_operation_notify_end (helper->mail_op);
3393
3394         /* If user defined callback function was defined, call it */
3395         if (helper->user_callback)
3396                 helper->user_callback (helper->mail_op, helper->user_data);
3397         
3398         /* Free */
3399         if (helper->more_msgs)
3400                 g_object_unref (helper->more_msgs);
3401         if (helper->headers)
3402                 g_object_unref (helper->headers);
3403         if (helper->dest_folder)
3404                 g_object_unref (helper->dest_folder);
3405         if (helper->mail_op)
3406                 g_object_unref (helper->mail_op);
3407         g_slice_free (XFerMsgsAsyncHelper, helper);
3408 }
3409
3410 static void
3411 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
3412 {
3413         XFerMsgsAsyncHelper *helper;
3414         ModestMailOperation *self;
3415         ModestMailOperationPrivate *priv;
3416         gboolean finished = TRUE;
3417
3418         helper = (XFerMsgsAsyncHelper *) user_data;
3419         self = helper->mail_op;
3420
3421         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3422
3423         if (cancelled) {
3424                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3425         } else if (err) {
3426                 priv->error = g_error_copy (err);
3427                 priv->done = 0;
3428                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
3429         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
3430                 if (helper->more_msgs) {
3431                         /* We'll transfer the next message in the list */
3432                         tny_iterator_next (helper->more_msgs);
3433                         if (!tny_iterator_is_done (helper->more_msgs)) {
3434                                 GObject *next_header;
3435                                 g_object_unref (helper->headers);
3436                                 helper->headers = tny_simple_list_new ();
3437                                 next_header = tny_iterator_get_current (helper->more_msgs);
3438                                 tny_list_append (helper->headers, next_header);
3439                                 g_object_unref (next_header);
3440                                 finished = FALSE;
3441                         }
3442                 }
3443                 if (finished) {
3444                         priv->done = 1;
3445                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3446                 }
3447         }
3448
3449         if (finished) {
3450                 /* Synchronize the source folder contents. This should
3451                    be done by tinymail but the camel_folder_sync it's
3452                    actually disabled in transfer_msgs_thread_clean
3453                    because it's supposed to cause hangs */
3454                 tny_folder_sync_async (folder, helper->delete, 
3455                                        transfer_msgs_sync_folder_cb, 
3456                                        NULL, helper);
3457         } else {
3458                 /* Transfer more messages */
3459                 tny_folder_transfer_msgs_async (folder,
3460                                                 helper->headers,
3461                                                 helper->dest_folder,
3462                                                 helper->delete,
3463                                                 transfer_msgs_cb,
3464                                                 transfer_msgs_status_cb,
3465                                                 helper);
3466         }
3467 }
3468
3469 /* Computes the size of the messages the headers in the list belongs
3470    to. If num_elements is different from 0 then it only takes into
3471    account the first num_elements for the calculation */
3472 static guint
3473 compute_message_list_size (TnyList *headers, 
3474                            guint num_elements)
3475 {
3476         TnyIterator *iter;
3477         guint size = 0, element = 0;
3478
3479         /* If num_elements is not valid then take all into account */
3480         if ((num_elements <= 0) || (num_elements > tny_list_get_length (headers)))
3481                 num_elements = tny_list_get_length (headers);
3482
3483         iter = tny_list_create_iterator (headers);
3484         while (!tny_iterator_is_done (iter) && element < num_elements) {
3485                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3486                 size += tny_header_get_message_size (header);
3487                 g_object_unref (header);
3488                 tny_iterator_next (iter);
3489                 element++;
3490         }
3491         g_object_unref (iter);
3492
3493         return size;
3494 }
3495
3496 void
3497 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
3498                                  TnyList *headers, 
3499                                  TnyFolder *folder, 
3500                                  gboolean delete_original,
3501                                  XferMsgsAsyncUserCallback user_callback,
3502                                  gpointer user_data)
3503 {
3504         ModestMailOperationPrivate *priv = NULL;
3505         TnyIterator *iter = NULL;
3506         TnyFolder *src_folder = NULL;
3507         XFerMsgsAsyncHelper *helper = NULL;
3508         TnyHeader *header = NULL;
3509         ModestTnyFolderRules rules = 0;
3510         TnyAccount *dst_account = NULL;
3511         gboolean leave_on_server;
3512         ModestMailOperationState *state;
3513         ModestProtocolRegistry *protocol_registry;
3514         ModestProtocolType account_protocol;
3515
3516         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
3517         g_return_if_fail (headers && TNY_IS_LIST (headers));
3518         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
3519
3520         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3521         protocol_registry = modest_runtime_get_protocol_registry ();
3522
3523         priv->total = tny_list_get_length (headers);
3524         priv->done = 0;
3525         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3526         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3527
3528         /* Apply folder rules */
3529         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3530         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3531                 /* Set status failed and set an error */
3532                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3533                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3534                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
3535                              _CS("ckct_ib_unable_to_paste_here"));
3536                 /* Notify the queue */
3537                 modest_mail_operation_notify_end (self);
3538                 return;
3539         }
3540                 
3541         /* Get source folder */
3542         iter = tny_list_create_iterator (headers);
3543         header = TNY_HEADER (tny_iterator_get_current (iter));
3544         if (header) {
3545                 src_folder = tny_header_get_folder (header);
3546                 g_object_unref (header);
3547         }
3548         g_object_unref (iter);
3549
3550         if (src_folder == NULL) {
3551                 /* Notify the queue */
3552                 modest_mail_operation_notify_end (self);
3553
3554                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
3555                 return;
3556         }
3557
3558         
3559         /* Check folder source and destination */
3560         if (src_folder == folder) {
3561                 /* Set status failed and set an error */
3562                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3563                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3564                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
3565                              _("mail_in_ui_folder_copy_target_error"));
3566                 
3567                 /* Notify the queue */
3568                 modest_mail_operation_notify_end (self);
3569                 
3570                 /* Free */
3571                 g_object_unref (src_folder);            
3572                 return;
3573         }
3574
3575         /* Create the helper */
3576         helper = g_slice_new0 (XFerMsgsAsyncHelper);
3577         helper->mail_op = g_object_ref(self);
3578         helper->dest_folder = g_object_ref(folder);
3579         helper->user_callback = user_callback;
3580         helper->user_data = user_data;
3581         helper->last_total_bytes = 0;
3582         helper->sum_total_bytes = 0;
3583         helper->total_bytes = compute_message_list_size (headers, 0);
3584
3585         /* Get account and set it into mail_operation */
3586         priv->account = modest_tny_folder_get_account (src_folder);
3587         dst_account = modest_tny_folder_get_account (folder);
3588
3589         if (priv->account == dst_account) {
3590                 /* Transfer all messages at once using the fast
3591                  * method. Note that depending on the server this
3592                  * might not be that fast, and might not be
3593                  * user-cancellable either */
3594                 helper->headers = g_object_ref (headers);
3595                 helper->more_msgs = NULL;
3596         } else {
3597                 /* Transfer messages one by one so the user can cancel
3598                  * the operation */
3599                 GObject *hdr;
3600                 helper->headers = tny_simple_list_new ();
3601                 helper->more_msgs = tny_list_create_iterator (headers);
3602                 hdr = tny_iterator_get_current (helper->more_msgs);
3603                 tny_list_append (helper->headers, hdr);
3604                 g_object_unref (hdr);
3605         }
3606
3607         /* If leave_on_server is set to TRUE then don't use
3608            delete_original, we always pass FALSE. This is because
3609            otherwise tinymail will try to sync the source folder and
3610            this could cause an error if we're offline while
3611            transferring an already downloaded message from a POP
3612            account */
3613         account_protocol = modest_tny_account_get_protocol_type (priv->account);
3614         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_protocol)) {
3615                 const gchar *account_name;
3616
3617                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
3618                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3619                                                                           account_name);
3620         } else {
3621                 leave_on_server = FALSE;
3622         }
3623
3624         /* Do not delete messages if leave on server is TRUE */
3625         helper->delete = (leave_on_server) ? FALSE : delete_original;
3626
3627         modest_mail_operation_notify_start (self);
3628
3629         /* Start notifying progress */
3630         state = modest_mail_operation_clone_state (self);
3631         state->done = 0;
3632         state->total = 0;
3633         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3634         g_slice_free (ModestMailOperationState, state);
3635
3636         tny_folder_transfer_msgs_async (src_folder, 
3637                                         helper->headers, 
3638                                         folder, 
3639                                         helper->delete, 
3640                                         transfer_msgs_cb, 
3641                                         transfer_msgs_status_cb,
3642                                         helper);
3643         g_object_unref (src_folder);
3644         g_object_unref (dst_account);
3645 }
3646
3647
3648 static void
3649 on_refresh_folder (TnyFolder   *folder, 
3650                    gboolean     cancelled, 
3651                    GError     *error,
3652                    gpointer     user_data)
3653 {
3654         RefreshAsyncHelper *helper = NULL;
3655         ModestMailOperation *self = NULL;
3656         ModestMailOperationPrivate *priv = NULL;
3657
3658         helper = (RefreshAsyncHelper *) user_data;
3659         self = helper->mail_op;
3660         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3661
3662         g_return_if_fail(priv!=NULL);
3663
3664         if (error) {
3665                 priv->error = g_error_copy (error);
3666                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3667                 goto out;
3668         }
3669
3670         if (cancelled) {
3671                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3672                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3673                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3674                              _("Error trying to refresh the contents of %s"),
3675                              tny_folder_get_name (folder));
3676                 goto out;
3677         }
3678
3679         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3680  out:
3681
3682         /* Call user defined callback, if it exists */
3683         if (helper->user_callback) {
3684
3685                 /* This is not a GDK lock because we are a Tinymail callback and
3686                  * Tinymail already acquires the Gdk lock */
3687                 helper->user_callback (self, folder, helper->user_data);
3688         }
3689
3690         /* Free */
3691         g_slice_free (RefreshAsyncHelper, helper);
3692
3693         /* Notify about operation end */
3694         modest_mail_operation_notify_end (self);
3695         g_object_unref(self);
3696 }
3697
3698 static void
3699 on_refresh_folder_status_update (GObject *obj,
3700                                  TnyStatus *status,
3701                                  gpointer user_data)
3702 {
3703         RefreshAsyncHelper *helper = NULL;
3704         ModestMailOperation *self = NULL;
3705         ModestMailOperationPrivate *priv = NULL;
3706         ModestMailOperationState *state;
3707
3708         g_return_if_fail (user_data != NULL);
3709         g_return_if_fail (status != NULL);
3710
3711         /* Show only the status information we want */
3712         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
3713                 return;
3714
3715         helper = (RefreshAsyncHelper *) user_data;
3716         self = helper->mail_op;
3717         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3718
3719         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3720
3721         priv->done = status->position;
3722         priv->total = status->of_total;
3723
3724         state = modest_mail_operation_clone_state (self);
3725
3726         /* This is not a GDK lock because we are a Tinymail callback and
3727          * Tinymail already acquires the Gdk lock */
3728         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3729
3730         g_slice_free (ModestMailOperationState, state);
3731 }
3732
3733 void 
3734 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3735                                        TnyFolder *folder,
3736                                        RefreshAsyncUserCallback user_callback,
3737                                        gpointer user_data)
3738 {
3739         ModestMailOperationPrivate *priv = NULL;
3740         RefreshAsyncHelper *helper = NULL;
3741
3742         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3743
3744         /* Check memory low */
3745         if (_check_memory_low (self)) {
3746                 if (user_callback)
3747                         user_callback (self, folder, user_data);
3748                 /* Notify about operation end */
3749                 modest_mail_operation_notify_end (self);
3750                 return;
3751         }
3752
3753         /* Get account and set it into mail_operation */
3754         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3755         priv->account = modest_tny_folder_get_account  (folder);
3756         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3757
3758         /* Create the helper */
3759         helper = g_slice_new0 (RefreshAsyncHelper);
3760         helper->mail_op = g_object_ref(self);
3761         helper->user_callback = user_callback;
3762         helper->user_data = user_data;
3763
3764         modest_mail_operation_notify_start (self);
3765         
3766         /* notify that the operation was started */
3767         ModestMailOperationState *state;
3768         state = modest_mail_operation_clone_state (self);
3769         state->done = 0;
3770         state->total = 0;
3771         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3772                         0, state, NULL);
3773         g_slice_free (ModestMailOperationState, state);
3774         
3775         tny_folder_refresh_async (folder,
3776                                   on_refresh_folder,
3777                                   on_refresh_folder_status_update,
3778                                   helper);
3779 }
3780
3781 static void
3782 run_queue_notify_and_destroy (RunQueueHelper *helper,
3783                               ModestMailOperationStatus status)
3784 {
3785         ModestMailOperationPrivate *priv;
3786
3787         /* Disconnect */
3788         if (helper->error_handler &&
3789             g_signal_handler_is_connected (helper->queue, helper->error_handler))
3790                 g_signal_handler_disconnect (helper->queue, helper->error_handler);
3791         if (helper->start_handler &&
3792             g_signal_handler_is_connected (helper->queue, helper->start_handler))
3793                 g_signal_handler_disconnect (helper->queue, helper->start_handler);
3794         if (helper->stop_handler &&
3795             g_signal_handler_is_connected (helper->queue, helper->stop_handler))
3796                 g_signal_handler_disconnect (helper->queue, helper->stop_handler);
3797
3798         /* Set status */
3799         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
3800         priv->status = status;
3801
3802         /* Notify end */
3803         modest_mail_operation_notify_end (helper->self);
3804
3805         /* Free data */
3806         g_object_unref (helper->queue);
3807         g_object_unref (helper->self);
3808         g_slice_free (RunQueueHelper, helper);
3809 }
3810
3811 static void
3812 run_queue_stop (ModestTnySendQueue *queue,
3813                 gpointer user_data)
3814 {
3815         RunQueueHelper *helper;
3816
3817         g_debug ("%s sending queue stopped", __FUNCTION__);
3818
3819         helper = (RunQueueHelper *) user_data;
3820         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
3821 }
3822
3823 void
3824 modest_mail_operation_run_queue (ModestMailOperation *self,
3825                                  ModestTnySendQueue *queue)
3826 {
3827         ModestMailOperationPrivate *priv;
3828         RunQueueHelper *helper;
3829
3830         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3831         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3832         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3833
3834         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3835         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3836         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3837
3838         /* Create the helper */
3839         helper = g_slice_new0 (RunQueueHelper);
3840         helper->queue = g_object_ref (queue);
3841         helper->self = g_object_ref (self);
3842         helper->stop_handler = g_signal_connect (queue, "queue-stop", 
3843                                                  G_CALLBACK (run_queue_stop), 
3844                                                  helper);
3845
3846         /* Notify operation has started */
3847         modest_mail_operation_notify_start (self);
3848         g_debug ("%s, run queue started", __FUNCTION__);
3849 }
3850
3851 static void
3852 queue_wakeup_callback (ModestTnySendQueue *queue,
3853                        gboolean cancelled,
3854                        GError *error,
3855                        gpointer userdata)
3856 {
3857         ModestMailOperation *mail_op;
3858         ModestMailOperationPrivate *priv;
3859
3860         mail_op = (ModestMailOperation *) userdata;
3861         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3862
3863         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3864         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (queue));
3865
3866         /* Notify end */
3867         modest_mail_operation_notify_end (mail_op);
3868         g_object_unref (mail_op);
3869 }
3870
3871 void
3872 modest_mail_operation_queue_wakeup (ModestMailOperation *self,
3873                                     ModestTnySendQueue *queue)
3874 {
3875         ModestMailOperationPrivate *priv;
3876
3877         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3878         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3879         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3880
3881         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3882         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3883         priv->op_type = MODEST_MAIL_OPERATION_TYPE_QUEUE_WAKEUP;
3884
3885         g_object_ref (self);
3886
3887         modest_tny_send_queue_wakeup (queue, queue_wakeup_callback, self);
3888         modest_mail_operation_notify_start (self);
3889 }
3890
3891 static void
3892 shutdown_callback (ModestTnyAccountStore *account_store, gpointer userdata)
3893 {
3894         ModestMailOperation *self = (ModestMailOperation *) userdata;
3895         ModestMailOperationPrivate *priv;
3896
3897         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3898         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3899         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3900
3901         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3902
3903         modest_mail_operation_notify_end (self);
3904         g_object_unref (self);
3905 }
3906
3907 void
3908 modest_mail_operation_shutdown (ModestMailOperation *self, ModestTnyAccountStore *account_store)
3909 {
3910         ModestMailOperationPrivate *priv;
3911
3912         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3913         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3914         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3915
3916         modest_mail_operation_queue_set_running_shutdown (modest_runtime_get_mail_operation_queue ());
3917
3918         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3919         priv->account = NULL;
3920         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SHUTDOWN;
3921
3922         modest_mail_operation_notify_start (self);
3923         g_object_ref (self);
3924         modest_tny_account_store_shutdown (account_store, shutdown_callback, self);
3925 }
3926
3927 static void
3928 sync_folder_finish_callback (TnyFolder *self,
3929                              gboolean cancelled,
3930                              GError *err,
3931                              gpointer user_data)
3932
3933 {
3934         ModestMailOperationPrivate *priv;
3935         SyncFolderHelper *helper;
3936
3937         helper = (SyncFolderHelper *) user_data;
3938         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->mail_op);
3939
3940         /* If canceled by the user, ignore the error given by Tinymail */
3941         if (cancelled) {
3942                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3943         } else if (err) {
3944                 /* If the operation was a sync then the status is
3945                    failed, but if it's part of another operation then
3946                    just set it as finished with errors */
3947                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
3948                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3949                 else
3950                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3951                 priv->error = g_error_copy ((const GError *) err);
3952                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3953         } else {
3954                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3955         }
3956
3957         /* User callback */
3958         if (helper->user_callback)
3959                 helper->user_callback (helper->mail_op, self, helper->user_data);
3960
3961         modest_mail_operation_notify_end (helper->mail_op);
3962
3963         /* Frees */
3964         g_object_unref (helper->mail_op);
3965         g_slice_free (SyncFolderHelper, helper);
3966 }
3967
3968 void
3969 modest_mail_operation_sync_folder (ModestMailOperation *self,
3970                                    TnyFolder *folder,
3971                                    gboolean expunge,
3972                                    SyncFolderCallback callback,
3973                                    gpointer user_data)
3974 {
3975         ModestMailOperationPrivate *priv;
3976         SyncFolderHelper *helper;
3977
3978         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3979         g_return_if_fail (TNY_IS_FOLDER (folder));
3980         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3981
3982         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3983         priv->account = modest_tny_folder_get_account (folder);
3984         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3985
3986         /* Create helper */
3987         helper = g_slice_new0 (SyncFolderHelper);
3988         helper->mail_op = g_object_ref (self);
3989         helper->user_callback = callback;
3990         helper->user_data = user_data;
3991
3992         modest_mail_operation_notify_start (self);
3993         tny_folder_sync_async (folder, expunge,
3994                                (TnyFolderCallback) sync_folder_finish_callback,
3995                                NULL, helper);
3996 }
3997
3998 static void
3999 modest_mail_operation_notify_start (ModestMailOperation *self)
4000 {
4001         ModestMailOperationPrivate *priv = NULL;
4002
4003         g_return_if_fail (self);
4004
4005         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4006
4007         /* Ensure that all the fields are filled correctly */
4008         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
4009
4010         /* Notify the observers about the mail operation. We do not
4011            wrapp this emission because we assume that this function is
4012            always called from within the main lock */
4013         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
4014 }
4015
4016 /**
4017  *
4018  * It's used by the mail operation queue to notify the observers
4019  * attached to that signal that the operation finished. We need to use
4020  * that because tinymail does not give us the progress of a given
4021  * operation when it finishes (it directly calls the operation
4022  * callback).
4023  */
4024 static void
4025 modest_mail_operation_notify_end (ModestMailOperation *self)
4026 {
4027         ModestMailOperationPrivate *priv = NULL;
4028
4029         g_return_if_fail (self);
4030
4031         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4032
4033         /* Notify the observers about the mail operation end. We do
4034            not wrapp this emission because we assume that this
4035            function is always called from within the main lock */
4036         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
4037
4038         /* Remove the error user data */
4039         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
4040                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
4041 }
4042
4043 TnyAccount *
4044 modest_mail_operation_get_account (ModestMailOperation *self)
4045 {
4046         ModestMailOperationPrivate *priv = NULL;
4047
4048         g_return_val_if_fail (self, NULL);
4049
4050         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4051
4052         return (priv->account) ? g_object_ref (priv->account) : NULL;
4053 }
4054
4055 void
4056 modest_mail_operation_noop (ModestMailOperation *self)
4057 {
4058         ModestMailOperationPrivate *priv = NULL;
4059
4060         g_return_if_fail (self);
4061
4062         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4063         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
4064         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
4065         priv->done = 0;
4066         priv->total = 0;
4067
4068         /* This mail operation does nothing actually */
4069         modest_mail_operation_notify_start (self);
4070         modest_mail_operation_notify_end (self);
4071 }
4072
4073
4074 gchar*
4075 modest_mail_operation_to_string (ModestMailOperation *self)
4076 {
4077         const gchar *type, *status, *account_id;
4078         ModestMailOperationPrivate *priv = NULL;
4079         
4080         g_return_val_if_fail (self, NULL);
4081
4082         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4083
4084         /* new operations don't have anything interesting */
4085         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
4086                 return g_strdup_printf ("%p <new operation>", self);
4087         
4088         switch (priv->op_type) {
4089         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
4090         case MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE:    type= "SEND-AND-RECEIVE";    break;
4091         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
4092         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
4093         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
4094         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
4095         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
4096         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
4097         case MODEST_MAIL_OPERATION_TYPE_SHUTDOWN: type= "SHUTDOWN"; break;
4098         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
4099         default: type = "UNEXPECTED"; break;
4100         }
4101
4102         switch (priv->status) {
4103         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
4104         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
4105         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
4106         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
4107         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
4108         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
4109         default:                                                status= "UNEXPECTED"; break;
4110         } 
4111
4112         account_id = priv->account ? tny_account_get_id (priv->account) : "";
4113
4114         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
4115                                 priv->done, priv->total,
4116                                 priv->error && priv->error->message ? priv->error->message : "");
4117 }
4118
4119 /* 
4120  * Once the mail operations were objects this will be no longer
4121  * needed. I don't like it, but we need it for the moment
4122  */
4123 static gboolean
4124 _check_memory_low (ModestMailOperation *mail_op)
4125 {
4126         if (modest_platform_check_memory_low (NULL, FALSE)) {
4127                 ModestMailOperationPrivate *priv;
4128
4129                 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
4130                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
4131                 g_set_error (&(priv->error),
4132                              MODEST_MAIL_OPERATION_ERROR,
4133                              MODEST_MAIL_OPERATION_ERROR_LOW_MEMORY,
4134                              "Not enough memory to complete the operation");
4135                 return TRUE;
4136         } else {
4137                 return FALSE;
4138         }
4139 }