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