2007-04-04 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 "modest-mail-operation.h"
31 /* include other impl specific header files */
32 #include <string.h>
33 #include <stdarg.h>
34 #include <tny-mime-part.h>
35 #include <tny-store-account.h>
36 #include <tny-folder-store.h>
37 #include <tny-folder-store-query.h>
38 #include <tny-camel-stream.h>
39 #include <tny-simple-list.h>
40 #include <tny-send-queue.h>
41 #include <camel/camel-stream-mem.h>
42 #include <glib/gi18n.h>
43 #include <modest-tny-account.h>
44 #include <modest-tny-send-queue.h>
45 #include <modest-runtime.h>
46 #include "modest-text-utils.h"
47 #include "modest-tny-msg.h"
48 #include "modest-tny-platform-factory.h"
49 #include "modest-marshal.h"
50 #include "modest-error.h"
51
52 /* 'private'/'protected' functions */
53 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
54 static void modest_mail_operation_init       (ModestMailOperation *obj);
55 static void modest_mail_operation_finalize   (GObject *obj);
56
57 static void     update_folders_cb    (TnyFolderStore *self, 
58                                       TnyList *list, 
59                                       GError **err, 
60                                       gpointer user_data);
61
62 enum _ModestMailOperationSignals 
63 {
64         PROGRESS_CHANGED_SIGNAL,
65
66         NUM_SIGNALS
67 };
68
69 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
70 struct _ModestMailOperationPrivate {
71         guint                      done;
72         guint                      total;
73         ModestMailOperationStatus  status;
74         GError                    *error;
75 };
76
77 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
78                                                    MODEST_TYPE_MAIL_OPERATION, \
79                                                    ModestMailOperationPrivate))
80
81 #define CHECK_EXCEPTION(priv, new_status, op)  if (priv->error) {\
82                                                    priv->status = new_status;\
83                                                    op;\
84                                                }
85
86 typedef struct _RefreshFolderAsyncHelper
87 {
88         ModestMailOperation *mail_op;
89         TnyIterator *iter;
90         guint failed;
91         guint canceled;
92
93 } RefreshFolderAsyncHelper;
94
95 typedef struct _XFerMsgAsyncHelper
96 {
97         ModestMailOperation *mail_op;
98         TnyList *headers;
99         TnyFolder *dest_folder;
100
101 } XFerMsgAsyncHelper;
102
103
104 /* globals */
105 static GObjectClass *parent_class = NULL;
106
107 static guint signals[NUM_SIGNALS] = {0};
108
109 GType
110 modest_mail_operation_get_type (void)
111 {
112         static GType my_type = 0;
113         if (!my_type) {
114                 static const GTypeInfo my_info = {
115                         sizeof(ModestMailOperationClass),
116                         NULL,           /* base init */
117                         NULL,           /* base finalize */
118                         (GClassInitFunc) modest_mail_operation_class_init,
119                         NULL,           /* class finalize */
120                         NULL,           /* class data */
121                         sizeof(ModestMailOperation),
122                         1,              /* n_preallocs */
123                         (GInstanceInitFunc) modest_mail_operation_init,
124                         NULL
125                 };
126                 my_type = g_type_register_static (G_TYPE_OBJECT,
127                                                   "ModestMailOperation",
128                                                   &my_info, 0);
129         }
130         return my_type;
131 }
132
133 static void
134 modest_mail_operation_class_init (ModestMailOperationClass *klass)
135 {
136         GObjectClass *gobject_class;
137         gobject_class = (GObjectClass*) klass;
138
139         parent_class            = g_type_class_peek_parent (klass);
140         gobject_class->finalize = modest_mail_operation_finalize;
141
142         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
143
144         /**
145          * ModestMailOperation::progress-changed
146          * @self: the #MailOperation that emits the signal
147          * @user_data: user data set when the signal handler was connected
148          *
149          * Emitted when the progress of a mail operation changes
150          */
151         signals[PROGRESS_CHANGED_SIGNAL] = 
152                 g_signal_new ("progress-changed",
153                               G_TYPE_FROM_CLASS (gobject_class),
154                               G_SIGNAL_RUN_FIRST,
155                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
156                               NULL, NULL,
157                               g_cclosure_marshal_VOID__VOID,
158                               G_TYPE_NONE, 0);
159 }
160
161 static void
162 modest_mail_operation_init (ModestMailOperation *obj)
163 {
164         ModestMailOperationPrivate *priv;
165
166         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
167
168         priv->status   = MODEST_MAIL_OPERATION_STATUS_INVALID;
169         priv->error    = NULL;
170         priv->done     = 0;
171         priv->total    = 0;
172 }
173
174 static void
175 modest_mail_operation_finalize (GObject *obj)
176 {
177         ModestMailOperationPrivate *priv;
178
179         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
180
181         if (priv->error) {
182                 g_error_free (priv->error);
183                 priv->error = NULL;
184         }
185
186         G_OBJECT_CLASS(parent_class)->finalize (obj);
187 }
188
189 ModestMailOperation*
190 modest_mail_operation_new (void)
191 {
192         return MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
193 }
194
195
196 void
197 modest_mail_operation_send_mail (ModestMailOperation *self,
198                                  TnyTransportAccount *transport_account,
199                                  TnyMsg* msg)
200 {
201         TnySendQueue *send_queue;
202         
203         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
204         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
205         g_return_if_fail (TNY_IS_MSG (msg));
206         
207         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
208         if (!TNY_IS_SEND_QUEUE(send_queue))
209                 g_printerr ("modest: could not find send queue for account\n");
210         else {
211                 GError *err = NULL;
212                 tny_send_queue_add (send_queue, msg, &err);
213                 if (err) {
214                         g_printerr ("modest: error adding msg to send queue: %s\n",
215                                     err->message);
216                         g_error_free (err);
217                 } else
218                         g_message ("modest: message added to send queue");
219         }
220
221         /* Notify the queue */
222         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
223 }
224
225 void
226 modest_mail_operation_send_new_mail (ModestMailOperation *self,
227                                      TnyTransportAccount *transport_account,
228                                      const gchar *from,  const gchar *to,
229                                      const gchar *cc,  const gchar *bcc,
230                                      const gchar *subject, const gchar *plain_body,
231                                      const gchar *html_body,
232                                      const GList *attachments_list)
233 {
234         TnyMsg *new_msg;
235         ModestMailOperationPrivate *priv = NULL;
236         /* GList *node = NULL; */
237
238         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
239         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
240
241         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
242
243         /* Check parametters */
244         if (to == NULL) {
245                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
246                              MODEST_MAIL_OPERATION_ERROR_MISSING_PARAMETER,
247                              _("Error trying to send a mail. You need to set at least one recipient"));
248                 return;
249         }
250
251         if (html_body == NULL) {
252                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
253         } else {
254                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
255         }
256         if (!new_msg) {
257                 g_printerr ("modest: failed to create a new msg\n");
258                 return;
259         }
260
261         /* Call mail operation */
262         modest_mail_operation_send_mail (self, transport_account, new_msg);
263
264         /* Free */
265         g_object_unref (G_OBJECT (new_msg));
266 }
267
268 static void
269 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
270 {
271         TnyIterator *iter;
272         TnyList *folders = tny_simple_list_new ();
273
274         tny_folder_store_get_folders (store, folders, query, NULL);
275         iter = tny_list_create_iterator (folders);
276
277         while (!tny_iterator_is_done (iter)) {
278
279                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
280
281                 tny_list_prepend (all_folders, G_OBJECT (folder));
282
283                 recurse_folders (folder, query, all_folders);
284             
285                 g_object_unref (G_OBJECT (folder));
286
287                 tny_iterator_next (iter);
288         }
289          g_object_unref (G_OBJECT (iter));
290          g_object_unref (G_OBJECT (folders));
291 }
292
293 static void
294 update_folders_cb (TnyFolderStore *folder_store, TnyList *list, GError **err, gpointer user_data)
295 {
296         ModestMailOperation *self;
297         ModestMailOperationPrivate *priv;
298         TnyIterator *iter;
299         TnyList *all_folders;
300         
301         self  = MODEST_MAIL_OPERATION (user_data);
302         priv  = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
303
304         if (*err) {
305                 priv->error = g_error_copy (*err);
306                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
307                 goto out;
308         }
309
310         /* Get all the folders We can do it synchronously because
311            we're already running in a different thread than the UI */
312         all_folders = tny_list_copy (list);
313         iter = tny_list_create_iterator (all_folders);
314         while (!tny_iterator_is_done (iter)) {
315                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
316
317                 recurse_folders (folder, NULL, all_folders);
318                 tny_iterator_next (iter);
319         }
320         g_object_unref (G_OBJECT (iter));
321
322         /* Refresh folders */
323         iter = tny_list_create_iterator (all_folders);
324         priv->total = tny_list_get_length (all_folders);
325
326         while (!tny_iterator_is_done (iter) && !priv->error) {
327
328                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
329
330                 /* Refresh the folder */
331                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
332
333                 if (priv->error) {
334                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
335                 } else {        
336                         /* Update status and notify */
337                         priv->done++;
338                         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
339                 }
340     
341                 g_object_unref (G_OBJECT (folder));
342             
343                 tny_iterator_next (iter);
344         }
345
346         g_object_unref (G_OBJECT (iter));
347  out:
348         g_object_unref (G_OBJECT (list));
349
350         /* Check if the operation was a success */
351         if (priv->done == priv->total && !priv->error)
352                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
353
354         /* Free */
355         g_object_unref (G_OBJECT (folder_store));
356
357         /* Notify the queue */
358         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
359 }
360
361 gboolean
362 modest_mail_operation_update_account (ModestMailOperation *self,
363                                       TnyStoreAccount *store_account)
364 {
365         ModestMailOperationPrivate *priv;
366         TnyList *folders;
367
368         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
369         g_return_val_if_fail (TNY_IS_STORE_ACCOUNT(store_account), FALSE);
370
371         /* Pick async call reference */
372         g_object_ref (store_account);
373
374         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
375
376         priv->total = 0;
377         priv->done  = 0;
378         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
379
380         /* Get subscribed folders & refresh them */
381         folders = TNY_LIST (tny_simple_list_new ());
382
383         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
384                                             folders, update_folders_cb, NULL, self);
385         
386         return TRUE;
387 }
388
389 ModestMailOperationStatus
390 modest_mail_operation_get_status (ModestMailOperation *self)
391 {
392         ModestMailOperationPrivate *priv;
393
394         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
395         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
396                               MODEST_MAIL_OPERATION_STATUS_INVALID);
397
398         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
399         return priv->status;
400 }
401
402 const GError *
403 modest_mail_operation_get_error (ModestMailOperation *self)
404 {
405         ModestMailOperationPrivate *priv;
406
407         g_return_val_if_fail (self, NULL);
408         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
409
410         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
411         return priv->error;
412 }
413
414 gboolean 
415 modest_mail_operation_cancel (ModestMailOperation *self)
416 {
417         /* TODO */
418         return TRUE;
419 }
420
421 guint 
422 modest_mail_operation_get_task_done (ModestMailOperation *self)
423 {
424         ModestMailOperationPrivate *priv;
425
426         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
427
428         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
429         return priv->done;
430 }
431
432 guint 
433 modest_mail_operation_get_task_total (ModestMailOperation *self)
434 {
435         ModestMailOperationPrivate *priv;
436
437         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
438
439         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
440         return priv->total;
441 }
442
443 gboolean
444 modest_mail_operation_is_finished (ModestMailOperation *self)
445 {
446         ModestMailOperationPrivate *priv;
447         gboolean retval = FALSE;
448
449         if (!MODEST_IS_MAIL_OPERATION (self)) {
450                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
451                 return retval;
452         }
453
454         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
455
456         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
457             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
458             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
459             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
460                 retval = TRUE;
461         } else {
462                 retval = FALSE;
463         }
464
465         return retval;
466 }
467
468 /* ******************************************************************* */
469 /* ************************** STORE  ACTIONS ************************* */
470 /* ******************************************************************* */
471
472
473 TnyFolder *
474 modest_mail_operation_create_folder (ModestMailOperation *self,
475                                      TnyFolderStore *parent,
476                                      const gchar *name)
477 {
478         ModestMailOperationPrivate *priv;
479         TnyFolder *new_folder = NULL;
480
481         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
482         g_return_val_if_fail (name, NULL);
483
484         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
485
486         /* Create the folder */
487         new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
488         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, return NULL);
489
490         /* Notify the queue */
491         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
492
493         return new_folder;
494 }
495
496 void
497 modest_mail_operation_remove_folder (ModestMailOperation *self,
498                                      TnyFolder           *folder,
499                                      gboolean             remove_to_trash)
500 {
501         TnyFolderStore *parent;
502         TnyAccount *account;
503         ModestMailOperationPrivate *priv;
504
505         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
506         g_return_if_fail (TNY_IS_FOLDER (folder));
507
508         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
509
510         /* Get the account */
511         account = tny_folder_get_account (folder);
512
513         /* Delete folder or move to trash */
514         if (remove_to_trash) {
515                 TnyFolder *trash_folder, *new_folder;
516                 trash_folder = modest_tny_account_get_special_folder (account,
517                                                                       TNY_FOLDER_TYPE_TRASH);
518                 /* TODO: error_handling */
519                 new_folder = modest_mail_operation_xfer_folder (self, folder, 
520                                                                 TNY_FOLDER_STORE (trash_folder), TRUE);
521                 g_object_unref (G_OBJECT (new_folder));
522         } else {
523                 parent = tny_folder_get_folder_store (folder);
524
525                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
526                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, );
527
528                 if (parent)
529                         g_object_unref (G_OBJECT (parent));
530         }
531         g_object_unref (G_OBJECT (account));
532
533         /* Notify the queue */
534         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
535 }
536
537 void
538 modest_mail_operation_rename_folder (ModestMailOperation *self,
539                                      TnyFolder *folder,
540                                      const gchar *name)
541 {
542         ModestMailOperationPrivate *priv;
543
544         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
545         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
546         g_return_if_fail (name);
547
548         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
549
550         /* FIXME: better error handling */
551         if (strrchr (name, '/') != NULL)
552                 goto out;
553
554         /* Rename. Camel handles folder subscription/unsubscription */
555         tny_folder_set_name (folder, name, &(priv->error));
556         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, goto out);
557
558  out:
559         /* Notify the queue */
560         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
561  }
562
563 TnyFolder *
564 modest_mail_operation_xfer_folder (ModestMailOperation *self,
565                                    TnyFolder *folder,
566                                    TnyFolderStore *parent,
567                                    gboolean delete_original)
568 {
569         ModestMailOperationPrivate *priv;
570         TnyFolder *new_folder;
571
572         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
573         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
574         g_return_val_if_fail (TNY_IS_FOLDER (folder), NULL);
575
576         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
577
578         new_folder = tny_folder_copy (folder,
579                                       parent,
580                                       tny_folder_get_name (folder),
581                                       delete_original, 
582                                       &(priv->error));
583
584         /* Notify the queue */
585         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
586
587         return new_folder;
588 }
589
590
591 /* ******************************************************************* */
592 /* **************************  MSG  ACTIONS  ************************* */
593 /* ******************************************************************* */
594
595 void 
596 modest_mail_operation_remove_msg (ModestMailOperation *self,
597                                   TnyHeader *header,
598                                   gboolean remove_to_trash)
599 {
600         TnyFolder *folder;
601         ModestMailOperationPrivate *priv;
602
603         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
604         g_return_if_fail (TNY_IS_HEADER (header));
605
606         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
607         folder = tny_header_get_folder (header);
608
609         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
610
611         /* Delete or move to trash */
612         if (remove_to_trash) {
613                 TnyFolder *trash_folder;
614                 TnyStoreAccount *store_account;
615
616                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
617                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
618                                                                       TNY_FOLDER_TYPE_TRASH);
619                 if (trash_folder) {
620                         modest_mail_operation_xfer_msg (self, header, trash_folder, TRUE);
621 /*                      g_object_unref (trash_folder); */
622                 } else {
623                         ModestMailOperationPrivate *priv;
624
625                         /* Set status failed and set an error */
626                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
627                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
628                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
629                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
630                                      _("Error trying to delete a message. Trash folder not found"));
631                 }
632
633                 g_object_unref (G_OBJECT (store_account));
634         } else {
635                 tny_folder_remove_msg (folder, header, &(priv->error));
636                 if (!priv->error)
637                         tny_folder_sync(folder, TRUE, &(priv->error));
638         }
639
640         /* Set status */
641         if (!priv->error)
642                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
643         else
644                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
645
646         /* Free */
647         g_object_unref (G_OBJECT (folder));
648
649         /* Notify the queue */
650         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
651 }
652
653 static void
654 transfer_msgs_cb (TnyFolder *folder, GError **err, gpointer user_data)
655 {
656         XFerMsgAsyncHelper *helper;
657         ModestMailOperation *self;
658         ModestMailOperationPrivate *priv;
659
660         helper = (XFerMsgAsyncHelper *) user_data;
661         self = helper->mail_op;
662         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
663
664         if (*err) {
665                 priv->error = g_error_copy (*err);
666                 priv->done = 0;
667                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
668         } else {
669                 priv->done = 1;
670                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
671         }
672
673         /* Free */
674         g_object_unref (helper->headers);
675         g_object_unref (helper->dest_folder);
676         g_object_unref (folder);
677         g_free (helper);
678
679         /* Notify the queue */
680         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
681 }
682
683 void
684 modest_mail_operation_xfer_msg (ModestMailOperation *self,
685                                 TnyHeader *header, 
686                                 TnyFolder *folder, 
687                                 gboolean delete_original)
688 {
689         ModestMailOperationPrivate *priv;
690         TnyFolder *src_folder;
691         TnyList *headers;
692         XFerMsgAsyncHelper *helper;
693
694         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
695         g_return_if_fail (TNY_IS_HEADER (header));
696         g_return_if_fail (TNY_IS_FOLDER (folder));
697
698         /* Pick references for async calls */
699         g_object_ref (folder);
700
701         headers = tny_simple_list_new ();
702         tny_list_prepend (headers, G_OBJECT (header));
703
704         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
705         priv->total = 1;
706         priv->done = 0;
707         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
708
709         /* Create the helper */
710         helper = g_malloc0 (sizeof (XFerMsgAsyncHelper));
711         helper->mail_op = self;
712         helper->dest_folder = folder;
713         helper->headers = headers;
714
715         src_folder = tny_header_get_folder (header);
716         tny_folder_transfer_msgs_async (src_folder, 
717                                         headers, 
718                                         folder, 
719                                         delete_original, 
720                                         transfer_msgs_cb, 
721                                         helper);
722 }
723
724 static void
725 on_refresh_folder (TnyFolder   *folder, 
726                    gboolean     cancelled, 
727                    GError     **error,
728                    gpointer     user_data)
729 {
730         ModestMailOperation *self;
731         ModestMailOperationPrivate *priv;
732
733         self = MODEST_MAIL_OPERATION (user_data);
734         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
735
736         if (*error) {
737                 priv->error = g_error_copy (*error);
738                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
739                 goto out;
740         }
741
742         if (cancelled) {
743                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
744                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
745                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
746                              _("Error trying to refresh the contents of %s"),
747                              tny_folder_get_name (folder));
748                 goto out;
749         }
750
751         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
752
753  out:
754         /* Free */
755         g_object_unref (folder);
756
757         /* Notify the queue */
758         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
759 }
760
761 static void
762 on_refresh_folder_status_update (TnyFolder *folder, const gchar *msg,
763                                  gint num, gint total,  gpointer user_data)
764 {
765         ModestMailOperation *self;
766         ModestMailOperationPrivate *priv;
767
768         /* TODO: if tinymail issues a status update before the
769            callback call then this could happen. If this is true the
770            we must review the design */
771         if (!G_IS_OBJECT (user_data))
772           return;
773
774         self = MODEST_MAIL_OPERATION (user_data);
775         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
776
777         priv->done = num;
778         priv->total = total;
779
780         if (num == 1 && total == 100)
781                 return;
782
783         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
784 }
785
786 void 
787 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
788                                        TnyFolder *folder)
789 {
790         ModestMailOperationPrivate *priv;
791
792         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
793
794         /* Pick a reference */
795         g_object_ref (folder);
796
797         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
798
799         /* Refresh the folder */
800         tny_folder_refresh_async (folder,
801                                   on_refresh_folder,
802                                   on_refresh_folder_status_update,
803                                   self);
804 }
805
806 void
807 _modest_mail_operation_notify_end (ModestMailOperation *self)
808 {
809         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
810 }