f23c9ccf7449f930185c3c37843b7284ad23ddaa
[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-simple-list.h>
38 #include <tny-send-queue.h>
39 #include <tny-status.h>
40 #include <tny-folder-observer.h>
41 #include <camel/camel-stream-mem.h>
42 #include <glib/gi18n.h>
43 #include "modest-platform.h"
44 #include <modest-tny-account.h>
45 #include <modest-tny-send-queue.h>
46 #include <modest-runtime.h>
47 #include "modest-text-utils.h"
48 #include "modest-tny-msg.h"
49 #include "modest-tny-folder.h"
50 #include "modest-tny-platform-factory.h"
51 #include "modest-marshal.h"
52 #include "modest-error.h"
53 #include "modest-mail-operation.h"
54
55 #define KB 1024
56
57 /* 'private'/'protected' functions */
58 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
59 static void modest_mail_operation_init       (ModestMailOperation *obj);
60 static void modest_mail_operation_finalize   (GObject *obj);
61
62 static void     get_msg_cb (TnyFolder *folder, 
63                             gboolean cancelled, 
64                             TnyMsg *msg, 
65                             GError **err, 
66                             gpointer user_data);
67
68 static void     get_msg_status_cb (GObject *obj,
69                                    TnyStatus *status,  
70                                    gpointer user_data);
71
72 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
73
74 enum _ModestMailOperationSignals 
75 {
76         PROGRESS_CHANGED_SIGNAL,
77
78         NUM_SIGNALS
79 };
80
81 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
82 struct _ModestMailOperationPrivate {
83         TnyAccount                 *account;
84         guint                      done;
85         guint                      total;
86         GObject                   *source;
87         GError                    *error;
88         ErrorCheckingUserCallback  error_checking;
89         gpointer                   error_checking_user_data;
90         ModestMailOperationStatus  status;      
91         ModestMailOperationTypeOperation op_type;
92 };
93
94 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
95                                                    MODEST_TYPE_MAIL_OPERATION, \
96                                                    ModestMailOperationPrivate))
97
98 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
99                                                    priv->status = new_status;\
100                                                }
101
102 typedef struct _GetMsgAsyncHelper {     
103         ModestMailOperation *mail_op;
104         GetMsgAsyncUserCallback user_callback;  
105         guint pending_ops;
106         gpointer user_data;
107 } GetMsgAsyncHelper;
108
109 typedef struct _XFerMsgAsyncHelper
110 {
111         ModestMailOperation *mail_op;
112         TnyList *headers;
113         TnyFolder *dest_folder;
114         XferMsgsAsynUserCallback user_callback; 
115         gpointer user_data;
116 } XFerMsgAsyncHelper;
117
118 /* globals */
119 static GObjectClass *parent_class = NULL;
120
121 static guint signals[NUM_SIGNALS] = {0};
122
123 GType
124 modest_mail_operation_get_type (void)
125 {
126         static GType my_type = 0;
127         if (!my_type) {
128                 static const GTypeInfo my_info = {
129                         sizeof(ModestMailOperationClass),
130                         NULL,           /* base init */
131                         NULL,           /* base finalize */
132                         (GClassInitFunc) modest_mail_operation_class_init,
133                         NULL,           /* class finalize */
134                         NULL,           /* class data */
135                         sizeof(ModestMailOperation),
136                         1,              /* n_preallocs */
137                         (GInstanceInitFunc) modest_mail_operation_init,
138                         NULL
139                 };
140                 my_type = g_type_register_static (G_TYPE_OBJECT,
141                                                   "ModestMailOperation",
142                                                   &my_info, 0);
143         }
144         return my_type;
145 }
146
147 static void
148 modest_mail_operation_class_init (ModestMailOperationClass *klass)
149 {
150         GObjectClass *gobject_class;
151         gobject_class = (GObjectClass*) klass;
152
153         parent_class            = g_type_class_peek_parent (klass);
154         gobject_class->finalize = modest_mail_operation_finalize;
155
156         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
157
158         /**
159          * ModestMailOperation::progress-changed
160          * @self: the #MailOperation that emits the signal
161          * @user_data: user data set when the signal handler was connected
162          *
163          * Emitted when the progress of a mail operation changes
164          */
165         signals[PROGRESS_CHANGED_SIGNAL] = 
166                 g_signal_new ("progress-changed",
167                               G_TYPE_FROM_CLASS (gobject_class),
168                               G_SIGNAL_RUN_FIRST,
169                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
170                               NULL, NULL,
171                               g_cclosure_marshal_VOID__POINTER,
172                               G_TYPE_NONE, 1, G_TYPE_POINTER);
173 }
174
175 static void
176 modest_mail_operation_init (ModestMailOperation *obj)
177 {
178         ModestMailOperationPrivate *priv;
179
180         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
181
182         priv->account  = NULL;
183         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
184         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
185         priv->error          = NULL;
186         priv->done           = 0;
187         priv->total          = 0;
188         priv->source         = NULL;
189         priv->error_checking = NULL;
190         priv->error_checking_user_data = NULL;
191 }
192
193 static void
194 modest_mail_operation_finalize (GObject *obj)
195 {
196         ModestMailOperationPrivate *priv;
197
198         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
199
200         if (priv->error) {
201                 g_error_free (priv->error);
202                 priv->error = NULL;
203         }
204         if (priv->source) {
205                 g_object_unref (priv->source);
206                 priv->source = NULL;
207         }
208         if (priv->account) {
209                 g_object_unref (priv->account);
210                 priv->account = NULL;
211         }
212
213
214         G_OBJECT_CLASS(parent_class)->finalize (obj);
215 }
216
217 ModestMailOperation*
218 modest_mail_operation_new (ModestMailOperationTypeOperation op_type, 
219                            GObject *source)
220 {
221         ModestMailOperation *obj;
222         ModestMailOperationPrivate *priv;
223                 
224         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
225         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
226
227         priv->op_type = op_type;
228         if (source != NULL)
229                 priv->source = g_object_ref(source);
230
231         return obj;
232 }
233
234 ModestMailOperation*
235 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
236                                                GObject *source,
237                                                ErrorCheckingUserCallback error_handler,
238                                                gpointer user_data)
239 {
240         ModestMailOperation *obj;
241         ModestMailOperationPrivate *priv;
242                 
243         obj = modest_mail_operation_new (op_type, source);
244         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
245         
246         g_return_val_if_fail (error_handler != NULL, obj);
247         priv->error_checking = error_handler;
248
249         return obj;
250 }
251
252 void
253 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
254 {
255         ModestMailOperationPrivate *priv;
256         
257         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
258         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
259
260         if (priv->error_checking != NULL)
261                 priv->error_checking (self, priv->error_checking_user_data);
262 }
263
264
265 ModestMailOperationTypeOperation
266 modest_mail_operation_get_type_operation (ModestMailOperation *self)
267 {
268         ModestMailOperationPrivate *priv;
269
270         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
271         
272         return priv->op_type;
273 }
274
275 gboolean 
276 modest_mail_operation_is_mine (ModestMailOperation *self, 
277                                GObject *me)
278 {
279         ModestMailOperationPrivate *priv;
280
281         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
282         if (priv->source == NULL) return FALSE;
283
284         return priv->source == me;
285 }
286
287 GObject *
288 modest_mail_operation_get_source (ModestMailOperation *self)
289 {
290         ModestMailOperationPrivate *priv;
291
292         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
293
294         return g_object_ref (priv->source);
295 }
296
297 ModestMailOperationStatus
298 modest_mail_operation_get_status (ModestMailOperation *self)
299 {
300         ModestMailOperationPrivate *priv;
301
302         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
303         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
304                               MODEST_MAIL_OPERATION_STATUS_INVALID);
305
306         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
307         return priv->status;
308 }
309
310 const GError *
311 modest_mail_operation_get_error (ModestMailOperation *self)
312 {
313         ModestMailOperationPrivate *priv;
314
315         g_return_val_if_fail (self, NULL);
316         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
317
318         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
319         return priv->error;
320 }
321
322 gboolean 
323 modest_mail_operation_cancel (ModestMailOperation *self)
324 {
325         ModestMailOperationPrivate *priv;
326
327         if (!MODEST_IS_MAIL_OPERATION (self)) {
328                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
329                 return FALSE;
330         }
331
332         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
333
334         /* TODO: Tinymail does not support cancel operation  */
335 /*      tny_account_cancel (); */
336
337         /* Set new status */
338         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
339
340         /* Notify about operation end */
341         modest_mail_operation_notify_end (self);
342
343         return TRUE;
344 }
345
346 guint 
347 modest_mail_operation_get_task_done (ModestMailOperation *self)
348 {
349         ModestMailOperationPrivate *priv;
350
351         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
352
353         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
354         return priv->done;
355 }
356
357 guint 
358 modest_mail_operation_get_task_total (ModestMailOperation *self)
359 {
360         ModestMailOperationPrivate *priv;
361
362         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
363
364         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
365         return priv->total;
366 }
367
368 gboolean
369 modest_mail_operation_is_finished (ModestMailOperation *self)
370 {
371         ModestMailOperationPrivate *priv;
372         gboolean retval = FALSE;
373
374         if (!MODEST_IS_MAIL_OPERATION (self)) {
375                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
376                 return retval;
377         }
378
379         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
380
381         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
382             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
383             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
384             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
385                 retval = TRUE;
386         } else {
387                 retval = FALSE;
388         }
389
390         return retval;
391 }
392
393 guint
394 modest_mail_operation_get_id (ModestMailOperation *self)
395 {
396         ModestMailOperationPrivate *priv;
397
398         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
399
400         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
401         return priv->done;
402 }
403
404 guint 
405 modest_mail_operation_set_id (ModestMailOperation *self,
406                               guint id)
407 {
408         ModestMailOperationPrivate *priv;
409
410         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
411
412         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
413         return priv->done;
414 }
415
416 /*
417  * Creates an image of the current state of a mail operation, the
418  * caller must free it
419  */
420 static ModestMailOperationState *
421 modest_mail_operation_clone_state (ModestMailOperation *self)
422 {
423         ModestMailOperationState *state;
424         ModestMailOperationPrivate *priv;
425
426         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
427
428         state = g_slice_new (ModestMailOperationState);
429
430         state->status = priv->status;
431         state->op_type = priv->op_type;
432         state->done = priv->done;
433         state->total = priv->total;
434         state->finished = modest_mail_operation_is_finished (self);
435
436         return state;
437 }
438
439 /* ******************************************************************* */
440 /* ************************** SEND   ACTIONS ************************* */
441 /* ******************************************************************* */
442
443 void
444 modest_mail_operation_send_mail (ModestMailOperation *self,
445                                  TnyTransportAccount *transport_account,
446                                  TnyMsg* msg)
447 {
448         TnySendQueue *send_queue = NULL;
449         ModestMailOperationPrivate *priv;
450         
451         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
452         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
453         g_return_if_fail (TNY_IS_MSG (msg));
454         
455         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
456
457         /* Get account and set it into mail_operation */
458         priv->account = g_object_ref (transport_account);
459
460         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
461         if (!TNY_IS_SEND_QUEUE(send_queue)) {
462                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
463                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
464                              "modest: could not find send queue for account\n");
465         } else {
466                 tny_send_queue_add (send_queue, msg, &(priv->error));
467         }
468
469         /* Notify about operation end */
470         modest_mail_operation_notify_end (self);
471 }
472
473 void
474 modest_mail_operation_send_new_mail (ModestMailOperation *self,
475                                      TnyTransportAccount *transport_account,
476                                      const gchar *from,  const gchar *to,
477                                      const gchar *cc,  const gchar *bcc,
478                                      const gchar *subject, const gchar *plain_body,
479                                      const gchar *html_body,
480                                      const GList *attachments_list,
481                                      TnyHeaderFlags priority_flags)
482 {
483         TnyMsg *new_msg = NULL;
484         ModestMailOperationPrivate *priv = NULL;
485
486         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
487         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
488
489         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
490
491         /* Get account and set it into mail_operation */
492         priv->account = g_object_ref (transport_account);
493
494         /* Check parametters */
495         if (to == NULL) {
496                 /* Set status failed and set an error */
497                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
498                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
499                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
500                              _("Error trying to send a mail. You need to set at least one recipient"));
501                 return;
502         }
503
504         if (html_body == NULL) {
505                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
506         } else {
507                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
508         }
509         if (!new_msg) {
510                 g_printerr ("modest: failed to create a new msg\n");
511                 return;
512         }
513
514         /* TODO: add priority handling. It's received in the priority_flags operator, and
515            it should have effect in the sending operation */
516
517         /* Call mail operation */
518         modest_mail_operation_send_mail (self, transport_account, new_msg);
519
520         /* Free */
521         g_object_unref (G_OBJECT (new_msg));
522 }
523
524 void
525 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
526                                       TnyTransportAccount *transport_account,
527                                       const gchar *from,  const gchar *to,
528                                       const gchar *cc,  const gchar *bcc,
529                                       const gchar *subject, const gchar *plain_body,
530                                       const gchar *html_body,
531                                       const GList *attachments_list,
532                                       TnyHeaderFlags priority_flags)
533 {
534         TnyMsg *msg = NULL;
535         TnyFolder *folder = NULL;
536         ModestMailOperationPrivate *priv = NULL;
537
538         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
539         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
540
541         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
542
543         /* Get account and set it into mail_operation */
544         priv->account = g_object_ref (transport_account);
545
546         if (html_body == NULL) {
547                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
548         } else {
549                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
550         }
551         if (!msg) {
552                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
553                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
554                              "modest: failed to create a new msg\n");
555                 goto end;
556         }
557
558         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
559         if (!folder) {
560                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
561                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
562                              "modest: failed to create a new msg\n");
563                 goto end;
564         }
565         
566         tny_folder_add_msg (folder, msg, &(priv->error));
567         if (priv->error)
568                 goto end;
569
570 end:
571         if (msg)
572                 g_object_unref (G_OBJECT(msg));
573         if (folder)
574                 g_object_unref (G_OBJECT(folder));
575
576         modest_mail_operation_notify_end (self);
577 }
578
579 typedef struct 
580 {
581         ModestMailOperation *mail_op;
582         TnyStoreAccount *account;
583         TnyTransportAccount *transport_account;
584         gint max_size;
585         gint retrieve_limit;
586         gchar *retrieve_type;
587 } UpdateAccountInfo;
588
589 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
590 /* We use this folder observer to track the headers that have been
591  * added to a folder */
592 typedef struct {
593         GObject parent;
594         TnyList *new_headers;
595 } InternalFolderObserver;
596
597 typedef struct {
598         GObjectClass parent;
599 } InternalFolderObserverClass;
600
601 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
602
603 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
604                          internal_folder_observer,
605                          G_TYPE_OBJECT,
606                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
607
608
609 static void
610 foreach_add_item (gpointer header, gpointer user_data)
611 {
612         tny_list_prepend (TNY_LIST (user_data), 
613                           g_object_ref (G_OBJECT (header)));
614 }
615
616 /* This is the method that looks for new messages in a folder */
617 static void
618 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
619 {
620         TnyFolderChangeChanged changed;
621
622         changed = tny_folder_change_get_changed (change);
623
624         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
625                 TnyList *list;
626
627                 /* Get added headers */
628                 list = tny_simple_list_new ();
629                 tny_folder_change_get_added_headers (change, list);
630
631                 /* Add them to the folder observer */
632                 tny_list_foreach (list, foreach_add_item, 
633                                   ((InternalFolderObserver *)self)->new_headers);
634
635                 g_object_unref (G_OBJECT (list));
636         }
637 }
638
639 static void
640 internal_folder_observer_init (InternalFolderObserver *self) 
641 {
642         self->new_headers = tny_simple_list_new ();
643 }
644 static void
645 internal_folder_observer_finalize (GObject *object) 
646 {
647         InternalFolderObserver *self;
648
649         self = (InternalFolderObserver *) object;
650         g_object_unref (self->new_headers);
651
652         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
653 }
654 static void
655 tny_folder_observer_init (TnyFolderObserverIface *iface) 
656 {
657         iface->update_func = internal_folder_observer_update;
658 }
659 static void
660 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
661 {
662         GObjectClass *object_class;
663
664         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
665         object_class = (GObjectClass*) klass;
666         object_class->finalize = internal_folder_observer_finalize;
667 }
668
669 /*****************/
670
671 static void
672 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
673 {
674         TnyIterator *iter;
675         TnyList *folders = tny_simple_list_new ();
676
677         tny_folder_store_get_folders (store, folders, query, NULL);
678         iter = tny_list_create_iterator (folders);
679
680         while (!tny_iterator_is_done (iter)) {
681
682                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
683
684                 tny_list_prepend (all_folders, G_OBJECT (folder));
685                 recurse_folders (folder, query, all_folders);    
686                 g_object_unref (G_OBJECT (folder));
687
688                 tny_iterator_next (iter);
689         }
690          g_object_unref (G_OBJECT (iter));
691          g_object_unref (G_OBJECT (folders));
692 }
693
694 /* 
695  * Issues the "progress-changed" signal. The timer won't be removed,
696  * so you must call g_source_remove to stop the signal emission
697  */
698 static gboolean
699 idle_notify_progress (gpointer data)
700 {
701         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
702         ModestMailOperationState *state;
703
704         state = modest_mail_operation_clone_state (mail_op);
705         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
706         g_slice_free (ModestMailOperationState, state);
707
708         return TRUE;
709 }
710
711 /* 
712  * Issues the "progress-changed" signal and removes the timer. It uses
713  * a lock to ensure that the progress information of the mail
714  * operation is not modified while there are notifications pending
715  */
716 static gboolean
717 idle_notify_progress_once (gpointer data)
718 {
719         ModestPair *pair;
720
721         pair = (ModestPair *) data;
722
723         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
724
725         /* Free the state and the reference to the mail operation */
726         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
727         g_object_unref (pair->first);
728
729         return FALSE;
730 }
731
732 /* 
733  * Used by update_account_thread to notify the queue from the main
734  * loop. We call it inside an idle call to achieve that
735  */
736 static gboolean
737 notify_update_account_queue (gpointer data)
738 {
739         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
740
741         modest_mail_operation_notify_end (mail_op);
742         g_object_unref (mail_op);
743
744         return FALSE;
745 }
746
747 static int
748 compare_headers_by_date (gconstpointer a, 
749                          gconstpointer b)
750 {
751         TnyHeader **header1, **header2;
752         time_t sent1, sent2;
753
754         header1 = (TnyHeader **) a;
755         header2 = (TnyHeader **) b;
756
757         sent1 = tny_header_get_date_sent (*header1);
758         sent2 = tny_header_get_date_sent (*header2);
759
760         /* We want the most recent ones (greater time_t) at the
761            beginning */
762         if (sent1 < sent2)
763                 return 1;
764         else
765                 return -1;
766 }
767
768 static gpointer
769 update_account_thread (gpointer thr_user_data)
770 {
771         UpdateAccountInfo *info;
772         TnyList *all_folders = NULL;
773         GPtrArray *new_headers;
774         TnyIterator *iter = NULL;
775         TnyFolderStoreQuery *query = NULL;
776         ModestMailOperationPrivate *priv;
777
778         info = (UpdateAccountInfo *) thr_user_data;
779         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
780
781         /* Get account and set it into mail_operation */
782         priv->account = g_object_ref (info->account);
783
784         /* Get all the folders We can do it synchronously because
785            we're already running in a different thread than the UI */
786         all_folders = tny_simple_list_new ();
787         query = tny_folder_store_query_new ();
788         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
789         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
790                                       all_folders,
791                                       query,
792                                       &(priv->error));
793         if (priv->error) {
794                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
795                 goto out;
796         }
797
798         iter = tny_list_create_iterator (all_folders);
799         while (!tny_iterator_is_done (iter)) {
800                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
801
802                 recurse_folders (folder, query, all_folders);
803                 tny_iterator_next (iter);
804         }
805         g_object_unref (G_OBJECT (iter));
806
807         /* Update status and notify. We need to call the notification
808            with a source functopm in order to call it from the main
809            loop. We need that in order not to get into trouble with
810            Gtk+. We use a timeout in order to provide more status
811            information, because the sync tinymail call does not
812            provide it for the moment */
813         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
814
815         /* Refresh folders */
816         new_headers = g_ptr_array_new ();
817         iter = tny_list_create_iterator (all_folders);
818         while (!tny_iterator_is_done (iter) && !priv->error) {
819
820                 InternalFolderObserver *observer;
821                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
822
823                 /* Refresh the folder */
824                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
825                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
826                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
827
828                 /* If the retrieve type is headers only do nothing more */
829                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
830                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
831                         TnyIterator *iter;
832
833                         iter = tny_list_create_iterator (observer->new_headers);
834                         while (!tny_iterator_is_done (iter)) {
835                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
836                                 /* Apply per-message size limits */
837                                 if (tny_header_get_message_size (header) < info->max_size)
838                                         g_ptr_array_add (new_headers, g_object_ref (header));
839
840                                 g_object_unref (header);
841                                 tny_iterator_next (iter);
842                         }
843                         g_object_unref (iter);
844                 }
845                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
846                 g_object_unref (observer);
847
848                 if (priv->error)
849                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
850
851                 g_object_unref (G_OBJECT (folder));
852                 tny_iterator_next (iter);
853         }
854         g_object_unref (G_OBJECT (iter));
855         g_source_remove (timeout);
856
857         if (new_headers->len > 0) {
858                 gint msg_num = 0;
859
860                 /* Order by date */
861                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
862
863                 /* Apply message count limit */
864                 /* TODO if the number of messages exceeds the maximum, ask the
865                    user to download them all */
866                 priv->done = 0;
867                 priv->total = MIN (new_headers->len, info->retrieve_limit);
868                 while ((msg_num < priv->total)) {
869
870                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
871                         TnyFolder *folder = tny_header_get_folder (header);
872                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
873                         ModestMailOperationState *state;
874                         ModestPair* pair;
875
876                         priv->done++;
877                         /* We can not just use the mail operation because the
878                            values of done and total could change before the
879                            idle is called */
880                         state = modest_mail_operation_clone_state (info->mail_op);
881                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
882                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
883                                          pair, (GDestroyNotify) modest_pair_free);
884
885                         g_object_unref (msg);
886                         g_object_unref (folder);
887
888                         msg_num++;
889                 }
890                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
891                 g_ptr_array_free (new_headers, FALSE);
892         }
893
894         /* Perform send */
895         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
896         priv->done = 0;
897         priv->total = 0;
898         if (priv->account != NULL) 
899                 g_object_unref (priv->account);
900         priv->account = g_object_ref (info->transport_account);
901         
902         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue
903                 (info->transport_account);
904
905         timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
906         modest_tny_send_queue_try_to_send (send_queue);
907         g_source_remove (timeout);
908
909         g_object_unref (G_OBJECT(send_queue));
910         
911         /* Check if the operation was a success */
912         if (!priv->error) {
913                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
914
915                 /* Update the last updated key */
916                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
917                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
918                                             MODEST_ACCOUNT_LAST_UPDATED, 
919                                             time(NULL), 
920                                             TRUE);
921         }
922
923  out:
924         /* Notify about operation end. Note that the info could be
925            freed before this idle happens, but the mail operation will
926            be still alive */
927         g_idle_add (notify_update_account_queue, info->mail_op);
928
929         /* Frees */
930         g_object_unref (query);
931         g_object_unref (all_folders);
932         g_object_unref (info->account);
933         g_object_unref (info->transport_account);
934         g_free (info->retrieve_type);
935         g_slice_free (UpdateAccountInfo, info);
936
937         return NULL;
938 }
939
940 gboolean
941 modest_mail_operation_update_account (ModestMailOperation *self,
942                                       const gchar *account_name)
943 {
944         GThread *thread;
945         UpdateAccountInfo *info;
946         ModestMailOperationPrivate *priv;
947         ModestAccountMgr *mgr;
948         TnyStoreAccount *modest_account;
949         TnyTransportAccount *transport_account;
950
951         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
952         g_return_val_if_fail (account_name, FALSE);
953
954         /* Init mail operation. Set total and done to 0, and do not
955            update them, this way the progress objects will know that
956            we have no clue about the number of the objects */
957         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
958         priv->total = 0;
959         priv->done  = 0;
960         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
961         
962         /* Get the Modest account */
963         modest_account = (TnyStoreAccount *)
964                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
965                                                                      account_name,
966                                                                      TNY_ACCOUNT_TYPE_STORE);
967
968         if (!modest_account) {
969                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
970                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
971                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
972                              "cannot get tny store account for %s\n", account_name);
973                 modest_mail_operation_notify_end (self);
974                 return FALSE;
975         }
976
977         /* Get the transport account, we can not do it in the thread
978            due to some problems with dbus */
979         transport_account = (TnyTransportAccount *)
980                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
981                                                                                     account_name);
982         if (!transport_account) {
983                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
984                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
985                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
986                              "cannot get tny transport account for %s\n", account_name);
987                 modest_mail_operation_notify_end (self);
988                 return FALSE;
989         }
990
991         /* Create the helper object */
992         info = g_slice_new (UpdateAccountInfo);
993         info->mail_op = self;
994         info->account = modest_account;
995         info->transport_account = transport_account;
996
997         /* Get the message size limit */
998         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
999                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1000         if (info->max_size == 0)
1001                 info->max_size = G_MAXINT;
1002         else
1003                 info->max_size = info->max_size * KB;
1004
1005         /* Get per-account retrieval type */
1006         mgr = modest_runtime_get_account_mgr ();
1007         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1008                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1009
1010         /* Get per-account message amount retrieval limit */
1011         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1012                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1013         if (info->retrieve_limit == 0)
1014                 info->retrieve_limit = G_MAXINT;
1015
1016         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1017
1018         return TRUE;
1019 }
1020
1021 /* ******************************************************************* */
1022 /* ************************** STORE  ACTIONS ************************* */
1023 /* ******************************************************************* */
1024
1025
1026 TnyFolder *
1027 modest_mail_operation_create_folder (ModestMailOperation *self,
1028                                      TnyFolderStore *parent,
1029                                      const gchar *name)
1030 {
1031         ModestTnyFolderRules rules;
1032         ModestMailOperationPrivate *priv;
1033         TnyFolder *new_folder = NULL;
1034         gboolean can_create = FALSE;
1035
1036         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1037         g_return_val_if_fail (name, NULL);
1038         
1039         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1040
1041         /* Get account and set it into mail_operation */
1042         priv->account = tny_folder_get_account (TNY_FOLDER(parent));
1043
1044         /* Check parent */
1045         if (!TNY_IS_FOLDER (parent)) {
1046                 /* Set status failed and set an error */
1047                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1048                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1049                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1050                              _("mail_in_ui_folder_create_error"));
1051         } else {
1052                 /* Check folder rules */
1053                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1054                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1055                         /* Set status failed and set an error */
1056                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1057                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1058                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1059                                      _("mail_in_ui_folder_create_error"));
1060                 } 
1061                 else
1062                         can_create = TRUE;              
1063         }
1064
1065         if (can_create) {
1066                 /* Create the folder */
1067                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1068                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1069         }
1070
1071         /* Notify about operation end */
1072         modest_mail_operation_notify_end (self);
1073
1074         return new_folder;
1075 }
1076
1077 void
1078 modest_mail_operation_remove_folder (ModestMailOperation *self,
1079                                      TnyFolder           *folder,
1080                                      gboolean             remove_to_trash)
1081 {
1082         TnyAccount *account;
1083         ModestMailOperationPrivate *priv;
1084         ModestTnyFolderRules rules;
1085
1086         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1087         g_return_if_fail (TNY_IS_FOLDER (folder));
1088         
1089         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1090         
1091         /* Check folder rules */
1092         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1093         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1094                 /* Set status failed and set an error */
1095                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1096                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1097                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1098                              _("mail_in_ui_folder_delete_error"));
1099                 goto end;
1100         }
1101
1102         /* Get the account */
1103         account = tny_folder_get_account (folder);
1104         priv->account = g_object_ref(account);
1105
1106         /* Delete folder or move to trash */
1107         if (remove_to_trash) {
1108                 TnyFolder *trash_folder = NULL;
1109                 trash_folder = modest_tny_account_get_special_folder (account,
1110                                                                       TNY_FOLDER_TYPE_TRASH);
1111                 /* TODO: error_handling */
1112                  modest_mail_operation_xfer_folder (self, folder,
1113                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1114         } else {
1115                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1116
1117                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1118                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1119
1120                 if (parent)
1121                         g_object_unref (G_OBJECT (parent));
1122         }
1123         g_object_unref (G_OBJECT (account));
1124
1125  end:
1126         /* Notify about operation end */
1127         modest_mail_operation_notify_end (self);
1128 }
1129
1130 static void
1131 transfer_folder_status_cb (GObject *obj,
1132                            TnyStatus *status,
1133                            gpointer user_data)
1134 {
1135         ModestMailOperation *self;
1136         ModestMailOperationPrivate *priv;
1137         ModestMailOperationState *state;
1138
1139         g_return_if_fail (status != NULL);
1140         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1141
1142         self = MODEST_MAIL_OPERATION (user_data);
1143         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1144
1145         if ((status->position == 1) && (status->of_total == 100))
1146                 return;
1147
1148         priv->done = status->position;
1149         priv->total = status->of_total;
1150
1151         state = modest_mail_operation_clone_state (self);
1152         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1153         g_slice_free (ModestMailOperationState, state);
1154 }
1155
1156
1157 static void
1158 transfer_folder_cb (TnyFolder *folder, 
1159                     TnyFolderStore *into, 
1160                     gboolean cancelled, 
1161                     TnyFolder *new_folder, GError **err, 
1162                     gpointer user_data)
1163 {
1164         ModestMailOperation *self = NULL;
1165         ModestMailOperationPrivate *priv = NULL;
1166
1167         self = MODEST_MAIL_OPERATION (user_data);
1168
1169         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1170
1171         if (*err) {
1172                 priv->error = g_error_copy (*err);
1173                 priv->done = 0;
1174                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1175         } else if (cancelled) {
1176                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1177                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1178                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1179                              _("Transference of %s was cancelled."),
1180                              tny_folder_get_name (folder));
1181         } else {
1182                 priv->done = 1;
1183                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1184         }
1185                 
1186         /* Free */
1187         g_object_unref (folder);
1188         g_object_unref (into);
1189         if (new_folder != NULL)
1190                 g_object_unref (new_folder);
1191
1192         /* Notify about operation end */
1193         modest_mail_operation_notify_end (self);
1194 }
1195
1196 void
1197 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1198                                    TnyFolder *folder,
1199                                    TnyFolderStore *parent,
1200                                    gboolean delete_original)
1201 {
1202         ModestMailOperationPrivate *priv = NULL;
1203         ModestTnyFolderRules parent_rules, rules;
1204
1205         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1206         g_return_if_fail (TNY_IS_FOLDER (folder));
1207         g_return_if_fail (TNY_IS_FOLDER (parent));
1208
1209         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1210
1211         /* Get account and set it into mail_operation */
1212         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1213         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1214
1215         /* Get folder rules */
1216         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1217         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1218
1219         if (!TNY_IS_FOLDER_STORE (parent)) {
1220                 
1221         }
1222         
1223         /* The moveable restriction is applied also to copy operation */
1224         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1225                 /* Set status failed and set an error */
1226                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1227                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1228                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1229                              _("mail_in_ui_folder_move_target_error"));
1230
1231                 /* Notify the queue */
1232                 modest_mail_operation_notify_end (self);
1233         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1234                 /* Set status failed and set an error */
1235                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1236                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1237                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1238                              _("FIXME: parent folder does not accept new folders"));
1239
1240                 /* Notify the queue */
1241                 modest_mail_operation_notify_end (self);
1242         } else {
1243                 /* Pick references for async calls */
1244                 g_object_ref (folder);
1245                 g_object_ref (parent);
1246
1247                 /* Move/Copy folder */          
1248                 tny_folder_copy_async (folder,
1249                                        parent,
1250                                        tny_folder_get_name (folder),
1251                                        delete_original,
1252                                        transfer_folder_cb,
1253                                        transfer_folder_status_cb,
1254                                        self);
1255         }
1256 }
1257
1258 void
1259 modest_mail_operation_rename_folder (ModestMailOperation *self,
1260                                      TnyFolder *folder,
1261                                      const gchar *name)
1262 {
1263         ModestMailOperationPrivate *priv;
1264         ModestTnyFolderRules rules;
1265
1266         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1267         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1268         g_return_if_fail (name);
1269         
1270         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1271
1272         /* Get account and set it into mail_operation */
1273         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1274
1275         /* Check folder rules */
1276         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1277         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1278                 /* Set status failed and set an error */
1279                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1280                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1281                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1282                              _("FIXME: unable to rename"));
1283
1284                 /* Notify about operation end */
1285                 modest_mail_operation_notify_end (self);
1286         } else {
1287                 /* Rename. Camel handles folder subscription/unsubscription */
1288                 TnyFolderStore *into;
1289
1290                 into = tny_folder_get_folder_store (folder);
1291                 tny_folder_copy_async (folder, into, name, TRUE,
1292                                  transfer_folder_cb,
1293                                  transfer_folder_status_cb,
1294                                  self);
1295                 if (into)
1296                         g_object_unref (into);
1297                 
1298         }
1299  }
1300
1301 /* ******************************************************************* */
1302 /* **************************  MSG  ACTIONS  ************************* */
1303 /* ******************************************************************* */
1304
1305 void modest_mail_operation_get_msg (ModestMailOperation *self,
1306                                     TnyHeader *header,
1307                                     GetMsgAsyncUserCallback user_callback,
1308                                     gpointer user_data)
1309 {
1310         GetMsgAsyncHelper *helper = NULL;
1311         TnyFolder *folder;
1312         ModestMailOperationPrivate *priv;
1313         
1314         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1315         g_return_if_fail (TNY_IS_HEADER (header));
1316         
1317         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1318         folder = tny_header_get_folder (header);
1319
1320         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1321
1322         /* Get message from folder */
1323         if (folder) {
1324                 /* Get account and set it into mail_operation */
1325                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));            
1326
1327                 helper = g_slice_new0 (GetMsgAsyncHelper);
1328                 helper->mail_op = self;
1329                 helper->user_callback = user_callback;
1330                 helper->pending_ops = 1;
1331                 helper->user_data = user_data;
1332
1333                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1334
1335                 g_object_unref (G_OBJECT (folder));
1336         } else {
1337                 /* Set status failed and set an error */
1338                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1339                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1340                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1341                              _("Error trying to get a message. No folder found for header"));
1342
1343                 /* Notify the queue */
1344                 modest_mail_operation_notify_end (self);
1345         }
1346 }
1347
1348 static void
1349 get_msg_cb (TnyFolder *folder, 
1350             gboolean cancelled, 
1351             TnyMsg *msg, 
1352             GError **error, 
1353             gpointer user_data)
1354 {
1355         GetMsgAsyncHelper *helper = NULL;
1356         ModestMailOperation *self = NULL;
1357         ModestMailOperationPrivate *priv = NULL;
1358
1359         helper = (GetMsgAsyncHelper *) user_data;
1360         g_return_if_fail (helper != NULL);       
1361         self = helper->mail_op;
1362         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1363         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1364         
1365         helper->pending_ops--;
1366
1367         /* Check errors and cancel */
1368         if (*error) {
1369                 priv->error = g_error_copy (*error);
1370                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1371                 goto out;
1372         }
1373         if (cancelled) {
1374                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1375                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1376                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1377                              _("Error trying to refresh the contents of %s"),
1378                              tny_folder_get_name (folder));
1379                 goto out;
1380         }
1381
1382         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1383
1384         /* If user defined callback function was defined, call it */
1385         if (helper->user_callback) {
1386                 helper->user_callback (self, NULL, msg, helper->user_data);
1387         }
1388
1389         /* Free */
1390  out:
1391         if (helper->pending_ops == 0) {
1392                 g_slice_free (GetMsgAsyncHelper, helper);
1393                 
1394                 /* Notify about operation end */
1395                 modest_mail_operation_notify_end (self);        
1396         }
1397 }
1398
1399 static void     
1400 get_msg_status_cb (GObject *obj,
1401                    TnyStatus *status,  
1402                    gpointer user_data)
1403 {
1404         GetMsgAsyncHelper *helper = NULL;
1405         ModestMailOperation *self;
1406         ModestMailOperationPrivate *priv;
1407         ModestMailOperationState *state;
1408
1409         g_return_if_fail (status != NULL);
1410         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1411
1412         helper = (GetMsgAsyncHelper *) user_data;
1413         g_return_if_fail (helper != NULL);       
1414
1415         self = helper->mail_op;
1416         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1417
1418         if ((status->position == 1) && (status->of_total == 100))
1419                 return;
1420
1421         priv->done = 1;
1422         priv->total = 1;
1423
1424         state = modest_mail_operation_clone_state (self);
1425         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1426         g_slice_free (ModestMailOperationState, state);
1427 }
1428
1429 /****************************************************/
1430 typedef struct {
1431         ModestMailOperation *mail_op;
1432         TnyList *headers;
1433         GetMsgAsyncUserCallback user_callback;
1434         gpointer user_data;
1435         GDestroyNotify notify;
1436 } GetFullMsgsInfo;
1437
1438 typedef struct {
1439         GetMsgAsyncUserCallback user_callback;
1440         TnyHeader *header;
1441         TnyMsg *msg;
1442         gpointer user_data;
1443         ModestMailOperation *mail_op;
1444 } NotifyGetMsgsInfo;
1445
1446
1447 /* 
1448  * Used by get_msgs_full_thread to call the user_callback for each
1449  * message that has been read
1450  */
1451 static gboolean
1452 notify_get_msgs_full (gpointer data)
1453 {
1454         NotifyGetMsgsInfo *info;
1455
1456         info = (NotifyGetMsgsInfo *) data;      
1457
1458         /* Call the user callback */
1459         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1460
1461         g_slice_free (NotifyGetMsgsInfo, info);
1462
1463         return FALSE;
1464 }
1465
1466 /* 
1467  * Used by get_msgs_full_thread to free al the thread resources and to
1468  * call the destroy function for the passed user_data
1469  */
1470 static gboolean
1471 get_msgs_full_destroyer (gpointer data)
1472 {
1473         GetFullMsgsInfo *info;
1474
1475         info = (GetFullMsgsInfo *) data;
1476
1477         if (info->notify)
1478                 info->notify (info->user_data);
1479
1480         /* free */
1481         g_object_unref (info->headers);
1482         g_slice_free (GetFullMsgsInfo, info);
1483
1484         return FALSE;
1485 }
1486
1487 static gpointer
1488 get_msgs_full_thread (gpointer thr_user_data)
1489 {
1490         GetFullMsgsInfo *info;
1491         ModestMailOperationPrivate *priv = NULL;
1492         TnyIterator *iter = NULL;
1493         
1494         info = (GetFullMsgsInfo *) thr_user_data;       
1495         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1496
1497         iter = tny_list_create_iterator (info->headers);
1498         while (!tny_iterator_is_done (iter)) { 
1499                 TnyHeader *header;
1500                 TnyFolder *folder;
1501                 
1502                 header = TNY_HEADER (tny_iterator_get_current (iter));
1503                 folder = tny_header_get_folder (header);
1504                                 
1505                 /* Get message from folder */
1506                 if (folder) {
1507                         TnyMsg *msg;
1508                         /* The callback will call it per each header */
1509                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1510
1511                         if (msg) {
1512                                 ModestMailOperationState *state;
1513                                 ModestPair *pair;
1514
1515                                 priv->done++;
1516
1517                                 /* notify progress */
1518                                 state = modest_mail_operation_clone_state (info->mail_op);
1519                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1520                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1521                                                  pair, (GDestroyNotify) modest_pair_free);
1522
1523                                 /* The callback is the responsible for
1524                                    freeing the message */
1525                                 if (info->user_callback) {
1526                                         NotifyGetMsgsInfo *info_notify;
1527                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1528                                         info_notify->user_callback = info->user_callback;
1529                                         info_notify->mail_op = info->mail_op;
1530                                         info_notify->header = g_object_ref (header);
1531                                         info_notify->msg = g_object_ref (msg);
1532                                         info_notify->user_data = info->user_data;
1533                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1534                                                          notify_get_msgs_full, 
1535                                                          info_notify, NULL);
1536                                 }
1537                                 g_object_unref (msg);
1538                         }
1539                 } else {
1540                         /* Set status failed and set an error */
1541                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1542                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1543                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1544                                      "Error trying to get a message. No folder found for header");
1545                 }
1546                 g_object_unref (header);                
1547                 tny_iterator_next (iter);
1548         }
1549
1550         /* Set operation status */
1551         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1552                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1553
1554         /* Notify about operation end */
1555         g_idle_add (notify_update_account_queue, info->mail_op);
1556
1557         /* Free thread resources. Will be called after all previous idles */
1558         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1559
1560         return NULL;
1561 }
1562
1563 void 
1564 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1565                                      TnyList *header_list, 
1566                                      GetMsgAsyncUserCallback user_callback,
1567                                      gpointer user_data,
1568                                      GDestroyNotify notify)
1569 {
1570         TnyHeader *header = NULL;
1571         TnyFolder *folder = NULL;
1572         GThread *thread;
1573         ModestMailOperationPrivate *priv = NULL;
1574         GetFullMsgsInfo *info = NULL;
1575         gboolean size_ok = TRUE;
1576         gint max_size;
1577         TnyIterator *iter = NULL;
1578         
1579         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1580         
1581         /* Init mail operation */
1582         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1583         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1584         priv->done = 0;
1585         priv->total = tny_list_get_length(header_list);
1586
1587         /* Get account and set it into mail_operation */
1588         if (tny_list_get_length (header_list) > 1) {
1589                 iter = tny_list_create_iterator (header_list);          
1590                 header = TNY_HEADER (tny_iterator_get_current (iter));
1591                 folder = tny_header_get_folder (header);                
1592                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1593                 g_object_unref (header);
1594                 g_object_unref (folder);
1595         }
1596
1597         /* Get msg size limit */
1598         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1599                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1600                                          &(priv->error));
1601         if (priv->error) {
1602                 g_clear_error (&(priv->error));
1603                 max_size = G_MAXINT;
1604         } else {
1605                 max_size = max_size * KB;
1606         }
1607
1608         /* Check message size limits. If there is only one message
1609            always retrieve it */
1610         if (iter != NULL) {
1611                 while (!tny_iterator_is_done (iter) && size_ok) {
1612                         header = TNY_HEADER (tny_iterator_get_current (iter));
1613                         if (tny_header_get_message_size (header) >= max_size)
1614                                 size_ok = FALSE;
1615                         g_object_unref (header);
1616                         tny_iterator_next (iter);
1617                 }
1618                 g_object_unref (iter);
1619         }
1620
1621         if (size_ok) {
1622                 /* Create the info */
1623                 info = g_slice_new0 (GetFullMsgsInfo);
1624                 info->mail_op = self;
1625                 info->user_callback = user_callback;
1626                 info->user_data = user_data;
1627                 info->headers = g_object_ref (header_list);
1628                 info->notify = notify;
1629
1630                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1631         } else {
1632                 /* Set status failed and set an error */
1633                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1634                 /* FIXME: the error msg is different for pop */
1635                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1636                              MODEST_MAIL_OPERATION_ERROR_SIZE_LIMIT,
1637                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1638                 /* Remove from queue and free resources */
1639                 modest_mail_operation_notify_end (self);
1640                 if (notify)
1641                         notify (user_data);
1642         }
1643 }
1644
1645
1646 void 
1647 modest_mail_operation_remove_msg (ModestMailOperation *self,
1648                                   TnyHeader *header,
1649                                   gboolean remove_to_trash)
1650 {
1651         TnyFolder *folder;
1652         ModestMailOperationPrivate *priv;
1653
1654         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1655         g_return_if_fail (TNY_IS_HEADER (header));
1656
1657         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1658         folder = tny_header_get_folder (header);
1659
1660         /* Get account and set it into mail_operation */
1661         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1662
1663         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1664
1665         /* Delete or move to trash */
1666         if (remove_to_trash) {
1667                 TnyFolder *trash_folder;
1668                 TnyStoreAccount *store_account;
1669
1670                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1671                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1672                                                                       TNY_FOLDER_TYPE_TRASH);
1673                 if (trash_folder) {
1674                         TnyList *headers;
1675
1676                         /* Create list */
1677                         headers = tny_simple_list_new ();
1678                         tny_list_append (headers, G_OBJECT (header));
1679                         g_object_unref (header);
1680
1681                         /* Move to trash */
1682                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1683                         g_object_unref (headers);
1684 /*                      g_object_unref (trash_folder); */
1685                 } else {
1686                         ModestMailOperationPrivate *priv;
1687
1688                         /* Set status failed and set an error */
1689                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1690                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1691                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1692                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1693                                      _("Error trying to delete a message. Trash folder not found"));
1694                 }
1695
1696                 g_object_unref (G_OBJECT (store_account));
1697         } else {
1698                 tny_folder_remove_msg (folder, header, &(priv->error));
1699                 if (!priv->error)
1700                         tny_folder_sync(folder, TRUE, &(priv->error));
1701         }
1702
1703         /* Set status */
1704         if (!priv->error)
1705                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1706         else
1707                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1708
1709         /* Free */
1710         g_object_unref (G_OBJECT (folder));
1711
1712         /* Notify about operation end */
1713         modest_mail_operation_notify_end (self);
1714 }
1715
1716 static void
1717 transfer_msgs_status_cb (GObject *obj,
1718                          TnyStatus *status,  
1719                          gpointer user_data)
1720 {
1721         XFerMsgAsyncHelper *helper = NULL;
1722         ModestMailOperation *self;
1723         ModestMailOperationPrivate *priv;
1724         ModestMailOperationState *state;
1725
1726
1727         g_return_if_fail (status != NULL);
1728         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1729
1730         helper = (XFerMsgAsyncHelper *) user_data;
1731         g_return_if_fail (helper != NULL);       
1732
1733         self = helper->mail_op;
1734         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1735
1736         if ((status->position == 1) && (status->of_total == 100))
1737                 return;
1738
1739         priv->done = status->position;
1740         priv->total = status->of_total;
1741
1742         state = modest_mail_operation_clone_state (self);
1743         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1744         g_slice_free (ModestMailOperationState, state);
1745 }
1746
1747
1748 static void
1749 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1750 {
1751         XFerMsgAsyncHelper *helper;
1752         ModestMailOperation *self;
1753         ModestMailOperationPrivate *priv;
1754
1755         helper = (XFerMsgAsyncHelper *) user_data;
1756         self = helper->mail_op;
1757
1758         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1759
1760         if (*err) {
1761                 priv->error = g_error_copy (*err);
1762                 priv->done = 0;
1763                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1764         } else if (cancelled) {
1765                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1766                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1767                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1768                              _("Error trying to refresh the contents of %s"),
1769                              tny_folder_get_name (folder));
1770         } else {
1771                 priv->done = 1;
1772                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1773         }
1774
1775         /* If user defined callback function was defined, call it */
1776         if (helper->user_callback) {
1777                 helper->user_callback (priv->source, helper->user_data);
1778         }
1779
1780         /* Free */
1781         g_object_unref (helper->headers);
1782         g_object_unref (helper->dest_folder);
1783         g_object_unref (helper->mail_op);
1784         g_slice_free   (XFerMsgAsyncHelper, helper);
1785         g_object_unref (folder);
1786
1787         /* Notify about operation end */
1788         modest_mail_operation_notify_end (self);
1789 }
1790
1791 void
1792 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1793                                  TnyList *headers, 
1794                                  TnyFolder *folder, 
1795                                  gboolean delete_original,
1796                                  XferMsgsAsynUserCallback user_callback,
1797                                  gpointer user_data)
1798 {
1799         ModestMailOperationPrivate *priv;
1800         TnyIterator *iter;
1801         TnyFolder *src_folder;
1802         XFerMsgAsyncHelper *helper;
1803         TnyHeader *header;
1804         ModestTnyFolderRules rules;
1805
1806         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1807         g_return_if_fail (TNY_IS_LIST (headers));
1808         g_return_if_fail (TNY_IS_FOLDER (folder));
1809
1810         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1811         priv->total = 1;
1812         priv->done = 0;
1813         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1814
1815         /* Apply folder rules */
1816         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1817
1818         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1819                 /* Set status failed and set an error */
1820                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1821                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1822                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1823                              _("FIXME: folder does not accept msgs"));
1824                 /* Notify the queue */
1825                 modest_mail_operation_notify_end (self);
1826                 return;
1827         }
1828
1829         /* Create the helper */
1830         helper = g_slice_new0 (XFerMsgAsyncHelper);
1831         helper->mail_op = g_object_ref(self);
1832         helper->dest_folder = g_object_ref(folder);
1833         helper->headers = g_object_ref(headers);
1834         helper->user_callback = user_callback;
1835         helper->user_data = user_data;
1836
1837         /* Get source folder */
1838         iter = tny_list_create_iterator (headers);
1839         header = TNY_HEADER (tny_iterator_get_current (iter));
1840         src_folder = tny_header_get_folder (header);
1841         g_object_unref (header);
1842         g_object_unref (iter);
1843
1844         /* Get account and set it into mail_operation */
1845         priv->account = tny_folder_get_account (src_folder);
1846
1847         /* Transfer messages */
1848         tny_folder_transfer_msgs_async (src_folder, 
1849                                         headers, 
1850                                         folder, 
1851                                         delete_original, 
1852                                         transfer_msgs_cb, 
1853                                         transfer_msgs_status_cb,
1854                                         helper);
1855 }
1856
1857
1858 static void
1859 on_refresh_folder (TnyFolder   *folder, 
1860                    gboolean     cancelled, 
1861                    GError     **error,
1862                    gpointer     user_data)
1863 {
1864         ModestMailOperation *self;
1865         ModestMailOperationPrivate *priv;
1866
1867         self = MODEST_MAIL_OPERATION (user_data);
1868         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1869
1870         if (*error) {
1871                 priv->error = g_error_copy (*error);
1872                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1873                 goto out;
1874         }
1875
1876         if (cancelled) {
1877                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1878                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1879                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1880                              _("Error trying to refresh the contents of %s"),
1881                              tny_folder_get_name (folder));
1882                 goto out;
1883         }
1884
1885         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1886
1887  out:
1888         /* Free */
1889         g_object_unref (folder);
1890
1891         /* Notify about operation end */
1892         modest_mail_operation_notify_end (self);
1893 }
1894
1895 static void
1896 on_refresh_folder_status_update (GObject *obj,
1897                                  TnyStatus *status,
1898                                  gpointer user_data)
1899 {
1900         ModestMailOperation *self;
1901         ModestMailOperationPrivate *priv;
1902         ModestMailOperationState *state;
1903
1904         g_return_if_fail (status != NULL);
1905         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1906
1907         self = MODEST_MAIL_OPERATION (user_data);
1908         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1909
1910         priv->done = status->position;
1911         priv->total = status->of_total;
1912
1913         state = modest_mail_operation_clone_state (self);
1914         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1915         g_slice_free (ModestMailOperationState, state);
1916 }
1917
1918 void 
1919 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1920                                        TnyFolder *folder)
1921 {
1922         ModestMailOperationPrivate *priv;
1923
1924         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1925
1926         /* Pick a reference */
1927         g_object_ref (folder);
1928
1929         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1930
1931         /* Get account and set it into mail_operation */
1932         priv->account = tny_folder_get_account (folder);
1933
1934         /* Refresh the folder. TODO: tinymail could issue a status
1935            updates before the callback call then this could happen. We
1936            must review the design */
1937         tny_folder_refresh_async (folder,
1938                                   on_refresh_folder,
1939                                   on_refresh_folder_status_update,
1940                                   self);
1941 }
1942
1943 /**
1944  *
1945  * It's used by the mail operation queue to notify the observers
1946  * attached to that signal that the operation finished. We need to use
1947  * that because tinymail does not give us the progress of a given
1948  * operation when it finishes (it directly calls the operation
1949  * callback).
1950  */
1951 static void
1952 modest_mail_operation_notify_end (ModestMailOperation *self)
1953 {
1954         ModestMailOperationState *state;
1955
1956         /* Notify the observers about the mail opertation end */
1957         state = modest_mail_operation_clone_state (self);
1958         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1959         g_slice_free (ModestMailOperationState, state);
1960
1961         /* Notify the queue */
1962         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1963 }