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