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