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