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