2007-06-03 Armin Burgmeier <armin@openismus.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         /* 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                 if (tny_list_get_length (header_list) == 1) {
1641                         g_object_unref (iter);
1642                         iter = NULL;
1643                 }
1644         }
1645
1646         /* Get msg size limit */
1647         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1648                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1649                                          &(priv->error));
1650         if (priv->error) {
1651                 g_clear_error (&(priv->error));
1652                 max_size = G_MAXINT;
1653         } else {
1654                 max_size = max_size * KB;
1655         }
1656
1657         /* Check message size limits. If there is only one message
1658            always retrieve it */
1659         if (iter != NULL) {
1660                 while (!tny_iterator_is_done (iter) && size_ok) {
1661                         header = TNY_HEADER (tny_iterator_get_current (iter));
1662                         if (tny_header_get_message_size (header) >= max_size)
1663                                 size_ok = FALSE;
1664                         g_object_unref (header);
1665                         tny_iterator_next (iter);
1666                 }
1667                 g_object_unref (iter);
1668         }
1669
1670         if (size_ok) {
1671                 /* Create the info */
1672                 info = g_slice_new0 (GetFullMsgsInfo);
1673                 info->mail_op = self;
1674                 info->user_callback = user_callback;
1675                 info->user_data = user_data;
1676                 info->headers = g_object_ref (header_list);
1677                 info->notify = notify;
1678
1679                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1680         } else {
1681                 /* Set status failed and set an error */
1682                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1683                 /* FIXME: the error msg is different for pop */
1684                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1685                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1686                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1687                 /* Remove from queue and free resources */
1688                 modest_mail_operation_notify_end (self);
1689                 if (notify)
1690                         notify (user_data);
1691         }
1692 }
1693
1694
1695 void 
1696 modest_mail_operation_remove_msg (ModestMailOperation *self,
1697                                   TnyHeader *header,
1698                                   gboolean remove_to_trash)
1699 {
1700         TnyFolder *folder;
1701         ModestMailOperationPrivate *priv;
1702
1703         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1704         g_return_if_fail (TNY_IS_HEADER (header));
1705
1706         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1707         folder = tny_header_get_folder (header);
1708
1709         /* Get account and set it into mail_operation */
1710         priv->account = tny_folder_get_account (TNY_FOLDER(folder));
1711
1712         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1713
1714         /* Delete or move to trash */
1715         if (remove_to_trash) {
1716                 TnyFolder *trash_folder;
1717                 TnyStoreAccount *store_account;
1718
1719                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1720                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1721                                                                       TNY_FOLDER_TYPE_TRASH);
1722                 if (trash_folder) {
1723                         TnyList *headers;
1724
1725                         /* Create list */
1726                         headers = tny_simple_list_new ();
1727                         tny_list_append (headers, G_OBJECT (header));
1728                         g_object_unref (header);
1729
1730                         /* Move to trash */
1731                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1732                         g_object_unref (headers);
1733 /*                      g_object_unref (trash_folder); */
1734                 } else {
1735                         ModestMailOperationPrivate *priv;
1736
1737                         /* Set status failed and set an error */
1738                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1739                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1740                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1741                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1742                                      _("Error trying to delete a message. Trash folder not found"));
1743                 }
1744
1745                 g_object_unref (G_OBJECT (store_account));
1746         } else {
1747                 tny_folder_remove_msg (folder, header, &(priv->error));
1748                 if (!priv->error)
1749                         tny_folder_sync(folder, TRUE, &(priv->error));
1750         }
1751
1752         /* Set status */
1753         if (!priv->error)
1754                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1755         else
1756                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1757
1758         /* Free */
1759         g_object_unref (G_OBJECT (folder));
1760
1761         /* Notify about operation end */
1762         modest_mail_operation_notify_end (self);
1763 }
1764
1765 static void
1766 transfer_msgs_status_cb (GObject *obj,
1767                          TnyStatus *status,  
1768                          gpointer user_data)
1769 {
1770         XFerMsgAsyncHelper *helper = NULL;
1771         ModestMailOperation *self;
1772         ModestMailOperationPrivate *priv;
1773         ModestMailOperationState *state;
1774
1775
1776         g_return_if_fail (status != NULL);
1777         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1778
1779         helper = (XFerMsgAsyncHelper *) user_data;
1780         g_return_if_fail (helper != NULL);       
1781
1782         self = helper->mail_op;
1783         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1784
1785         if ((status->position == 1) && (status->of_total == 100))
1786                 return;
1787
1788         priv->done = status->position;
1789         priv->total = status->of_total;
1790
1791         state = modest_mail_operation_clone_state (self);
1792         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1793         g_slice_free (ModestMailOperationState, state);
1794 }
1795
1796
1797 static void
1798 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1799 {
1800         XFerMsgAsyncHelper *helper;
1801         ModestMailOperation *self;
1802         ModestMailOperationPrivate *priv;
1803
1804         helper = (XFerMsgAsyncHelper *) user_data;
1805         self = helper->mail_op;
1806
1807         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1808
1809         if (*err) {
1810                 priv->error = g_error_copy (*err);
1811                 priv->done = 0;
1812                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1813         } else if (cancelled) {
1814                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1815                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1816                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1817                              _("Error trying to refresh the contents of %s"),
1818                              tny_folder_get_name (folder));
1819         } else {
1820                 priv->done = 1;
1821                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1822         }
1823
1824         /* If user defined callback function was defined, call it */
1825         if (helper->user_callback) {
1826                 helper->user_callback (priv->source, helper->user_data);
1827         }
1828
1829         /* Free */
1830         g_object_unref (helper->headers);
1831         g_object_unref (helper->dest_folder);
1832         g_object_unref (helper->mail_op);
1833         g_slice_free   (XFerMsgAsyncHelper, helper);
1834         g_object_unref (folder);
1835
1836         /* Notify about operation end */
1837         modest_mail_operation_notify_end (self);
1838 }
1839
1840 void
1841 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1842                                  TnyList *headers, 
1843                                  TnyFolder *folder, 
1844                                  gboolean delete_original,
1845                                  XferMsgsAsynUserCallback user_callback,
1846                                  gpointer user_data)
1847 {
1848         ModestMailOperationPrivate *priv;
1849         TnyIterator *iter;
1850         TnyFolder *src_folder;
1851         XFerMsgAsyncHelper *helper;
1852         TnyHeader *header;
1853         ModestTnyFolderRules rules;
1854
1855         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1856         g_return_if_fail (TNY_IS_LIST (headers));
1857         g_return_if_fail (TNY_IS_FOLDER (folder));
1858
1859         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1860         priv->total = 1;
1861         priv->done = 0;
1862         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1863
1864         /* Apply folder rules */
1865         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1866
1867         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1868                 /* Set status failed and set an error */
1869                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1870                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1871                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1872                              _("FIXME: folder does not accept msgs"));
1873                 /* Notify the queue */
1874                 modest_mail_operation_notify_end (self);
1875                 return;
1876         }
1877
1878         /* Create the helper */
1879         helper = g_slice_new0 (XFerMsgAsyncHelper);
1880         helper->mail_op = g_object_ref(self);
1881         helper->dest_folder = g_object_ref(folder);
1882         helper->headers = g_object_ref(headers);
1883         helper->user_callback = user_callback;
1884         helper->user_data = user_data;
1885
1886         /* Get source folder */
1887         iter = tny_list_create_iterator (headers);
1888         header = TNY_HEADER (tny_iterator_get_current (iter));
1889         src_folder = tny_header_get_folder (header);
1890         g_object_unref (header);
1891         g_object_unref (iter);
1892
1893         /* Get account and set it into mail_operation */
1894         priv->account = tny_folder_get_account (src_folder);
1895
1896         /* Transfer messages */
1897         tny_folder_transfer_msgs_async (src_folder, 
1898                                         headers, 
1899                                         folder, 
1900                                         delete_original, 
1901                                         transfer_msgs_cb, 
1902                                         transfer_msgs_status_cb,
1903                                         helper);
1904 }
1905
1906
1907 static void
1908 on_refresh_folder (TnyFolder   *folder, 
1909                    gboolean     cancelled, 
1910                    GError     **error,
1911                    gpointer     user_data)
1912 {
1913         ModestMailOperation *self;
1914         ModestMailOperationPrivate *priv;
1915
1916         self = MODEST_MAIL_OPERATION (user_data);
1917         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1918
1919         if (*error) {
1920                 priv->error = g_error_copy (*error);
1921                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1922                 goto out;
1923         }
1924
1925         if (cancelled) {
1926                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1927                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1928                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1929                              _("Error trying to refresh the contents of %s"),
1930                              tny_folder_get_name (folder));
1931                 goto out;
1932         }
1933
1934         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1935
1936  out:
1937         /* Free */
1938         g_object_unref (folder);
1939
1940         /* Notify about operation end */
1941         modest_mail_operation_notify_end (self);
1942 }
1943
1944 static void
1945 on_refresh_folder_status_update (GObject *obj,
1946                                  TnyStatus *status,
1947                                  gpointer user_data)
1948 {
1949         ModestMailOperation *self;
1950         ModestMailOperationPrivate *priv;
1951         ModestMailOperationState *state;
1952
1953         g_return_if_fail (status != NULL);
1954         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1955
1956         self = MODEST_MAIL_OPERATION (user_data);
1957         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1958
1959         priv->done = status->position;
1960         priv->total = status->of_total;
1961
1962         state = modest_mail_operation_clone_state (self);
1963         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1964         g_slice_free (ModestMailOperationState, state);
1965 }
1966
1967 void 
1968 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1969                                        TnyFolder *folder)
1970 {
1971         ModestMailOperationPrivate *priv;
1972
1973         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1974
1975         /* Pick a reference */
1976         g_object_ref (folder);
1977
1978         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1979
1980         /* Get account and set it into mail_operation */
1981         priv->account = tny_folder_get_account (folder);
1982
1983         /* Refresh the folder. TODO: tinymail could issue a status
1984            updates before the callback call then this could happen. We
1985            must review the design */
1986         tny_folder_refresh_async (folder,
1987                                   on_refresh_folder,
1988                                   on_refresh_folder_status_update,
1989                                   self);
1990 }
1991
1992 /**
1993  *
1994  * It's used by the mail operation queue to notify the observers
1995  * attached to that signal that the operation finished. We need to use
1996  * that because tinymail does not give us the progress of a given
1997  * operation when it finishes (it directly calls the operation
1998  * callback).
1999  */
2000 static void
2001 modest_mail_operation_notify_end (ModestMailOperation *self)
2002 {
2003         ModestMailOperationState *state;
2004
2005         /* Notify the observers about the mail opertation end */
2006         state = modest_mail_operation_clone_state (self);
2007         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2008         g_slice_free (ModestMailOperationState, state);
2009 }