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