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