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