* Partially reverted last commit, sometimes the gdk_threads_enter blocks, still...
[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                 /* This thread is not in the main lock */
1096                 gdk_threads_enter ();
1097                 info->callback (info->mail_op, 
1098                                 (new_headers) ? new_headers->len : 0, 
1099                                 info->user_data);
1100                 gdk_threads_leave ();
1101         }
1102         
1103         /* Frees */
1104         g_object_unref (query);
1105         g_object_unref (all_folders);
1106         g_object_unref (info->account);
1107         g_object_unref (info->transport_account);
1108         g_free (info->retrieve_type);
1109         g_slice_free (UpdateAccountInfo, info);
1110
1111         first_time = FALSE;
1112
1113         return NULL;
1114 }
1115
1116 gboolean
1117 modest_mail_operation_update_account (ModestMailOperation *self,
1118                                       const gchar *account_name,
1119                                       UpdateAccountCallback callback,
1120                                       gpointer user_data)
1121 {
1122         GThread *thread;
1123         UpdateAccountInfo *info;
1124         ModestMailOperationPrivate *priv;
1125         ModestAccountMgr *mgr;
1126         TnyStoreAccount *modest_account;
1127         TnyTransportAccount *transport_account;
1128
1129         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1130         g_return_val_if_fail (account_name, FALSE);
1131
1132         /* Init mail operation. Set total and done to 0, and do not
1133            update them, this way the progress objects will know that
1134            we have no clue about the number of the objects */
1135         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1136         priv->total = 0;
1137         priv->done  = 0;
1138         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1139
1140         /* Make sure that we have a connection, and request one 
1141          * if necessary:
1142          * TODO: Is there some way to trigger this for every attempt to 
1143          * use the network? */
1144         if (!modest_platform_connect_and_wait(NULL))
1145                 goto error;
1146
1147         /* Get the Modest account */
1148         modest_account = (TnyStoreAccount *)
1149                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1150                                                                      account_name,
1151                                                                      TNY_ACCOUNT_TYPE_STORE);
1152
1153         if (!modest_account) {
1154                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1155                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1156                              "cannot get tny store account for %s\n", account_name);
1157                 goto error;
1158         }
1159
1160         
1161         /* Get the transport account, we can not do it in the thread
1162            due to some problems with dbus */
1163         transport_account = (TnyTransportAccount *)
1164                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1165                                                                                     account_name);
1166         if (!transport_account) {
1167                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1168                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1169                              "cannot get tny transport account for %s\n", account_name);
1170                 goto error;
1171         }
1172
1173         /* Create the helper object */
1174         info = g_slice_new (UpdateAccountInfo);
1175         info->mail_op = self;
1176         info->account = modest_account;
1177         info->transport_account = transport_account;
1178         info->callback = callback;
1179         info->user_data = user_data;
1180
1181         /* Get the message size limit */
1182         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1183                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1184         if (info->max_size == 0)
1185                 info->max_size = G_MAXINT;
1186         else
1187                 info->max_size = info->max_size * KB;
1188
1189         /* Get per-account retrieval type */
1190         mgr = modest_runtime_get_account_mgr ();
1191         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1192                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1193
1194         /* Get per-account message amount retrieval limit */
1195         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1196                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1197         if (info->retrieve_limit == 0)
1198                 info->retrieve_limit = G_MAXINT;
1199                 
1200         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1201
1202         /* Set account busy */
1203         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1204         priv->account_name = g_strdup(account_name);
1205         
1206         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1207
1208         return TRUE;
1209
1210  error:
1211         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1212         if (callback) 
1213                 callback (self, 0, user_data);
1214         modest_mail_operation_notify_end (self);
1215         return FALSE;
1216 }
1217
1218 /* ******************************************************************* */
1219 /* ************************** STORE  ACTIONS ************************* */
1220 /* ******************************************************************* */
1221
1222
1223 TnyFolder *
1224 modest_mail_operation_create_folder (ModestMailOperation *self,
1225                                      TnyFolderStore *parent,
1226                                      const gchar *name)
1227 {
1228         ModestMailOperationPrivate *priv;
1229         TnyFolder *new_folder = NULL;
1230
1231         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1232         g_return_val_if_fail (name, NULL);
1233         
1234         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1235
1236         /* Check parent */
1237         if (TNY_IS_FOLDER (parent)) {
1238                 /* Check folder rules */
1239                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1240                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1241                         /* Set status failed and set an error */
1242                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1243                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1244                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1245                                      _("mail_in_ui_folder_create_error"));
1246                 }
1247         }
1248
1249         if (!priv->error) {
1250                 /* Create the folder */
1251                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1252                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1253                 if (!priv->error)
1254                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1255         }
1256
1257         /* Notify about operation end */
1258         modest_mail_operation_notify_end (self);
1259
1260         return new_folder;
1261 }
1262
1263 void
1264 modest_mail_operation_remove_folder (ModestMailOperation *self,
1265                                      TnyFolder           *folder,
1266                                      gboolean             remove_to_trash)
1267 {
1268         TnyAccount *account;
1269         ModestMailOperationPrivate *priv;
1270         ModestTnyFolderRules rules;
1271
1272         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1273         g_return_if_fail (TNY_IS_FOLDER (folder));
1274         
1275         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1276         
1277         /* Check folder rules */
1278         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1279         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1280                 /* Set status failed and set an error */
1281                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1282                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1283                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1284                              _("mail_in_ui_folder_delete_error"));
1285                 goto end;
1286         }
1287
1288         /* Get the account */
1289         account = modest_tny_folder_get_account (folder);
1290         priv->account = g_object_ref(account);
1291
1292         /* Delete folder or move to trash */
1293         if (remove_to_trash) {
1294                 TnyFolder *trash_folder = NULL;
1295                 trash_folder = modest_tny_account_get_special_folder (account,
1296                                                                       TNY_FOLDER_TYPE_TRASH);
1297                 /* TODO: error_handling */
1298                  modest_mail_operation_xfer_folder (self, folder,
1299                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1300         } else {
1301                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1302
1303                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1304                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1305
1306                 if (parent)
1307                         g_object_unref (G_OBJECT (parent));
1308         }
1309         g_object_unref (G_OBJECT (account));
1310
1311  end:
1312         /* Notify about operation end */
1313         modest_mail_operation_notify_end (self);
1314 }
1315
1316 static void
1317 transfer_folder_status_cb (GObject *obj,
1318                            TnyStatus *status,
1319                            gpointer user_data)
1320 {
1321         ModestMailOperation *self;
1322         ModestMailOperationPrivate *priv;
1323         ModestMailOperationState *state;
1324
1325         g_return_if_fail (status != NULL);
1326         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1327
1328         self = MODEST_MAIL_OPERATION (user_data);
1329         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1330
1331         if ((status->position == 1) && (status->of_total == 100))
1332                 return;
1333
1334         priv->done = status->position;
1335         priv->total = status->of_total;
1336
1337         state = modest_mail_operation_clone_state (self);
1338         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1339         g_slice_free (ModestMailOperationState, state);
1340 }
1341
1342
1343 static void
1344 transfer_folder_cb (TnyFolder *folder, 
1345                     TnyFolderStore *into, 
1346                     gboolean cancelled, 
1347                     TnyFolder *new_folder, GError **err, 
1348                     gpointer user_data)
1349 {
1350         ModestMailOperation *self = NULL;
1351         ModestMailOperationPrivate *priv = NULL;
1352
1353         self = MODEST_MAIL_OPERATION (user_data);
1354
1355         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1356
1357         if (*err) {
1358                 priv->error = g_error_copy (*err);
1359                 priv->done = 0;
1360                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1361         } else if (cancelled) {
1362                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1363                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1364                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1365                              _("Transference of %s was cancelled."),
1366                              tny_folder_get_name (folder));
1367         } else {
1368                 priv->done = 1;
1369                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1370         }
1371                 
1372         /* Free */
1373         g_object_unref (folder);
1374         g_object_unref (into);
1375         if (new_folder != NULL)
1376                 g_object_unref (new_folder);
1377
1378         /* Notify about operation end */
1379         modest_mail_operation_notify_end (self);
1380 }
1381
1382 void
1383 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1384                                    TnyFolder *folder,
1385                                    TnyFolderStore *parent,
1386                                    gboolean delete_original)
1387 {
1388         ModestMailOperationPrivate *priv = NULL;
1389         ModestTnyFolderRules parent_rules, rules;
1390
1391         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1392         g_return_if_fail (TNY_IS_FOLDER (folder));
1393         g_return_if_fail (TNY_IS_FOLDER (parent));
1394
1395         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1396
1397         /* Get account and set it into mail_operation */
1398         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1399         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1400
1401         /* Get folder rules */
1402         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1403         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1404
1405         if (!TNY_IS_FOLDER_STORE (parent)) {
1406                 
1407         }
1408         
1409         /* The moveable restriction is applied also to copy operation */
1410         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1411                 /* Set status failed and set an error */
1412                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1413                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1414                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1415                              _("mail_in_ui_folder_move_target_error"));
1416
1417                 /* Notify the queue */
1418                 modest_mail_operation_notify_end (self);
1419         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1420                 /* Set status failed and set an error */
1421                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1422                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1423                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1424                              _("FIXME: parent folder does not accept new folders"));
1425
1426                 /* Notify the queue */
1427                 modest_mail_operation_notify_end (self);
1428         } else {
1429                 /* Pick references for async calls */
1430                 g_object_ref (folder);
1431                 g_object_ref (parent);
1432
1433                 /* Move/Copy folder */          
1434                 tny_folder_copy_async (folder,
1435                                        parent,
1436                                        tny_folder_get_name (folder),
1437                                        delete_original,
1438                                        transfer_folder_cb,
1439                                        transfer_folder_status_cb,
1440                                        self);
1441         }
1442 }
1443
1444 void
1445 modest_mail_operation_rename_folder (ModestMailOperation *self,
1446                                      TnyFolder *folder,
1447                                      const gchar *name)
1448 {
1449         ModestMailOperationPrivate *priv;
1450         ModestTnyFolderRules rules;
1451
1452         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1453         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1454         g_return_if_fail (name);
1455         
1456         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1457
1458         /* Get account and set it into mail_operation */
1459         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1460
1461         /* Check folder rules */
1462         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1463         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1464                 /* Set status failed and set an error */
1465                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1466                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1467                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1468                              _("FIXME: unable to rename"));
1469
1470                 /* Notify about operation end */
1471                 modest_mail_operation_notify_end (self);
1472         } else {
1473                 /* Rename. Camel handles folder subscription/unsubscription */
1474                 TnyFolderStore *into;
1475
1476                 into = tny_folder_get_folder_store (folder);
1477                 tny_folder_copy_async (folder, into, name, TRUE,
1478                                  transfer_folder_cb,
1479                                  transfer_folder_status_cb,
1480                                  self);
1481                 if (into)
1482                         g_object_unref (into);
1483                 
1484         }
1485  }
1486
1487 /* ******************************************************************* */
1488 /* **************************  MSG  ACTIONS  ************************* */
1489 /* ******************************************************************* */
1490
1491 void modest_mail_operation_get_msg (ModestMailOperation *self,
1492                                     TnyHeader *header,
1493                                     GetMsgAsyncUserCallback user_callback,
1494                                     gpointer user_data)
1495 {
1496         GetMsgAsyncHelper *helper = NULL;
1497         TnyFolder *folder;
1498         ModestMailOperationPrivate *priv;
1499         
1500         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1501         g_return_if_fail (TNY_IS_HEADER (header));
1502         
1503         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1504         folder = tny_header_get_folder (header);
1505
1506         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1507
1508         /* Get message from folder */
1509         if (folder) {
1510                 /* Get account and set it into mail_operation */
1511                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1512
1513                 helper = g_slice_new0 (GetMsgAsyncHelper);
1514                 helper->mail_op = self;
1515                 helper->user_callback = user_callback;
1516                 helper->user_data = user_data;
1517                 helper->header = g_object_ref (header);
1518
1519                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1520
1521                 g_object_unref (G_OBJECT (folder));
1522         } else {
1523                 /* Set status failed and set an error */
1524                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1525                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1526                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1527                              _("Error trying to get a message. No folder found for header"));
1528
1529                 /* Notify the queue */
1530                 modest_mail_operation_notify_end (self);
1531         }
1532 }
1533
1534 static void
1535 get_msg_cb (TnyFolder *folder, 
1536             gboolean cancelled, 
1537             TnyMsg *msg, 
1538             GError **error, 
1539             gpointer user_data)
1540 {
1541         GetMsgAsyncHelper *helper = NULL;
1542         ModestMailOperation *self = NULL;
1543         ModestMailOperationPrivate *priv = NULL;
1544
1545         helper = (GetMsgAsyncHelper *) user_data;
1546         g_return_if_fail (helper != NULL);       
1547         self = helper->mail_op;
1548         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1549         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1550
1551         /* Check errors and cancel */
1552         if (*error) {
1553                 priv->error = g_error_copy (*error);
1554                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1555                 goto out;
1556         }
1557         if (cancelled) {
1558                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1559                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1560                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1561                              _("Error trying to refresh the contents of %s"),
1562                              tny_folder_get_name (folder));
1563                 goto out;
1564         }
1565
1566         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1567
1568         /* If user defined callback function was defined, call it */
1569         if (helper->user_callback) {
1570                 /* This callback is called into an iddle by tinymail,
1571                    and idles are not in the main lock */
1572                 gdk_threads_enter ();
1573                 helper->user_callback (self, helper->header, msg, helper->user_data);
1574                 gdk_threads_leave ();
1575         }
1576
1577  out:
1578         /* Free */
1579         g_object_unref (helper->header);
1580         g_slice_free (GetMsgAsyncHelper, helper);
1581                 
1582         /* Notify about operation end */
1583         modest_mail_operation_notify_end (self);        
1584 }
1585
1586 static void     
1587 get_msg_status_cb (GObject *obj,
1588                    TnyStatus *status,  
1589                    gpointer user_data)
1590 {
1591         GetMsgAsyncHelper *helper = NULL;
1592         ModestMailOperation *self;
1593         ModestMailOperationPrivate *priv;
1594         ModestMailOperationState *state;
1595
1596         g_return_if_fail (status != NULL);
1597         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1598
1599         helper = (GetMsgAsyncHelper *) user_data;
1600         g_return_if_fail (helper != NULL);       
1601
1602         self = helper->mail_op;
1603         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1604
1605         if ((status->position == 1) && (status->of_total == 100))
1606                 return;
1607
1608         priv->done = 1;
1609         priv->total = 1;
1610
1611         state = modest_mail_operation_clone_state (self);
1612         state->bytes_done = status->position;
1613         state->bytes_total = status->of_total;
1614         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1615         g_slice_free (ModestMailOperationState, state);
1616 }
1617
1618 /****************************************************/
1619 typedef struct {
1620         ModestMailOperation *mail_op;
1621         TnyList *headers;
1622         GetMsgAsyncUserCallback user_callback;
1623         gpointer user_data;
1624         GDestroyNotify notify;
1625 } GetFullMsgsInfo;
1626
1627 typedef struct {
1628         GetMsgAsyncUserCallback user_callback;
1629         TnyHeader *header;
1630         TnyMsg *msg;
1631         gpointer user_data;
1632         ModestMailOperation *mail_op;
1633 } NotifyGetMsgsInfo;
1634
1635
1636 /* 
1637  * Used by get_msgs_full_thread to call the user_callback for each
1638  * message that has been read
1639  */
1640 static gboolean
1641 notify_get_msgs_full (gpointer data)
1642 {
1643         NotifyGetMsgsInfo *info;
1644
1645         info = (NotifyGetMsgsInfo *) data;      
1646
1647         /* Call the user callback. Idles are not in the main lock, so
1648            lock it */
1649         gdk_threads_enter ();
1650         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1651         gdk_threads_leave ();
1652
1653         g_slice_free (NotifyGetMsgsInfo, info);
1654
1655         return FALSE;
1656 }
1657
1658 /* 
1659  * Used by get_msgs_full_thread to free al the thread resources and to
1660  * call the destroy function for the passed user_data
1661  */
1662 static gboolean
1663 get_msgs_full_destroyer (gpointer data)
1664 {
1665         GetFullMsgsInfo *info;
1666
1667         info = (GetFullMsgsInfo *) data;
1668
1669         if (info->notify)
1670                 info->notify (info->user_data);
1671
1672         /* free */
1673         g_object_unref (info->headers);
1674         g_slice_free (GetFullMsgsInfo, info);
1675
1676         return FALSE;
1677 }
1678
1679 static gpointer
1680 get_msgs_full_thread (gpointer thr_user_data)
1681 {
1682         GetFullMsgsInfo *info;
1683         ModestMailOperationPrivate *priv = NULL;
1684         TnyIterator *iter = NULL;
1685         
1686         info = (GetFullMsgsInfo *) thr_user_data;       
1687         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1688
1689         iter = tny_list_create_iterator (info->headers);
1690         while (!tny_iterator_is_done (iter)) { 
1691                 TnyHeader *header;
1692                 TnyFolder *folder;
1693                 
1694                 header = TNY_HEADER (tny_iterator_get_current (iter));
1695                 folder = tny_header_get_folder (header);
1696                                 
1697                 /* Get message from folder */
1698                 if (folder) {
1699                         TnyMsg *msg;
1700                         /* The callback will call it per each header */
1701                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1702
1703                         if (msg) {
1704                                 ModestMailOperationState *state;
1705                                 ModestPair *pair;
1706
1707                                 priv->done++;
1708
1709                                 /* notify progress */
1710                                 state = modest_mail_operation_clone_state (info->mail_op);
1711                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1712                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1713                                                  pair, (GDestroyNotify) modest_pair_free);
1714
1715                                 /* The callback is the responsible for
1716                                    freeing the message */
1717                                 if (info->user_callback) {
1718                                         NotifyGetMsgsInfo *info_notify;
1719                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1720                                         info_notify->user_callback = info->user_callback;
1721                                         info_notify->mail_op = info->mail_op;
1722                                         info_notify->header = g_object_ref (header);
1723                                         info_notify->msg = g_object_ref (msg);
1724                                         info_notify->user_data = info->user_data;
1725                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1726                                                          notify_get_msgs_full, 
1727                                                          info_notify, NULL);
1728                                 }
1729                                 g_object_unref (msg);
1730                         }
1731                 } else {
1732                         /* Set status failed and set an error */
1733                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1734                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1735                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1736                                      "Error trying to get a message. No folder found for header");
1737                 }
1738                 g_object_unref (header);                
1739                 tny_iterator_next (iter);
1740         }
1741
1742         /* Set operation status */
1743         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1744                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1745
1746         /* Notify about operation end */
1747         g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1748
1749         /* Free thread resources. Will be called after all previous idles */
1750         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1751
1752         return NULL;
1753 }
1754
1755 void 
1756 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1757                                      TnyList *header_list, 
1758                                      GetMsgAsyncUserCallback user_callback,
1759                                      gpointer user_data,
1760                                      GDestroyNotify notify)
1761 {
1762         TnyHeader *header = NULL;
1763         TnyFolder *folder = NULL;
1764         GThread *thread;
1765         ModestMailOperationPrivate *priv = NULL;
1766         GetFullMsgsInfo *info = NULL;
1767         gboolean size_ok = TRUE;
1768         gint max_size;
1769         TnyIterator *iter = NULL;
1770         
1771         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1772         
1773         /* Init mail operation */
1774         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1775         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1776         priv->done = 0;
1777         priv->total = tny_list_get_length(header_list);
1778
1779         /* Get account and set it into mail_operation */
1780         if (tny_list_get_length (header_list) >= 1) {
1781                 iter = tny_list_create_iterator (header_list);
1782                 header = TNY_HEADER (tny_iterator_get_current (iter));
1783                 folder = tny_header_get_folder (header);                
1784                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1785                 g_object_unref (header);
1786                 g_object_unref (folder);
1787
1788                 if (tny_list_get_length (header_list) == 1) {
1789                         g_object_unref (iter);
1790                         iter = NULL;
1791                 }
1792         }
1793
1794         /* Get msg size limit */
1795         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1796                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1797                                          &(priv->error));
1798         if (priv->error) {
1799                 g_clear_error (&(priv->error));
1800                 max_size = G_MAXINT;
1801         } else {
1802                 max_size = max_size * KB;
1803         }
1804
1805         /* Check message size limits. If there is only one message
1806            always retrieve it */
1807         if (iter != NULL) {
1808                 while (!tny_iterator_is_done (iter) && size_ok) {
1809                         header = TNY_HEADER (tny_iterator_get_current (iter));
1810                         if (tny_header_get_message_size (header) >= max_size)
1811                                 size_ok = FALSE;
1812                         g_object_unref (header);
1813                         tny_iterator_next (iter);
1814                 }
1815                 g_object_unref (iter);
1816         }
1817
1818         if (size_ok) {
1819                 /* Create the info */
1820                 info = g_slice_new0 (GetFullMsgsInfo);
1821                 info->mail_op = self;
1822                 info->user_callback = user_callback;
1823                 info->user_data = user_data;
1824                 info->headers = g_object_ref (header_list);
1825                 info->notify = notify;
1826
1827                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1828         } else {
1829                 /* Set status failed and set an error */
1830                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1831                 /* FIXME: the error msg is different for pop */
1832                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1833                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1834                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1835                 /* Remove from queue and free resources */
1836                 modest_mail_operation_notify_end (self);
1837                 if (notify)
1838                         notify (user_data);
1839         }
1840 }
1841
1842
1843 void 
1844 modest_mail_operation_remove_msg (ModestMailOperation *self,
1845                                   TnyHeader *header,
1846                                   gboolean remove_to_trash)
1847 {
1848         TnyFolder *folder;
1849         ModestMailOperationPrivate *priv;
1850
1851         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1852         g_return_if_fail (TNY_IS_HEADER (header));
1853
1854         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1855         folder = tny_header_get_folder (header);
1856
1857         /* Get account and set it into mail_operation */
1858         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1859
1860         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1861
1862         /* Delete or move to trash */
1863         if (remove_to_trash) {
1864                 TnyFolder *trash_folder;
1865                 TnyStoreAccount *store_account;
1866
1867                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1868                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1869                                                                       TNY_FOLDER_TYPE_TRASH);
1870                 if (trash_folder) {
1871                         TnyList *headers;
1872
1873                         /* Create list */
1874                         headers = tny_simple_list_new ();
1875                         tny_list_append (headers, G_OBJECT (header));
1876                         g_object_unref (header);
1877
1878                         /* Move to trash */
1879                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1880                         g_object_unref (headers);
1881 /*                      g_object_unref (trash_folder); */
1882                 } else {
1883                         ModestMailOperationPrivate *priv;
1884
1885                         /* Set status failed and set an error */
1886                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1887                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1888                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1889                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1890                                      _("Error trying to delete a message. Trash folder not found"));
1891                 }
1892
1893                 g_object_unref (G_OBJECT (store_account));
1894         } else {
1895                 tny_folder_remove_msg (folder, header, &(priv->error));
1896                 if (!priv->error)
1897                         tny_folder_sync(folder, TRUE, &(priv->error));
1898         }
1899
1900         /* Set status */
1901         if (!priv->error)
1902                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1903         else
1904                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1905
1906         /* Free */
1907         g_object_unref (G_OBJECT (folder));
1908
1909         /* Notify about operation end */
1910         modest_mail_operation_notify_end (self);
1911 }
1912
1913 static void
1914 transfer_msgs_status_cb (GObject *obj,
1915                          TnyStatus *status,  
1916                          gpointer user_data)
1917 {
1918         XFerMsgAsyncHelper *helper = NULL;
1919         ModestMailOperation *self;
1920         ModestMailOperationPrivate *priv;
1921         ModestMailOperationState *state;
1922
1923
1924         g_return_if_fail (status != NULL);
1925         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1926
1927         helper = (XFerMsgAsyncHelper *) user_data;
1928         g_return_if_fail (helper != NULL);       
1929
1930         self = helper->mail_op;
1931         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1932
1933         if ((status->position == 1) && (status->of_total == 100))
1934                 return;
1935
1936         priv->done = status->position;
1937         priv->total = status->of_total;
1938
1939         state = modest_mail_operation_clone_state (self);
1940         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1941         g_slice_free (ModestMailOperationState, state);
1942 }
1943
1944
1945 static void
1946 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1947 {
1948         XFerMsgAsyncHelper *helper;
1949         ModestMailOperation *self;
1950         ModestMailOperationPrivate *priv;
1951
1952         helper = (XFerMsgAsyncHelper *) user_data;
1953         self = helper->mail_op;
1954
1955         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1956
1957         if (*err) {
1958                 priv->error = g_error_copy (*err);
1959                 priv->done = 0;
1960                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1961         } else if (cancelled) {
1962                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1963                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1964                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1965                              _("Error trying to refresh the contents of %s"),
1966                              tny_folder_get_name (folder));
1967         } else {
1968                 priv->done = 1;
1969                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1970         }
1971
1972         /* If user defined callback function was defined, call it */
1973         if (helper->user_callback) {
1974                 gdk_threads_enter ();
1975                 helper->user_callback (priv->source, helper->user_data);
1976                 gdk_threads_leave ();
1977         }
1978
1979         /* Free */
1980         g_object_unref (helper->headers);
1981         g_object_unref (helper->dest_folder);
1982         g_object_unref (helper->mail_op);
1983         g_slice_free   (XFerMsgAsyncHelper, helper);
1984         g_object_unref (folder);
1985
1986         /* Notify about operation end */
1987         modest_mail_operation_notify_end (self);
1988 }
1989
1990 void
1991 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1992                                  TnyList *headers, 
1993                                  TnyFolder *folder, 
1994                                  gboolean delete_original,
1995                                  XferMsgsAsynUserCallback user_callback,
1996                                  gpointer user_data)
1997 {
1998         ModestMailOperationPrivate *priv;
1999         TnyIterator *iter;
2000         TnyFolder *src_folder;
2001         XFerMsgAsyncHelper *helper;
2002         TnyHeader *header;
2003         ModestTnyFolderRules rules;
2004
2005         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2006         g_return_if_fail (TNY_IS_LIST (headers));
2007         g_return_if_fail (TNY_IS_FOLDER (folder));
2008
2009         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2010         priv->total = 1;
2011         priv->done = 0;
2012         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2013
2014         /* Apply folder rules */
2015         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2016         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2017                 /* Set status failed and set an error */
2018                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2019                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2020                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2021                              _("FIXME: folder does not accept msgs"));
2022                 /* Notify the queue */
2023                 modest_mail_operation_notify_end (self);
2024                 return;
2025         }
2026
2027         /* Create the helper */
2028         helper = g_slice_new0 (XFerMsgAsyncHelper);
2029         helper->mail_op = g_object_ref(self);
2030         helper->dest_folder = g_object_ref(folder);
2031         helper->headers = g_object_ref(headers);
2032         helper->user_callback = user_callback;
2033         helper->user_data = user_data;
2034
2035         /* Get source folder */
2036         iter = tny_list_create_iterator (headers);
2037         header = TNY_HEADER (tny_iterator_get_current (iter));
2038         src_folder = tny_header_get_folder (header);
2039         g_object_unref (header);
2040         g_object_unref (iter);
2041
2042         /* Get account and set it into mail_operation */
2043         priv->account = modest_tny_folder_get_account (src_folder);
2044
2045         /* Transfer messages */
2046         tny_folder_transfer_msgs_async (src_folder, 
2047                                         headers, 
2048                                         folder, 
2049                                         delete_original, 
2050                                         transfer_msgs_cb, 
2051                                         transfer_msgs_status_cb,
2052                                         helper);
2053 }
2054
2055
2056 static void
2057 on_refresh_folder (TnyFolder   *folder, 
2058                    gboolean     cancelled, 
2059                    GError     **error,
2060                    gpointer     user_data)
2061 {
2062         RefreshAsyncHelper *helper = NULL;
2063         ModestMailOperation *self = NULL;
2064         ModestMailOperationPrivate *priv = NULL;
2065
2066         helper = (RefreshAsyncHelper *) user_data;
2067         self = helper->mail_op;
2068         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2069
2070         if (*error) {
2071                 priv->error = g_error_copy (*error);
2072                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2073                 goto out;
2074         }
2075
2076         if (cancelled) {
2077                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2078                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2079                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2080                              _("Error trying to refresh the contents of %s"),
2081                              tny_folder_get_name (folder));
2082                 goto out;
2083         }
2084
2085         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2086
2087  out:
2088         /* Call user defined callback, if it exists */
2089         if (helper->user_callback) {
2090 /*              gdk_threads_enter (); */
2091                 helper->user_callback (priv->source, folder, helper->user_data);
2092 /*              gdk_threads_leave (); */
2093         }
2094
2095         /* Free */
2096         g_object_unref (helper->mail_op);
2097         g_slice_free   (RefreshAsyncHelper, helper);
2098         g_object_unref (folder);
2099
2100         /* Notify about operation end */
2101         modest_mail_operation_notify_end (self);
2102 }
2103
2104 static void
2105 on_refresh_folder_status_update (GObject *obj,
2106                                  TnyStatus *status,
2107                                  gpointer user_data)
2108 {
2109         RefreshAsyncHelper *helper = NULL;
2110         ModestMailOperation *self = NULL;
2111         ModestMailOperationPrivate *priv = NULL;
2112         ModestMailOperationState *state;
2113
2114         g_return_if_fail (user_data != NULL);
2115         g_return_if_fail (status != NULL);
2116         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2117
2118         helper = (RefreshAsyncHelper *) user_data;
2119         self = helper->mail_op;
2120         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2121
2122         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2123
2124         priv->done = status->position;
2125         priv->total = status->of_total;
2126
2127         state = modest_mail_operation_clone_state (self);
2128         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2129         g_slice_free (ModestMailOperationState, state);
2130 }
2131
2132 void 
2133 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2134                                        TnyFolder *folder,
2135                                        RefreshAsyncUserCallback user_callback,
2136                                        gpointer user_data)
2137 {
2138         ModestMailOperationPrivate *priv = NULL;
2139         RefreshAsyncHelper *helper = NULL;
2140
2141         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2142
2143         /* Pick a reference */
2144         g_object_ref (folder);
2145
2146         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2147
2148         /* Get account and set it into mail_operation */
2149         priv->account = modest_tny_folder_get_account  (folder);
2150
2151         /* Create the helper */
2152         helper = g_slice_new0 (RefreshAsyncHelper);
2153         helper->mail_op = g_object_ref(self);
2154         helper->user_callback = user_callback;
2155         helper->user_data = user_data;
2156
2157         /* Refresh the folder. TODO: tinymail could issue a status
2158            updates before the callback call then this could happen. We
2159            must review the design */
2160         tny_folder_refresh_async (folder,
2161                                   on_refresh_folder,
2162                                   on_refresh_folder_status_update,
2163                                   helper);
2164 }
2165
2166 /**
2167  *
2168  * It's used by the mail operation queue to notify the observers
2169  * attached to that signal that the operation finished. We need to use
2170  * that because tinymail does not give us the progress of a given
2171  * operation when it finishes (it directly calls the operation
2172  * callback).
2173  */
2174 static void
2175 modest_mail_operation_notify_end (ModestMailOperation *self)
2176 {
2177         ModestMailOperationState *state;
2178         ModestMailOperationPrivate *priv = NULL;
2179
2180         g_return_if_fail (self);
2181
2182         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2183
2184         if (!priv) {
2185                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2186                 return;
2187         }
2188         
2189         /* Set the account back to not busy */
2190         if (priv->account_name) {
2191                 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2192                                                                                                                                                                 FALSE);
2193                 g_free(priv->account_name);
2194                 priv->account_name = NULL;
2195         }
2196         
2197         /* Notify the observers about the mail opertation end */
2198         state = modest_mail_operation_clone_state (self);
2199         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2200         g_slice_free (ModestMailOperationState, state);
2201 }