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