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