* modest-mail-operation.c: Set FAILED status if an exception is received.
[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                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1018                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1019                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1020                              _("FIXME: unable to rename"));
1021         } else {
1022                 helper = g_slice_new0 (XFerFolderAsyncHelper);
1023                 helper->mail_op = self;
1024
1025                 /* Move/Copy folder */          
1026                 tny_folder_copy_async (folder,
1027                                        parent,
1028                                        tny_folder_get_name (folder),
1029                                        delete_original,
1030                                        transfer_folder_cb,
1031                                        transfer_folder_status_cb,
1032                                        helper);
1033         }
1034 }
1035
1036
1037 /* ******************************************************************* */
1038 /* **************************  MSG  ACTIONS  ************************* */
1039 /* ******************************************************************* */
1040
1041 void modest_mail_operation_get_msg (ModestMailOperation *self,
1042                                     TnyHeader *header,
1043                                     GetMsgAsynUserCallback user_callback,
1044                                     gpointer user_data)
1045 {
1046         GetMsgAsyncHelper *helper = NULL;
1047         TnyFolder *folder;
1048         ModestMailOperationPrivate *priv;
1049         
1050         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1051         g_return_if_fail (TNY_IS_HEADER (header));
1052         
1053         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1054         folder = tny_header_get_folder (header);
1055
1056         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1057
1058         /* Get message from folder */
1059         if (folder) {
1060                 helper = g_slice_new0 (GetMsgAsyncHelper);
1061                 helper->mail_op = self;
1062                 helper->user_callback = user_callback;
1063                 helper->pending_ops = 1;
1064                 helper->user_data = user_data;
1065
1066                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1067
1068                 g_object_unref (G_OBJECT (folder));
1069         } else {
1070                 /* Set status failed and set an error */
1071                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1072                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1073                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1074                              _("Error trying to get a message. No folder found for header"));
1075         }
1076 }
1077
1078 static void
1079 get_msg_cb (TnyFolder *folder, 
1080             gboolean cancelled, 
1081             TnyMsg *msg, 
1082             GError **error, 
1083             gpointer user_data)
1084 {
1085         GetMsgAsyncHelper *helper = NULL;
1086         ModestMailOperation *self = NULL;
1087         ModestMailOperationPrivate *priv = NULL;
1088
1089         helper = (GetMsgAsyncHelper *) user_data;
1090         g_return_if_fail (helper != NULL);       
1091         self = helper->mail_op;
1092         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1093         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1094         
1095         helper->pending_ops--;
1096
1097         /* Check errors and cancel */
1098         if (*error) {
1099                 priv->error = g_error_copy (*error);
1100                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1101                 goto out;
1102         }
1103         if (cancelled) {
1104                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1105                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1106                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1107                              _("Error trying to refresh the contents of %s"),
1108                              tny_folder_get_name (folder));
1109                 goto out;
1110         }
1111
1112         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1113
1114         /* If user defined callback function was defined, call it */
1115         if (helper->user_callback) {
1116                 helper->user_callback (priv->source, msg, helper->user_data);
1117         }
1118
1119         /* Free */
1120  out:
1121         if (helper->pending_ops == 0) {
1122                 g_slice_free (GetMsgAsyncHelper, helper);
1123                 
1124                 /* Notify the queue */
1125                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);  
1126         }
1127 }
1128
1129 static void     
1130 get_msg_status_cb (GObject *obj,
1131                    TnyStatus *status,  
1132                    gpointer user_data)
1133 {
1134         GetMsgAsyncHelper *helper = NULL;
1135         ModestMailOperation *self;
1136         ModestMailOperationPrivate *priv;
1137
1138         g_return_if_fail (status != NULL);
1139         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1140
1141         helper = (GetMsgAsyncHelper *) user_data;
1142         g_return_if_fail (helper != NULL);       
1143
1144         /* Temporary FIX: useful when tinymail send us status
1145            information *after* calling the function callback */
1146         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1147                 return;
1148
1149         self = helper->mail_op;
1150         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1151
1152         if ((status->position == 1) && (status->of_total == 100))
1153                 return;
1154
1155         priv->done = 1;
1156         priv->total = 1;
1157
1158         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1159 }
1160
1161 /****************************************************/
1162 typedef struct {
1163         ModestMailOperation *mail_op;
1164         TnyList *headers;
1165         GetMsgAsynUserCallback user_callback;
1166         gpointer user_data;
1167         GDestroyNotify notify;
1168 } GetFullMsgsInfo;
1169
1170 /* 
1171  * Used by get_msgs_full_thread to emit the "progress-changed" signal
1172  * from the main loop. We call it inside an idle call to achieve that
1173  */
1174 static gboolean
1175 notify_get_msgs_full_observers (gpointer data)
1176 {
1177         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1178
1179         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1180
1181         g_object_unref (mail_op);
1182
1183         return FALSE;
1184 }
1185
1186 typedef struct {
1187         GetMsgAsynUserCallback user_callback;
1188         TnyMsg *msg;
1189         gpointer user_data;
1190         GObject *source;
1191 } NotifyGetMsgsInfo;
1192
1193
1194 /* 
1195  * Used by get_msgs_full_thread to call the user_callback for each
1196  * message that has been read
1197  */
1198 static gboolean
1199 notify_get_msgs_full (gpointer data)
1200 {
1201         NotifyGetMsgsInfo *info;
1202
1203         info = (NotifyGetMsgsInfo *) data;      
1204
1205         /* Call the user callback */
1206         info->user_callback (info->source, info->msg, info->user_data);
1207
1208         g_slice_free (NotifyGetMsgsInfo, info);
1209
1210         return FALSE;
1211 }
1212
1213 /* 
1214  * Used by get_msgs_full_thread to free al the thread resources and to
1215  * call the destroy function for the passed user_data
1216  */
1217 static gboolean
1218 get_msgs_full_destroyer (gpointer data)
1219 {
1220         GetFullMsgsInfo *info;
1221
1222         info = (GetFullMsgsInfo *) data;
1223
1224         if (info->notify)
1225                 info->notify (info->user_data);
1226
1227         /* free */
1228         g_object_unref (info->headers);
1229         g_slice_free (GetFullMsgsInfo, info);
1230
1231         return FALSE;
1232 }
1233
1234 static gpointer
1235 get_msgs_full_thread (gpointer thr_user_data)
1236 {
1237         GetFullMsgsInfo *info;
1238         ModestMailOperationPrivate *priv = NULL;
1239         TnyIterator *iter = NULL;
1240         
1241         info = (GetFullMsgsInfo *) thr_user_data;       
1242         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1243
1244         iter = tny_list_create_iterator (info->headers);
1245         while (!tny_iterator_is_done (iter)) { 
1246                 TnyHeader *header;
1247                 TnyFolder *folder;
1248                 
1249                 header = TNY_HEADER (tny_iterator_get_current (iter));
1250                 folder = tny_header_get_folder (header);
1251                                 
1252                 /* Get message from folder */
1253                 if (folder) {
1254                         TnyMsg *msg;
1255                         /* The callback will call it per each header */
1256                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1257
1258                         if (msg) {
1259                                 priv->done++;
1260
1261                                 /* notify progress */
1262                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1263                                                  notify_get_msgs_full_observers, 
1264                                                  g_object_ref (info->mail_op), NULL);
1265
1266                                 /* The callback is the responsible for
1267                                    freeing the message */
1268                                 if (info->user_callback) {
1269                                         NotifyGetMsgsInfo *info_notify;
1270                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1271                                         info_notify->user_callback = info->user_callback;
1272                                         info_notify->source = priv->source;
1273                                         info_notify->msg = msg;
1274                                         info_notify->user_data = info->user_data;
1275                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1276                                                          notify_get_msgs_full, 
1277                                                          info_notify, NULL);
1278                                 } else {
1279                                         g_object_unref (msg);
1280                                 }
1281                         }
1282                 } else {
1283                         /* Set status failed and set an error */
1284                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1285                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1286                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1287                                      "Error trying to get a message. No folder found for header");
1288                 }
1289                 g_object_unref (header);                
1290                 tny_iterator_next (iter);
1291         }
1292
1293         /* Notify the queue */
1294         g_idle_add (notify_update_account_queue, info->mail_op);
1295
1296         /* Free thread resources. Will be called after all previous idles */
1297         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1298
1299         return NULL;
1300 }
1301
1302 void 
1303 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1304                                      TnyList *header_list, 
1305                                      GetMsgAsynUserCallback user_callback,
1306                                      gpointer user_data,
1307                                      GDestroyNotify notify)
1308 {
1309         GThread *thread;
1310         ModestMailOperationPrivate *priv = NULL;
1311         GetFullMsgsInfo *info = NULL;
1312         gboolean size_ok = TRUE;
1313         gint max_size;
1314         GError *error = NULL;
1315         const gint KB = 1024;
1316         
1317         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1318         
1319         /* Init mail operation */
1320         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1321         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1322         priv->done = 0;
1323         priv->total = tny_list_get_length(header_list);
1324
1325         /* Get msg size limit */
1326         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1327                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1328                                          &error);
1329         if (error) {
1330                 g_clear_error (&error);
1331                 max_size = G_MAXINT;
1332         } else {
1333                 max_size = max_size * KB;
1334         }
1335
1336         /* Check message size limits. If there is only one message
1337            always retrieve it */
1338         if (tny_list_get_length (header_list) > 1) {
1339                 TnyIterator *iter;
1340
1341                 iter = tny_list_create_iterator (header_list);
1342                 while (!tny_iterator_is_done (iter) && size_ok) {
1343                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1344                         if (tny_header_get_message_size (header) >= max_size)
1345                                 size_ok = FALSE;
1346                         g_object_unref (header);
1347                         tny_iterator_next (iter);
1348                 }
1349                 g_object_unref (iter);
1350         }
1351
1352         if (size_ok) {
1353                 /* Create the info */
1354                 info = g_slice_new0 (GetFullMsgsInfo);
1355                 info->mail_op = self;
1356                 info->user_callback = user_callback;
1357                 info->user_data = user_data;
1358                 info->headers = g_object_ref (header_list);
1359                 info->notify = notify;
1360
1361                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1362         } else {
1363                 /* FIXME: the error msg is different for pop */
1364                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1365                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1366                              _("emev_ni_ui_imap_msg_sizelimit_error"));
1367                 /* Remove from queue and free resources */
1368                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1369                 if (notify)
1370                         notify (user_data);
1371         }
1372 }
1373
1374
1375 void 
1376 modest_mail_operation_remove_msg (ModestMailOperation *self,
1377                                   TnyHeader *header,
1378                                   gboolean remove_to_trash)
1379 {
1380         TnyFolder *folder;
1381         ModestMailOperationPrivate *priv;
1382
1383         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1384         g_return_if_fail (TNY_IS_HEADER (header));
1385
1386         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1387         folder = tny_header_get_folder (header);
1388
1389         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1390
1391         /* Delete or move to trash */
1392         if (remove_to_trash) {
1393                 TnyFolder *trash_folder;
1394                 TnyStoreAccount *store_account;
1395
1396                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1397                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1398                                                                       TNY_FOLDER_TYPE_TRASH);
1399                 if (trash_folder) {
1400                         TnyList *headers;
1401
1402                         /* Create list */
1403                         headers = tny_simple_list_new ();
1404                         tny_list_append (headers, G_OBJECT (header));
1405                         g_object_unref (header);
1406
1407                         /* Move to trash */
1408                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1409                         g_object_unref (headers);
1410 /*                      g_object_unref (trash_folder); */
1411                 } else {
1412                         ModestMailOperationPrivate *priv;
1413
1414                         /* Set status failed and set an error */
1415                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1416                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1417                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1418                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1419                                      _("Error trying to delete a message. Trash folder not found"));
1420                 }
1421
1422                 g_object_unref (G_OBJECT (store_account));
1423         } else {
1424                 tny_folder_remove_msg (folder, header, &(priv->error));
1425                 if (!priv->error)
1426                         tny_folder_sync(folder, TRUE, &(priv->error));
1427         }
1428
1429         /* Set status */
1430         if (!priv->error)
1431                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1432         else
1433                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1434
1435         /* Free */
1436         g_object_unref (G_OBJECT (folder));
1437
1438         /* Notify the queue */
1439         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1440 }
1441
1442 static void
1443 transfer_msgs_status_cb (GObject *obj,
1444                          TnyStatus *status,  
1445                          gpointer user_data)
1446 {
1447         XFerMsgAsyncHelper *helper = NULL;
1448         ModestMailOperation *self;
1449         ModestMailOperationPrivate *priv;
1450
1451         g_return_if_fail (status != NULL);
1452         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1453
1454         helper = (XFerMsgAsyncHelper *) user_data;
1455         g_return_if_fail (helper != NULL);       
1456
1457         /* Temporary FIX: useful when tinymail send us status
1458            information *after* calling the function callback */
1459         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1460                 return;
1461
1462         self = helper->mail_op;
1463         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1464
1465         if ((status->position == 1) && (status->of_total == 100))
1466                 return;
1467
1468         priv->done = status->position;
1469         priv->total = status->of_total;
1470
1471         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1472 }
1473
1474
1475 static void
1476 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1477 {
1478         XFerMsgAsyncHelper *helper;
1479         ModestMailOperation *self;
1480         ModestMailOperationPrivate *priv;
1481
1482         helper = (XFerMsgAsyncHelper *) user_data;
1483         self = helper->mail_op;
1484
1485         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1486
1487         if (*err) {
1488                 priv->error = g_error_copy (*err);
1489                 priv->done = 0;
1490                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1491         } else if (cancelled) {
1492                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1493                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1494                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1495                              _("Error trying to refresh the contents of %s"),
1496                              tny_folder_get_name (folder));
1497         } else {
1498                 priv->done = 1;
1499                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1500         }
1501
1502         /* If user defined callback function was defined, call it */
1503         if (helper->user_callback) {
1504                 helper->user_callback (priv->source, helper->user_data);
1505         }
1506
1507         /* Free */
1508         g_object_unref (helper->headers);
1509         g_object_unref (helper->dest_folder);
1510         g_object_unref (helper->mail_op);
1511         g_slice_free   (XFerMsgAsyncHelper, helper);
1512         g_object_unref (folder);
1513
1514         /* Notify the queue */
1515         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1516 }
1517
1518 void
1519 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1520                                  TnyList *headers, 
1521                                  TnyFolder *folder, 
1522                                  gboolean delete_original,
1523                                  XferMsgsAsynUserCallback user_callback,
1524                                  gpointer user_data)
1525 {
1526         ModestMailOperationPrivate *priv;
1527         TnyIterator *iter;
1528         TnyFolder *src_folder;
1529         XFerMsgAsyncHelper *helper;
1530         TnyHeader *header;
1531
1532         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1533         g_return_if_fail (TNY_IS_LIST (headers));
1534         g_return_if_fail (TNY_IS_FOLDER (folder));
1535
1536         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1537         priv->total = 1;
1538         priv->done = 0;
1539         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1540
1541         /* Create the helper */
1542         helper = g_slice_new0 (XFerMsgAsyncHelper);
1543         helper->mail_op = g_object_ref(self);
1544         helper->dest_folder = g_object_ref(folder);
1545         helper->headers = g_object_ref(headers);
1546         helper->user_callback = user_callback;
1547         helper->user_data = user_data;
1548
1549         /* Get source folder */
1550         iter = tny_list_create_iterator (headers);
1551         header = TNY_HEADER (tny_iterator_get_current (iter));
1552         src_folder = tny_header_get_folder (header);
1553         g_object_unref (header);
1554         g_object_unref (iter);
1555
1556         /* Transfer messages */
1557         tny_folder_transfer_msgs_async (src_folder, 
1558                                         headers, 
1559                                         folder, 
1560                                         delete_original, 
1561                                         transfer_msgs_cb, 
1562                                         transfer_msgs_status_cb,
1563                                         helper);
1564 }
1565
1566
1567 static void
1568 on_refresh_folder (TnyFolder   *folder, 
1569                    gboolean     cancelled, 
1570                    GError     **error,
1571                    gpointer     user_data)
1572 {
1573         ModestMailOperation *self;
1574         ModestMailOperationPrivate *priv;
1575
1576         self = MODEST_MAIL_OPERATION (user_data);
1577         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1578
1579         if (*error) {
1580                 priv->error = g_error_copy (*error);
1581                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1582                 goto out;
1583         }
1584
1585         if (cancelled) {
1586                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1587                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1588                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1589                              _("Error trying to refresh the contents of %s"),
1590                              tny_folder_get_name (folder));
1591                 goto out;
1592         }
1593
1594         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1595
1596  out:
1597         /* Free */
1598         g_object_unref (folder);
1599
1600         /* Notify the queue */
1601         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1602 }
1603
1604 static void
1605 on_refresh_folder_status_update (GObject *obj,
1606                                  TnyStatus *status,
1607                                  gpointer user_data)
1608 {
1609         ModestMailOperation *self;
1610         ModestMailOperationPrivate *priv;
1611
1612         g_return_if_fail (status != NULL);
1613         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1614
1615         /* Temporary FIX: useful when tinymail send us status
1616            information *after* calling the function callback */
1617         if (!MODEST_IS_MAIL_OPERATION (user_data))
1618                 return;
1619
1620         self = MODEST_MAIL_OPERATION (user_data);
1621         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1622
1623         priv->done = status->position;
1624         priv->total = status->of_total;
1625
1626         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1627 }
1628
1629 void 
1630 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1631                                        TnyFolder *folder)
1632 {
1633         ModestMailOperationPrivate *priv;
1634
1635         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1636
1637         /* Pick a reference */
1638         g_object_ref (folder);
1639
1640         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1641
1642         /* Refresh the folder. TODO: tinymail could issue a status
1643            updates before the callback call then this could happen. We
1644            must review the design */
1645         tny_folder_refresh_async (folder,
1646                                   on_refresh_folder,
1647                                   on_refresh_folder_status_update,
1648                                   self);
1649 }
1650
1651 void
1652 _modest_mail_operation_notify_end (ModestMailOperation *self)
1653 {
1654         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1655 }