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