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