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