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