44328ea670d1e4957db6fa81fc8755ea090bd73c
[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 gpointer
813 update_account_thread (gpointer thr_user_data)
814 {
815         UpdateAccountInfo *info;
816         TnyList *all_folders = NULL;
817         GPtrArray *new_headers;
818         TnyIterator *iter = NULL;
819         TnyFolderStoreQuery *query = NULL;
820         ModestMailOperationPrivate *priv;
821         ModestTnySendQueue *send_queue;
822
823
824         info = (UpdateAccountInfo *) thr_user_data;
825         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
826
827         /* Get account and set it into mail_operation */
828         priv->account = g_object_ref (info->account);
829
830         /* Get all the folders. We can do it synchronously because
831            we're already running in a different thread than the UI */
832         all_folders = tny_simple_list_new ();
833         query = tny_folder_store_query_new ();
834         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
835         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
836                                       all_folders,
837                                       query,
838                                       &(priv->error));
839         if (priv->error) {
840                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
841                 goto out;
842         }
843
844         iter = tny_list_create_iterator (all_folders);
845         while (!tny_iterator_is_done (iter)) {
846                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
847
848                 recurse_folders (folder, query, all_folders);
849                 tny_iterator_next (iter);
850         }
851         g_object_unref (G_OBJECT (iter));
852
853         /* Update status and notify. We need to call the notification
854            with a source function in order to call it from the main
855            loop. We need that in order not to get into trouble with
856            Gtk+. We use a timeout in order to provide more status
857            information, because the sync tinymail call does not
858            provide it for the moment */
859         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
860
861         /* Refresh folders */
862         new_headers = g_ptr_array_new ();
863         iter = tny_list_create_iterator (all_folders);
864
865         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
866
867                 InternalFolderObserver *observer;
868                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
869
870                 /* Refresh the folder */
871                 /* Our observer receives notification of new emails during folder refreshes,
872                  * so we can use observer->new_headers.
873                  * TODO: This does not seem to be providing accurate numbers.
874                  * Possibly the observer is notified asynchronously.
875                  */
876                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
877                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
878                 
879                 /* This gets the status information (headers) from the server.
880                  * We use the blocking version, because we are already in a separate 
881                  * thread.
882                  */
883
884                 /* If the retrieve type is headers only do nothing more */
885                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
886                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
887                         TnyIterator *iter;
888
889                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
890
891                         iter = tny_list_create_iterator (observer->new_headers);
892                         while (!tny_iterator_is_done (iter)) {
893                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
894                                 /* printf ("  DEBUG1.2 %s: checking size: account=%s, subject=%s\n", 
895                                  *      __FUNCTION__, tny_account_get_id (priv->account), 
896                                  * tny_header_get_subject (header));
897                                  */
898                                  
899                                 /* Apply per-message size limits */
900                                 if (tny_header_get_message_size (header) < info->max_size)
901                                         g_ptr_array_add (new_headers, g_object_ref (header));
902
903                                 g_object_unref (header);
904                                 tny_iterator_next (iter);
905                         }
906                         g_object_unref (iter);
907                 } else
908                         tny_folder_poke_status (TNY_FOLDER (folder));
909                 
910                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
911                 g_object_unref (observer);
912                 observer = NULL;
913
914                 if (priv->error)
915                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
916
917                 g_object_unref (G_OBJECT (folder));
918                 tny_iterator_next (iter);
919         }
920
921         did_a_cancel = FALSE;
922
923         g_object_unref (G_OBJECT (iter));
924         g_source_remove (timeout);
925
926         if (new_headers->len > 0) {
927                 gint msg_num = 0;
928
929                 /* Order by date */
930                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
931
932                 /* Apply message count limit */
933                 /* If the number of messages exceeds the maximum, ask the
934                  * user to download them all,
935                  * as per the UI spec "Retrieval Limits" section in 4.4: 
936                  */
937                 printf ("DEBUG: %s: account=%s, len=%d, retrieve_limit = %d\n", __FUNCTION__, 
938                         tny_account_get_id (priv->account), new_headers->len, info->retrieve_limit);
939                 if (new_headers->len > info->retrieve_limit) {
940                         /* TODO: Ask the user, instead of just failing, showing mail_nc_msg_count_limit_exceeded, 
941                          * with 'Get all' and 'Newest only' buttons. */
942                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
943                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
944                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
945                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
946                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
947                         goto out;
948                 }
949                 
950                 priv->done = 0;
951                 priv->total = MIN (new_headers->len, info->retrieve_limit);
952                 while (msg_num < priv->total) {
953
954                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
955                         TnyFolder *folder = tny_header_get_folder (header);
956                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
957                         ModestMailOperationState *state;
958                         ModestPair* pair;
959
960                         priv->done++;
961                         /* We can not just use the mail operation because the
962                            values of done and total could change before the
963                            idle is called */
964                         state = modest_mail_operation_clone_state (info->mail_op);
965                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
966                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
967                                          pair, (GDestroyNotify) modest_pair_free);
968
969                         g_object_unref (msg);
970                         g_object_unref (folder);
971
972                         msg_num++;
973                 }
974                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
975                 g_ptr_array_free (new_headers, FALSE);
976         }
977
978         /* Perform send */
979         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
980         priv->done = 0;
981         priv->total = 0;
982         if (priv->account != NULL) 
983                 g_object_unref (priv->account);
984         priv->account = g_object_ref (info->transport_account);
985         
986         send_queue = modest_runtime_get_send_queue (info->transport_account);
987         if (send_queue) {
988                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
989                 modest_tny_send_queue_try_to_send (send_queue);
990                 g_source_remove (timeout);
991         } else {
992                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
993                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
994                              "cannot create a send queue for %s\n", 
995                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
996                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
997         }
998         
999         /* Check if the operation was a success */
1000         if (!priv->error) {
1001                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1002
1003                 /* Update the last updated key */
1004                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
1005                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
1006                                             MODEST_ACCOUNT_LAST_UPDATED, 
1007                                             time(NULL), 
1008                                             TRUE);
1009         }
1010
1011  out:
1012         /* Notify about operation end. Note that the info could be
1013            freed before this idle happens, but the mail operation will
1014            be still alive */
1015         g_idle_add (notify_update_account_queue, info->mail_op);
1016
1017         /* Frees */
1018         g_object_unref (query);
1019         g_object_unref (all_folders);
1020         g_object_unref (info->account);
1021         g_object_unref (info->transport_account);
1022         g_free (info->retrieve_type);
1023         g_slice_free (UpdateAccountInfo, info);
1024
1025         return NULL;
1026 }
1027
1028 gboolean
1029 modest_mail_operation_update_account (ModestMailOperation *self,
1030                                       const gchar *account_name)
1031 {
1032         GThread *thread;
1033         UpdateAccountInfo *info;
1034         ModestMailOperationPrivate *priv;
1035         ModestAccountMgr *mgr;
1036         TnyStoreAccount *modest_account;
1037         TnyTransportAccount *transport_account;
1038
1039         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1040         g_return_val_if_fail (account_name, FALSE);
1041
1042         /* Make sure that we have a connection, and request one 
1043          * if necessary:
1044          * TODO: Is there some way to trigger this for every attempt to 
1045          * use the network? */
1046         if (!modest_platform_connect_and_wait(NULL))
1047                 return FALSE;
1048         
1049         /* Init mail operation. Set total and done to 0, and do not
1050            update them, this way the progress objects will know that
1051            we have no clue about the number of the objects */
1052         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1053         priv->total = 0;
1054         priv->done  = 0;
1055         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1056         
1057         /* Get the Modest account */
1058         modest_account = (TnyStoreAccount *)
1059                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1060                                                                      account_name,
1061                                                                      TNY_ACCOUNT_TYPE_STORE);
1062
1063         if (!modest_account) {
1064                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1065                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1066                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1067                              "cannot get tny store account for %s\n", account_name);
1068                 modest_mail_operation_notify_end (self);
1069
1070                 return FALSE;
1071         }
1072
1073         /* Get the transport account, we can not do it in the thread
1074            due to some problems with dbus */
1075         transport_account = (TnyTransportAccount *)
1076                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1077                                                                                     account_name);
1078         if (!transport_account) {
1079                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1080                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1081                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1082                              "cannot get tny transport account for %s\n", account_name);
1083                 modest_mail_operation_notify_end (self);
1084
1085                 return FALSE;
1086         }
1087
1088         /* Create the helper object */
1089         info = g_slice_new (UpdateAccountInfo);
1090         info->mail_op = self;
1091         info->account = modest_account;
1092         info->transport_account = transport_account;
1093
1094         /* Get the message size limit */
1095         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1096                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1097         if (info->max_size == 0)
1098                 info->max_size = G_MAXINT;
1099         else
1100                 info->max_size = info->max_size * KB;
1101
1102         /* Get per-account retrieval type */
1103         mgr = modest_runtime_get_account_mgr ();
1104         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1105                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1106
1107         /* Get per-account message amount retrieval limit */
1108         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1109                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1110         if (info->retrieve_limit == 0)
1111                 info->retrieve_limit = G_MAXINT;
1112                 
1113         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1114
1115         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1116
1117         return TRUE;
1118 }
1119
1120 /* ******************************************************************* */
1121 /* ************************** STORE  ACTIONS ************************* */
1122 /* ******************************************************************* */
1123
1124
1125 TnyFolder *
1126 modest_mail_operation_create_folder (ModestMailOperation *self,
1127                                      TnyFolderStore *parent,
1128                                      const gchar *name)
1129 {
1130         ModestMailOperationPrivate *priv;
1131         TnyFolder *new_folder = NULL;
1132
1133         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1134         g_return_val_if_fail (name, NULL);
1135         
1136         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1137
1138         /* Check parent */
1139         if (TNY_IS_FOLDER (parent)) {
1140                 /* Check folder rules */
1141                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1142                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1143                         /* Set status failed and set an error */
1144                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1145                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1146                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1147                                      _("mail_in_ui_folder_create_error"));
1148                 }
1149         }
1150
1151         if (!priv->error) {
1152                 /* Create the folder */
1153                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1154                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1155                 if (!priv->error)
1156                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1157         }
1158
1159         /* Notify about operation end */
1160         modest_mail_operation_notify_end (self);
1161
1162         return new_folder;
1163 }
1164
1165 void
1166 modest_mail_operation_remove_folder (ModestMailOperation *self,
1167                                      TnyFolder           *folder,
1168                                      gboolean             remove_to_trash)
1169 {
1170         TnyAccount *account;
1171         ModestMailOperationPrivate *priv;
1172         ModestTnyFolderRules rules;
1173
1174         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1175         g_return_if_fail (TNY_IS_FOLDER (folder));
1176         
1177         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1178         
1179         /* Check folder rules */
1180         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1181         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1182                 /* Set status failed and set an error */
1183                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1184                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1185                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1186                              _("mail_in_ui_folder_delete_error"));
1187                 goto end;
1188         }
1189
1190         /* Get the account */
1191         account = modest_tny_folder_get_account (folder);
1192         priv->account = g_object_ref(account);
1193
1194         /* Delete folder or move to trash */
1195         if (remove_to_trash) {
1196                 TnyFolder *trash_folder = NULL;
1197                 trash_folder = modest_tny_account_get_special_folder (account,
1198                                                                       TNY_FOLDER_TYPE_TRASH);
1199                 /* TODO: error_handling */
1200                  modest_mail_operation_xfer_folder (self, folder,
1201                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1202         } else {
1203                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1204
1205                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1206                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1207
1208                 if (parent)
1209                         g_object_unref (G_OBJECT (parent));
1210         }
1211         g_object_unref (G_OBJECT (account));
1212
1213  end:
1214         /* Notify about operation end */
1215         modest_mail_operation_notify_end (self);
1216 }
1217
1218 static void
1219 transfer_folder_status_cb (GObject *obj,
1220                            TnyStatus *status,
1221                            gpointer user_data)
1222 {
1223         ModestMailOperation *self;
1224         ModestMailOperationPrivate *priv;
1225         ModestMailOperationState *state;
1226
1227         g_return_if_fail (status != NULL);
1228         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1229
1230         self = MODEST_MAIL_OPERATION (user_data);
1231         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1232
1233         if ((status->position == 1) && (status->of_total == 100))
1234                 return;
1235
1236         priv->done = status->position;
1237         priv->total = status->of_total;
1238
1239         state = modest_mail_operation_clone_state (self);
1240         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1241         g_slice_free (ModestMailOperationState, state);
1242 }
1243
1244
1245 static void
1246 transfer_folder_cb (TnyFolder *folder, 
1247                     TnyFolderStore *into, 
1248                     gboolean cancelled, 
1249                     TnyFolder *new_folder, GError **err, 
1250                     gpointer user_data)
1251 {
1252         ModestMailOperation *self = NULL;
1253         ModestMailOperationPrivate *priv = NULL;
1254
1255         self = MODEST_MAIL_OPERATION (user_data);
1256
1257         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1258
1259         if (*err) {
1260                 priv->error = g_error_copy (*err);
1261                 priv->done = 0;
1262                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1263         } else if (cancelled) {
1264                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1265                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1266                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1267                              _("Transference of %s was cancelled."),
1268                              tny_folder_get_name (folder));
1269         } else {
1270                 priv->done = 1;
1271                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1272         }
1273                 
1274         /* Free */
1275         g_object_unref (folder);
1276         g_object_unref (into);
1277         if (new_folder != NULL)
1278                 g_object_unref (new_folder);
1279
1280         /* Notify about operation end */
1281         modest_mail_operation_notify_end (self);
1282 }
1283
1284 void
1285 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1286                                    TnyFolder *folder,
1287                                    TnyFolderStore *parent,
1288                                    gboolean delete_original)
1289 {
1290         ModestMailOperationPrivate *priv = NULL;
1291         ModestTnyFolderRules parent_rules, rules;
1292
1293         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1294         g_return_if_fail (TNY_IS_FOLDER (folder));
1295         g_return_if_fail (TNY_IS_FOLDER (parent));
1296
1297         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1298
1299         /* Get account and set it into mail_operation */
1300         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1301         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1302
1303         /* Get folder rules */
1304         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1305         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1306
1307         if (!TNY_IS_FOLDER_STORE (parent)) {
1308                 
1309         }
1310         
1311         /* The moveable restriction is applied also to copy operation */
1312         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1313                 /* Set status failed and set an error */
1314                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1315                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1316                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1317                              _("mail_in_ui_folder_move_target_error"));
1318
1319                 /* Notify the queue */
1320                 modest_mail_operation_notify_end (self);
1321         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1322                 /* Set status failed and set an error */
1323                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1324                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1325                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1326                              _("FIXME: parent folder does not accept new folders"));
1327
1328                 /* Notify the queue */
1329                 modest_mail_operation_notify_end (self);
1330         } else {
1331                 /* Pick references for async calls */
1332                 g_object_ref (folder);
1333                 g_object_ref (parent);
1334
1335                 /* Move/Copy folder */          
1336                 tny_folder_copy_async (folder,
1337                                        parent,
1338                                        tny_folder_get_name (folder),
1339                                        delete_original,
1340                                        transfer_folder_cb,
1341                                        transfer_folder_status_cb,
1342                                        self);
1343         }
1344 }
1345
1346 void
1347 modest_mail_operation_rename_folder (ModestMailOperation *self,
1348                                      TnyFolder *folder,
1349                                      const gchar *name)
1350 {
1351         ModestMailOperationPrivate *priv;
1352         ModestTnyFolderRules rules;
1353
1354         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1355         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1356         g_return_if_fail (name);
1357         
1358         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1359
1360         /* Get account and set it into mail_operation */
1361         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1362
1363         /* Check folder rules */
1364         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1365         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1366                 /* Set status failed and set an error */
1367                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1368                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1369                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1370                              _("FIXME: unable to rename"));
1371
1372                 /* Notify about operation end */
1373                 modest_mail_operation_notify_end (self);
1374         } else {
1375                 /* Rename. Camel handles folder subscription/unsubscription */
1376                 TnyFolderStore *into;
1377
1378                 into = tny_folder_get_folder_store (folder);
1379                 tny_folder_copy_async (folder, into, name, TRUE,
1380                                  transfer_folder_cb,
1381                                  transfer_folder_status_cb,
1382                                  self);
1383                 if (into)
1384                         g_object_unref (into);
1385                 
1386         }
1387  }
1388
1389 /* ******************************************************************* */
1390 /* **************************  MSG  ACTIONS  ************************* */
1391 /* ******************************************************************* */
1392
1393 void modest_mail_operation_get_msg (ModestMailOperation *self,
1394                                     TnyHeader *header,
1395                                     GetMsgAsyncUserCallback user_callback,
1396                                     gpointer user_data)
1397 {
1398         GetMsgAsyncHelper *helper = NULL;
1399         TnyFolder *folder;
1400         ModestMailOperationPrivate *priv;
1401         
1402         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1403         g_return_if_fail (TNY_IS_HEADER (header));
1404         
1405         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1406         folder = tny_header_get_folder (header);
1407
1408         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1409
1410         /* Get message from folder */
1411         if (folder) {
1412                 /* Get account and set it into mail_operation */
1413                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1414
1415                 helper = g_slice_new0 (GetMsgAsyncHelper);
1416                 helper->mail_op = self;
1417                 helper->user_callback = user_callback;
1418                 helper->pending_ops = 1;
1419                 helper->user_data = user_data;
1420
1421                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1422
1423                 g_object_unref (G_OBJECT (folder));
1424         } else {
1425                 /* Set status failed and set an error */
1426                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1427                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1428                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1429                              _("Error trying to get a message. No folder found for header"));
1430
1431                 /* Notify the queue */
1432                 modest_mail_operation_notify_end (self);
1433         }
1434 }
1435
1436 static void
1437 get_msg_cb (TnyFolder *folder, 
1438             gboolean cancelled, 
1439             TnyMsg *msg, 
1440             GError **error, 
1441             gpointer user_data)
1442 {
1443         GetMsgAsyncHelper *helper = NULL;
1444         ModestMailOperation *self = NULL;
1445         ModestMailOperationPrivate *priv = NULL;
1446
1447         helper = (GetMsgAsyncHelper *) user_data;
1448         g_return_if_fail (helper != NULL);       
1449         self = helper->mail_op;
1450         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1451         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1452         
1453         helper->pending_ops--;
1454
1455         /* Check errors and cancel */
1456         if (*error) {
1457                 priv->error = g_error_copy (*error);
1458                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1459                 goto out;
1460         }
1461         if (cancelled) {
1462                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1463                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1464                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1465                              _("Error trying to refresh the contents of %s"),
1466                              tny_folder_get_name (folder));
1467                 goto out;
1468         }
1469
1470         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1471
1472         /* If user defined callback function was defined, call it */
1473         if (helper->user_callback) {
1474                 helper->user_callback (self, NULL, msg, helper->user_data);
1475         }
1476
1477         /* Free */
1478  out:
1479         if (helper->pending_ops == 0) {
1480                 g_slice_free (GetMsgAsyncHelper, helper);
1481                 
1482                 /* Notify about operation end */
1483                 modest_mail_operation_notify_end (self);        
1484         }
1485 }
1486
1487 static void     
1488 get_msg_status_cb (GObject *obj,
1489                    TnyStatus *status,  
1490                    gpointer user_data)
1491 {
1492         GetMsgAsyncHelper *helper = NULL;
1493         ModestMailOperation *self;
1494         ModestMailOperationPrivate *priv;
1495         ModestMailOperationState *state;
1496
1497         g_return_if_fail (status != NULL);
1498         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1499
1500         helper = (GetMsgAsyncHelper *) user_data;
1501         g_return_if_fail (helper != NULL);       
1502
1503         self = helper->mail_op;
1504         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1505
1506         if ((status->position == 1) && (status->of_total == 100))
1507                 return;
1508
1509         priv->done = 1;
1510         priv->total = 1;
1511
1512         state = modest_mail_operation_clone_state (self);
1513         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1514         g_slice_free (ModestMailOperationState, state);
1515 }
1516
1517 /****************************************************/
1518 typedef struct {
1519         ModestMailOperation *mail_op;
1520         TnyList *headers;
1521         GetMsgAsyncUserCallback user_callback;
1522         gpointer user_data;
1523         GDestroyNotify notify;
1524 } GetFullMsgsInfo;
1525
1526 typedef struct {
1527         GetMsgAsyncUserCallback user_callback;
1528         TnyHeader *header;
1529         TnyMsg *msg;
1530         gpointer user_data;
1531         ModestMailOperation *mail_op;
1532 } NotifyGetMsgsInfo;
1533
1534
1535 /* 
1536  * Used by get_msgs_full_thread to call the user_callback for each
1537  * message that has been read
1538  */
1539 static gboolean
1540 notify_get_msgs_full (gpointer data)
1541 {
1542         NotifyGetMsgsInfo *info;
1543
1544         info = (NotifyGetMsgsInfo *) data;      
1545
1546         /* Call the user callback */
1547         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1548
1549         g_slice_free (NotifyGetMsgsInfo, info);
1550
1551         return FALSE;
1552 }
1553
1554 /* 
1555  * Used by get_msgs_full_thread to free al the thread resources and to
1556  * call the destroy function for the passed user_data
1557  */
1558 static gboolean
1559 get_msgs_full_destroyer (gpointer data)
1560 {
1561         GetFullMsgsInfo *info;
1562
1563         info = (GetFullMsgsInfo *) data;
1564
1565         if (info->notify)
1566                 info->notify (info->user_data);
1567
1568         /* free */
1569         g_object_unref (info->headers);
1570         g_slice_free (GetFullMsgsInfo, info);
1571
1572         return FALSE;
1573 }
1574
1575 static gpointer
1576 get_msgs_full_thread (gpointer thr_user_data)
1577 {
1578         GetFullMsgsInfo *info;
1579         ModestMailOperationPrivate *priv = NULL;
1580         TnyIterator *iter = NULL;
1581         
1582         info = (GetFullMsgsInfo *) thr_user_data;       
1583         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1584
1585         iter = tny_list_create_iterator (info->headers);
1586         while (!tny_iterator_is_done (iter)) { 
1587                 TnyHeader *header;
1588                 TnyFolder *folder;
1589                 
1590                 header = TNY_HEADER (tny_iterator_get_current (iter));
1591                 folder = tny_header_get_folder (header);
1592                                 
1593                 /* Get message from folder */
1594                 if (folder) {
1595                         TnyMsg *msg;
1596                         /* The callback will call it per each header */
1597                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1598
1599                         if (msg) {
1600                                 ModestMailOperationState *state;
1601                                 ModestPair *pair;
1602
1603                                 priv->done++;
1604
1605                                 /* notify progress */
1606                                 state = modest_mail_operation_clone_state (info->mail_op);
1607                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1608                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1609                                                  pair, (GDestroyNotify) modest_pair_free);
1610
1611                                 /* The callback is the responsible for
1612                                    freeing the message */
1613                                 if (info->user_callback) {
1614                                         NotifyGetMsgsInfo *info_notify;
1615                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1616                                         info_notify->user_callback = info->user_callback;
1617                                         info_notify->mail_op = info->mail_op;
1618                                         info_notify->header = g_object_ref (header);
1619                                         info_notify->msg = g_object_ref (msg);
1620                                         info_notify->user_data = info->user_data;
1621                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1622                                                          notify_get_msgs_full, 
1623                                                          info_notify, NULL);
1624                                 }
1625                                 g_object_unref (msg);
1626                         }
1627                 } else {
1628                         /* Set status failed and set an error */
1629                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1630                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1631                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1632                                      "Error trying to get a message. No folder found for header");
1633                 }
1634                 g_object_unref (header);                
1635                 tny_iterator_next (iter);
1636         }
1637
1638         /* Set operation status */
1639         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1640                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1641
1642         /* Notify about operation end */
1643         g_idle_add (notify_update_account_queue, info->mail_op);
1644
1645         /* Free thread resources. Will be called after all previous idles */
1646         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1647
1648         return NULL;
1649 }
1650
1651 void 
1652 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1653                                      TnyList *header_list, 
1654                                      GetMsgAsyncUserCallback user_callback,
1655                                      gpointer user_data,
1656                                      GDestroyNotify notify)
1657 {
1658         TnyHeader *header = NULL;
1659         TnyFolder *folder = NULL;
1660         GThread *thread;
1661         ModestMailOperationPrivate *priv = NULL;
1662         GetFullMsgsInfo *info = NULL;
1663         gboolean size_ok = TRUE;
1664         gint max_size;
1665         TnyIterator *iter = NULL;
1666         
1667         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1668         
1669         /* Init mail operation */
1670         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1671         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1672         priv->done = 0;
1673         priv->total = tny_list_get_length(header_list);
1674
1675         /* Get account and set it into mail_operation */
1676         if (tny_list_get_length (header_list) >= 1) {
1677                 iter = tny_list_create_iterator (header_list);
1678                 header = TNY_HEADER (tny_iterator_get_current (iter));
1679                 folder = tny_header_get_folder (header);                
1680                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1681                 g_object_unref (header);
1682                 g_object_unref (folder);
1683
1684                 if (tny_list_get_length (header_list) == 1) {
1685                         g_object_unref (iter);
1686                         iter = NULL;
1687                 }
1688         }
1689
1690         /* Get msg size limit */
1691         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1692                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1693                                          &(priv->error));
1694         if (priv->error) {
1695                 g_clear_error (&(priv->error));
1696                 max_size = G_MAXINT;
1697         } else {
1698                 max_size = max_size * KB;
1699         }
1700
1701         /* Check message size limits. If there is only one message
1702            always retrieve it */
1703         if (iter != NULL) {
1704                 while (!tny_iterator_is_done (iter) && size_ok) {
1705                         header = TNY_HEADER (tny_iterator_get_current (iter));
1706                         if (tny_header_get_message_size (header) >= max_size)
1707                                 size_ok = FALSE;
1708                         g_object_unref (header);
1709                         tny_iterator_next (iter);
1710                 }
1711                 g_object_unref (iter);
1712         }
1713
1714         if (size_ok) {
1715                 /* Create the info */
1716                 info = g_slice_new0 (GetFullMsgsInfo);
1717                 info->mail_op = self;
1718                 info->user_callback = user_callback;
1719                 info->user_data = user_data;
1720                 info->headers = g_object_ref (header_list);
1721                 info->notify = notify;
1722
1723                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1724         } else {
1725                 /* Set status failed and set an error */
1726                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1727                 /* FIXME: the error msg is different for pop */
1728                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1729                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1730                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1731                 /* Remove from queue and free resources */
1732                 modest_mail_operation_notify_end (self);
1733                 if (notify)
1734                         notify (user_data);
1735         }
1736 }
1737
1738
1739 void 
1740 modest_mail_operation_remove_msg (ModestMailOperation *self,
1741                                   TnyHeader *header,
1742                                   gboolean remove_to_trash)
1743 {
1744         TnyFolder *folder;
1745         ModestMailOperationPrivate *priv;
1746
1747         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1748         g_return_if_fail (TNY_IS_HEADER (header));
1749
1750         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1751         folder = tny_header_get_folder (header);
1752
1753         /* Get account and set it into mail_operation */
1754         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1755
1756         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1757
1758         /* Delete or move to trash */
1759         if (remove_to_trash) {
1760                 TnyFolder *trash_folder;
1761                 TnyStoreAccount *store_account;
1762
1763                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1764                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1765                                                                       TNY_FOLDER_TYPE_TRASH);
1766                 if (trash_folder) {
1767                         TnyList *headers;
1768
1769                         /* Create list */
1770                         headers = tny_simple_list_new ();
1771                         tny_list_append (headers, G_OBJECT (header));
1772                         g_object_unref (header);
1773
1774                         /* Move to trash */
1775                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1776                         g_object_unref (headers);
1777 /*                      g_object_unref (trash_folder); */
1778                 } else {
1779                         ModestMailOperationPrivate *priv;
1780
1781                         /* Set status failed and set an error */
1782                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1783                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1784                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1785                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1786                                      _("Error trying to delete a message. Trash folder not found"));
1787                 }
1788
1789                 g_object_unref (G_OBJECT (store_account));
1790         } else {
1791                 tny_folder_remove_msg (folder, header, &(priv->error));
1792                 if (!priv->error)
1793                         tny_folder_sync(folder, TRUE, &(priv->error));
1794         }
1795
1796         /* Set status */
1797         if (!priv->error)
1798                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1799         else
1800                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1801
1802         /* Free */
1803         g_object_unref (G_OBJECT (folder));
1804
1805         /* Notify about operation end */
1806         modest_mail_operation_notify_end (self);
1807 }
1808
1809 static void
1810 transfer_msgs_status_cb (GObject *obj,
1811                          TnyStatus *status,  
1812                          gpointer user_data)
1813 {
1814         XFerMsgAsyncHelper *helper = NULL;
1815         ModestMailOperation *self;
1816         ModestMailOperationPrivate *priv;
1817         ModestMailOperationState *state;
1818
1819
1820         g_return_if_fail (status != NULL);
1821         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1822
1823         helper = (XFerMsgAsyncHelper *) user_data;
1824         g_return_if_fail (helper != NULL);       
1825
1826         self = helper->mail_op;
1827         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1828
1829         if ((status->position == 1) && (status->of_total == 100))
1830                 return;
1831
1832         priv->done = status->position;
1833         priv->total = status->of_total;
1834
1835         state = modest_mail_operation_clone_state (self);
1836         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1837         g_slice_free (ModestMailOperationState, state);
1838 }
1839
1840
1841 static void
1842 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1843 {
1844         XFerMsgAsyncHelper *helper;
1845         ModestMailOperation *self;
1846         ModestMailOperationPrivate *priv;
1847
1848         helper = (XFerMsgAsyncHelper *) user_data;
1849         self = helper->mail_op;
1850
1851         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1852
1853         if (*err) {
1854                 priv->error = g_error_copy (*err);
1855                 priv->done = 0;
1856                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1857         } else if (cancelled) {
1858                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1859                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1860                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1861                              _("Error trying to refresh the contents of %s"),
1862                              tny_folder_get_name (folder));
1863         } else {
1864                 priv->done = 1;
1865                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1866         }
1867
1868         /* If user defined callback function was defined, call it */
1869         if (helper->user_callback) {
1870                 helper->user_callback (priv->source, helper->user_data);
1871         }
1872
1873         /* Free */
1874         g_object_unref (helper->headers);
1875         g_object_unref (helper->dest_folder);
1876         g_object_unref (helper->mail_op);
1877         g_slice_free   (XFerMsgAsyncHelper, helper);
1878         g_object_unref (folder);
1879
1880         /* Notify about operation end */
1881         modest_mail_operation_notify_end (self);
1882 }
1883
1884 void
1885 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1886                                  TnyList *headers, 
1887                                  TnyFolder *folder, 
1888                                  gboolean delete_original,
1889                                  XferMsgsAsynUserCallback user_callback,
1890                                  gpointer user_data)
1891 {
1892         ModestMailOperationPrivate *priv;
1893         TnyIterator *iter;
1894         TnyFolder *src_folder;
1895         XFerMsgAsyncHelper *helper;
1896         TnyHeader *header;
1897         ModestTnyFolderRules rules;
1898
1899         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1900         g_return_if_fail (TNY_IS_LIST (headers));
1901         g_return_if_fail (TNY_IS_FOLDER (folder));
1902
1903         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1904         priv->total = 1;
1905         priv->done = 0;
1906         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1907
1908         /* Apply folder rules */
1909         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1910
1911         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1912                 /* Set status failed and set an error */
1913                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1914                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1915                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1916                              _("FIXME: folder does not accept msgs"));
1917                 /* Notify the queue */
1918                 modest_mail_operation_notify_end (self);
1919                 return;
1920         }
1921
1922         /* Create the helper */
1923         helper = g_slice_new0 (XFerMsgAsyncHelper);
1924         helper->mail_op = g_object_ref(self);
1925         helper->dest_folder = g_object_ref(folder);
1926         helper->headers = g_object_ref(headers);
1927         helper->user_callback = user_callback;
1928         helper->user_data = user_data;
1929
1930         /* Get source folder */
1931         iter = tny_list_create_iterator (headers);
1932         header = TNY_HEADER (tny_iterator_get_current (iter));
1933         src_folder = tny_header_get_folder (header);
1934         g_object_unref (header);
1935         g_object_unref (iter);
1936
1937         /* Get account and set it into mail_operation */
1938         priv->account = modest_tny_folder_get_account (src_folder);
1939
1940         /* Transfer messages */
1941         tny_folder_transfer_msgs_async (src_folder, 
1942                                         headers, 
1943                                         folder, 
1944                                         delete_original, 
1945                                         transfer_msgs_cb, 
1946                                         transfer_msgs_status_cb,
1947                                         helper);
1948 }
1949
1950
1951 static void
1952 on_refresh_folder (TnyFolder   *folder, 
1953                    gboolean     cancelled, 
1954                    GError     **error,
1955                    gpointer     user_data)
1956 {
1957         ModestMailOperation *self;
1958         ModestMailOperationPrivate *priv;
1959
1960         self = MODEST_MAIL_OPERATION (user_data);
1961         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1962
1963         if (*error) {
1964                 priv->error = g_error_copy (*error);
1965                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1966                 goto out;
1967         }
1968
1969         if (cancelled) {
1970                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1971                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1972                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1973                              _("Error trying to refresh the contents of %s"),
1974                              tny_folder_get_name (folder));
1975                 goto out;
1976         }
1977
1978         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1979
1980  out:
1981         /* Free */
1982         g_object_unref (folder);
1983
1984         /* Notify about operation end */
1985         modest_mail_operation_notify_end (self);
1986 }
1987
1988 static void
1989 on_refresh_folder_status_update (GObject *obj,
1990                                  TnyStatus *status,
1991                                  gpointer user_data)
1992 {
1993         ModestMailOperation *self;
1994         ModestMailOperationPrivate *priv;
1995         ModestMailOperationState *state;
1996
1997         g_return_if_fail (status != NULL);
1998         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1999
2000         self = MODEST_MAIL_OPERATION (user_data);
2001         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2002
2003         priv->done = status->position;
2004         priv->total = status->of_total;
2005
2006         state = modest_mail_operation_clone_state (self);
2007         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2008         g_slice_free (ModestMailOperationState, state);
2009 }
2010
2011 void 
2012 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2013                                        TnyFolder *folder)
2014 {
2015         ModestMailOperationPrivate *priv;
2016
2017         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2018
2019         /* Pick a reference */
2020         g_object_ref (folder);
2021
2022         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2023
2024         /* Get account and set it into mail_operation */
2025         priv->account = modest_tny_folder_get_account  (folder);
2026
2027         /* Refresh the folder. TODO: tinymail could issue a status
2028            updates before the callback call then this could happen. We
2029            must review the design */
2030         tny_folder_refresh_async (folder,
2031                                   on_refresh_folder,
2032                                   on_refresh_folder_status_update,
2033                                   self);
2034 }
2035
2036 /**
2037  *
2038  * It's used by the mail operation queue to notify the observers
2039  * attached to that signal that the operation finished. We need to use
2040  * that because tinymail does not give us the progress of a given
2041  * operation when it finishes (it directly calls the operation
2042  * callback).
2043  */
2044 static void
2045 modest_mail_operation_notify_end (ModestMailOperation *self)
2046 {
2047         ModestMailOperationState *state;
2048
2049         /* Notify the observers about the mail opertation end */
2050         state = modest_mail_operation_clone_state (self);
2051         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2052         g_slice_free (ModestMailOperationState, state);
2053 }