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