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