df946213e2f4e25cfdd16007f90fcba7ddccd760
[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         TnyList *list = tny_simple_list_new ();
1478         TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1479
1480         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1481         g_return_val_if_fail (name, NULL);
1482         
1483         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1484
1485         /* Check for already existing folder */
1486         tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1487         tny_folder_store_get_folders (parent, list, query, NULL);
1488         g_object_unref (G_OBJECT (query));
1489
1490         if (tny_list_get_length (list) > 0) {
1491                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1492                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1493                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1494                              _CS("ckdg_ib_folder_already_exists"));
1495         }
1496
1497         g_object_unref (G_OBJECT (list));
1498
1499         /* Check parent */
1500         if (TNY_IS_FOLDER (parent)) {
1501                 /* Check folder rules */
1502                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1503                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1504                         /* Set status failed and set an error */
1505                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1506                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1507                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1508                                      _("mail_in_ui_folder_create_error"));
1509                 }
1510         }
1511
1512         if (!strcmp (name, " ") || strchr (name, '/')) {
1513                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1514                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1515                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1516                              _("mail_in_ui_folder_create_error"));
1517         }
1518
1519         if (!priv->error) {
1520                 /* Create the folder */
1521                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1522                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1523                 if (!priv->error)
1524                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1525         }
1526
1527         /* Notify about operation end */
1528         modest_mail_operation_notify_end (self);
1529
1530         return new_folder;
1531 }
1532
1533 void
1534 modest_mail_operation_remove_folder (ModestMailOperation *self,
1535                                      TnyFolder           *folder,
1536                                      gboolean             remove_to_trash)
1537 {
1538         TnyAccount *account;
1539         ModestMailOperationPrivate *priv;
1540         ModestTnyFolderRules rules;
1541
1542         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1543         g_return_if_fail (TNY_IS_FOLDER (folder));
1544         
1545         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1546         
1547         /* Check folder rules */
1548         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1549         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1550                 /* Set status failed and set an error */
1551                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1552                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1553                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1554                              _("mail_in_ui_folder_delete_error"));
1555                 goto end;
1556         }
1557
1558         /* Get the account */
1559         account = modest_tny_folder_get_account (folder);
1560         priv->account = g_object_ref(account);
1561
1562         /* Delete folder or move to trash */
1563         if (remove_to_trash) {
1564                 TnyFolder *trash_folder = NULL;
1565                 trash_folder = modest_tny_account_get_special_folder (account,
1566                                                                       TNY_FOLDER_TYPE_TRASH);
1567                 /* TODO: error_handling */
1568                  modest_mail_operation_xfer_folder (self, folder,
1569                                                     TNY_FOLDER_STORE (trash_folder), 
1570                                                     TRUE, NULL, NULL);
1571         } else {
1572                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1573
1574                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1575                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1576
1577                 if (parent)
1578                         g_object_unref (G_OBJECT (parent));
1579         }
1580         g_object_unref (G_OBJECT (account));
1581
1582  end:
1583         /* Notify about operation end */
1584         modest_mail_operation_notify_end (self);
1585 }
1586
1587 static void
1588 transfer_folder_status_cb (GObject *obj,
1589                            TnyStatus *status,
1590                            gpointer user_data)
1591 {
1592         ModestMailOperation *self;
1593         ModestMailOperationPrivate *priv;
1594         ModestMailOperationState *state;
1595         XFerMsgAsyncHelper *helper;
1596
1597         g_return_if_fail (status != NULL);
1598         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1599
1600         helper = (XFerMsgAsyncHelper *) user_data;
1601         g_return_if_fail (helper != NULL);
1602
1603         self = helper->mail_op;
1604         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1605
1606         priv->done = status->position;
1607         priv->total = status->of_total;
1608
1609         state = modest_mail_operation_clone_state (self);
1610         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1611         g_slice_free (ModestMailOperationState, state);
1612 }
1613
1614
1615 static void
1616 transfer_folder_cb (TnyFolder *folder, 
1617                     TnyFolderStore *into, 
1618                     gboolean cancelled, 
1619                     TnyFolder *new_folder, 
1620                     GError **err, 
1621                     gpointer user_data)
1622 {
1623         XFerMsgAsyncHelper *helper;
1624         ModestMailOperation *self = NULL;
1625         ModestMailOperationPrivate *priv = NULL;
1626
1627         helper = (XFerMsgAsyncHelper *) user_data;
1628         g_return_if_fail (helper != NULL);       
1629
1630         self = helper->mail_op;
1631         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1632
1633         if (*err) {
1634                 priv->error = g_error_copy (*err);
1635                 priv->done = 0;
1636                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1637         } else if (cancelled) {
1638                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1639                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1640                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1641                              _("Transference of %s was cancelled."),
1642                              tny_folder_get_name (folder));
1643         } else {
1644                 priv->done = 1;
1645                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1646         }
1647                 
1648         /* Notify about operation end */
1649         modest_mail_operation_notify_end (self);
1650
1651         /* If user defined callback function was defined, call it */
1652         if (helper->user_callback) {
1653                 gdk_threads_enter ();
1654                 helper->user_callback (priv->source, helper->user_data);
1655                 gdk_threads_leave ();
1656         }
1657
1658         /* Free */
1659         g_object_unref (helper->mail_op);
1660         g_slice_free   (XFerMsgAsyncHelper, helper);
1661 }
1662
1663 void
1664 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1665                                    TnyFolder *folder,
1666                                    TnyFolderStore *parent,
1667                                    gboolean delete_original,
1668                                    XferMsgsAsynUserCallback user_callback,
1669                                    gpointer user_data)
1670 {
1671         ModestMailOperationPrivate *priv = NULL;
1672         ModestTnyFolderRules parent_rules = 0, rules; 
1673         XFerMsgAsyncHelper *helper = NULL;
1674
1675         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1676         g_return_if_fail (TNY_IS_FOLDER (folder));
1677
1678         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1679
1680         /* Get account and set it into mail_operation */
1681         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1682         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1683
1684         /* Get folder rules */
1685         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1686         if (TNY_IS_FOLDER (parent))
1687                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1688         
1689         /* The moveable restriction is applied also to copy operation */
1690         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1691                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1692                 /* Set status failed and set an error */
1693                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1694                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1695                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1696                              _("mail_in_ui_folder_move_target_error"));
1697
1698                 /* Notify the queue */
1699                 modest_mail_operation_notify_end (self);
1700         } else if (TNY_IS_FOLDER (parent) && 
1701                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1702                 /* Set status failed and set an error */
1703                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1704                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1705                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1706                              _("FIXME: parent folder does not accept new folders"));
1707
1708                 /* Notify the queue */
1709                 modest_mail_operation_notify_end (self);
1710         } else {
1711                 /* Create the helper */
1712                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1713                 helper->mail_op = g_object_ref(self);
1714                 helper->dest_folder = NULL;
1715                 helper->headers = NULL;
1716                 helper->user_callback = user_callback;
1717                 helper->user_data = user_data;
1718                 
1719                 /* Move/Copy folder */          
1720                 tny_folder_copy_async (folder,
1721                                        parent,
1722                                        tny_folder_get_name (folder),
1723                                        delete_original,
1724                                        transfer_folder_cb,
1725                                        transfer_folder_status_cb,
1726                                        helper);
1727 /*                                     self); */
1728         }
1729 }
1730
1731 void
1732 modest_mail_operation_rename_folder (ModestMailOperation *self,
1733                                      TnyFolder *folder,
1734                                      const gchar *name)
1735 {
1736         ModestMailOperationPrivate *priv;
1737         ModestTnyFolderRules rules;
1738         XFerMsgAsyncHelper *helper;
1739
1740         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1741         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1742         g_return_if_fail (name);
1743         
1744         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1745
1746         /* Get account and set it into mail_operation */
1747         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1748
1749         /* Check folder rules */
1750         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1751         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1752                 /* Set status failed and set an error */
1753                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1754                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1755                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1756                              _("FIXME: unable to rename"));
1757
1758                 /* Notify about operation end */
1759                 modest_mail_operation_notify_end (self);
1760         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1761                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1762                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1763                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1764                              _("FIXME: unable to rename"));
1765                 /* Notify about operation end */
1766                 modest_mail_operation_notify_end (self);
1767         } else {
1768                 TnyFolderStore *into;
1769
1770                 /* Create the helper */
1771                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1772                 helper->mail_op = g_object_ref(self);
1773                 helper->dest_folder = NULL;
1774                 helper->headers = NULL;
1775                 helper->user_callback = NULL;
1776                 helper->user_data = NULL;
1777
1778                 /* Rename. Camel handles folder subscription/unsubscription */
1779                 into = tny_folder_get_folder_store (folder);
1780                 tny_folder_copy_async (folder, into, name, TRUE,
1781                                  transfer_folder_cb,
1782                                  transfer_folder_status_cb,
1783                                  helper);
1784 /*                               self); */
1785                 if (into)
1786                         g_object_unref (into);          
1787         }
1788  }
1789
1790 /* ******************************************************************* */
1791 /* **************************  MSG  ACTIONS  ************************* */
1792 /* ******************************************************************* */
1793
1794 void modest_mail_operation_get_msg (ModestMailOperation *self,
1795                                     TnyHeader *header,
1796                                     GetMsgAsyncUserCallback user_callback,
1797                                     gpointer user_data)
1798 {
1799         GetMsgAsyncHelper *helper = NULL;
1800         TnyFolder *folder;
1801         ModestMailOperationPrivate *priv;
1802         
1803         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1804         g_return_if_fail (TNY_IS_HEADER (header));
1805         
1806         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1807         folder = tny_header_get_folder (header);
1808
1809         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1810
1811         /* Get message from folder */
1812         if (folder) {
1813                 /* Get account and set it into mail_operation */
1814                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1815
1816                 helper = g_slice_new0 (GetMsgAsyncHelper);
1817                 helper->mail_op = self;
1818                 helper->user_callback = user_callback;
1819                 helper->user_data = user_data;
1820                 helper->header = g_object_ref (header);
1821
1822                 // The callback's reference so that the mail op is not
1823                 // finalized until the async operation is completed even if
1824                 // the user canceled the request meanwhile.
1825                 g_object_ref (G_OBJECT (helper->mail_op));
1826
1827                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1828
1829                 g_object_unref (G_OBJECT (folder));
1830         } else {
1831                 /* Set status failed and set an error */
1832                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1833                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1834                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1835                              _("Error trying to get a message. No folder found for header"));
1836
1837                 /* Notify the queue */
1838                 modest_mail_operation_notify_end (self);
1839         }
1840 }
1841
1842 static gboolean
1843 idle_get_mime_part_size_cb (gpointer userdata)
1844 {
1845         GetMimePartSizeInfo *idle_info;
1846
1847         idle_info = (GetMimePartSizeInfo *) userdata;
1848
1849         gdk_threads_enter ();
1850         idle_info->callback (idle_info->mail_op,
1851                              idle_info->size,
1852                              idle_info->userdata);
1853         gdk_threads_leave ();
1854
1855         g_object_unref (idle_info->mail_op);
1856         g_slice_free (GetMimePartSizeInfo, idle_info);
1857
1858         return FALSE;
1859 }
1860
1861 static gpointer
1862 get_mime_part_size_thread (gpointer thr_user_data)
1863 {
1864         GetMimePartSizeInfo *info;
1865         gchar read_buffer[GET_SIZE_BUFFER_SIZE];
1866         TnyStream *stream;
1867         gssize readed_size;
1868         gssize total = 0;
1869         ModestMailOperationPrivate *priv;
1870
1871         info = (GetMimePartSizeInfo *) thr_user_data;
1872         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1873
1874         stream = tny_camel_mem_stream_new ();
1875         tny_mime_part_decode_to_stream (info->mime_part, stream);
1876         tny_stream_reset (stream);
1877         if (tny_stream_is_eos (stream)) {
1878                 tny_stream_close (stream);
1879                 stream = tny_mime_part_get_stream (info->mime_part);
1880         }
1881         
1882         while (!tny_stream_is_eos (stream)) {
1883                 readed_size = tny_stream_read (stream, read_buffer, GET_SIZE_BUFFER_SIZE);
1884                 total += readed_size;
1885         }
1886
1887         if (info->callback) {
1888                 GetMimePartSizeInfo *idle_info;
1889
1890                 idle_info = g_slice_new0 (GetMimePartSizeInfo);
1891                 idle_info->mail_op = g_object_ref (info->mail_op);
1892                 idle_info->size = total;
1893                 idle_info->callback = info->callback;
1894                 idle_info->userdata = info->userdata;
1895                 g_idle_add (idle_get_mime_part_size_cb, idle_info);
1896         }
1897
1898         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1899
1900         g_object_unref (info->mail_op);
1901         g_object_unref (stream);
1902         g_object_unref (info->mime_part);
1903         g_slice_free  (GetMimePartSizeInfo, info);
1904
1905         return NULL;
1906 }
1907
1908 void          
1909 modest_mail_operation_get_mime_part_size (ModestMailOperation *self,
1910                                           TnyMimePart *part,
1911                                           GetMimePartSizeCallback user_callback,
1912                                           gpointer user_data,
1913                                           GDestroyNotify notify)
1914 {
1915         GetMimePartSizeInfo *info;
1916         ModestMailOperationPrivate *priv;
1917         GThread *thread;
1918         
1919         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1920         g_return_if_fail (TNY_IS_MIME_PART (part));
1921         
1922         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1923
1924         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1925         info = g_slice_new0 (GetMimePartSizeInfo);
1926         info->mail_op = g_object_ref (self);
1927         info->mime_part = g_object_ref (part);
1928         info->callback = user_callback;
1929         info->userdata = user_data;
1930
1931         thread = g_thread_create (get_mime_part_size_thread, info, FALSE, NULL);
1932
1933 }
1934
1935 static void
1936 get_msg_cb (TnyFolder *folder, 
1937             gboolean cancelled, 
1938             TnyMsg *msg, 
1939             GError **error, 
1940             gpointer user_data)
1941 {
1942         GetMsgAsyncHelper *helper = NULL;
1943         ModestMailOperation *self = NULL;
1944         ModestMailOperationPrivate *priv = NULL;
1945
1946         helper = (GetMsgAsyncHelper *) user_data;
1947         g_return_if_fail (helper != NULL);       
1948         self = helper->mail_op;
1949         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1950         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1951
1952         /* Check errors and cancel */
1953         if (*error) {
1954                 priv->error = g_error_copy (*error);
1955                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1956         } else if (cancelled) {
1957                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1958                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1959                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1960                              _("Error trying to refresh the contents of %s"),
1961                              tny_folder_get_name (folder));
1962         } else {
1963                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1964         }
1965
1966         /* If user defined callback function was defined, call it even
1967            if the operation failed*/
1968         if (helper->user_callback) {
1969                 /* This callback is called into an iddle by tinymail,
1970                    and idles are not in the main lock */
1971                 gdk_threads_enter ();
1972                 helper->user_callback (self, helper->header, msg, helper->user_data);
1973                 gdk_threads_leave ();   
1974         }
1975
1976         /* Notify about operation end */
1977         modest_mail_operation_notify_end (self);
1978         /* Free */
1979         g_object_unref (helper->mail_op);
1980         g_object_unref (helper->header);
1981         g_slice_free (GetMsgAsyncHelper, helper);
1982                 
1983 }
1984
1985 static void     
1986 get_msg_status_cb (GObject *obj,
1987                    TnyStatus *status,  
1988                    gpointer user_data)
1989 {
1990         GetMsgAsyncHelper *helper = NULL;
1991         ModestMailOperation *self;
1992         ModestMailOperationPrivate *priv;
1993         ModestMailOperationState *state;
1994
1995         g_return_if_fail (status != NULL);
1996         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1997
1998         helper = (GetMsgAsyncHelper *) user_data;
1999         g_return_if_fail (helper != NULL);       
2000
2001         self = helper->mail_op;
2002         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2003
2004         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2005                 TnyFolder *folder = tny_header_get_folder (helper->header);
2006                 if (folder) {
2007                         TnyAccount *account;
2008                         account = tny_folder_get_account (folder);
2009                         if (account) {
2010                                 tny_account_cancel (account);
2011                                 g_object_unref (account);
2012                         }
2013                         g_object_unref (folder);
2014                 }
2015                
2016                 return;
2017         }
2018
2019         priv->done = 1;
2020         priv->total = 1;
2021
2022         state = modest_mail_operation_clone_state (self);
2023         state->bytes_done = status->position;
2024         state->bytes_total = status->of_total;
2025         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2026         g_slice_free (ModestMailOperationState, state);
2027 }
2028
2029 /****************************************************/
2030 typedef struct {
2031         ModestMailOperation *mail_op;
2032         TnyList *headers;
2033         GetMsgAsyncUserCallback user_callback;
2034         gpointer user_data;
2035         GDestroyNotify notify;
2036 } GetFullMsgsInfo;
2037
2038 typedef struct {
2039         GetMsgAsyncUserCallback user_callback;
2040         TnyHeader *header;
2041         TnyMsg *msg;
2042         gpointer user_data;
2043         ModestMailOperation *mail_op;
2044 } NotifyGetMsgsInfo;
2045
2046
2047 /* 
2048  * Used by get_msgs_full_thread to call the user_callback for each
2049  * message that has been read
2050  */
2051 static gboolean
2052 notify_get_msgs_full (gpointer data)
2053 {
2054         NotifyGetMsgsInfo *info;
2055
2056         info = (NotifyGetMsgsInfo *) data;      
2057
2058         /* Call the user callback. Idles are not in the main lock, so
2059            lock it */
2060         gdk_threads_enter ();
2061         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2062         gdk_threads_leave ();
2063
2064         g_slice_free (NotifyGetMsgsInfo, info);
2065
2066         return FALSE;
2067 }
2068
2069 /* 
2070  * Used by get_msgs_full_thread to free al the thread resources and to
2071  * call the destroy function for the passed user_data
2072  */
2073 static gboolean
2074 get_msgs_full_destroyer (gpointer data)
2075 {
2076         GetFullMsgsInfo *info;
2077
2078         info = (GetFullMsgsInfo *) data;
2079
2080         if (info->notify) {
2081                 gdk_threads_enter ();   
2082                 info->notify (info->user_data);
2083                 gdk_threads_leave ();
2084         }
2085
2086         /* free */
2087         g_object_unref (info->headers);
2088         g_slice_free (GetFullMsgsInfo, info);
2089
2090         return FALSE;
2091 }
2092
2093 static gpointer
2094 get_msgs_full_thread (gpointer thr_user_data)
2095 {
2096         GetFullMsgsInfo *info;
2097         ModestMailOperationPrivate *priv = NULL;
2098         TnyIterator *iter = NULL;
2099         
2100         info = (GetFullMsgsInfo *) thr_user_data;       
2101         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2102
2103         iter = tny_list_create_iterator (info->headers);
2104         while (!tny_iterator_is_done (iter)) { 
2105                 TnyHeader *header;
2106                 TnyFolder *folder;
2107                 
2108                 header = TNY_HEADER (tny_iterator_get_current (iter));
2109                 folder = tny_header_get_folder (header);
2110                                 
2111                 /* Get message from folder */
2112                 if (folder) {
2113                         TnyMsg *msg;
2114                         /* The callback will call it per each header */
2115                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2116
2117                         if (msg) {
2118                                 ModestMailOperationState *state;
2119                                 ModestPair *pair;
2120
2121                                 priv->done++;
2122
2123                                 /* notify progress */
2124                                 state = modest_mail_operation_clone_state (info->mail_op);
2125                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2126                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2127                                                  pair, (GDestroyNotify) modest_pair_free);
2128
2129                                 /* The callback is the responsible for
2130                                    freeing the message */
2131                                 if (info->user_callback) {
2132                                         NotifyGetMsgsInfo *info_notify;
2133                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2134                                         info_notify->user_callback = info->user_callback;
2135                                         info_notify->mail_op = info->mail_op;
2136                                         info_notify->header = g_object_ref (header);
2137                                         info_notify->msg = g_object_ref (msg);
2138                                         info_notify->user_data = info->user_data;
2139                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2140                                                          notify_get_msgs_full, 
2141                                                          info_notify, NULL);
2142                                 }
2143                                 g_object_unref (msg);
2144                         } 
2145                 } else {
2146                         /* Set status failed and set an error */
2147                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2148                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2149                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2150                                      "Error trying to get a message. No folder found for header");
2151                 }
2152                 g_object_unref (header);                
2153                 tny_iterator_next (iter);
2154         }
2155
2156         /* Set operation status */
2157         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2158                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2159
2160         /* Notify about operation end */
2161         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2162
2163         /* Free thread resources. Will be called after all previous idles */
2164         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2165
2166         return NULL;
2167 }
2168
2169 void 
2170 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2171                                      TnyList *header_list, 
2172                                      GetMsgAsyncUserCallback user_callback,
2173                                      gpointer user_data,
2174                                      GDestroyNotify notify)
2175 {
2176         TnyHeader *header = NULL;
2177         TnyFolder *folder = NULL;
2178         GThread *thread;
2179         ModestMailOperationPrivate *priv = NULL;
2180         GetFullMsgsInfo *info = NULL;
2181         gboolean size_ok = TRUE;
2182         gint max_size;
2183         TnyIterator *iter = NULL;
2184         
2185         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2186         
2187         /* Init mail operation */
2188         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2189         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2190         priv->done = 0;
2191         priv->total = tny_list_get_length(header_list);
2192
2193         /* Get account and set it into mail_operation */
2194         if (tny_list_get_length (header_list) >= 1) {
2195                 iter = tny_list_create_iterator (header_list);
2196                 header = TNY_HEADER (tny_iterator_get_current (iter));
2197                 folder = tny_header_get_folder (header);                
2198                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2199                 g_object_unref (header);
2200                 g_object_unref (folder);
2201
2202                 if (tny_list_get_length (header_list) == 1) {
2203                         g_object_unref (iter);
2204                         iter = NULL;
2205                 }
2206         }
2207
2208         /* Get msg size limit */
2209         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2210                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2211                                          &(priv->error));
2212         if (priv->error) {
2213                 g_clear_error (&(priv->error));
2214                 max_size = G_MAXINT;
2215         } else {
2216                 max_size = max_size * KB;
2217         }
2218
2219         /* Check message size limits. If there is only one message
2220            always retrieve it */
2221         if (iter != NULL) {
2222                 while (!tny_iterator_is_done (iter) && size_ok) {
2223                         header = TNY_HEADER (tny_iterator_get_current (iter));
2224                         if (tny_header_get_message_size (header) >= max_size)
2225                                 size_ok = FALSE;
2226                         g_object_unref (header);
2227                         tny_iterator_next (iter);
2228                 }
2229                 g_object_unref (iter);
2230         }
2231
2232         if (size_ok) {
2233                 /* Create the info */
2234                 info = g_slice_new0 (GetFullMsgsInfo);
2235                 info->mail_op = self;
2236                 info->user_callback = user_callback;
2237                 info->user_data = user_data;
2238                 info->headers = g_object_ref (header_list);
2239                 info->notify = notify;
2240
2241                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2242         } else {
2243                 /* Set status failed and set an error */
2244                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2245                 /* FIXME: the error msg is different for pop */
2246                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2247                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2248                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2249                 /* Remove from queue and free resources */
2250                 modest_mail_operation_notify_end (self);
2251                 if (notify)
2252                         notify (user_data);
2253         }
2254 }
2255
2256
2257 void 
2258 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
2259                                   gboolean remove_to_trash /*ignored*/)
2260 {
2261         TnyFolder *folder;
2262         ModestMailOperationPrivate *priv;
2263
2264         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2265         g_return_if_fail (TNY_IS_HEADER (header));
2266
2267         if (remove_to_trash)
2268                 g_warning ("remove to trash is not implemented");
2269
2270         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2271         folder = tny_header_get_folder (header);
2272
2273         /* Get account and set it into mail_operation */
2274         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2275
2276         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2277
2278
2279         tny_folder_remove_msg (folder, header, &(priv->error));
2280         if (!priv->error) {
2281                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2282                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2283
2284                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2285                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2286                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2287                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2288                 else
2289                         /* lcoal folders */
2290                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2291         }
2292         
2293         
2294         /* Set status */
2295         if (!priv->error)
2296                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2297         else
2298                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2299
2300         /* Free */
2301         g_object_unref (G_OBJECT (folder));
2302
2303         /* Notify about operation end */
2304         modest_mail_operation_notify_end (self);
2305 }
2306
2307 static void
2308 transfer_msgs_status_cb (GObject *obj,
2309                          TnyStatus *status,  
2310                          gpointer user_data)
2311 {
2312         XFerMsgAsyncHelper *helper = NULL;
2313         ModestMailOperation *self;
2314         ModestMailOperationPrivate *priv;
2315         ModestMailOperationState *state;
2316
2317
2318         g_return_if_fail (status != NULL);
2319         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2320
2321         helper = (XFerMsgAsyncHelper *) user_data;
2322         g_return_if_fail (helper != NULL);       
2323
2324         self = helper->mail_op;
2325         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2326
2327         priv->done = status->position;
2328         priv->total = status->of_total;
2329
2330         state = modest_mail_operation_clone_state (self);
2331         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2332         g_slice_free (ModestMailOperationState, state);
2333 }
2334
2335
2336 static void
2337 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2338 {
2339         XFerMsgAsyncHelper *helper;
2340         ModestMailOperation *self;
2341         ModestMailOperationPrivate *priv;
2342
2343         helper = (XFerMsgAsyncHelper *) user_data;
2344         self = helper->mail_op;
2345
2346         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2347
2348         if (*err) {
2349                 priv->error = g_error_copy (*err);
2350                 priv->done = 0;
2351                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2352         } else if (cancelled) {
2353                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2354                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2355                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2356                              _("Error trying to refresh the contents of %s"),
2357                              tny_folder_get_name (folder));
2358         } else {
2359                 priv->done = 1;
2360                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2361         }
2362
2363         /* Notify about operation end */
2364         modest_mail_operation_notify_end (self);
2365
2366         /* If user defined callback function was defined, call it */
2367         if (helper->user_callback) {
2368                 gdk_threads_enter ();
2369                 helper->user_callback (priv->source, helper->user_data);
2370                 gdk_threads_leave ();
2371         }
2372
2373         /* Free */
2374         g_object_unref (helper->headers);
2375         g_object_unref (helper->dest_folder);
2376         g_object_unref (helper->mail_op);
2377         g_slice_free   (XFerMsgAsyncHelper, helper);
2378         g_object_unref (folder);
2379
2380 }
2381
2382 void
2383 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2384                                  TnyList *headers, 
2385                                  TnyFolder *folder, 
2386                                  gboolean delete_original,
2387                                  XferMsgsAsynUserCallback user_callback,
2388                                  gpointer user_data)
2389 {
2390         ModestMailOperationPrivate *priv;
2391         TnyIterator *iter;
2392         TnyFolder *src_folder;
2393         XFerMsgAsyncHelper *helper;
2394         TnyHeader *header;
2395         ModestTnyFolderRules rules;
2396         const gchar *id1 = NULL;
2397         const gchar *id2 = NULL;
2398         gboolean same_folder = FALSE;
2399
2400         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2401         g_return_if_fail (TNY_IS_LIST (headers));
2402         g_return_if_fail (TNY_IS_FOLDER (folder));
2403
2404         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2405         priv->total = 1;
2406         priv->done = 0;
2407         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2408
2409         /* Apply folder rules */
2410         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2411         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2412                 /* Set status failed and set an error */
2413                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2414                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2415                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2416                              _CS("ckct_ib_unable_to_paste_here"));
2417                 /* Notify the queue */
2418                 modest_mail_operation_notify_end (self);
2419                 return;
2420         }
2421                 
2422         /* Get source folder */
2423         iter = tny_list_create_iterator (headers);
2424         header = TNY_HEADER (tny_iterator_get_current (iter));
2425         src_folder = tny_header_get_folder (header);
2426         g_object_unref (header);
2427         g_object_unref (iter);
2428
2429         /* Check folder source and destination */
2430         id1 = tny_folder_get_id (src_folder);
2431         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2432         same_folder = !g_ascii_strcasecmp (id1, id2);
2433         if (same_folder) {
2434                 /* Set status failed and set an error */
2435                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2436                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2437                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2438                              _("mcen_ib_unable_to_copy_samefolder"));
2439                 
2440                 /* Notify the queue */
2441                 modest_mail_operation_notify_end (self);
2442                 
2443                 /* Free */
2444                 g_object_unref (src_folder);            
2445                 return;
2446         }
2447
2448         /* Create the helper */
2449         helper = g_slice_new0 (XFerMsgAsyncHelper);
2450         helper->mail_op = g_object_ref(self);
2451         helper->dest_folder = g_object_ref(folder);
2452         helper->headers = g_object_ref(headers);
2453         helper->user_callback = user_callback;
2454         helper->user_data = user_data;
2455
2456         /* Get account and set it into mail_operation */
2457         priv->account = modest_tny_folder_get_account (src_folder);
2458
2459         /* Transfer messages */
2460         tny_folder_transfer_msgs_async (src_folder, 
2461                                         headers, 
2462                                         folder, 
2463                                         delete_original, 
2464                                         transfer_msgs_cb, 
2465                                         transfer_msgs_status_cb,
2466                                         helper);
2467 }
2468
2469
2470 static void
2471 on_refresh_folder (TnyFolder   *folder, 
2472                    gboolean     cancelled, 
2473                    GError     **error,
2474                    gpointer     user_data)
2475 {
2476         RefreshAsyncHelper *helper = NULL;
2477         ModestMailOperation *self = NULL;
2478         ModestMailOperationPrivate *priv = NULL;
2479
2480         helper = (RefreshAsyncHelper *) user_data;
2481         self = helper->mail_op;
2482         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2483
2484         if (*error) {
2485                 priv->error = g_error_copy (*error);
2486                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2487                 goto out;
2488         }
2489
2490         if (cancelled) {
2491                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2492                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2493                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2494                              _("Error trying to refresh the contents of %s"),
2495                              tny_folder_get_name (folder));
2496                 goto out;
2497         }
2498
2499         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2500  out:
2501         /* Call user defined callback, if it exists */
2502         if (helper->user_callback) {
2503                 gdk_threads_enter ();
2504                 helper->user_callback (self, folder, helper->user_data);
2505                 gdk_threads_leave ();
2506         }
2507
2508         /* Free */
2509         g_object_unref (helper->mail_op);
2510         g_slice_free   (RefreshAsyncHelper, helper);
2511
2512         /* Notify about operation end */
2513         modest_mail_operation_notify_end (self);
2514 }
2515
2516 static void
2517 on_refresh_folder_status_update (GObject *obj,
2518                                  TnyStatus *status,
2519                                  gpointer user_data)
2520 {
2521         RefreshAsyncHelper *helper = NULL;
2522         ModestMailOperation *self = NULL;
2523         ModestMailOperationPrivate *priv = NULL;
2524         ModestMailOperationState *state;
2525
2526         g_return_if_fail (user_data != NULL);
2527         g_return_if_fail (status != NULL);
2528         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2529
2530         helper = (RefreshAsyncHelper *) user_data;
2531         self = helper->mail_op;
2532         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2533
2534         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2535
2536         priv->done = status->position;
2537         priv->total = status->of_total;
2538
2539         state = modest_mail_operation_clone_state (self);
2540         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2541         g_slice_free (ModestMailOperationState, state);
2542 }
2543
2544 void 
2545 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2546                                        TnyFolder *folder,
2547                                        RefreshAsyncUserCallback user_callback,
2548                                        gpointer user_data)
2549 {
2550         ModestMailOperationPrivate *priv = NULL;
2551         RefreshAsyncHelper *helper = NULL;
2552
2553         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2554
2555         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2556
2557         /* Get account and set it into mail_operation */
2558         priv->account = modest_tny_folder_get_account  (folder);
2559
2560         /* Create the helper */
2561         helper = g_slice_new0 (RefreshAsyncHelper);
2562         helper->mail_op = g_object_ref(self);
2563         helper->user_callback = user_callback;
2564         helper->user_data = user_data;
2565
2566         /* Refresh the folder. TODO: tinymail could issue a status
2567            updates before the callback call then this could happen. We
2568            must review the design */
2569         tny_folder_refresh_async (folder,
2570                                   on_refresh_folder,
2571                                   on_refresh_folder_status_update,
2572                                   helper);
2573 }
2574
2575 /**
2576  *
2577  * It's used by the mail operation queue to notify the observers
2578  * attached to that signal that the operation finished. We need to use
2579  * that because tinymail does not give us the progress of a given
2580  * operation when it finishes (it directly calls the operation
2581  * callback).
2582  */
2583 static void
2584 modest_mail_operation_notify_end (ModestMailOperation *self)
2585 {
2586         ModestMailOperationState *state;
2587         ModestMailOperationPrivate *priv = NULL;
2588
2589         g_return_if_fail (self);
2590
2591         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2592
2593         /* Set the account back to not busy */
2594         if (priv->account_name) {
2595                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2596                                                      priv->account_name, FALSE);
2597                 g_free(priv->account_name);
2598                 priv->account_name = NULL;
2599         }
2600         
2601         /* Notify the observers about the mail opertation end */
2602         state = modest_mail_operation_clone_state (self);
2603         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2604         g_slice_free (ModestMailOperationState, state);
2605 }