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