Work to improve drafts folder management (fixes NB#56483).
[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                                      TnyMsg *draft_msg,
477                                      const gchar *from,  const gchar *to,
478                                      const gchar *cc,  const gchar *bcc,
479                                      const gchar *subject, const gchar *plain_body,
480                                      const gchar *html_body,
481                                      const GList *attachments_list,
482                                      TnyHeaderFlags priority_flags)
483 {
484         TnyMsg *new_msg = NULL;
485         TnyFolder *folder = NULL;
486         ModestMailOperationPrivate *priv = NULL;
487
488         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
489         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
490
491         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
492
493         /* Get account and set it into mail_operation */
494         priv->account = g_object_ref (transport_account);
495
496         /* Check parametters */
497         if (to == NULL) {
498                 /* Set status failed and set an error */
499                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
500                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
501                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
502                              _("Error trying to send a mail. You need to set at least one recipient"));
503                 return;
504         }
505
506         if (html_body == NULL) {
507                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
508         } else {
509                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
510         }
511         if (!new_msg) {
512                 g_printerr ("modest: failed to create a new msg\n");
513                 return;
514         }
515
516         /* TODO: add priority handling. It's received in the priority_flags operator, and
517            it should have effect in the sending operation */
518
519         /* Call mail operation */
520         modest_mail_operation_send_mail (self, transport_account, new_msg);
521
522         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
523         if (folder) {
524                 if (draft_msg != NULL) {
525                         TnyHeader *header = tny_msg_get_header (draft_msg);
526                         tny_folder_remove_msg (folder, header, NULL);
527                         g_object_unref (header);
528                 }
529         }
530
531         /* Free */
532         g_object_unref (G_OBJECT (new_msg));
533 }
534
535 void
536 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
537                                       TnyTransportAccount *transport_account,
538                                       TnyMsg *draft_msg,
539                                       const gchar *from,  const gchar *to,
540                                       const gchar *cc,  const gchar *bcc,
541                                       const gchar *subject, const gchar *plain_body,
542                                       const gchar *html_body,
543                                       const GList *attachments_list,
544                                       TnyHeaderFlags priority_flags)
545 {
546         TnyMsg *msg = NULL;
547         TnyFolder *folder = NULL;
548         ModestMailOperationPrivate *priv = NULL;
549
550         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
551         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
552
553         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
554
555         /* Get account and set it into mail_operation */
556         priv->account = g_object_ref (transport_account);
557
558         if (html_body == NULL) {
559                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
560         } else {
561                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
562         }
563         if (!msg) {
564                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
565                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
566                              "modest: failed to create a new msg\n");
567                 goto end;
568         }
569
570         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
571         if (!folder) {
572                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
573                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
574                              "modest: failed to create a new msg\n");
575                 goto end;
576         }
577
578         if (draft_msg != NULL) {
579                 TnyHeader *header = tny_msg_get_header (draft_msg);
580                 tny_folder_remove_msg (folder, header, NULL);
581                 g_object_unref (header);
582         }
583         
584         tny_folder_add_msg (folder, msg, &(priv->error));
585         if (priv->error)
586                 goto end;
587
588 end:
589         if (msg)
590                 g_object_unref (G_OBJECT(msg));
591         if (folder)
592                 g_object_unref (G_OBJECT(folder));
593
594         modest_mail_operation_notify_end (self);
595 }
596
597 typedef struct 
598 {
599         ModestMailOperation *mail_op;
600         TnyStoreAccount *account;
601         TnyTransportAccount *transport_account;
602         gint max_size;
603         gint retrieve_limit;
604         gchar *retrieve_type;
605 } UpdateAccountInfo;
606
607 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
608 /* We use this folder observer to track the headers that have been
609  * added to a folder */
610 typedef struct {
611         GObject parent;
612         TnyList *new_headers;
613 } InternalFolderObserver;
614
615 typedef struct {
616         GObjectClass parent;
617 } InternalFolderObserverClass;
618
619 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
620
621 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
622                          internal_folder_observer,
623                          G_TYPE_OBJECT,
624                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
625
626
627 static void
628 foreach_add_item (gpointer header, gpointer user_data)
629 {
630         /* printf("DEBUG: %s: header subject=%s\n", 
631          * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
632          */
633         tny_list_prepend (TNY_LIST (user_data), 
634                           g_object_ref (G_OBJECT (header)));
635 }
636
637 /* This is the method that looks for new messages in a folder */
638 static void
639 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
640 {
641         InternalFolderObserver *derived = (InternalFolderObserver *)self;
642         
643         TnyFolderChangeChanged changed;
644
645         changed = tny_folder_change_get_changed (change);
646
647         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
648                 TnyList *list;
649
650                 /* Get added headers */
651                 list = tny_simple_list_new ();
652                 tny_folder_change_get_added_headers (change, list);
653
654                 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n", 
655                  *      __FUNCTION__, tny_list_get_length(list));
656                  */
657                  
658                 /* Add them to the folder observer */
659                 tny_list_foreach (list, foreach_add_item, 
660                                   derived->new_headers);
661
662                 g_object_unref (G_OBJECT (list));
663         }
664 }
665
666 static void
667 internal_folder_observer_init (InternalFolderObserver *self) 
668 {
669         self->new_headers = tny_simple_list_new ();
670 }
671 static void
672 internal_folder_observer_finalize (GObject *object) 
673 {
674         InternalFolderObserver *self;
675
676         self = (InternalFolderObserver *) object;
677         g_object_unref (self->new_headers);
678
679         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
680 }
681 static void
682 tny_folder_observer_init (TnyFolderObserverIface *iface) 
683 {
684         iface->update_func = internal_folder_observer_update;
685 }
686 static void
687 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
688 {
689         GObjectClass *object_class;
690
691         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
692         object_class = (GObjectClass*) klass;
693         object_class->finalize = internal_folder_observer_finalize;
694 }
695
696 /*****************/
697
698 static void
699 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
700 {
701         TnyIterator *iter;
702         TnyList *folders = tny_simple_list_new ();
703
704         tny_folder_store_get_folders (store, folders, query, NULL);
705         iter = tny_list_create_iterator (folders);
706
707         while (!tny_iterator_is_done (iter)) {
708
709                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
710
711                 tny_list_prepend (all_folders, G_OBJECT (folder));
712                 recurse_folders (folder, query, all_folders);    
713                 g_object_unref (G_OBJECT (folder));
714
715                 tny_iterator_next (iter);
716         }
717          g_object_unref (G_OBJECT (iter));
718          g_object_unref (G_OBJECT (folders));
719 }
720
721 /* 
722  * Issues the "progress-changed" signal. The timer won't be removed,
723  * so you must call g_source_remove to stop the signal emission
724  */
725 static gboolean
726 idle_notify_progress (gpointer data)
727 {
728         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
729         ModestMailOperationState *state;
730
731         state = modest_mail_operation_clone_state (mail_op);
732         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
733         g_slice_free (ModestMailOperationState, state);
734
735         return TRUE;
736 }
737
738 /* 
739  * Issues the "progress-changed" signal and removes the timer. It uses
740  * a lock to ensure that the progress information of the mail
741  * operation is not modified while there are notifications pending
742  */
743 static gboolean
744 idle_notify_progress_once (gpointer data)
745 {
746         ModestPair *pair;
747
748         pair = (ModestPair *) data;
749
750         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
751
752         /* Free the state and the reference to the mail operation */
753         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
754         g_object_unref (pair->first);
755
756         return FALSE;
757 }
758
759 /* 
760  * Used by update_account_thread to notify the queue from the main
761  * loop. We call it inside an idle call to achieve that
762  */
763 static gboolean
764 notify_update_account_queue (gpointer data)
765 {
766         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
767
768         modest_mail_operation_notify_end (mail_op);
769         g_object_unref (mail_op);
770
771         return FALSE;
772 }
773
774 static int
775 compare_headers_by_date (gconstpointer a, 
776                          gconstpointer b)
777 {
778         TnyHeader **header1, **header2;
779         time_t sent1, sent2;
780
781         header1 = (TnyHeader **) a;
782         header2 = (TnyHeader **) b;
783
784         sent1 = tny_header_get_date_sent (*header1);
785         sent2 = tny_header_get_date_sent (*header2);
786
787         /* We want the most recent ones (greater time_t) at the
788            beginning */
789         if (sent1 < sent2)
790                 return 1;
791         else
792                 return -1;
793 }
794
795 static gpointer
796 update_account_thread (gpointer thr_user_data)
797 {
798         UpdateAccountInfo *info;
799         TnyList *all_folders = NULL;
800         GPtrArray *new_headers;
801         TnyIterator *iter = NULL;
802         TnyFolderStoreQuery *query = NULL;
803         ModestMailOperationPrivate *priv;
804         ModestTnySendQueue *send_queue;
805
806         info = (UpdateAccountInfo *) thr_user_data;
807         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
808
809         /* Get account and set it into mail_operation */
810         priv->account = g_object_ref (info->account);
811
812         /* Get all the folders. We can do it synchronously because
813            we're already running in a different thread than the UI */
814         all_folders = tny_simple_list_new ();
815         query = tny_folder_store_query_new ();
816         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
817         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
818                                       all_folders,
819                                       query,
820                                       &(priv->error));
821         if (priv->error) {
822                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
823                 goto out;
824         }
825
826         iter = tny_list_create_iterator (all_folders);
827         while (!tny_iterator_is_done (iter)) {
828                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
829
830                 recurse_folders (folder, query, all_folders);
831                 tny_iterator_next (iter);
832         }
833         g_object_unref (G_OBJECT (iter));
834
835         /* Update status and notify. We need to call the notification
836            with a source function in order to call it from the main
837            loop. We need that in order not to get into trouble with
838            Gtk+. We use a timeout in order to provide more status
839            information, because the sync tinymail call does not
840            provide it for the moment */
841         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
842
843         /* Refresh folders */
844         new_headers = g_ptr_array_new ();
845         iter = tny_list_create_iterator (all_folders);
846         while (!tny_iterator_is_done (iter) && !priv->error) {
847
848                 InternalFolderObserver *observer;
849                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
850
851                 /* Refresh the folder */
852                 /* Our observer receives notification of new emails during folder refreshes,
853                  * so we can use observer->new_headers.
854                  * TODO: This does not seem to be providing accurate numbers.
855                  * Possibly the observer is notified asynchronously.
856                  */
857                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
858                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
859                 
860                 /* This gets the status information (headers) from the server.
861                  * We use the blocking version, because we are already in a separate 
862                  * thread.
863                  */
864                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
865
866                 /* If the retrieve type is headers only do nothing more */
867                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
868                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
869                         TnyIterator *iter;
870
871                         iter = tny_list_create_iterator (observer->new_headers);
872                         while (!tny_iterator_is_done (iter)) {
873                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
874                                 /* printf ("  DEBUG1.2 %s: checking size: account=%s, subject=%s\n", 
875                                  *      __FUNCTION__, tny_account_get_id (priv->account), 
876                                  * tny_header_get_subject (header));
877                                  */
878                                  
879                                 /* Apply per-message size limits */
880                                 if (tny_header_get_message_size (header) < info->max_size)
881                                         g_ptr_array_add (new_headers, g_object_ref (header));
882
883                                 g_object_unref (header);
884                                 tny_iterator_next (iter);
885                         }
886                         g_object_unref (iter);
887                 }
888                 
889                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
890                 g_object_unref (observer);
891                 observer = NULL;
892
893                 if (priv->error)
894                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
895
896                 g_object_unref (G_OBJECT (folder));
897                 tny_iterator_next (iter);
898         }
899         g_object_unref (G_OBJECT (iter));
900         g_source_remove (timeout);
901
902         if (new_headers->len > 0) {
903                 gint msg_num = 0;
904
905                 /* Order by date */
906                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
907
908                 /* Apply message count limit */
909                 /* If the number of messages exceeds the maximum, ask the
910                  * user to download them all,
911                  * as per the UI spec "Retrieval Limits" section in 4.4: 
912                  */
913                 printf ("DEBUG: %s: account=%s, len=%d, retrieve_limit = %d\n", __FUNCTION__, 
914                         tny_account_get_id (priv->account), new_headers->len, info->retrieve_limit);
915                 if (new_headers->len > info->retrieve_limit) {
916                         /* TODO: Ask the user, instead of just failing, showing mail_nc_msg_count_limit_exceeded, 
917                          * with 'Get all' and 'Newest only' buttons. */
918                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
919                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
920                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
921                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
922                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
923                         goto out;
924                 }
925                 
926                 priv->done = 0;
927                 priv->total = MIN (new_headers->len, info->retrieve_limit);
928                 while (msg_num < priv->total) {
929
930                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
931                         TnyFolder *folder = tny_header_get_folder (header);
932                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
933                         ModestMailOperationState *state;
934                         ModestPair* pair;
935
936                         priv->done++;
937                         /* We can not just use the mail operation because the
938                            values of done and total could change before the
939                            idle is called */
940                         state = modest_mail_operation_clone_state (info->mail_op);
941                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
942                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
943                                          pair, (GDestroyNotify) modest_pair_free);
944
945                         g_object_unref (msg);
946                         g_object_unref (folder);
947
948                         msg_num++;
949                 }
950                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
951                 g_ptr_array_free (new_headers, FALSE);
952         }
953
954         /* Perform send */
955         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
956         priv->done = 0;
957         priv->total = 0;
958         if (priv->account != NULL) 
959                 g_object_unref (priv->account);
960         priv->account = g_object_ref (info->transport_account);
961         
962         send_queue = modest_runtime_get_send_queue (info->transport_account);
963         if (send_queue) {
964                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
965                 modest_tny_send_queue_try_to_send (send_queue);
966                 g_source_remove (timeout);
967         } else {
968                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
969                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
970                              "cannot create a send queue for %s\n", 
971                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
972                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
973         }
974         
975         /* Check if the operation was a success */
976         if (!priv->error) {
977                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
978
979                 /* Update the last updated key */
980                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
981                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
982                                             MODEST_ACCOUNT_LAST_UPDATED, 
983                                             time(NULL), 
984                                             TRUE);
985         }
986
987  out:
988         /* Notify about operation end. Note that the info could be
989            freed before this idle happens, but the mail operation will
990            be still alive */
991         g_idle_add (notify_update_account_queue, info->mail_op);
992
993         /* Frees */
994         g_object_unref (query);
995         g_object_unref (all_folders);
996         g_object_unref (info->account);
997         g_object_unref (info->transport_account);
998         g_free (info->retrieve_type);
999         g_slice_free (UpdateAccountInfo, info);
1000
1001         return NULL;
1002 }
1003
1004 gboolean
1005 modest_mail_operation_update_account (ModestMailOperation *self,
1006                                       const gchar *account_name)
1007 {
1008         GThread *thread;
1009         UpdateAccountInfo *info;
1010         ModestMailOperationPrivate *priv;
1011         ModestAccountMgr *mgr;
1012         TnyStoreAccount *modest_account;
1013         TnyTransportAccount *transport_account;
1014
1015         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1016         g_return_val_if_fail (account_name, FALSE);
1017
1018         /* Make sure that we have a connection, and request one 
1019          * if necessary:
1020          * TODO: Is there some way to trigger this for every attempt to 
1021          * use the network? */
1022         if (!modest_platform_connect_and_wait(NULL))
1023                 return FALSE;
1024         
1025         /* Init mail operation. Set total and done to 0, and do not
1026            update them, this way the progress objects will know that
1027            we have no clue about the number of the objects */
1028         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1029         priv->total = 0;
1030         priv->done  = 0;
1031         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1032         
1033         /* Get the Modest account */
1034         modest_account = (TnyStoreAccount *)
1035                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
1036                                                                      account_name,
1037                                                                      TNY_ACCOUNT_TYPE_STORE);
1038
1039         if (!modest_account) {
1040                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1041                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1042                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1043                              "cannot get tny store account for %s\n", account_name);
1044                 modest_mail_operation_notify_end (self);
1045                 return FALSE;
1046         }
1047
1048         /* Get the transport account, we can not do it in the thread
1049            due to some problems with dbus */
1050         transport_account = (TnyTransportAccount *)
1051                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1052                                                                                     account_name);
1053         if (!transport_account) {
1054                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1055                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1056                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1057                              "cannot get tny transport account for %s\n", account_name);
1058                 modest_mail_operation_notify_end (self);
1059                 return FALSE;
1060         }
1061
1062         /* Create the helper object */
1063         info = g_slice_new (UpdateAccountInfo);
1064         info->mail_op = self;
1065         info->account = modest_account;
1066         info->transport_account = transport_account;
1067
1068         /* Get the message size limit */
1069         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1070                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1071         if (info->max_size == 0)
1072                 info->max_size = G_MAXINT;
1073         else
1074                 info->max_size = info->max_size * KB;
1075
1076         /* Get per-account retrieval type */
1077         mgr = modest_runtime_get_account_mgr ();
1078         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1079                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1080
1081         /* Get per-account message amount retrieval limit */
1082         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1083                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1084         if (info->retrieve_limit == 0)
1085                 info->retrieve_limit = G_MAXINT;
1086                 
1087         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1088
1089         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1090
1091         return TRUE;
1092 }
1093
1094 /* ******************************************************************* */
1095 /* ************************** STORE  ACTIONS ************************* */
1096 /* ******************************************************************* */
1097
1098
1099 TnyFolder *
1100 modest_mail_operation_create_folder (ModestMailOperation *self,
1101                                      TnyFolderStore *parent,
1102                                      const gchar *name)
1103 {
1104         ModestMailOperationPrivate *priv;
1105         TnyFolder *new_folder = NULL;
1106
1107         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1108         g_return_val_if_fail (name, NULL);
1109         
1110         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1111
1112         /* Check parent */
1113         if (TNY_IS_FOLDER (parent)) {
1114                 /* Check folder rules */
1115                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1116                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1117                         /* Set status failed and set an error */
1118                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1119                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1120                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1121                                      _("mail_in_ui_folder_create_error"));
1122                 }
1123         }
1124
1125         if (!priv->error) {
1126                 /* Create the folder */
1127                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1128                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1129                 if (!priv->error)
1130                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1131         }
1132
1133         /* Notify about operation end */
1134         modest_mail_operation_notify_end (self);
1135
1136         return new_folder;
1137 }
1138
1139 void
1140 modest_mail_operation_remove_folder (ModestMailOperation *self,
1141                                      TnyFolder           *folder,
1142                                      gboolean             remove_to_trash)
1143 {
1144         TnyAccount *account;
1145         ModestMailOperationPrivate *priv;
1146         ModestTnyFolderRules rules;
1147
1148         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1149         g_return_if_fail (TNY_IS_FOLDER (folder));
1150         
1151         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1152         
1153         /* Check folder rules */
1154         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1155         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1156                 /* Set status failed and set an error */
1157                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1158                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1159                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1160                              _("mail_in_ui_folder_delete_error"));
1161                 goto end;
1162         }
1163
1164         /* Get the account */
1165         account = tny_folder_get_account (folder);
1166         priv->account = g_object_ref(account);
1167
1168         /* Delete folder or move to trash */
1169         if (remove_to_trash) {
1170                 TnyFolder *trash_folder = NULL;
1171                 trash_folder = modest_tny_account_get_special_folder (account,
1172                                                                       TNY_FOLDER_TYPE_TRASH);
1173                 /* TODO: error_handling */
1174                  modest_mail_operation_xfer_folder (self, folder,
1175                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1176         } else {
1177                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1178
1179                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1180                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1181
1182                 if (parent)
1183                         g_object_unref (G_OBJECT (parent));
1184         }
1185         g_object_unref (G_OBJECT (account));
1186
1187  end:
1188         /* Notify about operation end */
1189         modest_mail_operation_notify_end (self);
1190 }
1191
1192 static void
1193 transfer_folder_status_cb (GObject *obj,
1194                            TnyStatus *status,
1195                            gpointer user_data)
1196 {
1197         ModestMailOperation *self;
1198         ModestMailOperationPrivate *priv;
1199         ModestMailOperationState *state;
1200
1201         g_return_if_fail (status != NULL);
1202         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1203
1204         self = MODEST_MAIL_OPERATION (user_data);
1205         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1206
1207         if ((status->position == 1) && (status->of_total == 100))
1208                 return;
1209
1210         priv->done = status->position;
1211         priv->total = status->of_total;
1212
1213         state = modest_mail_operation_clone_state (self);
1214         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1215         g_slice_free (ModestMailOperationState, state);
1216 }
1217
1218
1219 static void
1220 transfer_folder_cb (TnyFolder *folder, 
1221                     TnyFolderStore *into, 
1222                     gboolean cancelled, 
1223                     TnyFolder *new_folder, GError **err, 
1224                     gpointer user_data)
1225 {
1226         ModestMailOperation *self = NULL;
1227         ModestMailOperationPrivate *priv = NULL;
1228
1229         self = MODEST_MAIL_OPERATION (user_data);
1230
1231         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1232
1233         if (*err) {
1234                 priv->error = g_error_copy (*err);
1235                 priv->done = 0;
1236                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1237         } else if (cancelled) {
1238                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1239                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1240                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1241                              _("Transference of %s was cancelled."),
1242                              tny_folder_get_name (folder));
1243         } else {
1244                 priv->done = 1;
1245                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1246         }
1247                 
1248         /* Free */
1249         g_object_unref (folder);
1250         g_object_unref (into);
1251         if (new_folder != NULL)
1252                 g_object_unref (new_folder);
1253
1254         /* Notify about operation end */
1255         modest_mail_operation_notify_end (self);
1256 }
1257
1258 void
1259 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1260                                    TnyFolder *folder,
1261                                    TnyFolderStore *parent,
1262                                    gboolean delete_original)
1263 {
1264         ModestMailOperationPrivate *priv = NULL;
1265         ModestTnyFolderRules parent_rules, rules;
1266
1267         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1268         g_return_if_fail (TNY_IS_FOLDER (folder));
1269         g_return_if_fail (TNY_IS_FOLDER (parent));
1270
1271         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1272
1273         /* Get account and set it into mail_operation */
1274         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1275         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1276
1277         /* Get folder rules */
1278         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1279         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1280
1281         if (!TNY_IS_FOLDER_STORE (parent)) {
1282                 
1283         }
1284         
1285         /* The moveable restriction is applied also to copy operation */
1286         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1287                 /* Set status failed and set an error */
1288                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1289                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1290                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1291                              _("mail_in_ui_folder_move_target_error"));
1292
1293                 /* Notify the queue */
1294                 modest_mail_operation_notify_end (self);
1295         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1296                 /* Set status failed and set an error */
1297                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1298                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1299                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1300                              _("FIXME: parent folder does not accept new folders"));
1301
1302                 /* Notify the queue */
1303                 modest_mail_operation_notify_end (self);
1304         } else {
1305                 /* Pick references for async calls */
1306                 g_object_ref (folder);
1307                 g_object_ref (parent);
1308
1309                 /* Move/Copy folder */          
1310                 tny_folder_copy_async (folder,
1311                                        parent,
1312                                        tny_folder_get_name (folder),
1313                                        delete_original,
1314                                        transfer_folder_cb,
1315                                        transfer_folder_status_cb,
1316                                        self);
1317         }
1318 }
1319
1320 void
1321 modest_mail_operation_rename_folder (ModestMailOperation *self,
1322                                      TnyFolder *folder,
1323                                      const gchar *name)
1324 {
1325         ModestMailOperationPrivate *priv;
1326         ModestTnyFolderRules rules;
1327
1328         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1329         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1330         g_return_if_fail (name);
1331         
1332         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1333
1334         /* Get account and set it into mail_operation */
1335         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1336
1337         /* Check folder rules */
1338         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1339         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1340                 /* Set status failed and set an error */
1341                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1342                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1343                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1344                              _("FIXME: unable to rename"));
1345
1346                 /* Notify about operation end */
1347                 modest_mail_operation_notify_end (self);
1348         } else {
1349                 /* Rename. Camel handles folder subscription/unsubscription */
1350                 TnyFolderStore *into;
1351
1352                 into = tny_folder_get_folder_store (folder);
1353                 tny_folder_copy_async (folder, into, name, TRUE,
1354                                  transfer_folder_cb,
1355                                  transfer_folder_status_cb,
1356                                  self);
1357                 if (into)
1358                         g_object_unref (into);
1359                 
1360         }
1361  }
1362
1363 /* ******************************************************************* */
1364 /* **************************  MSG  ACTIONS  ************************* */
1365 /* ******************************************************************* */
1366
1367 void modest_mail_operation_get_msg (ModestMailOperation *self,
1368                                     TnyHeader *header,
1369                                     GetMsgAsyncUserCallback user_callback,
1370                                     gpointer user_data)
1371 {
1372         GetMsgAsyncHelper *helper = NULL;
1373         TnyFolder *folder;
1374         ModestMailOperationPrivate *priv;
1375         
1376         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1377         g_return_if_fail (TNY_IS_HEADER (header));
1378         
1379         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1380         folder = tny_header_get_folder (header);
1381
1382         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1383
1384         /* Get message from folder */
1385         if (folder) {
1386                 /* Get account and set it into mail_operation */
1387                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1388
1389                 helper = g_slice_new0 (GetMsgAsyncHelper);
1390                 helper->mail_op = self;
1391                 helper->user_callback = user_callback;
1392                 helper->pending_ops = 1;
1393                 helper->user_data = user_data;
1394
1395                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1396
1397                 g_object_unref (G_OBJECT (folder));
1398         } else {
1399                 /* Set status failed and set an error */
1400                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1401                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1402                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1403                              _("Error trying to get a message. No folder found for header"));
1404
1405                 /* Notify the queue */
1406                 modest_mail_operation_notify_end (self);
1407         }
1408 }
1409
1410 static void
1411 get_msg_cb (TnyFolder *folder, 
1412             gboolean cancelled, 
1413             TnyMsg *msg, 
1414             GError **error, 
1415             gpointer user_data)
1416 {
1417         GetMsgAsyncHelper *helper = NULL;
1418         ModestMailOperation *self = NULL;
1419         ModestMailOperationPrivate *priv = NULL;
1420
1421         helper = (GetMsgAsyncHelper *) user_data;
1422         g_return_if_fail (helper != NULL);       
1423         self = helper->mail_op;
1424         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1425         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1426         
1427         helper->pending_ops--;
1428
1429         /* Check errors and cancel */
1430         if (*error) {
1431                 priv->error = g_error_copy (*error);
1432                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1433                 goto out;
1434         }
1435         if (cancelled) {
1436                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1437                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1438                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1439                              _("Error trying to refresh the contents of %s"),
1440                              tny_folder_get_name (folder));
1441                 goto out;
1442         }
1443
1444         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1445
1446         /* If user defined callback function was defined, call it */
1447         if (helper->user_callback) {
1448                 helper->user_callback (self, NULL, msg, helper->user_data);
1449         }
1450
1451         /* Free */
1452  out:
1453         if (helper->pending_ops == 0) {
1454                 g_slice_free (GetMsgAsyncHelper, helper);
1455                 
1456                 /* Notify about operation end */
1457                 modest_mail_operation_notify_end (self);        
1458         }
1459 }
1460
1461 static void     
1462 get_msg_status_cb (GObject *obj,
1463                    TnyStatus *status,  
1464                    gpointer user_data)
1465 {
1466         GetMsgAsyncHelper *helper = NULL;
1467         ModestMailOperation *self;
1468         ModestMailOperationPrivate *priv;
1469         ModestMailOperationState *state;
1470
1471         g_return_if_fail (status != NULL);
1472         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1473
1474         helper = (GetMsgAsyncHelper *) user_data;
1475         g_return_if_fail (helper != NULL);       
1476
1477         self = helper->mail_op;
1478         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1479
1480         if ((status->position == 1) && (status->of_total == 100))
1481                 return;
1482
1483         priv->done = 1;
1484         priv->total = 1;
1485
1486         state = modest_mail_operation_clone_state (self);
1487         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1488         g_slice_free (ModestMailOperationState, state);
1489 }
1490
1491 /****************************************************/
1492 typedef struct {
1493         ModestMailOperation *mail_op;
1494         TnyList *headers;
1495         GetMsgAsyncUserCallback user_callback;
1496         gpointer user_data;
1497         GDestroyNotify notify;
1498 } GetFullMsgsInfo;
1499
1500 typedef struct {
1501         GetMsgAsyncUserCallback user_callback;
1502         TnyHeader *header;
1503         TnyMsg *msg;
1504         gpointer user_data;
1505         ModestMailOperation *mail_op;
1506 } NotifyGetMsgsInfo;
1507
1508
1509 /* 
1510  * Used by get_msgs_full_thread to call the user_callback for each
1511  * message that has been read
1512  */
1513 static gboolean
1514 notify_get_msgs_full (gpointer data)
1515 {
1516         NotifyGetMsgsInfo *info;
1517
1518         info = (NotifyGetMsgsInfo *) data;      
1519
1520         /* Call the user callback */
1521         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1522
1523         g_slice_free (NotifyGetMsgsInfo, info);
1524
1525         return FALSE;
1526 }
1527
1528 /* 
1529  * Used by get_msgs_full_thread to free al the thread resources and to
1530  * call the destroy function for the passed user_data
1531  */
1532 static gboolean
1533 get_msgs_full_destroyer (gpointer data)
1534 {
1535         GetFullMsgsInfo *info;
1536
1537         info = (GetFullMsgsInfo *) data;
1538
1539         if (info->notify)
1540                 info->notify (info->user_data);
1541
1542         /* free */
1543         g_object_unref (info->headers);
1544         g_slice_free (GetFullMsgsInfo, info);
1545
1546         return FALSE;
1547 }
1548
1549 static gpointer
1550 get_msgs_full_thread (gpointer thr_user_data)
1551 {
1552         GetFullMsgsInfo *info;
1553         ModestMailOperationPrivate *priv = NULL;
1554         TnyIterator *iter = NULL;
1555         
1556         info = (GetFullMsgsInfo *) thr_user_data;       
1557         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1558
1559         iter = tny_list_create_iterator (info->headers);
1560         while (!tny_iterator_is_done (iter)) { 
1561                 TnyHeader *header;
1562                 TnyFolder *folder;
1563                 
1564                 header = TNY_HEADER (tny_iterator_get_current (iter));
1565                 folder = tny_header_get_folder (header);
1566                                 
1567                 /* Get message from folder */
1568                 if (folder) {
1569                         TnyMsg *msg;
1570                         /* The callback will call it per each header */
1571                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1572
1573                         if (msg) {
1574                                 ModestMailOperationState *state;
1575                                 ModestPair *pair;
1576
1577                                 priv->done++;
1578
1579                                 /* notify progress */
1580                                 state = modest_mail_operation_clone_state (info->mail_op);
1581                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1582                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1583                                                  pair, (GDestroyNotify) modest_pair_free);
1584
1585                                 /* The callback is the responsible for
1586                                    freeing the message */
1587                                 if (info->user_callback) {
1588                                         NotifyGetMsgsInfo *info_notify;
1589                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1590                                         info_notify->user_callback = info->user_callback;
1591                                         info_notify->mail_op = info->mail_op;
1592                                         info_notify->header = g_object_ref (header);
1593                                         info_notify->msg = g_object_ref (msg);
1594                                         info_notify->user_data = info->user_data;
1595                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1596                                                          notify_get_msgs_full, 
1597                                                          info_notify, NULL);
1598                                 }
1599                                 g_object_unref (msg);
1600                         }
1601                 } else {
1602                         /* Set status failed and set an error */
1603                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1604                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1605                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1606                                      "Error trying to get a message. No folder found for header");
1607                 }
1608                 g_object_unref (header);                
1609                 tny_iterator_next (iter);
1610         }
1611
1612         /* Set operation status */
1613         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1614                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1615
1616         /* Notify about operation end */
1617         g_idle_add (notify_update_account_queue, info->mail_op);
1618
1619         /* Free thread resources. Will be called after all previous idles */
1620         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1621
1622         return NULL;
1623 }
1624
1625 void 
1626 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1627                                      TnyList *header_list, 
1628                                      GetMsgAsyncUserCallback user_callback,
1629                                      gpointer user_data,
1630                                      GDestroyNotify notify)
1631 {
1632         TnyHeader *header = NULL;
1633         TnyFolder *folder = NULL;
1634         GThread *thread;
1635         ModestMailOperationPrivate *priv = NULL;
1636         GetFullMsgsInfo *info = NULL;
1637         gboolean size_ok = TRUE;
1638         gint max_size;
1639         TnyIterator *iter = NULL;
1640         
1641         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1642         
1643         /* Init mail operation */
1644         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1645         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1646         priv->done = 0;
1647         priv->total = tny_list_get_length(header_list);
1648
1649         /* Get account and set it into mail_operation */
1650         if (tny_list_get_length (header_list) >= 1) {
1651                 iter = tny_list_create_iterator (header_list);
1652                 header = TNY_HEADER (tny_iterator_get_current (iter));
1653                 folder = tny_header_get_folder (header);                
1654                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1655                 g_object_unref (header);
1656                 g_object_unref (folder);
1657
1658                 if (tny_list_get_length (header_list) == 1) {
1659                         g_object_unref (iter);
1660                         iter = NULL;
1661                 }
1662         }
1663
1664         /* Get msg size limit */
1665         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1666                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1667                                          &(priv->error));
1668         if (priv->error) {
1669                 g_clear_error (&(priv->error));
1670                 max_size = G_MAXINT;
1671         } else {
1672                 max_size = max_size * KB;
1673         }
1674
1675         /* Check message size limits. If there is only one message
1676            always retrieve it */
1677         if (iter != NULL) {
1678                 while (!tny_iterator_is_done (iter) && size_ok) {
1679                         header = TNY_HEADER (tny_iterator_get_current (iter));
1680                         if (tny_header_get_message_size (header) >= max_size)
1681                                 size_ok = FALSE;
1682                         g_object_unref (header);
1683                         tny_iterator_next (iter);
1684                 }
1685                 g_object_unref (iter);
1686         }
1687
1688         if (size_ok) {
1689                 /* Create the info */
1690                 info = g_slice_new0 (GetFullMsgsInfo);
1691                 info->mail_op = self;
1692                 info->user_callback = user_callback;
1693                 info->user_data = user_data;
1694                 info->headers = g_object_ref (header_list);
1695                 info->notify = notify;
1696
1697                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1698         } else {
1699                 /* Set status failed and set an error */
1700                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1701                 /* FIXME: the error msg is different for pop */
1702                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1703                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1704                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1705                 /* Remove from queue and free resources */
1706                 modest_mail_operation_notify_end (self);
1707                 if (notify)
1708                         notify (user_data);
1709         }
1710 }
1711
1712
1713 void 
1714 modest_mail_operation_remove_msg (ModestMailOperation *self,
1715                                   TnyHeader *header,
1716                                   gboolean remove_to_trash)
1717 {
1718         TnyFolder *folder;
1719         ModestMailOperationPrivate *priv;
1720
1721         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1722         g_return_if_fail (TNY_IS_HEADER (header));
1723
1724         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1725         folder = tny_header_get_folder (header);
1726
1727         /* Get account and set it into mail_operation */
1728         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1729
1730         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1731
1732         /* Delete or move to trash */
1733         if (remove_to_trash) {
1734                 TnyFolder *trash_folder;
1735                 TnyStoreAccount *store_account;
1736
1737                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1738                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1739                                                                       TNY_FOLDER_TYPE_TRASH);
1740                 if (trash_folder) {
1741                         TnyList *headers;
1742
1743                         /* Create list */
1744                         headers = tny_simple_list_new ();
1745                         tny_list_append (headers, G_OBJECT (header));
1746                         g_object_unref (header);
1747
1748                         /* Move to trash */
1749                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1750                         g_object_unref (headers);
1751 /*                      g_object_unref (trash_folder); */
1752                 } else {
1753                         ModestMailOperationPrivate *priv;
1754
1755                         /* Set status failed and set an error */
1756                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1757                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1758                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1759                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1760                                      _("Error trying to delete a message. Trash folder not found"));
1761                 }
1762
1763                 g_object_unref (G_OBJECT (store_account));
1764         } else {
1765                 tny_folder_remove_msg (folder, header, &(priv->error));
1766                 if (!priv->error)
1767                         tny_folder_sync(folder, TRUE, &(priv->error));
1768         }
1769
1770         /* Set status */
1771         if (!priv->error)
1772                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1773         else
1774                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1775
1776         /* Free */
1777         g_object_unref (G_OBJECT (folder));
1778
1779         /* Notify about operation end */
1780         modest_mail_operation_notify_end (self);
1781 }
1782
1783 static void
1784 transfer_msgs_status_cb (GObject *obj,
1785                          TnyStatus *status,  
1786                          gpointer user_data)
1787 {
1788         XFerMsgAsyncHelper *helper = NULL;
1789         ModestMailOperation *self;
1790         ModestMailOperationPrivate *priv;
1791         ModestMailOperationState *state;
1792
1793
1794         g_return_if_fail (status != NULL);
1795         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1796
1797         helper = (XFerMsgAsyncHelper *) user_data;
1798         g_return_if_fail (helper != NULL);       
1799
1800         self = helper->mail_op;
1801         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1802
1803         if ((status->position == 1) && (status->of_total == 100))
1804                 return;
1805
1806         priv->done = status->position;
1807         priv->total = status->of_total;
1808
1809         state = modest_mail_operation_clone_state (self);
1810         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1811         g_slice_free (ModestMailOperationState, state);
1812 }
1813
1814
1815 static void
1816 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1817 {
1818         XFerMsgAsyncHelper *helper;
1819         ModestMailOperation *self;
1820         ModestMailOperationPrivate *priv;
1821
1822         helper = (XFerMsgAsyncHelper *) user_data;
1823         self = helper->mail_op;
1824
1825         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1826
1827         if (*err) {
1828                 priv->error = g_error_copy (*err);
1829                 priv->done = 0;
1830                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1831         } else if (cancelled) {
1832                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1833                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1834                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1835                              _("Error trying to refresh the contents of %s"),
1836                              tny_folder_get_name (folder));
1837         } else {
1838                 priv->done = 1;
1839                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1840         }
1841
1842         /* If user defined callback function was defined, call it */
1843         if (helper->user_callback) {
1844                 helper->user_callback (priv->source, helper->user_data);
1845         }
1846
1847         /* Free */
1848         g_object_unref (helper->headers);
1849         g_object_unref (helper->dest_folder);
1850         g_object_unref (helper->mail_op);
1851         g_slice_free   (XFerMsgAsyncHelper, helper);
1852         g_object_unref (folder);
1853
1854         /* Notify about operation end */
1855         modest_mail_operation_notify_end (self);
1856 }
1857
1858 void
1859 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1860                                  TnyList *headers, 
1861                                  TnyFolder *folder, 
1862                                  gboolean delete_original,
1863                                  XferMsgsAsynUserCallback user_callback,
1864                                  gpointer user_data)
1865 {
1866         ModestMailOperationPrivate *priv;
1867         TnyIterator *iter;
1868         TnyFolder *src_folder;
1869         XFerMsgAsyncHelper *helper;
1870         TnyHeader *header;
1871         ModestTnyFolderRules rules;
1872
1873         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1874         g_return_if_fail (TNY_IS_LIST (headers));
1875         g_return_if_fail (TNY_IS_FOLDER (folder));
1876
1877         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1878         priv->total = 1;
1879         priv->done = 0;
1880         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1881
1882         /* Apply folder rules */
1883         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1884
1885         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1886                 /* Set status failed and set an error */
1887                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1888                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1889                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1890                              _("FIXME: folder does not accept msgs"));
1891                 /* Notify the queue */
1892                 modest_mail_operation_notify_end (self);
1893                 return;
1894         }
1895
1896         /* Create the helper */
1897         helper = g_slice_new0 (XFerMsgAsyncHelper);
1898         helper->mail_op = g_object_ref(self);
1899         helper->dest_folder = g_object_ref(folder);
1900         helper->headers = g_object_ref(headers);
1901         helper->user_callback = user_callback;
1902         helper->user_data = user_data;
1903
1904         /* Get source folder */
1905         iter = tny_list_create_iterator (headers);
1906         header = TNY_HEADER (tny_iterator_get_current (iter));
1907         src_folder = tny_header_get_folder (header);
1908         g_object_unref (header);
1909         g_object_unref (iter);
1910
1911         /* Get account and set it into mail_operation */
1912         priv->account = tny_folder_get_account (src_folder);
1913
1914         /* Transfer messages */
1915         tny_folder_transfer_msgs_async (src_folder, 
1916                                         headers, 
1917                                         folder, 
1918                                         delete_original, 
1919                                         transfer_msgs_cb, 
1920                                         transfer_msgs_status_cb,
1921                                         helper);
1922 }
1923
1924
1925 static void
1926 on_refresh_folder (TnyFolder   *folder, 
1927                    gboolean     cancelled, 
1928                    GError     **error,
1929                    gpointer     user_data)
1930 {
1931         ModestMailOperation *self;
1932         ModestMailOperationPrivate *priv;
1933
1934         self = MODEST_MAIL_OPERATION (user_data);
1935         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1936
1937         if (*error) {
1938                 priv->error = g_error_copy (*error);
1939                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1940                 goto out;
1941         }
1942
1943         if (cancelled) {
1944                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1945                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1946                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1947                              _("Error trying to refresh the contents of %s"),
1948                              tny_folder_get_name (folder));
1949                 goto out;
1950         }
1951
1952         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1953
1954  out:
1955         /* Free */
1956         g_object_unref (folder);
1957
1958         /* Notify about operation end */
1959         modest_mail_operation_notify_end (self);
1960 }
1961
1962 static void
1963 on_refresh_folder_status_update (GObject *obj,
1964                                  TnyStatus *status,
1965                                  gpointer user_data)
1966 {
1967         ModestMailOperation *self;
1968         ModestMailOperationPrivate *priv;
1969         ModestMailOperationState *state;
1970
1971         g_return_if_fail (status != NULL);
1972         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1973
1974         self = MODEST_MAIL_OPERATION (user_data);
1975         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1976
1977         priv->done = status->position;
1978         priv->total = status->of_total;
1979
1980         state = modest_mail_operation_clone_state (self);
1981         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1982         g_slice_free (ModestMailOperationState, state);
1983 }
1984
1985 void 
1986 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1987                                        TnyFolder *folder)
1988 {
1989         ModestMailOperationPrivate *priv;
1990
1991         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1992
1993         /* Pick a reference */
1994         g_object_ref (folder);
1995
1996         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1997
1998         /* Get account and set it into mail_operation */
1999         priv->account = tny_folder_get_account (folder);
2000
2001         /* Refresh the folder. TODO: tinymail could issue a status
2002            updates before the callback call then this could happen. We
2003            must review the design */
2004         tny_folder_refresh_async (folder,
2005                                   on_refresh_folder,
2006                                   on_refresh_folder_status_update,
2007                                   self);
2008 }
2009
2010 /**
2011  *
2012  * It's used by the mail operation queue to notify the observers
2013  * attached to that signal that the operation finished. We need to use
2014  * that because tinymail does not give us the progress of a given
2015  * operation when it finishes (it directly calls the operation
2016  * callback).
2017  */
2018 static void
2019 modest_mail_operation_notify_end (ModestMailOperation *self)
2020 {
2021         ModestMailOperationState *state;
2022
2023         /* Notify the observers about the mail opertation end */
2024         state = modest_mail_operation_clone_state (self);
2025         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2026         g_slice_free (ModestMailOperationState, state);
2027 }