* Removed call to mail_operation_queue_remove from
[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 /*      tny_account_cancel (); */
687
688         /* Set new status */
689         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
690
691         /* Notify the queue */
692         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
693
694         return TRUE;
695 }
696
697 guint 
698 modest_mail_operation_get_task_done (ModestMailOperation *self)
699 {
700         ModestMailOperationPrivate *priv;
701
702         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
703
704         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
705         return priv->done;
706 }
707
708 guint 
709 modest_mail_operation_get_task_total (ModestMailOperation *self)
710 {
711         ModestMailOperationPrivate *priv;
712
713         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
714
715         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
716         return priv->total;
717 }
718
719 gboolean
720 modest_mail_operation_is_finished (ModestMailOperation *self)
721 {
722         ModestMailOperationPrivate *priv;
723         gboolean retval = FALSE;
724
725         if (!MODEST_IS_MAIL_OPERATION (self)) {
726                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
727                 return retval;
728         }
729
730         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
731
732         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
733             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
734             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
735             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
736                 retval = TRUE;
737         } else {
738                 retval = FALSE;
739         }
740
741         return retval;
742 }
743
744 /* ******************************************************************* */
745 /* ************************** STORE  ACTIONS ************************* */
746 /* ******************************************************************* */
747
748
749 TnyFolder *
750 modest_mail_operation_create_folder (ModestMailOperation *self,
751                                      TnyFolderStore *parent,
752                                      const gchar *name)
753 {
754         ModestTnyFolderRules rules;
755         ModestMailOperationPrivate *priv;
756         TnyFolder *new_folder = NULL;
757         gboolean can_create = FALSE;
758
759         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
760         g_return_val_if_fail (name, NULL);
761         
762         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
763
764         /* Check parent */
765         if (!TNY_IS_FOLDER (parent)) {
766                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
767                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
768                              _("mail_in_ui_folder_create_error"));
769         } else {
770                 /* Check folder rules */
771                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
772                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)
773                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
774                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
775                                      _("mail_in_ui_folder_create_error"));
776                 else
777                         can_create = TRUE;              
778         }
779
780         if (can_create) {
781                 /* Create the folder */
782                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
783                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
784         }
785
786         /* Notify the queue */
787         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
788
789         return new_folder;
790 }
791
792 void
793 modest_mail_operation_remove_folder (ModestMailOperation *self,
794                                      TnyFolder           *folder,
795                                      gboolean             remove_to_trash)
796 {
797         TnyAccount *account;
798         ModestMailOperationPrivate *priv;
799         ModestTnyFolderRules rules;
800
801         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
802         g_return_if_fail (TNY_IS_FOLDER (folder));
803         
804         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
805         
806         /* Check folder rules */
807         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
808         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
809                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
810                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
811                              _("mail_in_ui_folder_delete_error"));
812                 goto end;
813         }
814
815         /* Get the account */
816         account = tny_folder_get_account (folder);
817
818         /* Delete folder or move to trash */
819         if (remove_to_trash) {
820                 TnyFolder *trash_folder = NULL;
821 /*              TnyFolder *trash_folder, *new_folder; */
822                 trash_folder = modest_tny_account_get_special_folder (account,
823                                                                       TNY_FOLDER_TYPE_TRASH);
824                 /* TODO: error_handling */
825                  modest_mail_operation_xfer_folder (self, folder,
826                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
827 /*              new_folder = modest_mail_operation_xfer_folder (self, folder,  */
828 /*                                                              TNY_FOLDER_STORE (trash_folder), TRUE); */
829 /*              g_object_unref (G_OBJECT (new_folder)); */
830         } else {
831                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
832
833                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
834                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
835
836                 if (parent)
837                         g_object_unref (G_OBJECT (parent));
838         }
839         g_object_unref (G_OBJECT (account));
840
841  end:
842         /* Notify the queue */
843         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
844 }
845
846 void
847 modest_mail_operation_rename_folder (ModestMailOperation *self,
848                                      TnyFolder *folder,
849                                      const gchar *name)
850 {
851         ModestMailOperationPrivate *priv;
852         ModestTnyFolderRules rules;
853
854         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
855         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
856         g_return_if_fail (name);
857         
858         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
859
860         /* Check folder rules */
861         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
862         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
863                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
864                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
865                              _("FIXME: unable to rename"));
866         } else {
867                 /* Rename. Camel handles folder subscription/unsubscription */
868                 TnyFolderStore *into;
869                 TnyFolder *nfol;
870
871                 into = tny_folder_get_folder_store (folder);
872                 nfol = tny_folder_copy (folder, into, name, TRUE, &(priv->error));
873                 if (into)
874                         g_object_unref (into);
875                 if (nfol)
876                         g_object_unref (nfol);
877
878                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
879                 
880         }
881
882         /* Notify the queue */
883         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
884  }
885
886 static void
887 transfer_folder_status_cb (GObject *obj,
888                            TnyStatus *status,  
889                            gpointer user_data)
890 {
891         XFerMsgAsyncHelper *helper = NULL;
892         ModestMailOperation *self;
893         ModestMailOperationPrivate *priv;
894
895         g_return_if_fail (status != NULL);
896         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
897
898         helper = (XFerMsgAsyncHelper *) user_data;
899         g_return_if_fail (helper != NULL);       
900
901         /* Temporary FIX: useful when tinymail send us status
902            information *after* calling the function callback */
903         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
904                 return;
905
906         self = helper->mail_op;
907         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
908
909         if ((status->position == 1) && (status->of_total == 100))
910                 return;
911
912         priv->done = status->position;
913         priv->total = status->of_total;
914
915
916         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
917 }
918
919
920 static void
921 transfer_folder_cb (TnyFolder *folder, TnyFolderStore *into, gboolean cancelled, TnyFolder *new_folder, GError **err, gpointer user_data)
922 {
923         XFerFolderAsyncHelper *helper = NULL;
924         ModestMailOperation *self = NULL;
925         ModestMailOperationPrivate *priv = NULL;
926
927         helper = (XFerFolderAsyncHelper *) user_data;
928         self = helper->mail_op;
929
930         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
931
932         if (*err) {
933                 priv->error = g_error_copy (*err);
934                 priv->done = 0;
935                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
936         } else if (cancelled) {
937                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
938                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
939                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
940                              _("Transference of %s was cancelled."),
941                              tny_folder_get_name (folder));
942         } else {
943                 priv->done = 1;
944                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
945         }
946                 
947         /* Free */
948         g_slice_free   (XFerFolderAsyncHelper, helper);
949         g_object_unref (folder);
950         g_object_unref (into);
951         if (new_folder != NULL) 
952                 g_object_unref (new_folder);
953
954         /* Notify the queue */
955         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
956 }
957
958 TnyFolder *
959 modest_mail_operation_xfer_folder (ModestMailOperation *self,
960                                    TnyFolder *folder,
961                                    TnyFolderStore *parent,
962                                    gboolean delete_original)
963 {
964         ModestMailOperationPrivate *priv;
965         TnyFolder *new_folder = NULL;
966         ModestTnyFolderRules rules;
967
968         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
969         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
970         g_return_val_if_fail (TNY_IS_FOLDER (folder), NULL);
971
972         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
973
974         /* The moveable restriction is applied also to copy operation */
975         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
976         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
977                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
978                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
979                              _("FIXME: unable to rename"));
980         } else {
981                 /* Move/Copy folder */
982                 new_folder = tny_folder_copy (folder,
983                                               parent,
984                                               tny_folder_get_name (folder),
985                                               delete_original,
986                                               &(priv->error));
987         }
988
989         /* Notify the queue */
990         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
991
992         return new_folder;
993 }
994
995 void
996 modest_mail_operation_xfer_folder_async (ModestMailOperation *self,
997                                          TnyFolder *folder,
998                                          TnyFolderStore *parent,
999                                          gboolean delete_original)
1000 {
1001         XFerFolderAsyncHelper *helper = NULL;
1002         ModestMailOperationPrivate *priv = NULL;
1003         ModestTnyFolderRules rules;
1004
1005         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1006         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1007         g_return_if_fail (TNY_IS_FOLDER (folder));
1008
1009         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1010
1011         /* Pick references for async calls */
1012         g_object_ref (folder);
1013         g_object_ref (parent);
1014
1015         /* The moveable restriction is applied also to copy operation */
1016         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1017         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
1018                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1019                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1020                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1021                              _("FIXME: unable to rename"));
1022
1023                 /* Notify the queue */
1024                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1025         } else {
1026                 helper = g_slice_new0 (XFerFolderAsyncHelper);
1027                 helper->mail_op = self;
1028
1029                 /* Move/Copy folder */          
1030                 tny_folder_copy_async (folder,
1031                                        parent,
1032                                        tny_folder_get_name (folder),
1033                                        delete_original,
1034                                        transfer_folder_cb,
1035                                        transfer_folder_status_cb,
1036                                        helper);
1037         }
1038 }
1039
1040
1041 /* ******************************************************************* */
1042 /* **************************  MSG  ACTIONS  ************************* */
1043 /* ******************************************************************* */
1044
1045 void modest_mail_operation_get_msg (ModestMailOperation *self,
1046                                     TnyHeader *header,
1047                                     GetMsgAsynUserCallback user_callback,
1048                                     gpointer user_data)
1049 {
1050         GetMsgAsyncHelper *helper = NULL;
1051         TnyFolder *folder;
1052         ModestMailOperationPrivate *priv;
1053         
1054         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1055         g_return_if_fail (TNY_IS_HEADER (header));
1056         
1057         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1058         folder = tny_header_get_folder (header);
1059
1060         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1061
1062         /* Get message from folder */
1063         if (folder) {
1064                 helper = g_slice_new0 (GetMsgAsyncHelper);
1065                 helper->mail_op = self;
1066                 helper->user_callback = user_callback;
1067                 helper->pending_ops = 1;
1068                 helper->user_data = user_data;
1069
1070                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1071
1072                 g_object_unref (G_OBJECT (folder));
1073         } else {
1074                 /* Set status failed and set an error */
1075                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1076                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1077                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1078                              _("Error trying to get a message. No folder found for header"));
1079         }
1080 }
1081
1082 static void
1083 get_msg_cb (TnyFolder *folder, 
1084             gboolean cancelled, 
1085             TnyMsg *msg, 
1086             GError **error, 
1087             gpointer user_data)
1088 {
1089         GetMsgAsyncHelper *helper = NULL;
1090         ModestMailOperation *self = NULL;
1091         ModestMailOperationPrivate *priv = NULL;
1092
1093         helper = (GetMsgAsyncHelper *) user_data;
1094         g_return_if_fail (helper != NULL);       
1095         self = helper->mail_op;
1096         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1097         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1098         
1099         helper->pending_ops--;
1100
1101         /* Check errors and cancel */
1102         if (*error) {
1103                 priv->error = g_error_copy (*error);
1104                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1105                 goto out;
1106         }
1107         if (cancelled) {
1108                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1109                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1110                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1111                              _("Error trying to refresh the contents of %s"),
1112                              tny_folder_get_name (folder));
1113                 goto out;
1114         }
1115
1116         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1117
1118         /* If user defined callback function was defined, call it */
1119         if (helper->user_callback) {
1120                 helper->user_callback (priv->source, msg, helper->user_data);
1121         }
1122
1123         /* Free */
1124  out:
1125         if (helper->pending_ops == 0) {
1126                 g_slice_free (GetMsgAsyncHelper, helper);
1127                 
1128                 /* Notify the queue */
1129                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);  
1130         }
1131 }
1132
1133 static void     
1134 get_msg_status_cb (GObject *obj,
1135                    TnyStatus *status,  
1136                    gpointer user_data)
1137 {
1138         GetMsgAsyncHelper *helper = NULL;
1139         ModestMailOperation *self;
1140         ModestMailOperationPrivate *priv;
1141
1142         g_return_if_fail (status != NULL);
1143         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1144
1145         helper = (GetMsgAsyncHelper *) user_data;
1146         g_return_if_fail (helper != NULL);       
1147
1148         /* Temporary FIX: useful when tinymail send us status
1149            information *after* calling the function callback */
1150         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1151                 return;
1152
1153         self = helper->mail_op;
1154         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1155
1156         if ((status->position == 1) && (status->of_total == 100))
1157                 return;
1158
1159         priv->done = 1;
1160         priv->total = 1;
1161
1162         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1163 }
1164
1165 /****************************************************/
1166 typedef struct {
1167         ModestMailOperation *mail_op;
1168         TnyList *headers;
1169         GetMsgAsynUserCallback user_callback;
1170         gpointer user_data;
1171         GDestroyNotify notify;
1172 } GetFullMsgsInfo;
1173
1174 /* 
1175  * Used by get_msgs_full_thread to emit the "progress-changed" signal
1176  * from the main loop. We call it inside an idle call to achieve that
1177  */
1178 static gboolean
1179 notify_get_msgs_full_observers (gpointer data)
1180 {
1181         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1182
1183         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1184
1185         g_object_unref (mail_op);
1186
1187         return FALSE;
1188 }
1189
1190 typedef struct {
1191         GetMsgAsynUserCallback user_callback;
1192         TnyMsg *msg;
1193         gpointer user_data;
1194         GObject *source;
1195 } NotifyGetMsgsInfo;
1196
1197
1198 /* 
1199  * Used by get_msgs_full_thread to call the user_callback for each
1200  * message that has been read
1201  */
1202 static gboolean
1203 notify_get_msgs_full (gpointer data)
1204 {
1205         NotifyGetMsgsInfo *info;
1206
1207         info = (NotifyGetMsgsInfo *) data;      
1208
1209         /* Call the user callback */
1210         info->user_callback (info->source, info->msg, info->user_data);
1211
1212         g_slice_free (NotifyGetMsgsInfo, info);
1213
1214         return FALSE;
1215 }
1216
1217 /* 
1218  * Used by get_msgs_full_thread to free al the thread resources and to
1219  * call the destroy function for the passed user_data
1220  */
1221 static gboolean
1222 get_msgs_full_destroyer (gpointer data)
1223 {
1224         GetFullMsgsInfo *info;
1225
1226         info = (GetFullMsgsInfo *) data;
1227
1228         if (info->notify)
1229                 info->notify (info->user_data);
1230
1231         /* free */
1232         g_object_unref (info->headers);
1233         g_slice_free (GetFullMsgsInfo, info);
1234
1235         return FALSE;
1236 }
1237
1238 static gpointer
1239 get_msgs_full_thread (gpointer thr_user_data)
1240 {
1241         GetFullMsgsInfo *info;
1242         ModestMailOperationPrivate *priv = NULL;
1243         TnyIterator *iter = NULL;
1244         
1245         info = (GetFullMsgsInfo *) thr_user_data;       
1246         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1247
1248         iter = tny_list_create_iterator (info->headers);
1249         while (!tny_iterator_is_done (iter)) { 
1250                 TnyHeader *header;
1251                 TnyFolder *folder;
1252                 
1253                 header = TNY_HEADER (tny_iterator_get_current (iter));
1254                 folder = tny_header_get_folder (header);
1255                                 
1256                 /* Get message from folder */
1257                 if (folder) {
1258                         TnyMsg *msg;
1259                         /* The callback will call it per each header */
1260                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1261
1262                         if (msg) {
1263                                 priv->done++;
1264
1265                                 /* notify progress */
1266                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1267                                                  notify_get_msgs_full_observers, 
1268                                                  g_object_ref (info->mail_op), NULL);
1269
1270                                 /* The callback is the responsible for
1271                                    freeing the message */
1272                                 if (info->user_callback) {
1273                                         NotifyGetMsgsInfo *info_notify;
1274                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1275                                         info_notify->user_callback = info->user_callback;
1276                                         info_notify->source = priv->source;
1277                                         info_notify->msg = msg;
1278                                         info_notify->user_data = info->user_data;
1279                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1280                                                          notify_get_msgs_full, 
1281                                                          info_notify, NULL);
1282                                 } else {
1283                                         g_object_unref (msg);
1284                                 }
1285                         }
1286                 } else {
1287                         /* Set status failed and set an error */
1288                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1289                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1290                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1291                                      "Error trying to get a message. No folder found for header");
1292                 }
1293                 g_object_unref (header);                
1294                 tny_iterator_next (iter);
1295         }
1296
1297         /* Notify the queue */
1298         g_idle_add (notify_update_account_queue, info->mail_op);
1299
1300         /* Free thread resources. Will be called after all previous idles */
1301         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1302
1303         return NULL;
1304 }
1305
1306 void 
1307 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1308                                      TnyList *header_list, 
1309                                      GetMsgAsynUserCallback user_callback,
1310                                      gpointer user_data,
1311                                      GDestroyNotify notify)
1312 {
1313         GThread *thread;
1314         ModestMailOperationPrivate *priv = NULL;
1315         GetFullMsgsInfo *info = NULL;
1316         gboolean size_ok = TRUE;
1317         gint max_size;
1318         GError *error = NULL;
1319         const gint KB = 1024;
1320         
1321         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1322         
1323         /* Init mail operation */
1324         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1325         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1326         priv->done = 0;
1327         priv->total = tny_list_get_length(header_list);
1328
1329         /* Get msg size limit */
1330         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1331                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1332                                          &error);
1333         if (error) {
1334                 g_clear_error (&error);
1335                 max_size = G_MAXINT;
1336         } else {
1337                 max_size = max_size * KB;
1338         }
1339
1340         /* Check message size limits. If there is only one message
1341            always retrieve it */
1342         if (tny_list_get_length (header_list) > 1) {
1343                 TnyIterator *iter;
1344
1345                 iter = tny_list_create_iterator (header_list);
1346                 while (!tny_iterator_is_done (iter) && size_ok) {
1347                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1348                         if (tny_header_get_message_size (header) >= max_size)
1349                                 size_ok = FALSE;
1350                         g_object_unref (header);
1351                         tny_iterator_next (iter);
1352                 }
1353                 g_object_unref (iter);
1354         }
1355
1356         if (size_ok) {
1357                 /* Create the info */
1358                 info = g_slice_new0 (GetFullMsgsInfo);
1359                 info->mail_op = self;
1360                 info->user_callback = user_callback;
1361                 info->user_data = user_data;
1362                 info->headers = g_object_ref (header_list);
1363                 info->notify = notify;
1364
1365                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1366         } else {
1367                 /* FIXME: the error msg is different for pop */
1368                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1369                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1370                              _("emev_ni_ui_imap_msg_sizelimit_error"));
1371                 /* Remove from queue and free resources */
1372                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1373                 if (notify)
1374                         notify (user_data);
1375         }
1376 }
1377
1378
1379 void 
1380 modest_mail_operation_remove_msg (ModestMailOperation *self,
1381                                   TnyHeader *header,
1382                                   gboolean remove_to_trash)
1383 {
1384         TnyFolder *folder;
1385         ModestMailOperationPrivate *priv;
1386
1387         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1388         g_return_if_fail (TNY_IS_HEADER (header));
1389
1390         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1391         folder = tny_header_get_folder (header);
1392
1393         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1394
1395         /* Delete or move to trash */
1396         if (remove_to_trash) {
1397                 TnyFolder *trash_folder;
1398                 TnyStoreAccount *store_account;
1399
1400                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1401                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1402                                                                       TNY_FOLDER_TYPE_TRASH);
1403                 if (trash_folder) {
1404                         TnyList *headers;
1405
1406                         /* Create list */
1407                         headers = tny_simple_list_new ();
1408                         tny_list_append (headers, G_OBJECT (header));
1409                         g_object_unref (header);
1410
1411                         /* Move to trash */
1412                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1413                         g_object_unref (headers);
1414 /*                      g_object_unref (trash_folder); */
1415                 } else {
1416                         ModestMailOperationPrivate *priv;
1417
1418                         /* Set status failed and set an error */
1419                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1420                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1421                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1422                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1423                                      _("Error trying to delete a message. Trash folder not found"));
1424                 }
1425
1426                 g_object_unref (G_OBJECT (store_account));
1427         } else {
1428                 tny_folder_remove_msg (folder, header, &(priv->error));
1429                 if (!priv->error)
1430                         tny_folder_sync(folder, TRUE, &(priv->error));
1431         }
1432
1433         /* Set status */
1434         if (!priv->error)
1435                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1436         else
1437                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1438
1439         /* Free */
1440         g_object_unref (G_OBJECT (folder));
1441
1442         /* Notify the queue */
1443         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1444 }
1445
1446 static void
1447 transfer_msgs_status_cb (GObject *obj,
1448                          TnyStatus *status,  
1449                          gpointer user_data)
1450 {
1451         XFerMsgAsyncHelper *helper = NULL;
1452         ModestMailOperation *self;
1453         ModestMailOperationPrivate *priv;
1454
1455         g_return_if_fail (status != NULL);
1456         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1457
1458         helper = (XFerMsgAsyncHelper *) user_data;
1459         g_return_if_fail (helper != NULL);       
1460
1461         /* Temporary FIX: useful when tinymail send us status
1462            information *after* calling the function callback */
1463         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1464                 return;
1465
1466         self = helper->mail_op;
1467         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1468
1469         if ((status->position == 1) && (status->of_total == 100))
1470                 return;
1471
1472         priv->done = status->position;
1473         priv->total = status->of_total;
1474
1475         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1476 }
1477
1478
1479 static void
1480 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1481 {
1482         XFerMsgAsyncHelper *helper;
1483         ModestMailOperation *self;
1484         ModestMailOperationPrivate *priv;
1485
1486         helper = (XFerMsgAsyncHelper *) user_data;
1487         self = helper->mail_op;
1488
1489         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1490
1491         if (*err) {
1492                 priv->error = g_error_copy (*err);
1493                 priv->done = 0;
1494                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1495         } else if (cancelled) {
1496                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1497                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1498                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1499                              _("Error trying to refresh the contents of %s"),
1500                              tny_folder_get_name (folder));
1501         } else {
1502                 priv->done = 1;
1503                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1504         }
1505
1506         /* If user defined callback function was defined, call it */
1507         if (helper->user_callback) {
1508                 helper->user_callback (priv->source, helper->user_data);
1509         }
1510
1511         /* Free */
1512         g_object_unref (helper->headers);
1513         g_object_unref (helper->dest_folder);
1514         g_object_unref (helper->mail_op);
1515         g_slice_free   (XFerMsgAsyncHelper, helper);
1516         g_object_unref (folder);
1517
1518         /* Notify the queue */
1519         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1520 }
1521
1522 void
1523 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1524                                  TnyList *headers, 
1525                                  TnyFolder *folder, 
1526                                  gboolean delete_original,
1527                                  XferMsgsAsynUserCallback user_callback,
1528                                  gpointer user_data)
1529 {
1530         ModestMailOperationPrivate *priv;
1531         TnyIterator *iter;
1532         TnyFolder *src_folder;
1533         XFerMsgAsyncHelper *helper;
1534         TnyHeader *header;
1535
1536         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1537         g_return_if_fail (TNY_IS_LIST (headers));
1538         g_return_if_fail (TNY_IS_FOLDER (folder));
1539
1540         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1541         priv->total = 1;
1542         priv->done = 0;
1543         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1544
1545         /* Create the helper */
1546         helper = g_slice_new0 (XFerMsgAsyncHelper);
1547         helper->mail_op = g_object_ref(self);
1548         helper->dest_folder = g_object_ref(folder);
1549         helper->headers = g_object_ref(headers);
1550         helper->user_callback = user_callback;
1551         helper->user_data = user_data;
1552
1553         /* Get source folder */
1554         iter = tny_list_create_iterator (headers);
1555         header = TNY_HEADER (tny_iterator_get_current (iter));
1556         src_folder = tny_header_get_folder (header);
1557         g_object_unref (header);
1558         g_object_unref (iter);
1559
1560         /* Transfer messages */
1561         tny_folder_transfer_msgs_async (src_folder, 
1562                                         headers, 
1563                                         folder, 
1564                                         delete_original, 
1565                                         transfer_msgs_cb, 
1566                                         transfer_msgs_status_cb,
1567                                         helper);
1568 }
1569
1570
1571 static void
1572 on_refresh_folder (TnyFolder   *folder, 
1573                    gboolean     cancelled, 
1574                    GError     **error,
1575                    gpointer     user_data)
1576 {
1577         ModestMailOperation *self;
1578         ModestMailOperationPrivate *priv;
1579
1580         self = MODEST_MAIL_OPERATION (user_data);
1581         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1582
1583         if (*error) {
1584                 priv->error = g_error_copy (*error);
1585                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1586                 goto out;
1587         }
1588
1589         if (cancelled) {
1590                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1591                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1592                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1593                              _("Error trying to refresh the contents of %s"),
1594                              tny_folder_get_name (folder));
1595                 goto out;
1596         }
1597
1598         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1599
1600  out:
1601         /* Free */
1602         g_object_unref (folder);
1603
1604         /* Notify the queue */
1605         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1606 }
1607
1608 static void
1609 on_refresh_folder_status_update (GObject *obj,
1610                                  TnyStatus *status,
1611                                  gpointer user_data)
1612 {
1613         ModestMailOperation *self;
1614         ModestMailOperationPrivate *priv;
1615
1616         g_return_if_fail (status != NULL);
1617         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1618
1619         /* Temporary FIX: useful when tinymail send us status
1620            information *after* calling the function callback */
1621         if (!MODEST_IS_MAIL_OPERATION (user_data))
1622                 return;
1623
1624         self = MODEST_MAIL_OPERATION (user_data);
1625         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1626
1627         priv->done = status->position;
1628         priv->total = status->of_total;
1629
1630         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1631 }
1632
1633 void 
1634 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1635                                        TnyFolder *folder)
1636 {
1637         ModestMailOperationPrivate *priv;
1638
1639         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1640
1641         /* Pick a reference */
1642         g_object_ref (folder);
1643
1644         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1645
1646         /* Refresh the folder. TODO: tinymail could issue a status
1647            updates before the callback call then this could happen. We
1648            must review the design */
1649         tny_folder_refresh_async (folder,
1650                                   on_refresh_folder,
1651                                   on_refresh_folder_status_update,
1652                                   self);
1653 }
1654
1655 void
1656 _modest_mail_operation_notify_end (ModestMailOperation *self)
1657 {
1658         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1659 }