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