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