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