* Dramatically refactored the message transfers, now the same code is used for a...
[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                 if (outbox) { /* this could fail in some cases */
1357                         num_messages = tny_folder_get_all_count (outbox);
1358                         g_object_unref (outbox);
1359                 } else {
1360                         g_warning ("%s: could not get outbox", __FUNCTION__);
1361                         num_messages = 0;
1362                 }
1363                 
1364                 if (num_messages != 0) {
1365                         /* Send mails */
1366                         g_object_unref (priv->account);
1367
1368                         /* Reenable suspended items */
1369                         modest_tny_send_queue_wakeup (MODEST_TNY_SEND_QUEUE (send_queue));
1370
1371                         /* Try to send */
1372                         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (send_queue));
1373                 }
1374         } 
1375
1376         /* Check if the operation was a success */
1377         if (!priv->error)
1378                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1379
1380         /* Set the account back to not busy */
1381         modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1382
1383         /* Call the user callback */
1384         if (info->callback)
1385                 info->callback (info->mail_op, new_headers, info->user_data);
1386
1387         /* Notify about operation end */
1388         modest_mail_operation_notify_end (info->mail_op);
1389
1390         /* Frees */
1391         if (new_headers)
1392                 g_object_unref (new_headers);
1393         destroy_update_account_info (info);
1394 }
1395
1396 static void 
1397 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1398                           gboolean canceled,
1399                           TnyList *list, 
1400                           GError *err, 
1401                           gpointer user_data)
1402 {
1403         UpdateAccountInfo *info;
1404         ModestMailOperationPrivate *priv;
1405     
1406         info = (UpdateAccountInfo *) user_data;
1407         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1408
1409         if (err || canceled) {
1410                 /* Try to continue anyway */
1411         } else {
1412                 TnyIterator *iter = tny_list_create_iterator (list);
1413                 while (!tny_iterator_is_done (iter)) {
1414                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1415                         TnyList *folders = tny_simple_list_new ();
1416
1417                         /* Add to the list of all folders */
1418                         tny_list_append (info->folders, (GObject *) folder);
1419                         
1420                         /* Add pending call */
1421                         info->pending_calls++;
1422                         
1423                         tny_folder_store_get_folders_async (folder, folders, NULL,
1424                                                             recurse_folders_async_cb, 
1425                                                             NULL, info);
1426                         
1427                         g_object_unref (G_OBJECT (folder));
1428                         
1429                         tny_iterator_next (iter);           
1430                 }
1431                 g_object_unref (G_OBJECT (iter));
1432                 g_object_unref (G_OBJECT (list));
1433         }
1434
1435         /* Remove my own pending call */
1436         info->pending_calls--;
1437
1438         /* This means that we have all the folders */
1439         if (info->pending_calls == 0) {
1440                 TnyIterator *iter_all_folders;
1441                 TnyFolder *inbox = NULL;
1442
1443                 iter_all_folders = tny_list_create_iterator (info->folders);
1444
1445                 /* Do a poke status over all folders */
1446                 while (!tny_iterator_is_done (iter_all_folders) &&
1447                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1448                         TnyFolder *folder = NULL;
1449
1450                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1451
1452                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1453                                 /* Get a reference to the INBOX */
1454                                 inbox = g_object_ref (folder);
1455                         } else {
1456                                 /* Issue a poke status over the folder */
1457                                 if (info->poke_all)
1458                                         tny_folder_poke_status (folder);
1459                         }
1460
1461                         /* Free and go to next */
1462                         g_object_unref (folder);
1463                         tny_iterator_next (iter_all_folders);
1464                 }
1465                 g_object_unref (iter_all_folders);
1466
1467                 /* Refresh the INBOX */
1468                 if (inbox) {
1469                         /* Refresh the folder. Our observer receives
1470                          * the new emails during folder refreshes, so
1471                          * we can use observer->new_headers
1472                          */
1473                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1474                         tny_folder_add_observer (inbox, info->inbox_observer);
1475
1476                         /* Refresh the INBOX */
1477                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1478                         g_object_unref (inbox);
1479                 } else {
1480                         /* We could not perform the inbox refresh but
1481                            we'll try to send mails anyway */
1482                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1483                 }
1484         }
1485 }
1486
1487 void
1488 modest_mail_operation_update_account (ModestMailOperation *self,
1489                                       const gchar *account_name,
1490                                       gboolean poke_all,
1491                                       RetrieveAllCallback retrieve_all_cb,
1492                                       UpdateAccountCallback callback,
1493                                       gpointer user_data)
1494 {
1495         UpdateAccountInfo *info = NULL;
1496         ModestMailOperationPrivate *priv = NULL;
1497         ModestTnyAccountStore *account_store = NULL;
1498         TnyStoreAccount *store_account = NULL;
1499         TnyList *folders;
1500         ModestMailOperationState *state;
1501
1502         /* Init mail operation */
1503         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1504         priv->total = 0;
1505         priv->done  = 0;
1506         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1507         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
1508
1509         /* Get the store account */
1510         account_store = modest_runtime_get_account_store ();
1511         store_account = (TnyStoreAccount *)
1512                 modest_tny_account_store_get_server_account (account_store,
1513                                                              account_name,
1514                                                              TNY_ACCOUNT_TYPE_STORE);
1515         priv->account = g_object_ref (store_account);
1516
1517         /* Create the helper object */
1518         info = g_slice_new0 (UpdateAccountInfo);
1519         info->pending_calls = 1;
1520         info->folders = tny_simple_list_new ();
1521         info->mail_op = g_object_ref (self);
1522         info->poke_all = poke_all;
1523         info->account_name = g_strdup (account_name);
1524         info->callback = callback;
1525         info->user_data = user_data;
1526         info->retrieve_all_cb = retrieve_all_cb;
1527
1528         /* Set account busy */
1529         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1530         modest_mail_operation_notify_start (self);
1531
1532         /* notify about the start of the operation */ 
1533         state = modest_mail_operation_clone_state (self);
1534         state->done = 0;
1535         state->total = 0;
1536
1537         /* Start notifying progress */
1538         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1539         g_slice_free (ModestMailOperationState, state);
1540         
1541         /* Get all folders and continue in the callback */ 
1542         folders = tny_simple_list_new ();
1543         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
1544                                             folders, NULL,
1545                                             recurse_folders_async_cb, 
1546                                             NULL, info);
1547 }
1548
1549 /*
1550  * Used to notify the queue from the main
1551  * loop. We call it inside an idle call to achieve that
1552  */
1553 static gboolean
1554 idle_notify_queue (gpointer data)
1555 {
1556         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1557
1558         gdk_threads_enter ();
1559         modest_mail_operation_notify_end (mail_op);
1560         gdk_threads_leave ();
1561         g_object_unref (mail_op);
1562
1563         return FALSE;
1564 }
1565
1566 static int
1567 compare_headers_by_date (gconstpointer a,
1568                          gconstpointer b)
1569 {
1570         TnyHeader **header1, **header2;
1571         time_t sent1, sent2;
1572
1573         header1 = (TnyHeader **) a;
1574         header2 = (TnyHeader **) b;
1575
1576         sent1 = tny_header_get_date_sent (*header1);
1577         sent2 = tny_header_get_date_sent (*header2);
1578
1579         /* We want the most recent ones (greater time_t) at the
1580            beginning */
1581         if (sent1 < sent2)
1582                 return 1;
1583         else
1584                 return -1;
1585 }
1586
1587
1588 /* ******************************************************************* */
1589 /* ************************** STORE  ACTIONS ************************* */
1590 /* ******************************************************************* */
1591
1592 typedef struct {
1593         ModestMailOperation *mail_op;
1594         CreateFolderUserCallback callback;
1595         gpointer user_data;
1596 } CreateFolderInfo;
1597
1598
1599 static void
1600 create_folder_cb (TnyFolderStore *parent_folder, 
1601                   gboolean canceled, 
1602                   TnyFolder *new_folder, 
1603                   GError *err, 
1604                   gpointer user_data)
1605 {
1606         ModestMailOperationPrivate *priv;
1607         CreateFolderInfo *info;
1608
1609         info = (CreateFolderInfo *) user_data;
1610         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1611
1612         if (canceled || err) {
1613                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1614                 if (err)
1615                         priv->error = g_error_copy (err);
1616                 else
1617                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1618                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1619                                      "canceled");               
1620         } else {
1621                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1622         }
1623
1624         /* The user will unref the new_folder */
1625         if (info->callback)
1626                 info->callback (info->mail_op, parent_folder, 
1627                                 new_folder, info->user_data);
1628         
1629         /* Notify about operation end */
1630         modest_mail_operation_notify_end (info->mail_op);
1631
1632         /* Frees */
1633         g_object_unref (info->mail_op);
1634         g_slice_free (CreateFolderInfo, info);
1635 }
1636
1637 void
1638 modest_mail_operation_create_folder (ModestMailOperation *self,
1639                                      TnyFolderStore *parent,
1640                                      const gchar *name,
1641                                      CreateFolderUserCallback callback,
1642                                      gpointer user_data)
1643 {
1644         ModestMailOperationPrivate *priv;
1645
1646         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1647         g_return_if_fail (name);
1648         
1649         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1650         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1651         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1652                 g_object_ref (parent) : 
1653                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1654
1655         /* Check for already existing folder */
1656         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1657                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1658                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1659                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1660                              _CS("ckdg_ib_folder_already_exists"));
1661         }
1662
1663         /* Check parent */
1664         if (TNY_IS_FOLDER (parent)) {
1665                 /* Check folder rules */
1666                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1667                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1668                         /* Set status failed and set an error */
1669                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1670                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1671                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1672                                      _("mail_in_ui_folder_create_error"));
1673                 }
1674         }
1675
1676         if (!strcmp (name, " ") || strchr (name, '/')) {
1677                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1678                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1679                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1680                              _("mail_in_ui_folder_create_error"));
1681         }
1682
1683         if (!priv->error) {
1684                 CreateFolderInfo *info;
1685
1686                 info = g_slice_new0 (CreateFolderInfo);
1687                 info->mail_op = g_object_ref (self);
1688                 info->callback = callback;
1689                 info->user_data = user_data;
1690
1691                 modest_mail_operation_notify_start (self);
1692
1693                 /* Create the folder */
1694                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
1695                                                       NULL, info);
1696         } else {
1697                 /* Call the user callback anyway */
1698                 if (callback)
1699                         callback (self, parent, NULL, user_data);
1700                 /* Notify about operation end */
1701                 modest_mail_operation_notify_end (self);
1702         }
1703 }
1704
1705 void
1706 modest_mail_operation_remove_folder (ModestMailOperation *self,
1707                                      TnyFolder           *folder,
1708                                      gboolean             remove_to_trash)
1709 {
1710         TnyAccount *account;
1711         ModestMailOperationPrivate *priv;
1712         ModestTnyFolderRules rules;
1713
1714         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1715         g_return_if_fail (TNY_IS_FOLDER (folder));
1716         
1717         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1718         
1719         /* Check folder rules */
1720         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1721         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1722                 /* Set status failed and set an error */
1723                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1724                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1725                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1726                              _("mail_in_ui_folder_delete_error"));
1727                 goto end;
1728         }
1729
1730         /* Get the account */
1731         account = modest_tny_folder_get_account (folder);
1732         priv->account = g_object_ref(account);
1733         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1734
1735         /* Delete folder or move to trash */
1736         if (remove_to_trash) {
1737                 TnyFolder *trash_folder = NULL;
1738                 trash_folder = modest_tny_account_get_special_folder (account,
1739                                                                       TNY_FOLDER_TYPE_TRASH);
1740                 /* TODO: error_handling */
1741                 if (trash_folder) {
1742                         modest_mail_operation_notify_start (self);
1743                         modest_mail_operation_xfer_folder (self, folder,
1744                                                     TNY_FOLDER_STORE (trash_folder), 
1745                                                     TRUE, NULL, NULL);
1746                         g_object_unref (trash_folder);
1747                 } else {
1748                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
1749                 }
1750         } else {
1751                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1752                 if (parent) {
1753                         modest_mail_operation_notify_start (self);
1754                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
1755                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1756                         
1757                         if (!priv->error)
1758                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1759
1760                         g_object_unref (parent);
1761                 } else
1762                         g_warning ("%s: could not get parent folder", __FUNCTION__);
1763         }
1764         g_object_unref (G_OBJECT (account));
1765
1766  end:
1767         /* Notify about operation end */
1768         modest_mail_operation_notify_end (self);
1769 }
1770
1771 static void
1772 transfer_folder_status_cb (GObject *obj,
1773                            TnyStatus *status,
1774                            gpointer user_data)
1775 {
1776         ModestMailOperation *self;
1777         ModestMailOperationPrivate *priv;
1778         ModestMailOperationState *state;
1779         XFerFolderAsyncHelper *helper;
1780
1781         g_return_if_fail (status != NULL);
1782         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1783
1784         helper = (XFerFolderAsyncHelper *) user_data;
1785         g_return_if_fail (helper != NULL);
1786
1787         self = helper->mail_op;
1788         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1789
1790         priv->done = status->position;
1791         priv->total = status->of_total;
1792
1793         state = modest_mail_operation_clone_state (self);
1794
1795         /* This is not a GDK lock because we are a Tinymail callback
1796          * which is already GDK locked by Tinymail */
1797
1798         /* no gdk_threads_enter (), CHECKED */
1799
1800         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1801
1802         /* no gdk_threads_leave (), CHECKED */
1803
1804         g_slice_free (ModestMailOperationState, state);
1805 }
1806
1807
1808 static void
1809 transfer_folder_cb (TnyFolder *folder, 
1810                     gboolean cancelled, 
1811                     TnyFolderStore *into, 
1812                     TnyFolder *new_folder, 
1813                     GError *err, 
1814                     gpointer user_data)
1815 {
1816         XFerFolderAsyncHelper *helper;
1817         ModestMailOperation *self = NULL;
1818         ModestMailOperationPrivate *priv = NULL;
1819
1820         helper = (XFerFolderAsyncHelper *) user_data;
1821         g_return_if_fail (helper != NULL);       
1822
1823         self = helper->mail_op;
1824         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1825
1826         if (err) {
1827                 priv->error = g_error_copy (err);
1828                 priv->done = 0;
1829                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1830         } else if (cancelled) {
1831                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1832                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1833                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1834                              _("Transference of %s was cancelled."),
1835                              tny_folder_get_name (folder));
1836         } else {
1837                 priv->done = 1;
1838                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1839         }
1840                 
1841         /* Notify about operation end */
1842         modest_mail_operation_notify_end (self);
1843
1844         /* If user defined callback function was defined, call it */
1845         if (helper->user_callback) {
1846
1847                 /* This is not a GDK lock because we are a Tinymail callback
1848                  * which is already GDK locked by Tinymail */
1849
1850                 /* no gdk_threads_enter (), CHECKED */
1851                 helper->user_callback (self, new_folder, helper->user_data);
1852                 /* no gdk_threads_leave () , CHECKED */
1853         }
1854
1855         /* Free */
1856         g_object_unref (helper->mail_op);
1857         g_slice_free   (XFerFolderAsyncHelper, helper);
1858 }
1859
1860 /**
1861  *
1862  * This function checks if the new name is a valid name for our local
1863  * folders account. The new name could not be the same than then name
1864  * of any of the mandatory local folders
1865  *
1866  * We can not rely on tinymail because tinymail does not check the
1867  * name of the virtual folders that the account could have in the case
1868  * that we're doing a rename (because it directly calls Camel which
1869  * knows nothing about our virtual folders). 
1870  *
1871  * In the case of an actual copy/move (i.e. move/copy a folder between
1872  * accounts) tinymail uses the tny_folder_store_create_account which
1873  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1874  * checks the new name of the folder, so this call in that case
1875  * wouldn't be needed. *But* NOTE that if tinymail changes its
1876  * implementation (if folder transfers within the same account is no
1877  * longer implemented as a rename) this call will allow Modest to work
1878  * perfectly
1879  *
1880  * If the new name is not valid, this function will set the status to
1881  * failed and will set also an error in the mail operation
1882  */
1883 static gboolean
1884 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1885                                  TnyFolderStore *into,
1886                                  const gchar *new_name)
1887 {
1888         if (TNY_IS_ACCOUNT (into) && 
1889             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1890             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1891                                                                  new_name)) {
1892                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1893                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1894                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1895                              _CS("ckdg_ib_folder_already_exists"));
1896                 return FALSE;
1897         } else
1898                 return TRUE;
1899 }
1900
1901 void
1902 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1903                                    TnyFolder *folder,
1904                                    TnyFolderStore *parent,
1905                                    gboolean delete_original,
1906                                    XferFolderAsyncUserCallback user_callback,
1907                                    gpointer user_data)
1908 {
1909         ModestMailOperationPrivate *priv = NULL;
1910         ModestTnyFolderRules parent_rules = 0, rules; 
1911         XFerFolderAsyncHelper *helper = NULL;
1912         const gchar *folder_name = NULL;
1913         const gchar *error_msg;
1914
1915         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1916         g_return_if_fail (TNY_IS_FOLDER (folder));
1917         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1918
1919         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1920         folder_name = tny_folder_get_name (folder);
1921
1922         /* Set the error msg */
1923         error_msg = _("mail_in_ui_folder_move_target_error");
1924
1925         /* Get account and set it into mail_operation */
1926         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1927         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1928         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1929
1930         /* Get folder rules */
1931         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1932         if (TNY_IS_FOLDER (parent))
1933                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1934         
1935         /* Apply operation constraints */
1936         if ((gpointer) parent == (gpointer) folder ||
1937             (!TNY_IS_FOLDER_STORE (parent)) || 
1938             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1939                 /* Folder rules */
1940                 goto error;
1941         } else if (TNY_IS_FOLDER (parent) && 
1942                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1943                 /* Folder rules */
1944                 goto error;
1945
1946         } else if (TNY_IS_FOLDER (parent) &&
1947                    TNY_IS_FOLDER_STORE (folder) &&
1948                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
1949                                                   TNY_FOLDER_STORE (folder))) {
1950                 /* Do not move a parent into a child */
1951                 goto error;
1952         } else if (TNY_IS_FOLDER_STORE (parent) &&
1953                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1954                 /* Check that the new folder name is not used by any
1955                    parent subfolder */
1956                 goto error;     
1957         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1958                 /* Check that the new folder name is not used by any
1959                    special local folder */
1960                 goto error;
1961         } else {
1962                 /* Create the helper */
1963                 helper = g_slice_new0 (XFerFolderAsyncHelper);
1964                 helper->mail_op = g_object_ref (self);
1965                 helper->user_callback = user_callback;
1966                 helper->user_data = user_data;
1967                 
1968                 /* Move/Copy folder */
1969                 modest_mail_operation_notify_start (self);
1970                 tny_folder_copy_async (folder,
1971                                        parent,
1972                                        tny_folder_get_name (folder),
1973                                        delete_original,
1974                                        transfer_folder_cb,
1975                                        transfer_folder_status_cb,
1976                                        helper);
1977                 return;
1978         }
1979
1980  error:
1981         /* Set status failed and set an error */
1982         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1983         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1984                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1985                      error_msg);
1986
1987         /* Call the user callback if exists */
1988         if (user_callback)
1989                 user_callback (self, NULL, user_data);
1990
1991         /* Notify the queue */
1992         modest_mail_operation_notify_end (self);
1993 }
1994
1995 void
1996 modest_mail_operation_rename_folder (ModestMailOperation *self,
1997                                      TnyFolder *folder,
1998                                      const gchar *name,
1999                                      XferFolderAsyncUserCallback user_callback,
2000                                      gpointer user_data)
2001 {
2002         ModestMailOperationPrivate *priv;
2003         ModestTnyFolderRules rules;
2004         XFerFolderAsyncHelper *helper;
2005
2006         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2007         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2008         g_return_if_fail (name);
2009         
2010         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2011
2012         /* Get account and set it into mail_operation */
2013         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2014         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2015
2016         /* Check folder rules */
2017         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2018         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2019                 goto error;
2020         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2021                 goto error;
2022         } else {
2023                 TnyFolderStore *into;
2024
2025                 into = tny_folder_get_folder_store (folder);    
2026
2027                 /* Check that the new folder name is not used by any
2028                    special local folder */
2029                 if (new_name_valid_if_local_account (priv, into, name)) {
2030                         /* Create the helper */
2031                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2032                         helper->mail_op = g_object_ref(self);
2033                         helper->user_callback = user_callback;
2034                         helper->user_data = user_data;
2035                 
2036                         /* Rename. Camel handles folder subscription/unsubscription */
2037                         modest_mail_operation_notify_start (self);
2038                         tny_folder_copy_async (folder, into, name, TRUE,
2039                                                transfer_folder_cb,
2040                                                transfer_folder_status_cb,
2041                                                helper);
2042                 } else {
2043                         goto error;
2044                 }
2045                 g_object_unref (into);
2046
2047                 return;
2048         }
2049  error:
2050         /* Set status failed and set an error */
2051         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2052         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2053                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2054                      _("FIXME: unable to rename"));
2055         
2056         if (user_callback)
2057                 user_callback (self, NULL, user_data);
2058
2059         /* Notify about operation end */
2060         modest_mail_operation_notify_end (self);
2061 }
2062
2063 /* ******************************************************************* */
2064 /* **************************  MSG  ACTIONS  ************************* */
2065 /* ******************************************************************* */
2066
2067 void 
2068 modest_mail_operation_get_msg (ModestMailOperation *self,
2069                                TnyHeader *header,
2070                                GetMsgAsyncUserCallback user_callback,
2071                                gpointer user_data)
2072 {
2073         GetMsgInfo *helper = NULL;
2074         TnyFolder *folder;
2075         ModestMailOperationPrivate *priv;
2076         
2077         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2078         g_return_if_fail (TNY_IS_HEADER (header));
2079         
2080         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2081         folder = tny_header_get_folder (header);
2082
2083         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2084         priv->total = 1;
2085         priv->done = 0;
2086
2087         /* Get account and set it into mail_operation */
2088         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2089         
2090         /* Check for cached messages */
2091         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2092                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2093         else 
2094                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2095         
2096         /* Create the helper */
2097         helper = g_slice_new0 (GetMsgInfo);
2098         helper->header = g_object_ref (header);
2099         helper->mail_op = g_object_ref (self);
2100         helper->user_callback = user_callback;
2101         helper->user_data = user_data;
2102         helper->destroy_notify = NULL;
2103         helper->last_total_bytes = 0;
2104         helper->sum_total_bytes = 0;
2105         helper->total_bytes = tny_header_get_message_size (header);
2106
2107         modest_mail_operation_notify_start (self);
2108         
2109         /* notify about the start of the operation */ 
2110         ModestMailOperationState *state;
2111         state = modest_mail_operation_clone_state (self);
2112         state->done = 0;
2113         state->total = 0;
2114         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2115                                 0, state, NULL);
2116         
2117         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2118
2119         g_object_unref (G_OBJECT (folder));
2120 }
2121
2122 static void     
2123 get_msg_status_cb (GObject *obj,
2124                    TnyStatus *status,  
2125                    gpointer user_data)
2126 {
2127         GetMsgInfo *helper = NULL;
2128
2129         g_return_if_fail (status != NULL);
2130         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2131
2132         helper = (GetMsgInfo *) user_data;
2133         g_return_if_fail (helper != NULL);       
2134
2135         /* Notify progress */
2136         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2137                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2138 }
2139
2140 static void
2141 get_msg_async_cb (TnyFolder *folder, 
2142                   gboolean canceled, 
2143                   TnyMsg *msg, 
2144                   GError *err, 
2145                   gpointer user_data)
2146 {
2147         GetMsgInfo *info = NULL;
2148         ModestMailOperationPrivate *priv = NULL;
2149         gboolean finished;
2150
2151         info = (GetMsgInfo *) user_data;
2152
2153         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2154         priv->done++;
2155
2156         if (info->more_msgs) {
2157                 tny_iterator_next (info->more_msgs);
2158                 finished = (tny_iterator_is_done (info->more_msgs));
2159         } else {
2160                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2161         }
2162
2163         /* Check errors */
2164         if (canceled || err) {
2165                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2166                 if (err) {
2167                         priv->error = g_error_copy ((const GError *) err);
2168                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2169                 }
2170                 if (!priv->error) {
2171                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2172                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2173                                      err->message);
2174                 }
2175         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2176                 /* Set the success status before calling the user callback */
2177                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2178         } else if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2179                 canceled = TRUE;
2180                 finished = TRUE;
2181         }
2182
2183
2184         /* Call the user callback */
2185         if (info->user_callback)
2186                 info->user_callback (info->mail_op, info->header, canceled, 
2187                                      msg, err, info->user_data);
2188
2189         /* Notify about operation end if this is the last callback */
2190         if (finished) {
2191                 /* Free user data */
2192                 if (info->destroy_notify)
2193                         info->destroy_notify (info->user_data);
2194
2195                 /* Notify about operation end */
2196                 modest_mail_operation_notify_end (info->mail_op);
2197
2198                 /* Clean */
2199                 if (info->more_msgs)
2200                         g_object_unref (info->more_msgs);
2201                 g_object_unref (info->header);
2202                 g_object_unref (info->mail_op);
2203                 g_slice_free (GetMsgInfo, info);
2204         } else if (info->more_msgs) {
2205                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
2206                 TnyFolder *folder = tny_header_get_folder (header);
2207
2208                 g_object_unref (info->header);
2209                 info->header = g_object_ref (header);
2210
2211                 /* Retrieve the next message */
2212                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
2213
2214                 g_object_unref (header);
2215                 g_object_unref (folder);
2216         } else {
2217                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
2218         }
2219 }
2220
2221 void 
2222 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2223                                      TnyList *header_list, 
2224                                      GetMsgAsyncUserCallback user_callback,
2225                                      gpointer user_data,
2226                                      GDestroyNotify notify)
2227 {
2228         ModestMailOperationPrivate *priv = NULL;
2229         gint msg_list_size;
2230         TnyIterator *iter = NULL;
2231         
2232         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2233
2234         /* Init mail operation */
2235         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2236         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2237         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2238         priv->done = 0;
2239         priv->total = tny_list_get_length(header_list);
2240
2241         /* Get account and set it into mail_operation */
2242         if (tny_list_get_length (header_list) >= 1) {
2243                 TnyIterator *iterator = tny_list_create_iterator (header_list);
2244                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
2245                 if (header) {
2246                         TnyFolder *folder = tny_header_get_folder (header);
2247                         if (folder) {           
2248                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2249                                 g_object_unref (folder);
2250                         }
2251                         g_object_unref (header);
2252                 }
2253                 g_object_unref (iterator);
2254         }
2255
2256         msg_list_size = compute_message_list_size (header_list);
2257
2258         modest_mail_operation_notify_start (self);
2259         iter = tny_list_create_iterator (header_list);
2260         if (!tny_iterator_is_done (iter)) {
2261                 /* notify about the start of the operation */
2262                 ModestMailOperationState *state;
2263                 state = modest_mail_operation_clone_state (self);
2264                 state->done = 0;
2265                 state->total = 0;
2266                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2267                                0, state, NULL);
2268
2269                 GetMsgInfo *msg_info = NULL;
2270                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2271                 TnyFolder *folder = tny_header_get_folder (header);
2272
2273                 /* Create the message info */
2274                 msg_info = g_slice_new0 (GetMsgInfo);
2275                 msg_info->mail_op = g_object_ref (self);
2276                 msg_info->header = g_object_ref (header);
2277                 msg_info->more_msgs = g_object_ref (iter);
2278                 msg_info->user_callback = user_callback;
2279                 msg_info->user_data = user_data;
2280                 msg_info->destroy_notify = notify;
2281                 msg_info->last_total_bytes = 0;
2282                 msg_info->sum_total_bytes = 0;
2283                 msg_info->total_bytes = msg_list_size;
2284
2285                 /* The callback will call it per each header */
2286                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2287
2288                 /* Free and go on */
2289                 g_object_unref (header);
2290                 g_object_unref (folder);
2291                 g_slice_free (ModestMailOperationState, state);
2292         }
2293         g_object_unref (iter);
2294 }
2295
2296
2297 void 
2298 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2299                                   TnyHeader *header,
2300                                   gboolean remove_to_trash /*ignored*/)
2301 {
2302         TnyFolder *folder;
2303         ModestMailOperationPrivate *priv;
2304
2305         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2306         g_return_if_fail (TNY_IS_HEADER (header));
2307
2308         if (remove_to_trash)
2309                 g_warning ("remove to trash is not implemented");
2310
2311         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2312         folder = tny_header_get_folder (header);
2313
2314         /* Get account and set it into mail_operation */
2315         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2316         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2317         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2318
2319         /* remove message from folder */
2320         tny_folder_remove_msg (folder, header, &(priv->error));
2321         if (!priv->error) {
2322                 gboolean expunge, leave_on_server;
2323                 const gchar *account_name;
2324                 TnyAccount *account;
2325                 ModestTransportStoreProtocol account_proto;
2326
2327                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2328                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2329
2330                 modest_mail_operation_notify_start (self);
2331
2332                 /* Get leave on server setting */
2333                 account = tny_folder_get_account (folder);
2334                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2335                 leave_on_server =
2336                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2337                                                                 account_name);
2338
2339                 account_proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
2340
2341                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2342                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2343                         expunge = TRUE;
2344                 else
2345                         expunge = FALSE;
2346
2347                 /* Sync folder */
2348                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2349
2350                 /* Unref */
2351                 g_object_unref (account);
2352         }
2353         
2354         
2355         /* Set status */
2356         if (!priv->error)
2357                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2358         else
2359                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2360
2361         /* Free */
2362         g_object_unref (G_OBJECT (folder));
2363
2364         /* Notify about operation end */
2365         modest_mail_operation_notify_end (self);
2366 }
2367
2368 void 
2369 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2370                                    TnyList *headers,
2371                                   gboolean remove_to_trash /*ignored*/)
2372 {
2373         TnyFolder *folder = NULL;
2374         ModestMailOperationPrivate *priv;
2375         TnyIterator *iter = NULL;
2376         TnyHeader *header = NULL;
2377         TnyList *remove_headers = NULL;
2378         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2379
2380         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2381         g_return_if_fail (TNY_IS_LIST (headers));
2382
2383         if (remove_to_trash)
2384                 g_warning ("remove to trash is not implemented");
2385
2386         if (tny_list_get_length(headers) == 0) {
2387                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
2388                 goto cleanup; /* nothing to do */
2389         }
2390         
2391         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2392         remove_headers = g_object_ref(headers);
2393
2394         /* Get folder from first header and sync it */
2395         iter = tny_list_create_iterator (headers);      
2396         header = TNY_HEADER (tny_iterator_get_current (iter));
2397
2398         folder = tny_header_get_folder (header);        
2399         if (!TNY_IS_FOLDER(folder)) {
2400                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
2401                 goto cleanup;
2402         }
2403
2404         /* Don't remove messages that are being sent */
2405         if (modest_tny_folder_is_local_folder (folder)) {
2406                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2407         }
2408         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2409                 TnyTransportAccount *traccount = NULL;
2410                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2411                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2412                 if (traccount) {
2413                         ModestTnySendQueueStatus status;
2414                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2415                         TnyIterator *iter = tny_list_create_iterator(headers);
2416                         g_object_unref(remove_headers);
2417                         remove_headers = TNY_LIST(tny_simple_list_new());
2418                         while (!tny_iterator_is_done(iter)) {
2419                                 char *msg_id;
2420                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2421                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2422                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2423                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2424                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2425                                 }
2426                                 g_object_unref(hdr);
2427                                 g_free(msg_id);
2428                                 tny_iterator_next(iter);
2429                         }
2430                         g_object_unref(iter);
2431                         g_object_unref(traccount);
2432                 }
2433         }
2434
2435         /* Get account and set it into mail_operation */
2436         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2437         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2438         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2439
2440         /* remove message from folder */
2441         modest_mail_operation_notify_start (self);
2442
2443         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2444         if (!priv->error) {
2445                 gboolean expunge, leave_on_server;
2446                 const gchar *account_name;
2447                 const gchar *proto;
2448                 TnyAccount *account;
2449                 ModestTransportStoreProtocol account_proto = MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
2450                 
2451                 account = tny_folder_get_account (folder);
2452                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2453                 leave_on_server =
2454                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2455                                                                 account_name);
2456                 
2457                 proto = tny_account_get_proto (account);
2458                 if (proto) {
2459                         account_proto = modest_protocol_info_get_transport_store_protocol (proto);
2460                 }
2461                 
2462                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2463                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2464                         expunge = TRUE;
2465                 else
2466                         expunge = FALSE;
2467
2468                 /* Sync folder */
2469                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2470                 
2471                 g_object_unref (account);
2472         }
2473         
2474         
2475         /* Set status */
2476         if (!priv->error)
2477                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2478         else
2479                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2480
2481         /* Free */
2482 cleanup:
2483         if (remove_headers)
2484                 g_object_unref (remove_headers);
2485         if (header)
2486                 g_object_unref (header);
2487         if (iter)
2488                 g_object_unref (iter);
2489         if (folder)
2490                 g_object_unref (folder);
2491
2492         /* Notify about operation end */
2493         modest_mail_operation_notify_end (self);
2494 }
2495
2496 static void
2497 notify_progress_of_multiple_messages (ModestMailOperation *self,
2498                                       TnyStatus *status,
2499                                       gint *last_total_bytes,
2500                                       gint *sum_total_bytes,
2501                                       gint total_bytes, 
2502                                       gboolean increment_done)
2503 {
2504         ModestMailOperationPrivate *priv;
2505         ModestMailOperationState *state;
2506         gboolean is_num_bytes = FALSE;
2507
2508         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2509
2510         /* We know that tinymail sends us information about
2511          *  transferred bytes with this particular message
2512          *  
2513          *  (FIXME: this is very ugly, and no I (djcb) didn't write this code,
2514          *  I just added the 'if' so we don't get runtime warning)
2515          */
2516         if (status->message)
2517                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2518
2519         state = modest_mail_operation_clone_state (self);
2520         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2521                 /* We know that we're in a different message when the
2522                    total number of bytes to transfer is different. Of
2523                    course it could fail if we're transferring messages
2524                    of the same size, but this is a workarround */
2525                 if (status->of_total != *last_total_bytes) {
2526                         /* We need to increment the done when there is
2527                            no information about each individual
2528                            message, we need to do this in message
2529                            transfers, and we don't do it for getting
2530                            messages */
2531                         if (increment_done)
2532                                 priv->done++;
2533                         *sum_total_bytes += *last_total_bytes;
2534                         *last_total_bytes = status->of_total;
2535                 }
2536                 state->bytes_done += status->position + *sum_total_bytes;
2537                 state->bytes_total = total_bytes;
2538
2539                 /* Notify the status change. Only notify about changes
2540                    referred to bytes */
2541                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2542                                0, state, NULL);
2543         }
2544
2545         g_slice_free (ModestMailOperationState, state);
2546 }
2547
2548 static void
2549 transfer_msgs_status_cb (GObject *obj,
2550                          TnyStatus *status,  
2551                          gpointer user_data)
2552 {
2553         XFerMsgsAsyncHelper *helper;
2554
2555         g_return_if_fail (status != NULL);
2556         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2557
2558         helper = (XFerMsgsAsyncHelper *) user_data;
2559         g_return_if_fail (helper != NULL);       
2560
2561         /* Notify progress */
2562         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2563                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2564 }
2565
2566
2567 static void
2568 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2569 {
2570         XFerMsgsAsyncHelper *helper;
2571         ModestMailOperation *self;
2572         ModestMailOperationPrivate *priv;
2573         gboolean finished = TRUE;
2574
2575         helper = (XFerMsgsAsyncHelper *) user_data;
2576         self = helper->mail_op;
2577
2578         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2579
2580         if (err) {
2581                 priv->error = g_error_copy (err);
2582                 priv->done = 0;
2583                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2584         } else if (cancelled) {
2585                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2586                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2587                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2588                              _("Error trying to refresh the contents of %s"),
2589                              tny_folder_get_name (folder));
2590         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2591                 if (helper->more_msgs) {
2592                         /* We'll transfer the next message in the list */
2593                         tny_iterator_next (helper->more_msgs);
2594                         if (!tny_iterator_is_done (helper->more_msgs)) {
2595                                 GObject *next_header;
2596                                 g_object_unref (helper->headers);
2597                                 helper->headers = tny_simple_list_new ();
2598                                 next_header = tny_iterator_get_current (helper->more_msgs);
2599                                 tny_list_append (helper->headers, next_header);
2600                                 g_object_unref (next_header);
2601                                 finished = FALSE;
2602                         }
2603                 }
2604
2605                 if (finished) {
2606                         priv->done = 1;
2607                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2608                 }
2609         }
2610
2611         if (finished) {
2612
2613                 /* Update folder counts */
2614                 tny_folder_poke_status (folder);
2615                 tny_folder_poke_status (helper->dest_folder);
2616
2617                 /* Notify about operation end */
2618                 modest_mail_operation_notify_end (self);
2619
2620                 /* If user defined callback function was defined, call it */
2621                 if (helper->user_callback) {
2622                         /* This is not a GDK lock because we are a Tinymail callback and
2623                          * Tinymail already acquires the Gdk lock */
2624
2625                         /* no gdk_threads_enter (), CHECKED */
2626                         helper->user_callback (self, helper->user_data);
2627                         /* no gdk_threads_leave (), CHECKED */
2628                 }
2629
2630                 /* Free */
2631                 if (helper->more_msgs)
2632                         g_object_unref (helper->more_msgs);
2633                 if (helper->headers)
2634                         g_object_unref (helper->headers);
2635                 if (helper->dest_folder)
2636                         g_object_unref (helper->dest_folder);
2637                 if (helper->mail_op)
2638                         g_object_unref (helper->mail_op);
2639                 g_slice_free (XFerMsgsAsyncHelper, helper);
2640         } else {
2641                 /* Transfer more messages */
2642                 tny_folder_transfer_msgs_async (folder,
2643                                                 helper->headers,
2644                                                 helper->dest_folder,
2645                                                 helper->delete,
2646                                                 transfer_msgs_cb,
2647                                                 transfer_msgs_status_cb,
2648                                                 helper);
2649         }
2650 }
2651
2652 static guint
2653 compute_message_list_size (TnyList *headers)
2654 {
2655         TnyIterator *iter;
2656         guint size = 0;
2657
2658         iter = tny_list_create_iterator (headers);
2659         while (!tny_iterator_is_done (iter)) {
2660                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2661                 size += tny_header_get_message_size (header);
2662                 g_object_unref (header);
2663                 tny_iterator_next (iter);
2664         }
2665         g_object_unref (iter);
2666
2667         return size;
2668 }
2669
2670 static guint
2671 compute_message_array_size (GPtrArray *headers)
2672 {
2673         guint size = 0;
2674         gint i;
2675
2676         for (i = 0; i < headers->len; i++) {
2677                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2678                 size += tny_header_get_message_size (header);
2679         }
2680
2681         return size;
2682 }
2683
2684
2685 void
2686 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2687                                  TnyList *headers, 
2688                                  TnyFolder *folder, 
2689                                  gboolean delete_original,
2690                                  XferMsgsAsyncUserCallback user_callback,
2691                                  gpointer user_data)
2692 {
2693         ModestMailOperationPrivate *priv = NULL;
2694         TnyIterator *iter = NULL;
2695         TnyFolder *src_folder = NULL;
2696         XFerMsgsAsyncHelper *helper = NULL;
2697         TnyHeader *header = NULL;
2698         ModestTnyFolderRules rules = 0;
2699         TnyAccount *dst_account = NULL;
2700         gboolean leave_on_server;
2701
2702         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2703         g_return_if_fail (headers && TNY_IS_LIST (headers));
2704         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2705
2706         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2707         priv->total = tny_list_get_length (headers);
2708         priv->done = 0;
2709         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2710         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2711
2712         /* Apply folder rules */
2713         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2714         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2715                 /* Set status failed and set an error */
2716                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2717                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2718                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2719                              _CS("ckct_ib_unable_to_paste_here"));
2720                 /* Notify the queue */
2721                 modest_mail_operation_notify_end (self);
2722                 return;
2723         }
2724                 
2725         /* Get source folder */
2726         iter = tny_list_create_iterator (headers);
2727         header = TNY_HEADER (tny_iterator_get_current (iter));
2728         if (header) {
2729                 src_folder = tny_header_get_folder (header);
2730                 g_object_unref (header);
2731         }
2732         g_object_unref (iter);
2733
2734         if (src_folder == NULL) {
2735                 /* Notify the queue */
2736                 modest_mail_operation_notify_end (self);
2737
2738                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2739                 return;
2740         }
2741
2742         
2743         /* Check folder source and destination */
2744         if (src_folder == folder) {
2745                 /* Set status failed and set an error */
2746                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2747                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2748                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2749                              _("mail_in_ui_folder_copy_target_error"));
2750                 
2751                 /* Notify the queue */
2752                 modest_mail_operation_notify_end (self);
2753                 
2754                 /* Free */
2755                 g_object_unref (src_folder);            
2756                 return;
2757         }
2758
2759         /* Create the helper */
2760         helper = g_slice_new0 (XFerMsgsAsyncHelper);
2761         helper->mail_op = g_object_ref(self);
2762         helper->dest_folder = g_object_ref(folder);
2763         helper->user_callback = user_callback;
2764         helper->user_data = user_data;
2765         helper->delete = delete_original;
2766         helper->last_total_bytes = 0;
2767         helper->sum_total_bytes = 0;
2768         helper->total_bytes = compute_message_list_size (headers);
2769
2770         /* Get account and set it into mail_operation */
2771         priv->account = modest_tny_folder_get_account (src_folder);
2772         dst_account = modest_tny_folder_get_account (folder);
2773
2774         if (priv->account == dst_account) {
2775                 /* Transfer all messages at once using the fast
2776                  * method. Note that depending on the server this
2777                  * might not be that fast, and might not be
2778                  * user-cancellable either */
2779                 helper->headers = g_object_ref (headers);
2780                 helper->more_msgs = NULL;
2781         } else {
2782                 /* Transfer messages one by one so the user can cancel
2783                  * the operation */
2784                 GObject *hdr;
2785                 helper->headers = tny_simple_list_new ();
2786                 helper->more_msgs = tny_list_create_iterator (headers);
2787                 hdr = tny_iterator_get_current (helper->more_msgs);
2788                 tny_list_append (helper->headers, hdr);
2789                 g_object_unref (hdr);
2790         }
2791
2792         /* If leave_on_server is set to TRUE then don't use
2793            delete_original, we always pass FALSE. This is because
2794            otherwise tinymail will try to sync the source folder and
2795            this could cause an error if we're offline while
2796            transferring an already downloaded message from a POP
2797            account */
2798         if (modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (priv->account)) == 
2799             MODEST_PROTOCOL_STORE_POP) {
2800                 const gchar *account_name;
2801
2802                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
2803                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2804                                                                           account_name);
2805         } else {
2806                 leave_on_server = FALSE;
2807         }
2808
2809         modest_mail_operation_notify_start (self);
2810         tny_folder_transfer_msgs_async (src_folder, 
2811                                         helper->headers, 
2812                                         folder, 
2813                                         (leave_on_server) ? FALSE : delete_original, 
2814                                         transfer_msgs_cb, 
2815                                         transfer_msgs_status_cb,
2816                                         helper);
2817         g_object_unref (src_folder);
2818         g_object_unref (dst_account);
2819 }
2820
2821
2822 static void
2823 on_refresh_folder (TnyFolder   *folder, 
2824                    gboolean     cancelled, 
2825                    GError     *error,
2826                    gpointer     user_data)
2827 {
2828         RefreshAsyncHelper *helper = NULL;
2829         ModestMailOperation *self = NULL;
2830         ModestMailOperationPrivate *priv = NULL;
2831
2832         helper = (RefreshAsyncHelper *) user_data;
2833         self = helper->mail_op;
2834         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2835
2836         g_return_if_fail(priv!=NULL);
2837
2838         if (error) {
2839                 priv->error = g_error_copy (error);
2840                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2841                 goto out;
2842         }
2843
2844         if (cancelled) {
2845                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2846                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2847                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2848                              _("Error trying to refresh the contents of %s"),
2849                              tny_folder_get_name (folder));
2850                 goto out;
2851         }
2852
2853         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2854  out:
2855
2856         /* Call user defined callback, if it exists */
2857         if (helper->user_callback) {
2858
2859                 /* This is not a GDK lock because we are a Tinymail callback and
2860                  * Tinymail already acquires the Gdk lock */
2861                 helper->user_callback (self, folder, helper->user_data);
2862         }
2863
2864         /* Free */
2865         g_slice_free (RefreshAsyncHelper, helper);
2866
2867         /* Notify about operation end */
2868         modest_mail_operation_notify_end (self);
2869         g_object_unref(self);
2870 }
2871
2872 static void
2873 on_refresh_folder_status_update (GObject *obj,
2874                                  TnyStatus *status,
2875                                  gpointer user_data)
2876 {
2877         RefreshAsyncHelper *helper = NULL;
2878         ModestMailOperation *self = NULL;
2879         ModestMailOperationPrivate *priv = NULL;
2880         ModestMailOperationState *state;
2881
2882         g_return_if_fail (user_data != NULL);
2883         g_return_if_fail (status != NULL);
2884         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2885
2886         helper = (RefreshAsyncHelper *) user_data;
2887         self = helper->mail_op;
2888         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2889
2890         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2891
2892         priv->done = status->position;
2893         priv->total = status->of_total;
2894
2895         state = modest_mail_operation_clone_state (self);
2896
2897         /* This is not a GDK lock because we are a Tinymail callback and
2898          * Tinymail already acquires the Gdk lock */
2899         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2900
2901         g_slice_free (ModestMailOperationState, state);
2902 }
2903
2904 void 
2905 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2906                                        TnyFolder *folder,
2907                                        RefreshAsyncUserCallback user_callback,
2908                                        gpointer user_data)
2909 {
2910         ModestMailOperationPrivate *priv = NULL;
2911         RefreshAsyncHelper *helper = NULL;
2912
2913         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2914
2915         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2916
2917         /* Get account and set it into mail_operation */
2918         priv->account = modest_tny_folder_get_account  (folder);
2919         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2920
2921         /* Create the helper */
2922         helper = g_slice_new0 (RefreshAsyncHelper);
2923         helper->mail_op = g_object_ref(self);
2924         helper->user_callback = user_callback;
2925         helper->user_data = user_data;
2926
2927         /* Refresh the folder. TODO: tinymail could issue a status
2928            updates before the callback call then this could happen. We
2929            must review the design */
2930         modest_mail_operation_notify_start (self);
2931         
2932         /* notify that the operation was started */
2933         ModestMailOperationState *state;
2934         state = modest_mail_operation_clone_state (self);
2935         state->done = 0;
2936         state->total = 0;
2937         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2938                         0, state, NULL);
2939
2940         /* FIXME: we're leaking the state here, or? valgrind thinks so */
2941         
2942         tny_folder_refresh_async (folder,
2943                                   on_refresh_folder,
2944                                   on_refresh_folder_status_update,
2945                                   helper);
2946 }
2947
2948 static void
2949 run_queue_stop (ModestTnySendQueue *queue,
2950                 ModestMailOperation *self)
2951 {
2952         ModestMailOperationPrivate *priv;
2953
2954         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2955         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
2956         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2957
2958         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2959
2960         modest_mail_operation_notify_end (self);
2961         g_signal_handlers_disconnect_by_func (queue, run_queue_stop, self);
2962         g_object_unref (self);
2963 }
2964 void
2965 modest_mail_operation_run_queue (ModestMailOperation *self,
2966                                  ModestTnySendQueue *queue)
2967 {
2968         ModestMailOperationPrivate *priv;
2969
2970         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2971         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
2972         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2973
2974         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2975         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
2976         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
2977
2978         modest_mail_operation_notify_start (self);
2979         g_object_ref (self);
2980         g_signal_connect ((gpointer) queue, "queue-stop", G_CALLBACK (run_queue_stop), (gpointer) self);
2981 }
2982
2983 static void
2984 sync_folder_finish_callback (TnyFolder *self, gboolean cancelled, GError *err, ModestMailOperation *mail_op)
2985 {
2986         ModestMailOperationPrivate *priv;
2987
2988         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
2989         if (err != NULL) {
2990                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2991                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2992                              err->message);
2993                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2994         } else {
2995                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2996         }
2997         modest_mail_operation_notify_end (mail_op);
2998         g_object_unref (mail_op);
2999 }
3000
3001 void
3002 modest_mail_operation_sync_folder (ModestMailOperation *self,
3003                                    TnyFolder *folder, gboolean expunge)
3004 {
3005         ModestMailOperationPrivate *priv;
3006
3007         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3008         g_return_if_fail (TNY_IS_FOLDER (folder));
3009         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3010
3011         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3012         priv->account = TNY_ACCOUNT (tny_folder_get_account (folder));
3013         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3014
3015         modest_mail_operation_notify_start (self);
3016         g_object_ref (self);
3017         tny_folder_sync_async (folder, expunge, (TnyFolderCallback) sync_folder_finish_callback, NULL, self);
3018 }
3019
3020 static void
3021 modest_mail_operation_notify_start (ModestMailOperation *self)
3022 {
3023         ModestMailOperationPrivate *priv = NULL;
3024
3025         g_return_if_fail (self);
3026
3027         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3028
3029         /* Ensure that all the fields are filled correctly */
3030         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3031
3032         /* Notify the observers about the mail operation. We do not
3033            wrapp this emission because we assume that this function is
3034            always called from within the main lock */
3035         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3036 }
3037
3038 /**
3039  *
3040  * It's used by the mail operation queue to notify the observers
3041  * attached to that signal that the operation finished. We need to use
3042  * that because tinymail does not give us the progress of a given
3043  * operation when it finishes (it directly calls the operation
3044  * callback).
3045  */
3046 static void
3047 modest_mail_operation_notify_end (ModestMailOperation *self)
3048 {
3049         ModestMailOperationPrivate *priv = NULL;
3050
3051         g_return_if_fail (self);
3052
3053         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3054
3055         /* Notify the observers about the mail operation end. We do
3056            not wrapp this emission because we assume that this
3057            function is always called from within the main lock */
3058         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3059
3060         /* Remove the error user data */
3061         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3062                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3063 }
3064
3065 TnyAccount *
3066 modest_mail_operation_get_account (ModestMailOperation *self)
3067 {
3068         ModestMailOperationPrivate *priv = NULL;
3069
3070         g_return_val_if_fail (self, NULL);
3071
3072         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3073
3074         return (priv->account) ? g_object_ref (priv->account) : NULL;
3075 }
3076
3077 void
3078 modest_mail_operation_noop (ModestMailOperation *self)
3079 {
3080         ModestMailOperationPrivate *priv = NULL;
3081
3082         g_return_if_fail (self);
3083
3084         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3085         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3086         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3087         priv->done = 0;
3088         priv->total = 0;
3089
3090         /* This mail operation does nothing actually */
3091         modest_mail_operation_notify_start (self);
3092         modest_mail_operation_notify_end (self);
3093 }
3094
3095
3096 gchar*
3097 modest_mail_operation_to_string (ModestMailOperation *self)
3098 {
3099         const gchar *type, *status, *account_id;
3100         ModestMailOperationPrivate *priv = NULL;
3101         
3102         g_return_val_if_fail (self, NULL);
3103
3104         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3105
3106         /* new operations don't have anything interesting */
3107         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3108                 return g_strdup_printf ("%p <new operation>", self);
3109         
3110         switch (priv->op_type) {
3111         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3112         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3113         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3114         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3115         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3116         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
3117         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
3118         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3119         default: type = "UNEXPECTED"; break;
3120         }
3121
3122         switch (priv->status) {
3123         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3124         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3125         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3126         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3127         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3128         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3129         default:                                                status= "UNEXPECTED"; break;
3130         } 
3131
3132         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3133
3134         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3135                                 priv->done, priv->total,
3136                                 priv->error && priv->error->message ? priv->error->message : "");
3137 }