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