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