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