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