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