* Created a new get_full_msgs mail operation to asynchronously receive several full...
[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 <tny-status.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.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
54 /* 'private'/'protected' functions */
55 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
56 static void modest_mail_operation_init       (ModestMailOperation *obj);
57 static void modest_mail_operation_finalize   (GObject *obj);
58
59 /* static void     update_process_msg_status_cb (GObject *obj, */
60 /*                                            TnyStatus *status,   */
61 /*                                            gpointer user_data); */
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
73 enum _ModestMailOperationSignals 
74 {
75         PROGRESS_CHANGED_SIGNAL,
76
77         NUM_SIGNALS
78 };
79
80 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
81 struct _ModestMailOperationPrivate {
82         guint                      done;
83         guint                      total;
84         ModestMailOperationStatus  status;      
85         ModestMailOperationId      id;          
86         GObject                   *source;
87         GError                    *error;
88 };
89
90 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
91                                                    MODEST_TYPE_MAIL_OPERATION, \
92                                                    ModestMailOperationPrivate))
93
94 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
95                                                    priv->status = new_status;\
96                                                }
97
98 typedef struct _GetMsgAsyncHelper {     
99         ModestMailOperation *mail_op;
100         GetMsgAsynUserCallback user_callback;   
101         guint pending_ops;
102         gpointer user_data;
103 } GetMsgAsyncHelper;
104
105 typedef struct _XFerMsgAsyncHelper
106 {
107         ModestMailOperation *mail_op;
108         TnyList *headers;
109         TnyFolder *dest_folder;
110         XferMsgsAsynUserCallback user_callback; 
111         gpointer user_data;
112 } XFerMsgAsyncHelper;
113
114 typedef struct _XFerFolderAsyncHelper
115 {
116         ModestMailOperation *mail_op;
117
118 } XFerFolderAsyncHelper;
119
120 /* globals */
121 static GObjectClass *parent_class = NULL;
122
123 static guint signals[NUM_SIGNALS] = {0};
124
125 GType
126 modest_mail_operation_get_type (void)
127 {
128         static GType my_type = 0;
129         if (!my_type) {
130                 static const GTypeInfo my_info = {
131                         sizeof(ModestMailOperationClass),
132                         NULL,           /* base init */
133                         NULL,           /* base finalize */
134                         (GClassInitFunc) modest_mail_operation_class_init,
135                         NULL,           /* class finalize */
136                         NULL,           /* class data */
137                         sizeof(ModestMailOperation),
138                         1,              /* n_preallocs */
139                         (GInstanceInitFunc) modest_mail_operation_init,
140                         NULL
141                 };
142                 my_type = g_type_register_static (G_TYPE_OBJECT,
143                                                   "ModestMailOperation",
144                                                   &my_info, 0);
145         }
146         return my_type;
147 }
148
149 static void
150 modest_mail_operation_class_init (ModestMailOperationClass *klass)
151 {
152         GObjectClass *gobject_class;
153         gobject_class = (GObjectClass*) klass;
154
155         parent_class            = g_type_class_peek_parent (klass);
156         gobject_class->finalize = modest_mail_operation_finalize;
157
158         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
159
160         /**
161          * ModestMailOperation::progress-changed
162          * @self: the #MailOperation that emits the signal
163          * @user_data: user data set when the signal handler was connected
164          *
165          * Emitted when the progress of a mail operation changes
166          */
167         signals[PROGRESS_CHANGED_SIGNAL] = 
168                 g_signal_new ("progress-changed",
169                               G_TYPE_FROM_CLASS (gobject_class),
170                               G_SIGNAL_RUN_FIRST,
171                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
172                               NULL, NULL,
173                               g_cclosure_marshal_VOID__VOID,
174                               G_TYPE_NONE, 0);
175 }
176
177 static void
178 modest_mail_operation_init (ModestMailOperation *obj)
179 {
180         ModestMailOperationPrivate *priv;
181
182         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
183
184         priv->status   = MODEST_MAIL_OPERATION_STATUS_INVALID;
185         priv->id       = MODEST_MAIL_OPERATION_ID_UNKNOWN;
186         priv->error    = NULL;
187         priv->done     = 0;
188         priv->total    = 0;
189         priv->source = NULL;
190 }
191
192 static void
193 modest_mail_operation_finalize (GObject *obj)
194 {
195         ModestMailOperationPrivate *priv;
196
197         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
198
199         if (priv->error) {
200                 g_error_free (priv->error);
201                 priv->error = NULL;
202         }
203         if (priv->source) {
204                 g_object_unref (priv->source);
205                 priv->source = NULL;
206         }
207
208         G_OBJECT_CLASS(parent_class)->finalize (obj);
209 }
210
211 ModestMailOperation*
212 modest_mail_operation_new (ModestMailOperationId id, 
213                            GObject *source)
214 {
215         ModestMailOperation *obj;
216         ModestMailOperationPrivate *priv;
217                 
218         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
219         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
220
221         priv->id = id;
222         if (source != NULL)
223                 priv->source = g_object_ref(source);
224
225         return obj;
226 }
227
228
229 ModestMailOperationId
230 modest_mail_operation_get_id (ModestMailOperation *self)
231 {
232         ModestMailOperationPrivate *priv;
233
234         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
235         
236         return priv->id;
237 }
238
239 gboolean 
240 modest_mail_operation_is_mine (ModestMailOperation *self, 
241                                GObject *me)
242 {
243         ModestMailOperationPrivate *priv;
244
245         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
246         if (priv->source == NULL) return FALSE;
247
248         return priv->source == me;
249 }
250
251
252 void
253 modest_mail_operation_send_mail (ModestMailOperation *self,
254                                  TnyTransportAccount *transport_account,
255                                  TnyMsg* msg)
256 {
257         TnySendQueue *send_queue;
258         
259         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
260         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
261         g_return_if_fail (TNY_IS_MSG (msg));
262         
263         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
264         if (!TNY_IS_SEND_QUEUE(send_queue))
265                 g_printerr ("modest: could not find send queue for account\n");
266         else {
267                 GError *err = NULL;
268                 tny_send_queue_add (send_queue, msg, &err);
269                 if (err) {
270                         g_printerr ("modest: error adding msg to send queue: %s\n",
271                                     err->message);
272                         g_error_free (err);
273                 } else {
274                         /* g_message ("modest: message added to send queue"); */
275                 }
276         }
277
278         /* Notify the queue */
279         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
280 }
281
282 void
283 modest_mail_operation_send_new_mail (ModestMailOperation *self,
284                                      TnyTransportAccount *transport_account,
285                                      const gchar *from,  const gchar *to,
286                                      const gchar *cc,  const gchar *bcc,
287                                      const gchar *subject, const gchar *plain_body,
288                                      const gchar *html_body,
289                                      const GList *attachments_list,
290                                      TnyHeaderFlags priority_flags)
291 {
292         TnyMsg *new_msg;
293         ModestMailOperationPrivate *priv = NULL;
294         /* GList *node = NULL; */
295
296         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
297         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
298
299         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
300
301         /* Check parametters */
302         if (to == NULL) {
303                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
304                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
305                              _("Error trying to send a mail. You need to set at least one recipient"));
306                 return;
307         }
308
309         if (html_body == NULL) {
310                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
311         } else {
312                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
313         }
314         if (!new_msg) {
315                 g_printerr ("modest: failed to create a new msg\n");
316                 return;
317         }
318
319         /* TODO: add priority handling. It's received in the priority_flags operator, and
320            it should have effect in the sending operation */
321
322         /* Call mail operation */
323         modest_mail_operation_send_mail (self, transport_account, new_msg);
324
325         /* Free */
326         g_object_unref (G_OBJECT (new_msg));
327 }
328
329 void
330 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
331                                       TnyTransportAccount *transport_account,
332                                       const gchar *from,  const gchar *to,
333                                       const gchar *cc,  const gchar *bcc,
334                                       const gchar *subject, const gchar *plain_body,
335                                       const gchar *html_body,
336                                       const GList *attachments_list,
337                                       TnyHeaderFlags priority_flags)
338 {
339         TnyMsg *msg = NULL;
340         TnyFolder *folder = NULL;
341         ModestMailOperationPrivate *priv = NULL;
342         GError *err = NULL;
343
344         /* GList *node = NULL; */
345
346         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
347         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
348
349         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
350
351         if (html_body == NULL) {
352                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
353         } else {
354                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
355         }
356         if (!msg) {
357                 g_printerr ("modest: failed to create a new msg\n");
358                 goto cleanup;
359         }
360
361         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
362         if (!folder) {
363                 g_printerr ("modest: failed to find Drafts folder\n");
364                 goto cleanup;
365         }
366         
367         tny_folder_add_msg (folder, msg, &err);
368         if (err) {
369                 g_printerr ("modest: error adding msg to Drafts folder: %s",
370                             err->message);
371                 g_error_free (err);
372                 goto cleanup;
373         }
374
375         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
376
377         /* Free */
378 cleanup:
379         if (msg)
380                 g_object_unref (G_OBJECT(msg));
381         if (folder)
382                 g_object_unref (G_OBJECT(folder));
383 }
384
385 typedef struct 
386 {
387         ModestMailOperation *mail_op;
388         TnyStoreAccount *account;
389         TnyTransportAccount *transport_account;
390 } UpdateAccountInfo;
391
392 static void
393 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
394 {
395         TnyIterator *iter;
396         TnyList *folders = tny_simple_list_new ();
397
398         tny_folder_store_get_folders (store, folders, query, NULL);
399         iter = tny_list_create_iterator (folders);
400
401         while (!tny_iterator_is_done (iter)) {
402
403                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
404
405                 tny_list_prepend (all_folders, G_OBJECT (folder));
406                 recurse_folders (folder, query, all_folders);    
407                 g_object_unref (G_OBJECT (folder));
408
409                 tny_iterator_next (iter);
410         }
411          g_object_unref (G_OBJECT (iter));
412          g_object_unref (G_OBJECT (folders));
413 }
414
415 /* 
416  * Used by update_account_thread to emit the "progress-changed" signal
417  * from the main loop. We call it inside an idle call to achieve that
418  */
419 static gboolean
420 notify_update_account_observers (gpointer data)
421 {
422         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
423
424         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
425
426         return TRUE;
427 }
428
429 /* 
430  * Used by update_account_thread to notify the queue from the main
431  * loop. We call it inside an idle call to achieve that
432  */
433 static gboolean
434 notify_update_account_queue (gpointer data)
435 {
436         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
437
438         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), 
439                                             mail_op);
440         g_object_unref (mail_op);
441
442         return FALSE;
443 }
444
445 static gpointer
446 update_account_thread (gpointer thr_user_data)
447 {
448         UpdateAccountInfo *info;
449         TnyList *all_folders = NULL;
450         TnyIterator *iter = NULL;
451         TnyFolderStoreQuery *query = NULL;
452         ModestMailOperationPrivate *priv;
453         ModestTnySendQueue *send_queue;
454         gint timeout;
455
456         info = (UpdateAccountInfo *) thr_user_data;
457         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
458
459         /* Get all the folders We can do it synchronously because
460            we're already running in a different thread than the UI */
461         all_folders = tny_simple_list_new ();
462         query = tny_folder_store_query_new ();
463         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
464         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
465                                       all_folders,
466                                       query,
467                                       &(priv->error));
468         if (priv->error) {
469                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
470                 goto out;
471         }
472
473         iter = tny_list_create_iterator (all_folders);
474         while (!tny_iterator_is_done (iter)) {
475                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
476
477                 recurse_folders (folder, query, all_folders);
478                 tny_iterator_next (iter);
479         }
480         g_object_unref (G_OBJECT (iter));
481
482         /* Update status and notify. We need to call the notification
483            with a source functopm in order to call it from the main
484            loop. We need that in order not to get into trouble with
485            Gtk+. We use a timeout in order to provide more status
486            information, because the sync tinymail call does not
487            provide it for the moment */
488         timeout = g_timeout_add (250, notify_update_account_observers, info->mail_op);
489
490         /* Refresh folders */
491         iter = tny_list_create_iterator (all_folders);
492         while (!tny_iterator_is_done (iter) && !priv->error) {
493
494                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
495
496                 /* Refresh the folder */
497                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
498
499                 if (priv->error) {
500                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
501                 }
502
503                 g_object_unref (G_OBJECT (folder));
504                 tny_iterator_next (iter);
505         }
506         g_object_unref (G_OBJECT (iter));
507         g_source_remove (timeout);
508
509         /* Perform send */
510         priv->id = MODEST_MAIL_OPERATION_ID_SEND;
511
512         send_queue = modest_tny_send_queue_new (TNY_CAMEL_TRANSPORT_ACCOUNT(info->transport_account));
513
514         timeout = g_timeout_add (250, notify_update_account_observers, info->mail_op);
515         modest_tny_send_queue_flush (send_queue);
516         g_source_remove (timeout);
517
518         g_object_unref (G_OBJECT(send_queue));
519         
520         /* Check if the operation was a success */
521         if (!priv->error) {
522                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
523
524                 /* Update the last updated key */
525                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
526                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
527                                             MODEST_ACCOUNT_LAST_UPDATED, 
528                                             time(NULL), 
529                                             TRUE);
530         }
531
532  out:
533         /* Notify the queue. Note that the info could be freed before
534            this idle happens, but the mail operation will be still
535            alive */
536         g_idle_add (notify_update_account_queue, info->mail_op);
537
538         /* Frees */
539         g_object_unref (query);
540         g_object_unref (all_folders);
541         g_object_unref (info->account);
542         g_object_unref (info->transport_account);
543         g_slice_free (UpdateAccountInfo, info);
544
545         return NULL;
546 }
547
548 gboolean
549 modest_mail_operation_update_account (ModestMailOperation *self,
550                                       const gchar *account_name)
551 {
552         GThread *thread;
553         UpdateAccountInfo *info;
554         ModestMailOperationPrivate *priv;
555         TnyStoreAccount *modest_account;
556         TnyTransportAccount *transport_account;
557
558         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
559         g_return_val_if_fail (account_name, FALSE);
560
561         /* Init mail operation. Set total and done to 0, and do not
562            update them, this way the progress objects will know that
563            we have no clue about the number of the objects */
564         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
565         priv->total = 0;
566         priv->done  = 0;
567         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
568         
569         /* Get the Modest account */
570         modest_account = (TnyStoreAccount *)
571                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
572                                                                      account_name,
573                                                                      TNY_ACCOUNT_TYPE_STORE);
574
575         if (!modest_account) {
576                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
577                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
578                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
579                              "cannot get tny store account for %s\n", account_name);
580                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), 
581                                                     self);
582                 return FALSE;
583         }
584
585         /* Get the transport account, we can not do it in the thread
586            due to some problems with dbus */
587         transport_account = (TnyTransportAccount *)
588                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
589                                                                                     account_name);
590         if (!transport_account) {
591                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
592                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
593                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
594                              "cannot get tny transport account for %s\n", account_name);
595                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), 
596                                                     self);
597                 return FALSE;
598         }
599
600         /* Create the helper object */
601         info = g_slice_new (UpdateAccountInfo);
602         info->mail_op = self;
603         info->account = modest_account;
604         info->transport_account = transport_account;
605
606         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
607
608         return TRUE;
609 }
610
611 ModestMailOperationStatus
612 modest_mail_operation_get_status (ModestMailOperation *self)
613 {
614         ModestMailOperationPrivate *priv;
615
616         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
617         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
618                               MODEST_MAIL_OPERATION_STATUS_INVALID);
619
620         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
621         return priv->status;
622 }
623
624 const GError *
625 modest_mail_operation_get_error (ModestMailOperation *self)
626 {
627         ModestMailOperationPrivate *priv;
628
629         g_return_val_if_fail (self, NULL);
630         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
631
632         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
633         return priv->error;
634 }
635
636 gboolean 
637 modest_mail_operation_cancel (ModestMailOperation *self)
638 {
639         ModestMailOperationPrivate *priv;
640
641         if (!MODEST_IS_MAIL_OPERATION (self)) {
642                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
643                 return FALSE;
644         }
645
646         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
647
648         /* TODO: Tinymail does not support cancel operation  */
649         
650         /* Set new status */
651         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
652
653         /* Notify the queue */
654         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
655
656         return TRUE;
657 }
658
659 guint 
660 modest_mail_operation_get_task_done (ModestMailOperation *self)
661 {
662         ModestMailOperationPrivate *priv;
663
664         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
665
666         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
667         return priv->done;
668 }
669
670 guint 
671 modest_mail_operation_get_task_total (ModestMailOperation *self)
672 {
673         ModestMailOperationPrivate *priv;
674
675         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
676
677         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
678         return priv->total;
679 }
680
681 gboolean
682 modest_mail_operation_is_finished (ModestMailOperation *self)
683 {
684         ModestMailOperationPrivate *priv;
685         gboolean retval = FALSE;
686
687         if (!MODEST_IS_MAIL_OPERATION (self)) {
688                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
689                 return retval;
690         }
691
692         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
693
694         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
695             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
696             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
697             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
698                 retval = TRUE;
699         } else {
700                 retval = FALSE;
701         }
702
703         return retval;
704 }
705
706 /* ******************************************************************* */
707 /* ************************** STORE  ACTIONS ************************* */
708 /* ******************************************************************* */
709
710
711 TnyFolder *
712 modest_mail_operation_create_folder (ModestMailOperation *self,
713                                      TnyFolderStore *parent,
714                                      const gchar *name)
715 {
716         ModestTnyFolderRules rules;
717         ModestMailOperationPrivate *priv;
718         TnyFolder *new_folder = NULL;
719         gboolean can_create = FALSE;
720
721         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
722         g_return_val_if_fail (name, NULL);
723         
724         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
725
726         /* Check parent */
727         if (!TNY_IS_FOLDER (parent)) {
728                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
729                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
730                              _("mail_in_ui_folder_create_error"));
731         } else {
732                 /* Check folder rules */
733                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
734                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)
735                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
736                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
737                                      _("mail_in_ui_folder_create_error"));
738                 else
739                         can_create = TRUE;              
740         }
741
742         if (can_create) {
743                 /* Create the folder */
744                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
745                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
746         }
747
748         /* Notify the queue */
749         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
750
751         return new_folder;
752 }
753
754 void
755 modest_mail_operation_remove_folder (ModestMailOperation *self,
756                                      TnyFolder           *folder,
757                                      gboolean             remove_to_trash)
758 {
759         TnyAccount *account;
760         ModestMailOperationPrivate *priv;
761         ModestTnyFolderRules rules;
762
763         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
764         g_return_if_fail (TNY_IS_FOLDER (folder));
765         
766         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
767         
768         /* Check folder rules */
769         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
770         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
771                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
772                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
773                              _("mail_in_ui_folder_delete_error"));
774                 goto end;
775         }
776
777         /* Get the account */
778         account = tny_folder_get_account (folder);
779
780         /* Delete folder or move to trash */
781         if (remove_to_trash) {
782                 TnyFolder *trash_folder = NULL;
783 /*              TnyFolder *trash_folder, *new_folder; */
784                 trash_folder = modest_tny_account_get_special_folder (account,
785                                                                       TNY_FOLDER_TYPE_TRASH);
786                 /* TODO: error_handling */
787                  modest_mail_operation_xfer_folder (self, folder,
788                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
789 /*              new_folder = modest_mail_operation_xfer_folder (self, folder,  */
790 /*                                                              TNY_FOLDER_STORE (trash_folder), TRUE); */
791 /*              g_object_unref (G_OBJECT (new_folder)); */
792         } else {
793                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
794
795                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
796                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
797
798                 if (parent)
799                         g_object_unref (G_OBJECT (parent));
800         }
801         g_object_unref (G_OBJECT (account));
802
803  end:
804         /* Notify the queue */
805         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
806 }
807
808 void
809 modest_mail_operation_rename_folder (ModestMailOperation *self,
810                                      TnyFolder *folder,
811                                      const gchar *name)
812 {
813         ModestMailOperationPrivate *priv;
814         ModestTnyFolderRules rules;
815
816         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
817         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
818         g_return_if_fail (name);
819         
820         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
821
822         /* Check folder rules */
823         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
824         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
825                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
826                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
827                              _("FIXME: unable to rename"));
828         } else {
829                 /* Rename. Camel handles folder subscription/unsubscription */
830
831                 TnyFolderStore *into;
832                 TnyFolder *nfol;
833
834                 into = tny_folder_get_folder_store (folder);
835                 nfol = tny_folder_copy (folder, into, name, TRUE, &(priv->error));
836                 if (into)
837                         g_object_unref (into);
838                 if (nfol)
839                         g_object_unref (nfol);
840
841                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
842         }
843
844         /* Notify the queue */
845         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
846  }
847
848 static void
849 transfer_folder_status_cb (GObject *obj,
850                            TnyStatus *status,  
851                            gpointer user_data)
852 {
853         XFerMsgAsyncHelper *helper = NULL;
854         ModestMailOperation *self;
855         ModestMailOperationPrivate *priv;
856
857         g_return_if_fail (status != NULL);
858         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
859
860         helper = (XFerMsgAsyncHelper *) user_data;
861         g_return_if_fail (helper != NULL);       
862
863         /* Temporary FIX: useful when tinymail send us status
864            information *after* calling the function callback */
865         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
866                 return;
867
868         self = helper->mail_op;
869         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
870
871         if ((status->position == 1) && (status->of_total == 100))
872                 return;
873
874         priv->done = status->position;
875         priv->total = status->of_total;
876
877
878         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
879 }
880
881
882 static void
883 transfer_folder_cb (TnyFolder *folder, TnyFolderStore *into, gboolean cancelled, TnyFolder *new_folder, GError **err, gpointer user_data)
884 {
885         XFerFolderAsyncHelper *helper = NULL;
886         ModestMailOperation *self = NULL;
887         ModestMailOperationPrivate *priv = NULL;
888
889         helper = (XFerFolderAsyncHelper *) user_data;
890         self = helper->mail_op;
891
892         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
893
894         if (*err) {
895                 priv->error = g_error_copy (*err);
896                 priv->done = 0;
897                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
898         } else if (cancelled) {
899                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
900                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
901                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
902                              _("Error trying to refresh the contents of %s"),
903                              tny_folder_get_name (folder));
904         } else {
905                 priv->done = 1;
906                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
907         }
908
909         /* Free */
910         g_slice_free   (XFerFolderAsyncHelper, helper);
911         g_object_unref (folder);
912         g_object_unref (into);
913         g_object_unref (new_folder);
914
915         /* Notify the queue */
916         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
917 }
918
919 TnyFolder *
920 modest_mail_operation_xfer_folder (ModestMailOperation *self,
921                                    TnyFolder *folder,
922                                    TnyFolderStore *parent,
923                                    gboolean delete_original)
924 {
925         ModestMailOperationPrivate *priv;
926         TnyFolder *new_folder = NULL;
927         ModestTnyFolderRules rules;
928
929         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
930         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
931         g_return_val_if_fail (TNY_IS_FOLDER (folder), NULL);
932
933         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
934
935         /* The moveable restriction is applied also to copy operation */
936         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
937         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
938                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
939                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
940                              _("FIXME: unable to rename"));
941         } else {
942                 /* Move/Copy folder */
943                 new_folder = tny_folder_copy (folder,
944                                               parent,
945                                               tny_folder_get_name (folder),
946                                               delete_original,
947                                               &(priv->error));
948         }
949
950         /* Notify the queue */
951         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
952
953         return new_folder;
954 }
955
956 void
957 modest_mail_operation_xfer_folder_async (ModestMailOperation *self,
958                                          TnyFolder *folder,
959                                          TnyFolderStore *parent,
960                                          gboolean delete_original)
961 {
962         XFerFolderAsyncHelper *helper = NULL;
963         ModestMailOperationPrivate *priv = NULL;
964         ModestTnyFolderRules rules;
965
966         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
967         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
968         g_return_if_fail (TNY_IS_FOLDER (folder));
969
970         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
971
972         /* Pick references for async calls */
973         g_object_ref (folder);
974         g_object_ref (parent);
975
976         /* The moveable restriction is applied also to copy operation */
977         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
978         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
979                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
980                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
981                              _("FIXME: unable to rename"));
982         } else {
983                 helper = g_slice_new0 (XFerFolderAsyncHelper);
984                 helper->mail_op = self;
985
986                 /* Move/Copy folder */          
987                 tny_folder_copy_async (folder,
988                                        parent,
989                                        tny_folder_get_name (folder),
990                                        delete_original,
991                                        transfer_folder_cb,
992                                        transfer_folder_status_cb,
993                                        helper);
994         }
995 }
996
997
998 /* ******************************************************************* */
999 /* **************************  MSG  ACTIONS  ************************* */
1000 /* ******************************************************************* */
1001
1002 void modest_mail_operation_get_msg (ModestMailOperation *self,
1003                                     TnyHeader *header,
1004                                     GetMsgAsynUserCallback user_callback,
1005                                     gpointer user_data)
1006 {
1007         GetMsgAsyncHelper *helper = NULL;
1008         TnyFolder *folder;
1009         ModestMailOperationPrivate *priv;
1010         
1011         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1012         g_return_if_fail (TNY_IS_HEADER (header));
1013         
1014         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1015         folder = tny_header_get_folder (header);
1016
1017         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1018
1019         /* Get message from folder */
1020         if (folder) {
1021                 helper = g_slice_new0 (GetMsgAsyncHelper);
1022                 helper->mail_op = self;
1023                 helper->user_callback = user_callback;
1024                 helper->pending_ops = 1;
1025                 helper->user_data = user_data;
1026
1027                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1028
1029                 g_object_unref (G_OBJECT (folder));
1030         } else {
1031                 /* Set status failed and set an error */
1032                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1033                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1034                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1035                              _("Error trying to get a message. No folder found for header"));
1036         }
1037 }
1038
1039 static void
1040 get_msg_cb (TnyFolder *folder, 
1041             gboolean cancelled, 
1042             TnyMsg *msg, 
1043             GError **error, 
1044             gpointer user_data)
1045 {
1046         GetMsgAsyncHelper *helper = NULL;
1047         ModestMailOperation *self = NULL;
1048         ModestMailOperationPrivate *priv = NULL;
1049
1050         helper = (GetMsgAsyncHelper *) user_data;
1051         g_return_if_fail (helper != NULL);       
1052         self = helper->mail_op;
1053         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1054         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1055         
1056         helper->pending_ops--;
1057
1058         /* Check errors and cancel */
1059         if (*error) {
1060                 priv->error = g_error_copy (*error);
1061                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1062                 goto out;
1063         }
1064         if (cancelled) {
1065                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1066                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1067                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1068                              _("Error trying to refresh the contents of %s"),
1069                              tny_folder_get_name (folder));
1070                 goto out;
1071         }
1072
1073         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1074
1075         /* If user defined callback function was defined, call it */
1076         if (helper->user_callback) {
1077                 helper->user_callback (priv->source, msg, helper->user_data);
1078         }
1079
1080         /* Free */
1081  out:
1082         if (helper->pending_ops == 0) {
1083                 g_slice_free (GetMsgAsyncHelper, helper);
1084                 
1085                 /* Notify the queue */
1086                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);  
1087         }
1088 }
1089
1090 static void     
1091 get_msg_status_cb (GObject *obj,
1092                    TnyStatus *status,  
1093                    gpointer user_data)
1094 {
1095         GetMsgAsyncHelper *helper = NULL;
1096         ModestMailOperation *self;
1097         ModestMailOperationPrivate *priv;
1098
1099         g_return_if_fail (status != NULL);
1100         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1101
1102         helper = (GetMsgAsyncHelper *) user_data;
1103         g_return_if_fail (helper != NULL);       
1104
1105         /* Temporary FIX: useful when tinymail send us status
1106            information *after* calling the function callback */
1107         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1108                 return;
1109
1110         self = helper->mail_op;
1111         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1112
1113         if ((status->position == 1) && (status->of_total == 100))
1114                 return;
1115
1116         priv->done = 1;
1117         priv->total = 1;
1118
1119         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1120 }
1121
1122 /****************************************************/
1123 typedef struct {
1124         ModestMailOperation *mail_op;
1125         TnyList *headers;
1126         GetMsgAsynUserCallback user_callback;
1127         gpointer user_data;
1128         GDestroyNotify notify;
1129 } GetFullMsgsInfo;
1130
1131 /* 
1132  * Used by get_msgs_full_thread to emit the "progress-changed" signal
1133  * from the main loop. We call it inside an idle call to achieve that
1134  */
1135 static gboolean
1136 notify_get_msgs_full_observers (gpointer data)
1137 {
1138         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1139
1140         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1141
1142         g_object_unref (mail_op);
1143
1144         return FALSE;
1145 }
1146
1147 typedef struct {
1148         GetMsgAsynUserCallback user_callback;
1149         TnyMsg *msg;
1150         gpointer user_data;
1151         GObject *source;
1152 } NotifyGetMsgsInfo;
1153
1154 static gboolean
1155 notify_get_msgs_full (gpointer data)
1156 {
1157         NotifyGetMsgsInfo *info;
1158
1159         info = (NotifyGetMsgsInfo *) data;      
1160
1161         /* Call the user callback */
1162         info->user_callback (info->source, info->msg, info->user_data);
1163
1164         g_slice_free (NotifyGetMsgsInfo, info);
1165
1166         return FALSE;
1167 }
1168
1169 static gboolean
1170 get_msgs_full_destroyer (gpointer data)
1171 {
1172         GetFullMsgsInfo *info;
1173
1174         info = (GetFullMsgsInfo *) data;
1175
1176         if (info->notify)
1177                 info->notify (info->user_data);
1178
1179         /* free */
1180         g_object_unref (info->headers);
1181         g_slice_free (GetFullMsgsInfo, info);
1182
1183         return FALSE;
1184 }
1185
1186 static gpointer
1187 get_msgs_full_thread (gpointer thr_user_data)
1188 {
1189         GetFullMsgsInfo *info;
1190         ModestMailOperationPrivate *priv = NULL;
1191         TnyIterator *iter = NULL;
1192         
1193         info = (GetFullMsgsInfo *) thr_user_data;       
1194         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1195
1196         iter = tny_list_create_iterator (info->headers);
1197         while (!tny_iterator_is_done (iter) && !priv->error) { 
1198                 TnyHeader *header;
1199                 TnyFolder *folder;
1200                 
1201                 header = TNY_HEADER (tny_iterator_get_current (iter));
1202                 folder = tny_header_get_folder (header);
1203                                 
1204                 /* Get message from folder */
1205                 if (folder) {
1206                         TnyMsg *msg;
1207                         /* The callback will call it per each header */
1208                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1209
1210                         if (msg) {
1211                                 priv->done++;
1212
1213                                 /* notify progress */
1214                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1215                                                  notify_get_msgs_full_observers, 
1216                                                  g_object_ref (info->mail_op), NULL);
1217
1218                                 /* The callback is the responsible for
1219                                    freeing the message */
1220                                 if (info->user_callback) {
1221                                         NotifyGetMsgsInfo *info_notify;
1222                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1223                                         info_notify->user_callback = info->user_callback;
1224                                         info_notify->source = priv->source;
1225                                         info_notify->msg = msg;
1226                                         info_notify->user_data = info->user_data;
1227                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1228                                                          notify_get_msgs_full, 
1229                                                          info_notify, NULL);
1230                                 } else {
1231                                         g_object_unref (msg);
1232                                 }
1233                         }
1234                 } else {
1235                         /* Set status failed and set an error */
1236                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1237                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1238                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1239                                      "Error trying to get a message. No folder found for header");
1240                 }
1241
1242                 g_object_unref (header);                
1243                 tny_iterator_next (iter);
1244         }
1245
1246         /* Notify the queue */
1247         g_idle_add (notify_update_account_queue, info->mail_op);
1248
1249         /* Free thread resources. Will be called after all previous idles */
1250         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1251
1252         return NULL;
1253 }
1254
1255 void 
1256 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1257                                      TnyList *header_list, 
1258                                      GetMsgAsynUserCallback user_callback,
1259                                      gpointer user_data,
1260                                      GDestroyNotify notify)
1261 {
1262         GThread *thread;
1263         ModestMailOperationPrivate *priv = NULL;
1264         GetFullMsgsInfo *info = NULL;
1265         
1266         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1267         
1268         /* Init mail operation */
1269         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1270         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1271         priv->done = 0;
1272         priv->total = tny_list_get_length(header_list);
1273
1274         /* Create the info */
1275         info = g_slice_new0 (GetFullMsgsInfo);
1276         info->mail_op = self;
1277         info->user_callback = user_callback;
1278         info->user_data = user_data;
1279         info->headers = g_object_ref (header_list);
1280         info->notify = notify;
1281
1282         /* Call the thread */
1283         thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1284 }
1285
1286
1287 void 
1288 modest_mail_operation_remove_msg (ModestMailOperation *self,
1289                                   TnyHeader *header,
1290                                   gboolean remove_to_trash)
1291 {
1292         TnyFolder *folder;
1293         ModestMailOperationPrivate *priv;
1294
1295         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1296         g_return_if_fail (TNY_IS_HEADER (header));
1297
1298         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1299         folder = tny_header_get_folder (header);
1300
1301         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1302
1303         /* Delete or move to trash */
1304         if (remove_to_trash) {
1305                 TnyFolder *trash_folder;
1306                 TnyStoreAccount *store_account;
1307
1308                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1309                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1310                                                                       TNY_FOLDER_TYPE_TRASH);
1311                 if (trash_folder) {
1312                         TnyList *headers;
1313
1314                         /* Create list */
1315                         headers = tny_simple_list_new ();
1316                         tny_list_append (headers, G_OBJECT (header));
1317                         g_object_unref (header);
1318
1319                         /* Move to trash */
1320                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1321                         g_object_unref (headers);
1322 /*                      g_object_unref (trash_folder); */
1323                 } else {
1324                         ModestMailOperationPrivate *priv;
1325
1326                         /* Set status failed and set an error */
1327                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1328                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1329                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1330                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1331                                      _("Error trying to delete a message. Trash folder not found"));
1332                 }
1333
1334                 g_object_unref (G_OBJECT (store_account));
1335         } else {
1336                 tny_folder_remove_msg (folder, header, &(priv->error));
1337                 if (!priv->error)
1338                         tny_folder_sync(folder, TRUE, &(priv->error));
1339         }
1340
1341         /* Set status */
1342         if (!priv->error)
1343                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1344         else
1345                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1346
1347         /* Free */
1348         g_object_unref (G_OBJECT (folder));
1349
1350         /* Notify the queue */
1351         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1352 }
1353
1354 static void
1355 transfer_msgs_status_cb (GObject *obj,
1356                          TnyStatus *status,  
1357                          gpointer user_data)
1358 {
1359         XFerMsgAsyncHelper *helper = NULL;
1360         ModestMailOperation *self;
1361         ModestMailOperationPrivate *priv;
1362
1363         g_return_if_fail (status != NULL);
1364         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1365
1366         helper = (XFerMsgAsyncHelper *) user_data;
1367         g_return_if_fail (helper != NULL);       
1368
1369         /* Temporary FIX: useful when tinymail send us status
1370            information *after* calling the function callback */
1371         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1372                 return;
1373
1374         self = helper->mail_op;
1375         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1376
1377         if ((status->position == 1) && (status->of_total == 100))
1378                 return;
1379
1380         priv->done = status->position;
1381         priv->total = status->of_total;
1382
1383         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1384 }
1385
1386
1387 static void
1388 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1389 {
1390         XFerMsgAsyncHelper *helper;
1391         ModestMailOperation *self;
1392         ModestMailOperationPrivate *priv;
1393
1394         helper = (XFerMsgAsyncHelper *) user_data;
1395         self = helper->mail_op;
1396
1397         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1398
1399         if (*err) {
1400                 priv->error = g_error_copy (*err);
1401                 priv->done = 0;
1402                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1403         } else if (cancelled) {
1404                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1405                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1406                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1407                              _("Error trying to refresh the contents of %s"),
1408                              tny_folder_get_name (folder));
1409         } else {
1410                 priv->done = 1;
1411                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1412         }
1413
1414         /* If user defined callback function was defined, call it */
1415         if (helper->user_callback) {
1416                 helper->user_callback (priv->source, helper->user_data);
1417         }
1418
1419         /* Free */
1420         g_object_unref (helper->headers);
1421         g_object_unref (helper->dest_folder);
1422         g_object_unref (helper->mail_op);
1423         g_slice_free   (XFerMsgAsyncHelper, helper);
1424         g_object_unref (folder);
1425
1426         /* Notify the queue */
1427         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1428 }
1429
1430 void
1431 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1432                                  TnyList *headers, 
1433                                  TnyFolder *folder, 
1434                                  gboolean delete_original,
1435                                  XferMsgsAsynUserCallback user_callback,
1436                                  gpointer user_data)
1437 {
1438         ModestMailOperationPrivate *priv;
1439         TnyIterator *iter;
1440         TnyFolder *src_folder;
1441         XFerMsgAsyncHelper *helper;
1442         TnyHeader *header;
1443
1444         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1445         g_return_if_fail (TNY_IS_LIST (headers));
1446         g_return_if_fail (TNY_IS_FOLDER (folder));
1447
1448         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1449         priv->total = 1;
1450         priv->done = 0;
1451         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1452
1453         /* Create the helper */
1454         helper = g_slice_new0 (XFerMsgAsyncHelper);
1455         helper->mail_op = g_object_ref(self);
1456         helper->dest_folder = g_object_ref(folder);
1457         helper->headers = g_object_ref(headers);
1458         helper->user_callback = user_callback;
1459         helper->user_data = user_data;
1460
1461         /* Get source folder */
1462         iter = tny_list_create_iterator (headers);
1463         header = TNY_HEADER (tny_iterator_get_current (iter));
1464         src_folder = tny_header_get_folder (header);
1465         g_object_unref (header);
1466         g_object_unref (iter);
1467
1468         /* Transfer messages */
1469         tny_folder_transfer_msgs_async (src_folder, 
1470                                         headers, 
1471                                         folder, 
1472                                         delete_original, 
1473                                         transfer_msgs_cb, 
1474                                         transfer_msgs_status_cb,
1475                                         helper);
1476 }
1477
1478
1479 static void
1480 on_refresh_folder (TnyFolder   *folder, 
1481                    gboolean     cancelled, 
1482                    GError     **error,
1483                    gpointer     user_data)
1484 {
1485         ModestMailOperation *self;
1486         ModestMailOperationPrivate *priv;
1487
1488         self = MODEST_MAIL_OPERATION (user_data);
1489         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1490
1491         if (*error) {
1492                 priv->error = g_error_copy (*error);
1493                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1494                 goto out;
1495         }
1496
1497         if (cancelled) {
1498                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1499                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1500                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1501                              _("Error trying to refresh the contents of %s"),
1502                              tny_folder_get_name (folder));
1503                 goto out;
1504         }
1505
1506         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1507
1508  out:
1509         /* Free */
1510         g_object_unref (folder);
1511
1512         /* Notify the queue */
1513         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1514 }
1515
1516 static void
1517 on_refresh_folder_status_update (GObject *obj,
1518                                  TnyStatus *status,
1519                                  gpointer user_data)
1520 {
1521         ModestMailOperation *self;
1522         ModestMailOperationPrivate *priv;
1523
1524         g_return_if_fail (status != NULL);
1525         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1526
1527         /* Temporary FIX: useful when tinymail send us status
1528            information *after* calling the function callback */
1529         if (!MODEST_IS_MAIL_OPERATION (user_data))
1530                 return;
1531
1532         self = MODEST_MAIL_OPERATION (user_data);
1533         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1534
1535         priv->done = status->position;
1536         priv->total = status->of_total;
1537
1538         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1539 }
1540
1541 void 
1542 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1543                                        TnyFolder *folder)
1544 {
1545         ModestMailOperationPrivate *priv;
1546
1547         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1548
1549         /* Pick a reference */
1550         g_object_ref (folder);
1551
1552         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1553
1554         /* Refresh the folder. TODO: tinymail could issue a status
1555            updates before the callback call then this could happen. We
1556            must review the design */
1557         tny_folder_refresh_async (folder,
1558                                   on_refresh_folder,
1559                                   on_refresh_folder_status_update,
1560                                   self);
1561 }
1562
1563 void
1564 _modest_mail_operation_notify_end (ModestMailOperation *self)
1565 {
1566         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1567 }