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