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