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