* Removed an invalid g_object_unref in a mail operation
[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         /* cancel current operation in account */
335         tny_account_cancel (priv->account);
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         ModestTnySendQueue *send_queue;
778
779         info = (UpdateAccountInfo *) thr_user_data;
780         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
781
782         /* Get account and set it into mail_operation */
783         priv->account = g_object_ref (info->account);
784
785         /* Get all the folders We can do it synchronously because
786            we're already running in a different thread than the UI */
787         all_folders = tny_simple_list_new ();
788         query = tny_folder_store_query_new ();
789         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
790         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
791                                       all_folders,
792                                       query,
793                                       &(priv->error));
794         if (priv->error) {
795                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
796                 goto out;
797         }
798
799         iter = tny_list_create_iterator (all_folders);
800         while (!tny_iterator_is_done (iter)) {
801                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
802
803                 recurse_folders (folder, query, all_folders);
804                 tny_iterator_next (iter);
805         }
806         g_object_unref (G_OBJECT (iter));
807
808         /* Update status and notify. We need to call the notification
809            with a source functopm in order to call it from the main
810            loop. We need that in order not to get into trouble with
811            Gtk+. We use a timeout in order to provide more status
812            information, because the sync tinymail call does not
813            provide it for the moment */
814         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
815
816         /* Refresh folders */
817         new_headers = g_ptr_array_new ();
818         iter = tny_list_create_iterator (all_folders);
819         while (!tny_iterator_is_done (iter) && !priv->error) {
820
821                 InternalFolderObserver *observer;
822                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
823
824                 /* Refresh the folder */
825                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
826                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
827                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
828
829                 /* If the retrieve type is headers only do nothing more */
830                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
831                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
832                         TnyIterator *iter;
833
834                         iter = tny_list_create_iterator (observer->new_headers);
835                         while (!tny_iterator_is_done (iter)) {
836                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
837                                 /* Apply per-message size limits */
838                                 if (tny_header_get_message_size (header) < info->max_size)
839                                         g_ptr_array_add (new_headers, g_object_ref (header));
840
841                                 g_object_unref (header);
842                                 tny_iterator_next (iter);
843                         }
844                         g_object_unref (iter);
845                 }
846                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
847                 g_object_unref (observer);
848
849                 if (priv->error)
850                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
851
852                 g_object_unref (G_OBJECT (folder));
853                 tny_iterator_next (iter);
854         }
855         g_object_unref (G_OBJECT (iter));
856         g_source_remove (timeout);
857
858         if (new_headers->len > 0) {
859                 gint msg_num = 0;
860
861                 /* Order by date */
862                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
863
864                 /* Apply message count limit */
865                 /* TODO if the number of messages exceeds the maximum, ask the
866                    user to download them all */
867                 priv->done = 0;
868                 priv->total = MIN (new_headers->len, info->retrieve_limit);
869                 while ((msg_num < priv->total)) {
870
871                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
872                         TnyFolder *folder = tny_header_get_folder (header);
873                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
874                         ModestMailOperationState *state;
875                         ModestPair* pair;
876
877                         priv->done++;
878                         /* We can not just use the mail operation because the
879                            values of done and total could change before the
880                            idle is called */
881                         state = modest_mail_operation_clone_state (info->mail_op);
882                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
883                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
884                                          pair, (GDestroyNotify) modest_pair_free);
885
886                         g_object_unref (msg);
887                         g_object_unref (folder);
888
889                         msg_num++;
890                 }
891                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
892                 g_ptr_array_free (new_headers, FALSE);
893         }
894
895         /* Perform send */
896         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
897         priv->done = 0;
898         priv->total = 0;
899         if (priv->account != NULL) 
900                 g_object_unref (priv->account);
901         priv->account = g_object_ref (info->transport_account);
902         
903         send_queue = modest_runtime_get_send_queue (info->transport_account);
904         if (send_queue) {
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         } else {
909                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
910                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
911                              "cannot create a send queue for %s\n", 
912                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
913                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
914         }
915         
916         /* Check if the operation was a success */
917         if (!priv->error) {
918                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
919
920                 /* Update the last updated key */
921                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
922                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
923                                             MODEST_ACCOUNT_LAST_UPDATED, 
924                                             time(NULL), 
925                                             TRUE);
926         }
927
928  out:
929         /* Notify about operation end. Note that the info could be
930            freed before this idle happens, but the mail operation will
931            be still alive */
932         g_idle_add (notify_update_account_queue, info->mail_op);
933
934         /* Frees */
935         g_object_unref (query);
936         g_object_unref (all_folders);
937         g_object_unref (info->account);
938         g_object_unref (info->transport_account);
939         g_free (info->retrieve_type);
940         g_slice_free (UpdateAccountInfo, info);
941
942         return NULL;
943 }
944
945 gboolean
946 modest_mail_operation_update_account (ModestMailOperation *self,
947                                       const gchar *account_name)
948 {
949         GThread *thread;
950         UpdateAccountInfo *info;
951         ModestMailOperationPrivate *priv;
952         ModestAccountMgr *mgr;
953         TnyStoreAccount *modest_account;
954         TnyTransportAccount *transport_account;
955
956         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
957         g_return_val_if_fail (account_name, FALSE);
958
959         /* Init mail operation. Set total and done to 0, and do not
960            update them, this way the progress objects will know that
961            we have no clue about the number of the objects */
962         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
963         priv->total = 0;
964         priv->done  = 0;
965         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
966         
967         /* Get the Modest account */
968         modest_account = (TnyStoreAccount *)
969                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
970                                                                      account_name,
971                                                                      TNY_ACCOUNT_TYPE_STORE);
972
973         if (!modest_account) {
974                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
975                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
976                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
977                              "cannot get tny store account for %s\n", account_name);
978                 modest_mail_operation_notify_end (self);
979                 return FALSE;
980         }
981
982         /* Get the transport account, we can not do it in the thread
983            due to some problems with dbus */
984         transport_account = (TnyTransportAccount *)
985                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
986                                                                                     account_name);
987         if (!transport_account) {
988                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
989                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
990                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
991                              "cannot get tny transport account for %s\n", account_name);
992                 modest_mail_operation_notify_end (self);
993                 return FALSE;
994         }
995
996         /* Create the helper object */
997         info = g_slice_new (UpdateAccountInfo);
998         info->mail_op = self;
999         info->account = modest_account;
1000         info->transport_account = transport_account;
1001
1002         /* Get the message size limit */
1003         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1004                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1005         if (info->max_size == 0)
1006                 info->max_size = G_MAXINT;
1007         else
1008                 info->max_size = info->max_size * KB;
1009
1010         /* Get per-account retrieval type */
1011         mgr = modest_runtime_get_account_mgr ();
1012         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1013                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1014
1015         /* Get per-account message amount retrieval limit */
1016         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1017                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1018         if (info->retrieve_limit == 0)
1019                 info->retrieve_limit = G_MAXINT;
1020
1021         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1022
1023         return TRUE;
1024 }
1025
1026 /* ******************************************************************* */
1027 /* ************************** STORE  ACTIONS ************************* */
1028 /* ******************************************************************* */
1029
1030
1031 TnyFolder *
1032 modest_mail_operation_create_folder (ModestMailOperation *self,
1033                                      TnyFolderStore *parent,
1034                                      const gchar *name)
1035 {
1036         ModestMailOperationPrivate *priv;
1037         TnyFolder *new_folder = NULL;
1038
1039         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1040         g_return_val_if_fail (name, NULL);
1041         
1042         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1043
1044         /* Check parent */
1045         if (TNY_IS_FOLDER (parent)) {
1046                 /* Check folder rules */
1047                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1048                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1049                         /* Set status failed and set an error */
1050                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1051                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1052                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1053                                      _("mail_in_ui_folder_create_error"));
1054                 }
1055         }
1056
1057         if (!priv->error) {
1058                 /* Create the folder */
1059                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1060                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1061                 if (!priv->error)
1062                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1063         }
1064
1065         /* Notify about operation end */
1066         modest_mail_operation_notify_end (self);
1067
1068         return new_folder;
1069 }
1070
1071 void
1072 modest_mail_operation_remove_folder (ModestMailOperation *self,
1073                                      TnyFolder           *folder,
1074                                      gboolean             remove_to_trash)
1075 {
1076         TnyAccount *account;
1077         ModestMailOperationPrivate *priv;
1078         ModestTnyFolderRules rules;
1079
1080         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1081         g_return_if_fail (TNY_IS_FOLDER (folder));
1082         
1083         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1084         
1085         /* Check folder rules */
1086         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1087         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1088                 /* Set status failed and set an error */
1089                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1090                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1091                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1092                              _("mail_in_ui_folder_delete_error"));
1093                 goto end;
1094         }
1095
1096         /* Get the account */
1097         account = tny_folder_get_account (folder);
1098         priv->account = g_object_ref(account);
1099
1100         /* Delete folder or move to trash */
1101         if (remove_to_trash) {
1102                 TnyFolder *trash_folder = NULL;
1103                 trash_folder = modest_tny_account_get_special_folder (account,
1104                                                                       TNY_FOLDER_TYPE_TRASH);
1105                 /* TODO: error_handling */
1106                  modest_mail_operation_xfer_folder (self, folder,
1107                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1108         } else {
1109                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1110
1111                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1112                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1113
1114                 if (parent)
1115                         g_object_unref (G_OBJECT (parent));
1116         }
1117         g_object_unref (G_OBJECT (account));
1118
1119  end:
1120         /* Notify about operation end */
1121         modest_mail_operation_notify_end (self);
1122 }
1123
1124 static void
1125 transfer_folder_status_cb (GObject *obj,
1126                            TnyStatus *status,
1127                            gpointer user_data)
1128 {
1129         ModestMailOperation *self;
1130         ModestMailOperationPrivate *priv;
1131         ModestMailOperationState *state;
1132
1133         g_return_if_fail (status != NULL);
1134         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1135
1136         self = MODEST_MAIL_OPERATION (user_data);
1137         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1138
1139         if ((status->position == 1) && (status->of_total == 100))
1140                 return;
1141
1142         priv->done = status->position;
1143         priv->total = status->of_total;
1144
1145         state = modest_mail_operation_clone_state (self);
1146         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1147         g_slice_free (ModestMailOperationState, state);
1148 }
1149
1150
1151 static void
1152 transfer_folder_cb (TnyFolder *folder, 
1153                     TnyFolderStore *into, 
1154                     gboolean cancelled, 
1155                     TnyFolder *new_folder, GError **err, 
1156                     gpointer user_data)
1157 {
1158         ModestMailOperation *self = NULL;
1159         ModestMailOperationPrivate *priv = NULL;
1160
1161         self = MODEST_MAIL_OPERATION (user_data);
1162
1163         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1164
1165         if (*err) {
1166                 priv->error = g_error_copy (*err);
1167                 priv->done = 0;
1168                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1169         } else if (cancelled) {
1170                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1171                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1172                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1173                              _("Transference of %s was cancelled."),
1174                              tny_folder_get_name (folder));
1175         } else {
1176                 priv->done = 1;
1177                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1178         }
1179                 
1180         /* Free */
1181         g_object_unref (folder);
1182         g_object_unref (into);
1183         if (new_folder != NULL)
1184                 g_object_unref (new_folder);
1185
1186         /* Notify about operation end */
1187         modest_mail_operation_notify_end (self);
1188 }
1189
1190 void
1191 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1192                                    TnyFolder *folder,
1193                                    TnyFolderStore *parent,
1194                                    gboolean delete_original)
1195 {
1196         ModestMailOperationPrivate *priv = NULL;
1197         ModestTnyFolderRules parent_rules, rules;
1198
1199         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1200         g_return_if_fail (TNY_IS_FOLDER (folder));
1201         g_return_if_fail (TNY_IS_FOLDER (parent));
1202
1203         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1204
1205         /* Get account and set it into mail_operation */
1206         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1207         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1208
1209         /* Get folder rules */
1210         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1211         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1212
1213         if (!TNY_IS_FOLDER_STORE (parent)) {
1214                 
1215         }
1216         
1217         /* The moveable restriction is applied also to copy operation */
1218         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1219                 /* Set status failed and set an error */
1220                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1221                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1222                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1223                              _("mail_in_ui_folder_move_target_error"));
1224
1225                 /* Notify the queue */
1226                 modest_mail_operation_notify_end (self);
1227         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1228                 /* Set status failed and set an error */
1229                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1230                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1231                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1232                              _("FIXME: parent folder does not accept new folders"));
1233
1234                 /* Notify the queue */
1235                 modest_mail_operation_notify_end (self);
1236         } else {
1237                 /* Pick references for async calls */
1238                 g_object_ref (folder);
1239                 g_object_ref (parent);
1240
1241                 /* Move/Copy folder */          
1242                 tny_folder_copy_async (folder,
1243                                        parent,
1244                                        tny_folder_get_name (folder),
1245                                        delete_original,
1246                                        transfer_folder_cb,
1247                                        transfer_folder_status_cb,
1248                                        self);
1249         }
1250 }
1251
1252 void
1253 modest_mail_operation_rename_folder (ModestMailOperation *self,
1254                                      TnyFolder *folder,
1255                                      const gchar *name)
1256 {
1257         ModestMailOperationPrivate *priv;
1258         ModestTnyFolderRules rules;
1259
1260         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1261         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1262         g_return_if_fail (name);
1263         
1264         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1265
1266         /* Get account and set it into mail_operation */
1267         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1268
1269         /* Check folder rules */
1270         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1271         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1272                 /* Set status failed and set an error */
1273                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1274                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1275                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1276                              _("FIXME: unable to rename"));
1277
1278                 /* Notify about operation end */
1279                 modest_mail_operation_notify_end (self);
1280         } else {
1281                 /* Rename. Camel handles folder subscription/unsubscription */
1282                 TnyFolderStore *into;
1283
1284                 into = tny_folder_get_folder_store (folder);
1285                 tny_folder_copy_async (folder, into, name, TRUE,
1286                                  transfer_folder_cb,
1287                                  transfer_folder_status_cb,
1288                                  self);
1289                 if (into)
1290                         g_object_unref (into);
1291                 
1292         }
1293  }
1294
1295 /* ******************************************************************* */
1296 /* **************************  MSG  ACTIONS  ************************* */
1297 /* ******************************************************************* */
1298
1299 void modest_mail_operation_get_msg (ModestMailOperation *self,
1300                                     TnyHeader *header,
1301                                     GetMsgAsyncUserCallback user_callback,
1302                                     gpointer user_data)
1303 {
1304         GetMsgAsyncHelper *helper = NULL;
1305         TnyFolder *folder;
1306         ModestMailOperationPrivate *priv;
1307         
1308         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1309         g_return_if_fail (TNY_IS_HEADER (header));
1310         
1311         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1312         folder = tny_header_get_folder (header);
1313
1314         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1315
1316         /* Get message from folder */
1317         if (folder) {
1318                 /* Get account and set it into mail_operation */
1319                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));            
1320
1321                 helper = g_slice_new0 (GetMsgAsyncHelper);
1322                 helper->mail_op = self;
1323                 helper->user_callback = user_callback;
1324                 helper->pending_ops = 1;
1325                 helper->user_data = user_data;
1326
1327                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1328
1329                 g_object_unref (G_OBJECT (folder));
1330         } else {
1331                 /* Set status failed and set an error */
1332                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1333                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1334                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1335                              _("Error trying to get a message. No folder found for header"));
1336
1337                 /* Notify the queue */
1338                 modest_mail_operation_notify_end (self);
1339         }
1340 }
1341
1342 static void
1343 get_msg_cb (TnyFolder *folder, 
1344             gboolean cancelled, 
1345             TnyMsg *msg, 
1346             GError **error, 
1347             gpointer user_data)
1348 {
1349         GetMsgAsyncHelper *helper = NULL;
1350         ModestMailOperation *self = NULL;
1351         ModestMailOperationPrivate *priv = NULL;
1352
1353         helper = (GetMsgAsyncHelper *) user_data;
1354         g_return_if_fail (helper != NULL);       
1355         self = helper->mail_op;
1356         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1357         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1358         
1359         helper->pending_ops--;
1360
1361         /* Check errors and cancel */
1362         if (*error) {
1363                 priv->error = g_error_copy (*error);
1364                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1365                 goto out;
1366         }
1367         if (cancelled) {
1368                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1369                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1370                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1371                              _("Error trying to refresh the contents of %s"),
1372                              tny_folder_get_name (folder));
1373                 goto out;
1374         }
1375
1376         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1377
1378         /* If user defined callback function was defined, call it */
1379         if (helper->user_callback) {
1380                 helper->user_callback (self, NULL, msg, helper->user_data);
1381         }
1382
1383         /* Free */
1384  out:
1385         if (helper->pending_ops == 0) {
1386                 g_slice_free (GetMsgAsyncHelper, helper);
1387                 
1388                 /* Notify about operation end */
1389                 modest_mail_operation_notify_end (self);        
1390         }
1391 }
1392
1393 static void     
1394 get_msg_status_cb (GObject *obj,
1395                    TnyStatus *status,  
1396                    gpointer user_data)
1397 {
1398         GetMsgAsyncHelper *helper = NULL;
1399         ModestMailOperation *self;
1400         ModestMailOperationPrivate *priv;
1401         ModestMailOperationState *state;
1402
1403         g_return_if_fail (status != NULL);
1404         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1405
1406         helper = (GetMsgAsyncHelper *) user_data;
1407         g_return_if_fail (helper != NULL);       
1408
1409         self = helper->mail_op;
1410         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1411
1412         if ((status->position == 1) && (status->of_total == 100))
1413                 return;
1414
1415         priv->done = 1;
1416         priv->total = 1;
1417
1418         state = modest_mail_operation_clone_state (self);
1419         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1420         g_slice_free (ModestMailOperationState, state);
1421 }
1422
1423 /****************************************************/
1424 typedef struct {
1425         ModestMailOperation *mail_op;
1426         TnyList *headers;
1427         GetMsgAsyncUserCallback user_callback;
1428         gpointer user_data;
1429         GDestroyNotify notify;
1430 } GetFullMsgsInfo;
1431
1432 typedef struct {
1433         GetMsgAsyncUserCallback user_callback;
1434         TnyHeader *header;
1435         TnyMsg *msg;
1436         gpointer user_data;
1437         ModestMailOperation *mail_op;
1438 } NotifyGetMsgsInfo;
1439
1440
1441 /* 
1442  * Used by get_msgs_full_thread to call the user_callback for each
1443  * message that has been read
1444  */
1445 static gboolean
1446 notify_get_msgs_full (gpointer data)
1447 {
1448         NotifyGetMsgsInfo *info;
1449
1450         info = (NotifyGetMsgsInfo *) data;      
1451
1452         /* Call the user callback */
1453         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1454
1455         g_slice_free (NotifyGetMsgsInfo, info);
1456
1457         return FALSE;
1458 }
1459
1460 /* 
1461  * Used by get_msgs_full_thread to free al the thread resources and to
1462  * call the destroy function for the passed user_data
1463  */
1464 static gboolean
1465 get_msgs_full_destroyer (gpointer data)
1466 {
1467         GetFullMsgsInfo *info;
1468
1469         info = (GetFullMsgsInfo *) data;
1470
1471         if (info->notify)
1472                 info->notify (info->user_data);
1473
1474         /* free */
1475         g_object_unref (info->headers);
1476         g_slice_free (GetFullMsgsInfo, info);
1477
1478         return FALSE;
1479 }
1480
1481 static gpointer
1482 get_msgs_full_thread (gpointer thr_user_data)
1483 {
1484         GetFullMsgsInfo *info;
1485         ModestMailOperationPrivate *priv = NULL;
1486         TnyIterator *iter = NULL;
1487         
1488         info = (GetFullMsgsInfo *) thr_user_data;       
1489         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1490
1491         iter = tny_list_create_iterator (info->headers);
1492         while (!tny_iterator_is_done (iter)) { 
1493                 TnyHeader *header;
1494                 TnyFolder *folder;
1495                 
1496                 header = TNY_HEADER (tny_iterator_get_current (iter));
1497                 folder = tny_header_get_folder (header);
1498                                 
1499                 /* Get message from folder */
1500                 if (folder) {
1501                         TnyMsg *msg;
1502                         /* The callback will call it per each header */
1503                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1504
1505                         if (msg) {
1506                                 ModestMailOperationState *state;
1507                                 ModestPair *pair;
1508
1509                                 priv->done++;
1510
1511                                 /* notify progress */
1512                                 state = modest_mail_operation_clone_state (info->mail_op);
1513                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1514                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1515                                                  pair, (GDestroyNotify) modest_pair_free);
1516
1517                                 /* The callback is the responsible for
1518                                    freeing the message */
1519                                 if (info->user_callback) {
1520                                         NotifyGetMsgsInfo *info_notify;
1521                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1522                                         info_notify->user_callback = info->user_callback;
1523                                         info_notify->mail_op = info->mail_op;
1524                                         info_notify->header = g_object_ref (header);
1525                                         info_notify->msg = g_object_ref (msg);
1526                                         info_notify->user_data = info->user_data;
1527                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1528                                                          notify_get_msgs_full, 
1529                                                          info_notify, NULL);
1530                                 }
1531                                 g_object_unref (msg);
1532                         }
1533                 } else {
1534                         /* Set status failed and set an error */
1535                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1536                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1537                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1538                                      "Error trying to get a message. No folder found for header");
1539                 }
1540                 g_object_unref (header);                
1541                 tny_iterator_next (iter);
1542         }
1543
1544         /* Set operation status */
1545         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1546                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1547
1548         /* Notify about operation end */
1549         g_idle_add (notify_update_account_queue, info->mail_op);
1550
1551         /* Free thread resources. Will be called after all previous idles */
1552         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1553
1554         return NULL;
1555 }
1556
1557 void 
1558 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1559                                      TnyList *header_list, 
1560                                      GetMsgAsyncUserCallback user_callback,
1561                                      gpointer user_data,
1562                                      GDestroyNotify notify)
1563 {
1564         TnyHeader *header = NULL;
1565         TnyFolder *folder = NULL;
1566         GThread *thread;
1567         ModestMailOperationPrivate *priv = NULL;
1568         GetFullMsgsInfo *info = NULL;
1569         gboolean size_ok = TRUE;
1570         gint max_size;
1571         TnyIterator *iter = NULL;
1572         
1573         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1574         
1575         /* Init mail operation */
1576         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1577         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1578         priv->done = 0;
1579         priv->total = tny_list_get_length(header_list);
1580
1581         /* Get account and set it into mail_operation */
1582         if (tny_list_get_length (header_list) > 1) {
1583                 iter = tny_list_create_iterator (header_list);          
1584                 header = TNY_HEADER (tny_iterator_get_current (iter));
1585                 folder = tny_header_get_folder (header);                
1586                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1587                 g_object_unref (header);
1588                 g_object_unref (folder);
1589         }
1590
1591         /* Get msg size limit */
1592         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1593                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1594                                          &(priv->error));
1595         if (priv->error) {
1596                 g_clear_error (&(priv->error));
1597                 max_size = G_MAXINT;
1598         } else {
1599                 max_size = max_size * KB;
1600         }
1601
1602         /* Check message size limits. If there is only one message
1603            always retrieve it */
1604         if (iter != NULL) {
1605                 while (!tny_iterator_is_done (iter) && size_ok) {
1606                         header = TNY_HEADER (tny_iterator_get_current (iter));
1607                         if (tny_header_get_message_size (header) >= max_size)
1608                                 size_ok = FALSE;
1609                         g_object_unref (header);
1610                         tny_iterator_next (iter);
1611                 }
1612                 g_object_unref (iter);
1613         }
1614
1615         if (size_ok) {
1616                 /* Create the info */
1617                 info = g_slice_new0 (GetFullMsgsInfo);
1618                 info->mail_op = self;
1619                 info->user_callback = user_callback;
1620                 info->user_data = user_data;
1621                 info->headers = g_object_ref (header_list);
1622                 info->notify = notify;
1623
1624                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1625         } else {
1626                 /* Set status failed and set an error */
1627                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1628                 /* FIXME: the error msg is different for pop */
1629                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1630                              MODEST_MAIL_OPERATION_ERROR_SIZE_LIMIT,
1631                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1632                 /* Remove from queue and free resources */
1633                 modest_mail_operation_notify_end (self);
1634                 if (notify)
1635                         notify (user_data);
1636         }
1637 }
1638
1639
1640 void 
1641 modest_mail_operation_remove_msg (ModestMailOperation *self,
1642                                   TnyHeader *header,
1643                                   gboolean remove_to_trash)
1644 {
1645         TnyFolder *folder;
1646         ModestMailOperationPrivate *priv;
1647
1648         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1649         g_return_if_fail (TNY_IS_HEADER (header));
1650
1651         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1652         folder = tny_header_get_folder (header);
1653
1654         /* Get account and set it into mail_operation */
1655         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1656
1657         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1658
1659         /* Delete or move to trash */
1660         if (remove_to_trash) {
1661                 TnyFolder *trash_folder;
1662                 TnyStoreAccount *store_account;
1663
1664                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1665                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1666                                                                       TNY_FOLDER_TYPE_TRASH);
1667                 if (trash_folder) {
1668                         TnyList *headers;
1669
1670                         /* Create list */
1671                         headers = tny_simple_list_new ();
1672                         tny_list_append (headers, G_OBJECT (header));
1673                         g_object_unref (header);
1674
1675                         /* Move to trash */
1676                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1677                         g_object_unref (headers);
1678 /*                      g_object_unref (trash_folder); */
1679                 } else {
1680                         ModestMailOperationPrivate *priv;
1681
1682                         /* Set status failed and set an error */
1683                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1684                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1685                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1686                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1687                                      _("Error trying to delete a message. Trash folder not found"));
1688                 }
1689
1690                 g_object_unref (G_OBJECT (store_account));
1691         } else {
1692                 tny_folder_remove_msg (folder, header, &(priv->error));
1693                 if (!priv->error)
1694                         tny_folder_sync(folder, TRUE, &(priv->error));
1695         }
1696
1697         /* Set status */
1698         if (!priv->error)
1699                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1700         else
1701                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1702
1703         /* Free */
1704         g_object_unref (G_OBJECT (folder));
1705
1706         /* Notify about operation end */
1707         modest_mail_operation_notify_end (self);
1708 }
1709
1710 static void
1711 transfer_msgs_status_cb (GObject *obj,
1712                          TnyStatus *status,  
1713                          gpointer user_data)
1714 {
1715         XFerMsgAsyncHelper *helper = NULL;
1716         ModestMailOperation *self;
1717         ModestMailOperationPrivate *priv;
1718         ModestMailOperationState *state;
1719
1720
1721         g_return_if_fail (status != NULL);
1722         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1723
1724         helper = (XFerMsgAsyncHelper *) user_data;
1725         g_return_if_fail (helper != NULL);       
1726
1727         self = helper->mail_op;
1728         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1729
1730         if ((status->position == 1) && (status->of_total == 100))
1731                 return;
1732
1733         priv->done = status->position;
1734         priv->total = status->of_total;
1735
1736         state = modest_mail_operation_clone_state (self);
1737         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1738         g_slice_free (ModestMailOperationState, state);
1739 }
1740
1741
1742 static void
1743 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1744 {
1745         XFerMsgAsyncHelper *helper;
1746         ModestMailOperation *self;
1747         ModestMailOperationPrivate *priv;
1748
1749         helper = (XFerMsgAsyncHelper *) user_data;
1750         self = helper->mail_op;
1751
1752         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1753
1754         if (*err) {
1755                 priv->error = g_error_copy (*err);
1756                 priv->done = 0;
1757                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1758         } else if (cancelled) {
1759                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1760                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1761                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1762                              _("Error trying to refresh the contents of %s"),
1763                              tny_folder_get_name (folder));
1764         } else {
1765                 priv->done = 1;
1766                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1767         }
1768
1769         /* If user defined callback function was defined, call it */
1770         if (helper->user_callback) {
1771                 helper->user_callback (priv->source, helper->user_data);
1772         }
1773
1774         /* Free */
1775         g_object_unref (helper->headers);
1776         g_object_unref (helper->dest_folder);
1777         g_object_unref (helper->mail_op);
1778         g_slice_free   (XFerMsgAsyncHelper, helper);
1779         g_object_unref (folder);
1780
1781         /* Notify about operation end */
1782         modest_mail_operation_notify_end (self);
1783 }
1784
1785 void
1786 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1787                                  TnyList *headers, 
1788                                  TnyFolder *folder, 
1789                                  gboolean delete_original,
1790                                  XferMsgsAsynUserCallback user_callback,
1791                                  gpointer user_data)
1792 {
1793         ModestMailOperationPrivate *priv;
1794         TnyIterator *iter;
1795         TnyFolder *src_folder;
1796         XFerMsgAsyncHelper *helper;
1797         TnyHeader *header;
1798         ModestTnyFolderRules rules;
1799
1800         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1801         g_return_if_fail (TNY_IS_LIST (headers));
1802         g_return_if_fail (TNY_IS_FOLDER (folder));
1803
1804         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1805         priv->total = 1;
1806         priv->done = 0;
1807         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1808
1809         /* Apply folder rules */
1810         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1811
1812         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1813                 /* Set status failed and set an error */
1814                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1815                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1816                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1817                              _("FIXME: folder does not accept msgs"));
1818                 /* Notify the queue */
1819                 modest_mail_operation_notify_end (self);
1820                 return;
1821         }
1822
1823         /* Create the helper */
1824         helper = g_slice_new0 (XFerMsgAsyncHelper);
1825         helper->mail_op = g_object_ref(self);
1826         helper->dest_folder = g_object_ref(folder);
1827         helper->headers = g_object_ref(headers);
1828         helper->user_callback = user_callback;
1829         helper->user_data = user_data;
1830
1831         /* Get source folder */
1832         iter = tny_list_create_iterator (headers);
1833         header = TNY_HEADER (tny_iterator_get_current (iter));
1834         src_folder = tny_header_get_folder (header);
1835         g_object_unref (header);
1836         g_object_unref (iter);
1837
1838         /* Get account and set it into mail_operation */
1839         priv->account = tny_folder_get_account (src_folder);
1840
1841         /* Transfer messages */
1842         tny_folder_transfer_msgs_async (src_folder, 
1843                                         headers, 
1844                                         folder, 
1845                                         delete_original, 
1846                                         transfer_msgs_cb, 
1847                                         transfer_msgs_status_cb,
1848                                         helper);
1849 }
1850
1851
1852 static void
1853 on_refresh_folder (TnyFolder   *folder, 
1854                    gboolean     cancelled, 
1855                    GError     **error,
1856                    gpointer     user_data)
1857 {
1858         ModestMailOperation *self;
1859         ModestMailOperationPrivate *priv;
1860
1861         self = MODEST_MAIL_OPERATION (user_data);
1862         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1863
1864         if (*error) {
1865                 priv->error = g_error_copy (*error);
1866                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1867                 goto out;
1868         }
1869
1870         if (cancelled) {
1871                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1872                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1873                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1874                              _("Error trying to refresh the contents of %s"),
1875                              tny_folder_get_name (folder));
1876                 goto out;
1877         }
1878
1879         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1880
1881  out:
1882         /* Free */
1883         g_object_unref (folder);
1884
1885         /* Notify about operation end */
1886         modest_mail_operation_notify_end (self);
1887 }
1888
1889 static void
1890 on_refresh_folder_status_update (GObject *obj,
1891                                  TnyStatus *status,
1892                                  gpointer user_data)
1893 {
1894         ModestMailOperation *self;
1895         ModestMailOperationPrivate *priv;
1896         ModestMailOperationState *state;
1897
1898         g_return_if_fail (status != NULL);
1899         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1900
1901         self = MODEST_MAIL_OPERATION (user_data);
1902         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1903
1904         priv->done = status->position;
1905         priv->total = status->of_total;
1906
1907         state = modest_mail_operation_clone_state (self);
1908         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1909         g_slice_free (ModestMailOperationState, state);
1910 }
1911
1912 void 
1913 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1914                                        TnyFolder *folder)
1915 {
1916         ModestMailOperationPrivate *priv;
1917
1918         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1919
1920         /* Pick a reference */
1921         g_object_ref (folder);
1922
1923         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1924
1925         /* Get account and set it into mail_operation */
1926         priv->account = tny_folder_get_account (folder);
1927
1928         /* Refresh the folder. TODO: tinymail could issue a status
1929            updates before the callback call then this could happen. We
1930            must review the design */
1931         tny_folder_refresh_async (folder,
1932                                   on_refresh_folder,
1933                                   on_refresh_folder_status_update,
1934                                   self);
1935 }
1936
1937 /**
1938  *
1939  * It's used by the mail operation queue to notify the observers
1940  * attached to that signal that the operation finished. We need to use
1941  * that because tinymail does not give us the progress of a given
1942  * operation when it finishes (it directly calls the operation
1943  * callback).
1944  */
1945 static void
1946 modest_mail_operation_notify_end (ModestMailOperation *self)
1947 {
1948         ModestMailOperationState *state;
1949
1950         /* Notify the observers about the mail opertation end */
1951         state = modest_mail_operation_clone_state (self);
1952         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1953         g_slice_free (ModestMailOperationState, state);
1954
1955         /* Notify the queue */
1956         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1957 }