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