2007-05-31 Murray Cumming <murrayc@murrayc.com>
[modest] / src / modest-mail-operation.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include <stdarg.h>
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-simple-list.h>
38 #include <tny-send-queue.h>
39 #include <tny-status.h>
40 #include <tny-folder-observer.h>
41 #include <camel/camel-stream-mem.h>
42 #include <glib/gi18n.h>
43 #include "modest-platform.h"
44 #include <modest-tny-account.h>
45 #include <modest-tny-send-queue.h>
46 #include <modest-runtime.h>
47 #include "modest-text-utils.h"
48 #include "modest-tny-msg.h"
49 #include "modest-tny-folder.h"
50 #include "modest-tny-platform-factory.h"
51 #include "modest-marshal.h"
52 #include "modest-error.h"
53 #include "modest-mail-operation.h"
54
55 #define KB 1024
56
57 /* 'private'/'protected' functions */
58 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
59 static void modest_mail_operation_init       (ModestMailOperation *obj);
60 static void modest_mail_operation_finalize   (GObject *obj);
61
62 static void     get_msg_cb (TnyFolder *folder, 
63                             gboolean cancelled, 
64                             TnyMsg *msg, 
65                             GError **err, 
66                             gpointer user_data);
67
68 static void     get_msg_status_cb (GObject *obj,
69                                    TnyStatus *status,  
70                                    gpointer user_data);
71
72 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
73
74 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         /* Init mail operation. Set total and done to 0, and do not
1001            update them, this way the progress objects will know that
1002            we have no clue about the number of the objects */
1003         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1004         priv->total = 0;
1005         priv->done  = 0;
1006         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1007         
1008         /* Get the Modest account */
1009         modest_account = (TnyStoreAccount *)
1010                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
1011                                                                      account_name,
1012                                                                      TNY_ACCOUNT_TYPE_STORE);
1013
1014         if (!modest_account) {
1015                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1016                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1017                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1018                              "cannot get tny store account for %s\n", account_name);
1019                 modest_mail_operation_notify_end (self);
1020                 return FALSE;
1021         }
1022
1023         /* Get the transport account, we can not do it in the thread
1024            due to some problems with dbus */
1025         transport_account = (TnyTransportAccount *)
1026                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1027                                                                                     account_name);
1028         if (!transport_account) {
1029                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1030                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1031                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1032                              "cannot get tny transport account for %s\n", account_name);
1033                 modest_mail_operation_notify_end (self);
1034                 return FALSE;
1035         }
1036
1037         /* Create the helper object */
1038         info = g_slice_new (UpdateAccountInfo);
1039         info->mail_op = self;
1040         info->account = modest_account;
1041         info->transport_account = transport_account;
1042
1043         /* Get the message size limit */
1044         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1045                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1046         if (info->max_size == 0)
1047                 info->max_size = G_MAXINT;
1048         else
1049                 info->max_size = info->max_size * KB;
1050
1051         /* Get per-account retrieval type */
1052         mgr = modest_runtime_get_account_mgr ();
1053         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1054                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1055
1056         /* Get per-account message amount retrieval limit */
1057         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1058                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1059         if (info->retrieve_limit == 0)
1060                 info->retrieve_limit = G_MAXINT;
1061                 
1062         printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit);
1063
1064         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1065
1066         return TRUE;
1067 }
1068
1069 /* ******************************************************************* */
1070 /* ************************** STORE  ACTIONS ************************* */
1071 /* ******************************************************************* */
1072
1073
1074 TnyFolder *
1075 modest_mail_operation_create_folder (ModestMailOperation *self,
1076                                      TnyFolderStore *parent,
1077                                      const gchar *name)
1078 {
1079         ModestMailOperationPrivate *priv;
1080         TnyFolder *new_folder = NULL;
1081
1082         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1083         g_return_val_if_fail (name, NULL);
1084         
1085         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1086
1087         /* Check parent */
1088         if (TNY_IS_FOLDER (parent)) {
1089                 /* Check folder rules */
1090                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1091                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1092                         /* Set status failed and set an error */
1093                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1094                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1095                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1096                                      _("mail_in_ui_folder_create_error"));
1097                 }
1098         }
1099
1100         if (!priv->error) {
1101                 /* Create the folder */
1102                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1103                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1104                 if (!priv->error)
1105                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1106         }
1107
1108         /* Notify about operation end */
1109         modest_mail_operation_notify_end (self);
1110
1111         return new_folder;
1112 }
1113
1114 void
1115 modest_mail_operation_remove_folder (ModestMailOperation *self,
1116                                      TnyFolder           *folder,
1117                                      gboolean             remove_to_trash)
1118 {
1119         TnyAccount *account;
1120         ModestMailOperationPrivate *priv;
1121         ModestTnyFolderRules rules;
1122
1123         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1124         g_return_if_fail (TNY_IS_FOLDER (folder));
1125         
1126         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1127         
1128         /* Check folder rules */
1129         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1130         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1131                 /* Set status failed and set an error */
1132                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1133                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1134                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1135                              _("mail_in_ui_folder_delete_error"));
1136                 goto end;
1137         }
1138
1139         /* Get the account */
1140         account = tny_folder_get_account (folder);
1141         priv->account = g_object_ref(account);
1142
1143         /* Delete folder or move to trash */
1144         if (remove_to_trash) {
1145                 TnyFolder *trash_folder = NULL;
1146                 trash_folder = modest_tny_account_get_special_folder (account,
1147                                                                       TNY_FOLDER_TYPE_TRASH);
1148                 /* TODO: error_handling */
1149                  modest_mail_operation_xfer_folder (self, folder,
1150                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1151         } else {
1152                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1153
1154                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1155                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1156
1157                 if (parent)
1158                         g_object_unref (G_OBJECT (parent));
1159         }
1160         g_object_unref (G_OBJECT (account));
1161
1162  end:
1163         /* Notify about operation end */
1164         modest_mail_operation_notify_end (self);
1165 }
1166
1167 static void
1168 transfer_folder_status_cb (GObject *obj,
1169                            TnyStatus *status,
1170                            gpointer user_data)
1171 {
1172         ModestMailOperation *self;
1173         ModestMailOperationPrivate *priv;
1174         ModestMailOperationState *state;
1175
1176         g_return_if_fail (status != NULL);
1177         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1178
1179         self = MODEST_MAIL_OPERATION (user_data);
1180         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1181
1182         if ((status->position == 1) && (status->of_total == 100))
1183                 return;
1184
1185         priv->done = status->position;
1186         priv->total = status->of_total;
1187
1188         state = modest_mail_operation_clone_state (self);
1189         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1190         g_slice_free (ModestMailOperationState, state);
1191 }
1192
1193
1194 static void
1195 transfer_folder_cb (TnyFolder *folder, 
1196                     TnyFolderStore *into, 
1197                     gboolean cancelled, 
1198                     TnyFolder *new_folder, GError **err, 
1199                     gpointer user_data)
1200 {
1201         ModestMailOperation *self = NULL;
1202         ModestMailOperationPrivate *priv = NULL;
1203
1204         self = MODEST_MAIL_OPERATION (user_data);
1205
1206         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1207
1208         if (*err) {
1209                 priv->error = g_error_copy (*err);
1210                 priv->done = 0;
1211                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1212         } else if (cancelled) {
1213                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1214                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1215                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1216                              _("Transference of %s was cancelled."),
1217                              tny_folder_get_name (folder));
1218         } else {
1219                 priv->done = 1;
1220                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1221         }
1222                 
1223         /* Free */
1224         g_object_unref (folder);
1225         g_object_unref (into);
1226         if (new_folder != NULL)
1227                 g_object_unref (new_folder);
1228
1229         /* Notify about operation end */
1230         modest_mail_operation_notify_end (self);
1231 }
1232
1233 void
1234 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1235                                    TnyFolder *folder,
1236                                    TnyFolderStore *parent,
1237                                    gboolean delete_original)
1238 {
1239         ModestMailOperationPrivate *priv = NULL;
1240         ModestTnyFolderRules parent_rules, rules;
1241
1242         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1243         g_return_if_fail (TNY_IS_FOLDER (folder));
1244         g_return_if_fail (TNY_IS_FOLDER (parent));
1245
1246         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1247
1248         /* Get account and set it into mail_operation */
1249         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1250         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1251
1252         /* Get folder rules */
1253         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1254         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1255
1256         if (!TNY_IS_FOLDER_STORE (parent)) {
1257                 
1258         }
1259         
1260         /* The moveable restriction is applied also to copy operation */
1261         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1262                 /* Set status failed and set an error */
1263                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1264                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1265                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1266                              _("mail_in_ui_folder_move_target_error"));
1267
1268                 /* Notify the queue */
1269                 modest_mail_operation_notify_end (self);
1270         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1271                 /* Set status failed and set an error */
1272                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1273                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1274                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1275                              _("FIXME: parent folder does not accept new folders"));
1276
1277                 /* Notify the queue */
1278                 modest_mail_operation_notify_end (self);
1279         } else {
1280                 /* Pick references for async calls */
1281                 g_object_ref (folder);
1282                 g_object_ref (parent);
1283
1284                 /* Move/Copy folder */          
1285                 tny_folder_copy_async (folder,
1286                                        parent,
1287                                        tny_folder_get_name (folder),
1288                                        delete_original,
1289                                        transfer_folder_cb,
1290                                        transfer_folder_status_cb,
1291                                        self);
1292         }
1293 }
1294
1295 void
1296 modest_mail_operation_rename_folder (ModestMailOperation *self,
1297                                      TnyFolder *folder,
1298                                      const gchar *name)
1299 {
1300         ModestMailOperationPrivate *priv;
1301         ModestTnyFolderRules rules;
1302
1303         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1304         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1305         g_return_if_fail (name);
1306         
1307         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1308
1309         /* Get account and set it into mail_operation */
1310         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1311
1312         /* Check folder rules */
1313         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1314         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1315                 /* Set status failed and set an error */
1316                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1317                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1318                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1319                              _("FIXME: unable to rename"));
1320
1321                 /* Notify about operation end */
1322                 modest_mail_operation_notify_end (self);
1323         } else {
1324                 /* Rename. Camel handles folder subscription/unsubscription */
1325                 TnyFolderStore *into;
1326
1327                 into = tny_folder_get_folder_store (folder);
1328                 tny_folder_copy_async (folder, into, name, TRUE,
1329                                  transfer_folder_cb,
1330                                  transfer_folder_status_cb,
1331                                  self);
1332                 if (into)
1333                         g_object_unref (into);
1334                 
1335         }
1336  }
1337
1338 /* ******************************************************************* */
1339 /* **************************  MSG  ACTIONS  ************************* */
1340 /* ******************************************************************* */
1341
1342 void modest_mail_operation_get_msg (ModestMailOperation *self,
1343                                     TnyHeader *header,
1344                                     GetMsgAsyncUserCallback user_callback,
1345                                     gpointer user_data)
1346 {
1347         GetMsgAsyncHelper *helper = NULL;
1348         TnyFolder *folder;
1349         ModestMailOperationPrivate *priv;
1350         
1351         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1352         g_return_if_fail (TNY_IS_HEADER (header));
1353         
1354         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1355         folder = tny_header_get_folder (header);
1356
1357         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1358
1359         /* Get message from folder */
1360         if (folder) {
1361                 /* Get account and set it into mail_operation */
1362                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));            
1363
1364                 helper = g_slice_new0 (GetMsgAsyncHelper);
1365                 helper->mail_op = self;
1366                 helper->user_callback = user_callback;
1367                 helper->pending_ops = 1;
1368                 helper->user_data = user_data;
1369
1370                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1371
1372                 g_object_unref (G_OBJECT (folder));
1373         } else {
1374                 /* Set status failed and set an error */
1375                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1376                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1377                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1378                              _("Error trying to get a message. No folder found for header"));
1379
1380                 /* Notify the queue */
1381                 modest_mail_operation_notify_end (self);
1382         }
1383 }
1384
1385 static void
1386 get_msg_cb (TnyFolder *folder, 
1387             gboolean cancelled, 
1388             TnyMsg *msg, 
1389             GError **error, 
1390             gpointer user_data)
1391 {
1392         GetMsgAsyncHelper *helper = NULL;
1393         ModestMailOperation *self = NULL;
1394         ModestMailOperationPrivate *priv = NULL;
1395
1396         helper = (GetMsgAsyncHelper *) user_data;
1397         g_return_if_fail (helper != NULL);       
1398         self = helper->mail_op;
1399         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1400         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1401         
1402         helper->pending_ops--;
1403
1404         /* Check errors and cancel */
1405         if (*error) {
1406                 priv->error = g_error_copy (*error);
1407                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1408                 goto out;
1409         }
1410         if (cancelled) {
1411                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1412                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1413                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1414                              _("Error trying to refresh the contents of %s"),
1415                              tny_folder_get_name (folder));
1416                 goto out;
1417         }
1418
1419         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1420
1421         /* If user defined callback function was defined, call it */
1422         if (helper->user_callback) {
1423                 helper->user_callback (self, NULL, msg, helper->user_data);
1424         }
1425
1426         /* Free */
1427  out:
1428         if (helper->pending_ops == 0) {
1429                 g_slice_free (GetMsgAsyncHelper, helper);
1430                 
1431                 /* Notify about operation end */
1432                 modest_mail_operation_notify_end (self);        
1433         }
1434 }
1435
1436 static void     
1437 get_msg_status_cb (GObject *obj,
1438                    TnyStatus *status,  
1439                    gpointer user_data)
1440 {
1441         GetMsgAsyncHelper *helper = NULL;
1442         ModestMailOperation *self;
1443         ModestMailOperationPrivate *priv;
1444         ModestMailOperationState *state;
1445
1446         g_return_if_fail (status != NULL);
1447         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1448
1449         helper = (GetMsgAsyncHelper *) user_data;
1450         g_return_if_fail (helper != NULL);       
1451
1452         self = helper->mail_op;
1453         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1454
1455         if ((status->position == 1) && (status->of_total == 100))
1456                 return;
1457
1458         priv->done = 1;
1459         priv->total = 1;
1460
1461         state = modest_mail_operation_clone_state (self);
1462         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1463         g_slice_free (ModestMailOperationState, state);
1464 }
1465
1466 /****************************************************/
1467 typedef struct {
1468         ModestMailOperation *mail_op;
1469         TnyList *headers;
1470         GetMsgAsyncUserCallback user_callback;
1471         gpointer user_data;
1472         GDestroyNotify notify;
1473 } GetFullMsgsInfo;
1474
1475 typedef struct {
1476         GetMsgAsyncUserCallback user_callback;
1477         TnyHeader *header;
1478         TnyMsg *msg;
1479         gpointer user_data;
1480         ModestMailOperation *mail_op;
1481 } NotifyGetMsgsInfo;
1482
1483
1484 /* 
1485  * Used by get_msgs_full_thread to call the user_callback for each
1486  * message that has been read
1487  */
1488 static gboolean
1489 notify_get_msgs_full (gpointer data)
1490 {
1491         NotifyGetMsgsInfo *info;
1492
1493         info = (NotifyGetMsgsInfo *) data;      
1494
1495         /* Call the user callback */
1496         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1497
1498         g_slice_free (NotifyGetMsgsInfo, info);
1499
1500         return FALSE;
1501 }
1502
1503 /* 
1504  * Used by get_msgs_full_thread to free al the thread resources and to
1505  * call the destroy function for the passed user_data
1506  */
1507 static gboolean
1508 get_msgs_full_destroyer (gpointer data)
1509 {
1510         GetFullMsgsInfo *info;
1511
1512         info = (GetFullMsgsInfo *) data;
1513
1514         if (info->notify)
1515                 info->notify (info->user_data);
1516
1517         /* free */
1518         g_object_unref (info->headers);
1519         g_slice_free (GetFullMsgsInfo, info);
1520
1521         return FALSE;
1522 }
1523
1524 static gpointer
1525 get_msgs_full_thread (gpointer thr_user_data)
1526 {
1527         GetFullMsgsInfo *info;
1528         ModestMailOperationPrivate *priv = NULL;
1529         TnyIterator *iter = NULL;
1530         
1531         info = (GetFullMsgsInfo *) thr_user_data;       
1532         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1533
1534         iter = tny_list_create_iterator (info->headers);
1535         while (!tny_iterator_is_done (iter)) { 
1536                 TnyHeader *header;
1537                 TnyFolder *folder;
1538                 
1539                 header = TNY_HEADER (tny_iterator_get_current (iter));
1540                 folder = tny_header_get_folder (header);
1541                                 
1542                 /* Get message from folder */
1543                 if (folder) {
1544                         TnyMsg *msg;
1545                         /* The callback will call it per each header */
1546                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1547
1548                         if (msg) {
1549                                 ModestMailOperationState *state;
1550                                 ModestPair *pair;
1551
1552                                 priv->done++;
1553
1554                                 /* notify progress */
1555                                 state = modest_mail_operation_clone_state (info->mail_op);
1556                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1557                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1558                                                  pair, (GDestroyNotify) modest_pair_free);
1559
1560                                 /* The callback is the responsible for
1561                                    freeing the message */
1562                                 if (info->user_callback) {
1563                                         NotifyGetMsgsInfo *info_notify;
1564                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1565                                         info_notify->user_callback = info->user_callback;
1566                                         info_notify->mail_op = info->mail_op;
1567                                         info_notify->header = g_object_ref (header);
1568                                         info_notify->msg = g_object_ref (msg);
1569                                         info_notify->user_data = info->user_data;
1570                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1571                                                          notify_get_msgs_full, 
1572                                                          info_notify, NULL);
1573                                 }
1574                                 g_object_unref (msg);
1575                         }
1576                 } else {
1577                         /* Set status failed and set an error */
1578                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1579                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1580                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1581                                      "Error trying to get a message. No folder found for header");
1582                 }
1583                 g_object_unref (header);                
1584                 tny_iterator_next (iter);
1585         }
1586
1587         /* Set operation status */
1588         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1589                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1590
1591         /* Notify about operation end */
1592         g_idle_add (notify_update_account_queue, info->mail_op);
1593
1594         /* Free thread resources. Will be called after all previous idles */
1595         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1596
1597         return NULL;
1598 }
1599
1600 void 
1601 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1602                                      TnyList *header_list, 
1603                                      GetMsgAsyncUserCallback user_callback,
1604                                      gpointer user_data,
1605                                      GDestroyNotify notify)
1606 {
1607         TnyHeader *header = NULL;
1608         TnyFolder *folder = NULL;
1609         GThread *thread;
1610         ModestMailOperationPrivate *priv = NULL;
1611         GetFullMsgsInfo *info = NULL;
1612         gboolean size_ok = TRUE;
1613         gint max_size;
1614         TnyIterator *iter = NULL;
1615         
1616         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1617         
1618         /* Init mail operation */
1619         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1620         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1621         priv->done = 0;
1622         priv->total = tny_list_get_length(header_list);
1623
1624         /* Get account and set it into mail_operation */
1625         if (tny_list_get_length (header_list) > 1) {
1626                 iter = tny_list_create_iterator (header_list);          
1627                 header = TNY_HEADER (tny_iterator_get_current (iter));
1628                 folder = tny_header_get_folder (header);                
1629                 priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1630                 g_object_unref (header);
1631                 g_object_unref (folder);
1632         }
1633
1634         /* Get msg size limit */
1635         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1636                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1637                                          &(priv->error));
1638         if (priv->error) {
1639                 g_clear_error (&(priv->error));
1640                 max_size = G_MAXINT;
1641         } else {
1642                 max_size = max_size * KB;
1643         }
1644
1645         /* Check message size limits. If there is only one message
1646            always retrieve it */
1647         if (iter != NULL) {
1648                 while (!tny_iterator_is_done (iter) && size_ok) {
1649                         header = TNY_HEADER (tny_iterator_get_current (iter));
1650                         if (tny_header_get_message_size (header) >= max_size)
1651                                 size_ok = FALSE;
1652                         g_object_unref (header);
1653                         tny_iterator_next (iter);
1654                 }
1655                 g_object_unref (iter);
1656         }
1657
1658         if (size_ok) {
1659                 /* Create the info */
1660                 info = g_slice_new0 (GetFullMsgsInfo);
1661                 info->mail_op = self;
1662                 info->user_callback = user_callback;
1663                 info->user_data = user_data;
1664                 info->headers = g_object_ref (header_list);
1665                 info->notify = notify;
1666
1667                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1668         } else {
1669                 /* Set status failed and set an error */
1670                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1671                 /* FIXME: the error msg is different for pop */
1672                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1673                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1674                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1675                 /* Remove from queue and free resources */
1676                 modest_mail_operation_notify_end (self);
1677                 if (notify)
1678                         notify (user_data);
1679         }
1680 }
1681
1682
1683 void 
1684 modest_mail_operation_remove_msg (ModestMailOperation *self,
1685                                   TnyHeader *header,
1686                                   gboolean remove_to_trash)
1687 {
1688         TnyFolder *folder;
1689         ModestMailOperationPrivate *priv;
1690
1691         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1692         g_return_if_fail (TNY_IS_HEADER (header));
1693
1694         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1695         folder = tny_header_get_folder (header);
1696
1697         /* Get account and set it into mail_operation */
1698         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1699
1700         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1701
1702         /* Delete or move to trash */
1703         if (remove_to_trash) {
1704                 TnyFolder *trash_folder;
1705                 TnyStoreAccount *store_account;
1706
1707                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1708                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1709                                                                       TNY_FOLDER_TYPE_TRASH);
1710                 if (trash_folder) {
1711                         TnyList *headers;
1712
1713                         /* Create list */
1714                         headers = tny_simple_list_new ();
1715                         tny_list_append (headers, G_OBJECT (header));
1716                         g_object_unref (header);
1717
1718                         /* Move to trash */
1719                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1720                         g_object_unref (headers);
1721 /*                      g_object_unref (trash_folder); */
1722                 } else {
1723                         ModestMailOperationPrivate *priv;
1724
1725                         /* Set status failed and set an error */
1726                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1727                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1728                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1729                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1730                                      _("Error trying to delete a message. Trash folder not found"));
1731                 }
1732
1733                 g_object_unref (G_OBJECT (store_account));
1734         } else {
1735                 tny_folder_remove_msg (folder, header, &(priv->error));
1736                 if (!priv->error)
1737                         tny_folder_sync(folder, TRUE, &(priv->error));
1738         }
1739
1740         /* Set status */
1741         if (!priv->error)
1742                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1743         else
1744                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1745
1746         /* Free */
1747         g_object_unref (G_OBJECT (folder));
1748
1749         /* Notify about operation end */
1750         modest_mail_operation_notify_end (self);
1751 }
1752
1753 static void
1754 transfer_msgs_status_cb (GObject *obj,
1755                          TnyStatus *status,  
1756                          gpointer user_data)
1757 {
1758         XFerMsgAsyncHelper *helper = NULL;
1759         ModestMailOperation *self;
1760         ModestMailOperationPrivate *priv;
1761         ModestMailOperationState *state;
1762
1763
1764         g_return_if_fail (status != NULL);
1765         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1766
1767         helper = (XFerMsgAsyncHelper *) user_data;
1768         g_return_if_fail (helper != NULL);       
1769
1770         self = helper->mail_op;
1771         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1772
1773         if ((status->position == 1) && (status->of_total == 100))
1774                 return;
1775
1776         priv->done = status->position;
1777         priv->total = status->of_total;
1778
1779         state = modest_mail_operation_clone_state (self);
1780         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1781         g_slice_free (ModestMailOperationState, state);
1782 }
1783
1784
1785 static void
1786 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1787 {
1788         XFerMsgAsyncHelper *helper;
1789         ModestMailOperation *self;
1790         ModestMailOperationPrivate *priv;
1791
1792         helper = (XFerMsgAsyncHelper *) user_data;
1793         self = helper->mail_op;
1794
1795         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1796
1797         if (*err) {
1798                 priv->error = g_error_copy (*err);
1799                 priv->done = 0;
1800                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1801         } else if (cancelled) {
1802                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1803                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1804                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1805                              _("Error trying to refresh the contents of %s"),
1806                              tny_folder_get_name (folder));
1807         } else {
1808                 priv->done = 1;
1809                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1810         }
1811
1812         /* If user defined callback function was defined, call it */
1813         if (helper->user_callback) {
1814                 helper->user_callback (priv->source, helper->user_data);
1815         }
1816
1817         /* Free */
1818         g_object_unref (helper->headers);
1819         g_object_unref (helper->dest_folder);
1820         g_object_unref (helper->mail_op);
1821         g_slice_free   (XFerMsgAsyncHelper, helper);
1822         g_object_unref (folder);
1823
1824         /* Notify about operation end */
1825         modest_mail_operation_notify_end (self);
1826 }
1827
1828 void
1829 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1830                                  TnyList *headers, 
1831                                  TnyFolder *folder, 
1832                                  gboolean delete_original,
1833                                  XferMsgsAsynUserCallback user_callback,
1834                                  gpointer user_data)
1835 {
1836         ModestMailOperationPrivate *priv;
1837         TnyIterator *iter;
1838         TnyFolder *src_folder;
1839         XFerMsgAsyncHelper *helper;
1840         TnyHeader *header;
1841         ModestTnyFolderRules rules;
1842
1843         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1844         g_return_if_fail (TNY_IS_LIST (headers));
1845         g_return_if_fail (TNY_IS_FOLDER (folder));
1846
1847         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1848         priv->total = 1;
1849         priv->done = 0;
1850         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1851
1852         /* Apply folder rules */
1853         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1854
1855         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1856                 /* Set status failed and set an error */
1857                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1858                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1859                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1860                              _("FIXME: folder does not accept msgs"));
1861                 /* Notify the queue */
1862                 modest_mail_operation_notify_end (self);
1863                 return;
1864         }
1865
1866         /* Create the helper */
1867         helper = g_slice_new0 (XFerMsgAsyncHelper);
1868         helper->mail_op = g_object_ref(self);
1869         helper->dest_folder = g_object_ref(folder);
1870         helper->headers = g_object_ref(headers);
1871         helper->user_callback = user_callback;
1872         helper->user_data = user_data;
1873
1874         /* Get source folder */
1875         iter = tny_list_create_iterator (headers);
1876         header = TNY_HEADER (tny_iterator_get_current (iter));
1877         src_folder = tny_header_get_folder (header);
1878         g_object_unref (header);
1879         g_object_unref (iter);
1880
1881         /* Get account and set it into mail_operation */
1882         priv->account = tny_folder_get_account (src_folder);
1883
1884         /* Transfer messages */
1885         tny_folder_transfer_msgs_async (src_folder, 
1886                                         headers, 
1887                                         folder, 
1888                                         delete_original, 
1889                                         transfer_msgs_cb, 
1890                                         transfer_msgs_status_cb,
1891                                         helper);
1892 }
1893
1894
1895 static void
1896 on_refresh_folder (TnyFolder   *folder, 
1897                    gboolean     cancelled, 
1898                    GError     **error,
1899                    gpointer     user_data)
1900 {
1901         ModestMailOperation *self;
1902         ModestMailOperationPrivate *priv;
1903
1904         self = MODEST_MAIL_OPERATION (user_data);
1905         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1906
1907         if (*error) {
1908                 priv->error = g_error_copy (*error);
1909                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1910                 goto out;
1911         }
1912
1913         if (cancelled) {
1914                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1915                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1916                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1917                              _("Error trying to refresh the contents of %s"),
1918                              tny_folder_get_name (folder));
1919                 goto out;
1920         }
1921
1922         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1923
1924  out:
1925         /* Free */
1926         g_object_unref (folder);
1927
1928         /* Notify about operation end */
1929         modest_mail_operation_notify_end (self);
1930 }
1931
1932 static void
1933 on_refresh_folder_status_update (GObject *obj,
1934                                  TnyStatus *status,
1935                                  gpointer user_data)
1936 {
1937         ModestMailOperation *self;
1938         ModestMailOperationPrivate *priv;
1939         ModestMailOperationState *state;
1940
1941         g_return_if_fail (status != NULL);
1942         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1943
1944         self = MODEST_MAIL_OPERATION (user_data);
1945         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1946
1947         priv->done = status->position;
1948         priv->total = status->of_total;
1949
1950         state = modest_mail_operation_clone_state (self);
1951         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1952         g_slice_free (ModestMailOperationState, state);
1953 }
1954
1955 void 
1956 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1957                                        TnyFolder *folder)
1958 {
1959         ModestMailOperationPrivate *priv;
1960
1961         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1962
1963         /* Pick a reference */
1964         g_object_ref (folder);
1965
1966         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1967
1968         /* Get account and set it into mail_operation */
1969         priv->account = tny_folder_get_account (folder);
1970
1971         /* Refresh the folder. TODO: tinymail could issue a status
1972            updates before the callback call then this could happen. We
1973            must review the design */
1974         tny_folder_refresh_async (folder,
1975                                   on_refresh_folder,
1976                                   on_refresh_folder_status_update,
1977                                   self);
1978 }
1979
1980 /**
1981  *
1982  * It's used by the mail operation queue to notify the observers
1983  * attached to that signal that the operation finished. We need to use
1984  * that because tinymail does not give us the progress of a given
1985  * operation when it finishes (it directly calls the operation
1986  * callback).
1987  */
1988 static void
1989 modest_mail_operation_notify_end (ModestMailOperation *self)
1990 {
1991         ModestMailOperationState *state;
1992
1993         /* Notify the observers about the mail opertation end */
1994         state = modest_mail_operation_clone_state (self);
1995         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1996         g_slice_free (ModestMailOperationState, state);
1997
1998         /* Notify the queue */
1999         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
2000 }