0564f08c45456a9a518976fd0cfa5a8389210354
[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         gdk_threads_enter ();
798
799         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
800         ModestMailOperationState *state;
801
802         state = modest_mail_operation_clone_state (mail_op);
803         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
804         g_slice_free (ModestMailOperationState, state);
805         
806         gdk_threads_leave ();
807
808         return TRUE;
809 }
810
811 /* 
812  * Issues the "progress-changed" signal and removes the timer. It uses
813  * a lock to ensure that the progress information of the mail
814  * operation is not modified while there are notifications pending
815  */
816 static gboolean
817 idle_notify_progress_once (gpointer data)
818 {
819         gdk_threads_enter ();
820
821         ModestPair *pair;
822
823         pair = (ModestPair *) data;
824
825         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
826
827         /* Free the state and the reference to the mail operation */
828         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
829         g_object_unref (pair->first);
830
831         gdk_threads_leave ();
832
833         return FALSE;
834 }
835
836 /* 
837  * Used by update_account_thread to notify the queue from the main
838  * loop. We call it inside an idle call to achieve that
839  */
840 static gboolean
841 idle_notify_update_account_queue (gpointer data)
842 {
843         gdk_threads_enter ();
844
845         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
846         ModestMailOperationPrivate *priv = NULL;
847
848         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
849         
850         modest_mail_operation_notify_end (mail_op);
851         g_object_unref (mail_op);
852
853         gdk_threads_leave ();
854
855         return FALSE;
856 }
857
858 static int
859 compare_headers_by_date (gconstpointer a, 
860                          gconstpointer b)
861 {
862         TnyHeader **header1, **header2;
863         time_t sent1, sent2;
864
865         header1 = (TnyHeader **) a;
866         header2 = (TnyHeader **) b;
867
868         sent1 = tny_header_get_date_sent (*header1);
869         sent2 = tny_header_get_date_sent (*header2);
870
871         /* We want the most recent ones (greater time_t) at the
872            beginning */
873         if (sent1 < sent2)
874                 return 1;
875         else
876                 return -1;
877 }
878
879 static gboolean 
880 set_last_updated_idle (gpointer data)
881 {
882         gdk_threads_enter ();
883
884         /* It does not matter if the time is not exactly the same than
885            the time when this idle was called, it's just an
886            approximation and it won't be very different */
887         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
888                                     (gchar *) data, 
889                                     MODEST_ACCOUNT_LAST_UPDATED, 
890                                     time(NULL), 
891                                     TRUE);
892
893         gdk_threads_leave ();
894
895         return FALSE;
896 }
897
898 static gpointer
899 update_account_thread (gpointer thr_user_data)
900 {
901         static gboolean first_time = TRUE;
902         UpdateAccountInfo *info;
903         TnyList *all_folders = NULL;
904         GPtrArray *new_headers = NULL;
905         TnyIterator *iter = NULL;
906         TnyFolderStoreQuery *query = NULL;
907         ModestMailOperationPrivate *priv = NULL;
908         ModestTnySendQueue *send_queue = NULL;
909
910         info = (UpdateAccountInfo *) thr_user_data;
911         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
912
913         /* Get account and set it into mail_operation */
914         priv->account = g_object_ref (info->account);
915
916         /*
917          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
918          * show any updates unless we do that
919          */
920         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
921                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
922
923         /* Get all the folders. We can do it synchronously because
924            we're already running in a different thread than the UI */
925         all_folders = tny_simple_list_new ();
926         query = tny_folder_store_query_new ();
927         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
928         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
929                                       all_folders,
930                                       query,
931                                       &(priv->error));
932         if (priv->error) {
933                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
934                 goto out;
935         }
936
937         iter = tny_list_create_iterator (all_folders);
938         while (!tny_iterator_is_done (iter)) {
939                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
940
941                 recurse_folders (folder, query, all_folders);
942                 tny_iterator_next (iter);
943         }
944         g_object_unref (G_OBJECT (iter));
945
946         /* Update status and notify. We need to call the notification
947            with a source function in order to call it from the main
948            loop. We need that in order not to get into trouble with
949            Gtk+. We use a timeout in order to provide more status
950            information, because the sync tinymail call does not
951            provide it for the moment */
952         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
953
954         /* Refresh folders */
955         new_headers = g_ptr_array_new ();
956         iter = tny_list_create_iterator (all_folders);
957
958         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
959
960                 InternalFolderObserver *observer;
961                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
962
963                 /* Refresh the folder */
964                 /* Our observer receives notification of new emails during folder refreshes,
965                  * so we can use observer->new_headers.
966                  */
967                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
968                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
969                 
970                 /* This gets the status information (headers) from the server.
971                  * We use the blocking version, because we are already in a separate 
972                  * thread.
973                  */
974
975                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
976                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
977                         TnyIterator *iter;
978
979                         /* If the retrieve type is full messages, refresh and get the messages */
980                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
981
982                         iter = tny_list_create_iterator (observer->new_headers);
983                         while (!tny_iterator_is_done (iter)) {
984                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
985                                  
986                                 /* Apply per-message size limits */
987                                 if (tny_header_get_message_size (header) < info->max_size)
988                                         g_ptr_array_add (new_headers, g_object_ref (header));
989
990                                 g_object_unref (header);
991                                 tny_iterator_next (iter);
992                         }
993                         g_object_unref (iter);
994                 } else {
995                         /* We do not need to do it the first time
996                            because it's automatically done by the tree
997                            model */
998                         if (G_UNLIKELY (!first_time))
999                                 tny_folder_poke_status (TNY_FOLDER (folder));
1000                 }
1001                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1002                 g_object_unref (observer);
1003                 observer = NULL;                        
1004
1005                 g_object_unref (G_OBJECT (folder));
1006                 if (priv->error)
1007                 {
1008                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1009                         goto out;
1010                 }
1011                 
1012                 tny_iterator_next (iter);
1013         }
1014
1015         did_a_cancel = FALSE;
1016
1017         g_object_unref (G_OBJECT (iter));
1018         g_source_remove (timeout);
1019
1020         if (new_headers->len > 0) {
1021                 gint msg_num = 0;
1022
1023                 /* Order by date */
1024                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1025
1026                 /* Apply message count limit */
1027                 /* If the number of messages exceeds the maximum, ask the
1028                  * user to download them all,
1029                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1030                  */
1031                 if (new_headers->len > info->retrieve_limit) {
1032                         /* TODO: Ask the user, instead of just
1033                          * failing, showing
1034                          * mail_nc_msg_count_limit_exceeded, with 'Get
1035                          * all' and 'Newest only' buttons. */
1036                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1037                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1038                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1039                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1040                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1041                         goto out;
1042                 }
1043                 
1044                 priv->done = 0;
1045                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1046                 while (msg_num < priv->total) {
1047
1048                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1049                         TnyFolder *folder = tny_header_get_folder (header);
1050                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1051                         ModestMailOperationState *state;
1052                         ModestPair* pair;
1053
1054                         priv->done++;
1055                         /* We can not just use the mail operation because the
1056                            values of done and total could change before the
1057                            idle is called */
1058                         state = modest_mail_operation_clone_state (info->mail_op);
1059                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1060                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1061                                          pair, (GDestroyNotify) modest_pair_free);
1062
1063                         g_object_unref (msg);
1064                         g_object_unref (folder);
1065
1066                         msg_num++;
1067                 }
1068                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1069                 g_ptr_array_free (new_headers, FALSE);
1070         }
1071         
1072         /* Perform send */
1073         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1074         priv->done = 0;
1075         priv->total = 0;
1076         if (priv->account != NULL) 
1077                 g_object_unref (priv->account);
1078         priv->account = g_object_ref (info->transport_account);
1079         
1080         send_queue = modest_runtime_get_send_queue (info->transport_account);
1081         if (send_queue) {
1082                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1083                 modest_tny_send_queue_try_to_send (send_queue);
1084                 g_source_remove (timeout);
1085         } else {
1086                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1087                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1088                              "cannot create a send queue for %s\n", 
1089                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1090                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1091         }
1092         
1093         /* Check if the operation was a success */
1094         if (!priv->error) {
1095                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1096
1097                 /* Update the last updated key */
1098                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1099                                  set_last_updated_idle, 
1100                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1101                                  (GDestroyNotify) g_free);
1102         }
1103
1104  out:
1105         /* Notify about operation end. Note that the info could be
1106            freed before this idle happens, but the mail operation will
1107            be still alive */
1108         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1109
1110         if (info->callback) {
1111                 /* This thread is not in the main lock */
1112                 gdk_threads_enter ();
1113                 info->callback (info->mail_op, 
1114                                 (new_headers) ? new_headers->len : 0, 
1115                                 info->user_data);
1116                 gdk_threads_leave ();
1117         }
1118         
1119         /* Frees */
1120         g_object_unref (query);
1121         g_object_unref (all_folders);
1122         g_object_unref (info->account);
1123         g_object_unref (info->transport_account);
1124         g_free (info->retrieve_type);
1125         g_slice_free (UpdateAccountInfo, info);
1126
1127         first_time = FALSE;
1128
1129         return NULL;
1130 }
1131
1132 gboolean
1133 modest_mail_operation_update_account (ModestMailOperation *self,
1134                                       const gchar *account_name,
1135                                       UpdateAccountCallback callback,
1136                                       gpointer user_data)
1137 {
1138         GThread *thread;
1139         UpdateAccountInfo *info;
1140         ModestMailOperationPrivate *priv;
1141         ModestAccountMgr *mgr;
1142         TnyStoreAccount *modest_account;
1143         TnyTransportAccount *transport_account;
1144
1145         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1146         g_return_val_if_fail (account_name, FALSE);
1147
1148         /* Init mail operation. Set total and done to 0, and do not
1149            update them, this way the progress objects will know that
1150            we have no clue about the number of the objects */
1151         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1152         priv->total = 0;
1153         priv->done  = 0;
1154         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1155
1156         /* Make sure that we have a connection, and request one 
1157          * if necessary:
1158          * TODO: Is there some way to trigger this for every attempt to 
1159          * use the network? */
1160         if (!modest_platform_connect_and_wait(NULL))
1161                 goto error;
1162
1163         /* Get the Modest account */
1164         modest_account = (TnyStoreAccount *)
1165                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1166                                                                      account_name,
1167                                                                      TNY_ACCOUNT_TYPE_STORE);
1168
1169         if (!modest_account) {
1170                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1171                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1172                              "cannot get tny store account for %s\n", account_name);
1173                 goto error;
1174         }
1175
1176         
1177         /* Get the transport account, we can not do it in the thread
1178            due to some problems with dbus */
1179         transport_account = (TnyTransportAccount *)
1180                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1181                                                                                     account_name);
1182         if (!transport_account) {
1183                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1184                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1185                              "cannot get tny transport account for %s\n", account_name);
1186                 goto error;
1187         }
1188
1189         /* Create the helper object */
1190         info = g_slice_new (UpdateAccountInfo);
1191         info->mail_op = self;
1192         info->account = modest_account;
1193         info->transport_account = transport_account;
1194         info->callback = callback;
1195         info->user_data = user_data;
1196
1197         /* Get the message size limit */
1198         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1199                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1200         if (info->max_size == 0)
1201                 info->max_size = G_MAXINT;
1202         else
1203                 info->max_size = info->max_size * KB;
1204
1205         /* Get per-account retrieval type */
1206         mgr = modest_runtime_get_account_mgr ();
1207         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1208                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1209
1210         /* Get per-account message amount retrieval limit */
1211         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1212                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1213         if (info->retrieve_limit == 0)
1214                 info->retrieve_limit = G_MAXINT;
1215                 
1216         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1217
1218         /* Set account busy */
1219         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1220         priv->account_name = g_strdup(account_name);
1221         
1222         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1223
1224         return TRUE;
1225
1226  error:
1227         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1228         if (callback) 
1229                 callback (self, 0, user_data);
1230         modest_mail_operation_notify_end (self);
1231         return FALSE;
1232 }
1233
1234 /* ******************************************************************* */
1235 /* ************************** STORE  ACTIONS ************************* */
1236 /* ******************************************************************* */
1237
1238
1239 TnyFolder *
1240 modest_mail_operation_create_folder (ModestMailOperation *self,
1241                                      TnyFolderStore *parent,
1242                                      const gchar *name)
1243 {
1244         ModestMailOperationPrivate *priv;
1245         TnyFolder *new_folder = NULL;
1246
1247         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1248         g_return_val_if_fail (name, NULL);
1249         
1250         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1251
1252         /* Check parent */
1253         if (TNY_IS_FOLDER (parent)) {
1254                 /* Check folder rules */
1255                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1256                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1257                         /* Set status failed and set an error */
1258                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1259                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1260                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1261                                      _("mail_in_ui_folder_create_error"));
1262                 }
1263         }
1264
1265         if (!priv->error) {
1266                 /* Create the folder */
1267                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1268                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1269                 if (!priv->error)
1270                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1271         }
1272
1273         /* Notify about operation end */
1274         modest_mail_operation_notify_end (self);
1275
1276         return new_folder;
1277 }
1278
1279 void
1280 modest_mail_operation_remove_folder (ModestMailOperation *self,
1281                                      TnyFolder           *folder,
1282                                      gboolean             remove_to_trash)
1283 {
1284         TnyAccount *account;
1285         ModestMailOperationPrivate *priv;
1286         ModestTnyFolderRules rules;
1287
1288         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1289         g_return_if_fail (TNY_IS_FOLDER (folder));
1290         
1291         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1292         
1293         /* Check folder rules */
1294         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1295         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1296                 /* Set status failed and set an error */
1297                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1298                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1299                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1300                              _("mail_in_ui_folder_delete_error"));
1301                 goto end;
1302         }
1303
1304         /* Get the account */
1305         account = modest_tny_folder_get_account (folder);
1306         priv->account = g_object_ref(account);
1307
1308         /* Delete folder or move to trash */
1309         if (remove_to_trash) {
1310                 TnyFolder *trash_folder = NULL;
1311                 trash_folder = modest_tny_account_get_special_folder (account,
1312                                                                       TNY_FOLDER_TYPE_TRASH);
1313                 /* TODO: error_handling */
1314                  modest_mail_operation_xfer_folder (self, folder,
1315                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1316         } else {
1317                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1318
1319                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1320                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1321
1322                 if (parent)
1323                         g_object_unref (G_OBJECT (parent));
1324         }
1325         g_object_unref (G_OBJECT (account));
1326
1327  end:
1328         /* Notify about operation end */
1329         modest_mail_operation_notify_end (self);
1330 }
1331
1332 static void
1333 transfer_folder_status_cb (GObject *obj,
1334                            TnyStatus *status,
1335                            gpointer user_data)
1336 {
1337         ModestMailOperation *self;
1338         ModestMailOperationPrivate *priv;
1339         ModestMailOperationState *state;
1340
1341         g_return_if_fail (status != NULL);
1342         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1343
1344         self = MODEST_MAIL_OPERATION (user_data);
1345         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1346
1347         if ((status->position == 1) && (status->of_total == 100))
1348                 return;
1349
1350         priv->done = status->position;
1351         priv->total = status->of_total;
1352
1353         state = modest_mail_operation_clone_state (self);
1354         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1355         g_slice_free (ModestMailOperationState, state);
1356 }
1357
1358
1359 static void
1360 transfer_folder_cb (TnyFolder *folder, 
1361                     TnyFolderStore *into, 
1362                     gboolean cancelled, 
1363                     TnyFolder *new_folder, 
1364                     GError **err, 
1365                     gpointer user_data)
1366 {
1367         ModestMailOperation *self = NULL;
1368         ModestMailOperationPrivate *priv = NULL;
1369
1370         self = MODEST_MAIL_OPERATION (user_data);
1371
1372         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1373
1374         if (*err) {
1375                 priv->error = g_error_copy (*err);
1376                 priv->done = 0;
1377                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1378         } else if (cancelled) {
1379                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1380                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1381                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1382                              _("Transference of %s was cancelled."),
1383                              tny_folder_get_name (folder));
1384         } else {
1385                 priv->done = 1;
1386                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1387         }
1388                 
1389         /* Free */
1390         g_object_unref (folder);
1391         g_object_unref (into);
1392         if (new_folder != NULL)
1393                 g_object_unref (new_folder);
1394
1395         /* Notify about operation end */
1396         modest_mail_operation_notify_end (self);
1397 }
1398
1399 void
1400 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1401                                    TnyFolder *folder,
1402                                    TnyFolderStore *parent,
1403                                    gboolean delete_original)
1404 {
1405         ModestMailOperationPrivate *priv = NULL;
1406         ModestTnyFolderRules parent_rules, rules;
1407
1408         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1409         g_return_if_fail (TNY_IS_FOLDER (folder));
1410         g_return_if_fail (TNY_IS_FOLDER (parent));
1411
1412         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1413
1414         /* Get account and set it into mail_operation */
1415         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1416         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1417
1418         /* Get folder rules */
1419         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1420         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1421
1422         if (!TNY_IS_FOLDER_STORE (parent)) {
1423                 
1424         }
1425         
1426         /* The moveable restriction is applied also to copy operation */
1427         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1428                 /* Set status failed and set an error */
1429                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1430                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1431                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1432                              _("mail_in_ui_folder_move_target_error"));
1433
1434                 /* Notify the queue */
1435                 modest_mail_operation_notify_end (self);
1436         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1437                 /* Set status failed and set an error */
1438                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1439                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1440                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1441                              _("FIXME: parent folder does not accept new folders"));
1442
1443                 /* Notify the queue */
1444                 modest_mail_operation_notify_end (self);
1445         } else {
1446                 /* Pick references for async calls */
1447                 g_object_ref (folder);
1448                 g_object_ref (parent);
1449
1450                 /* Move/Copy folder */          
1451                 tny_folder_copy_async (folder,
1452                                        parent,
1453                                        tny_folder_get_name (folder),
1454                                        delete_original,
1455                                        transfer_folder_cb,
1456                                        transfer_folder_status_cb,
1457                                        self);
1458         }
1459 }
1460
1461 void
1462 modest_mail_operation_rename_folder (ModestMailOperation *self,
1463                                      TnyFolder *folder,
1464                                      const gchar *name)
1465 {
1466         ModestMailOperationPrivate *priv;
1467         ModestTnyFolderRules rules;
1468
1469         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1470         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1471         g_return_if_fail (name);
1472         
1473         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1474
1475         /* Get account and set it into mail_operation */
1476         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1477
1478         /* Check folder rules */
1479         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1480         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1481                 /* Set status failed and set an error */
1482                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1483                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1484                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1485                              _("FIXME: unable to rename"));
1486
1487                 /* Notify about operation end */
1488                 modest_mail_operation_notify_end (self);
1489         } else {
1490                 /* Rename. Camel handles folder subscription/unsubscription */
1491                 TnyFolderStore *into;
1492
1493                 into = tny_folder_get_folder_store (folder);
1494                 tny_folder_copy_async (folder, into, name, TRUE,
1495                                  transfer_folder_cb,
1496                                  transfer_folder_status_cb,
1497                                  self);
1498                 if (into)
1499                         g_object_unref (into);
1500                 
1501         }
1502  }
1503
1504 /* ******************************************************************* */
1505 /* **************************  MSG  ACTIONS  ************************* */
1506 /* ******************************************************************* */
1507
1508 void modest_mail_operation_get_msg (ModestMailOperation *self,
1509                                     TnyHeader *header,
1510                                     GetMsgAsyncUserCallback user_callback,
1511                                     gpointer user_data)
1512 {
1513         GetMsgAsyncHelper *helper = NULL;
1514         TnyFolder *folder;
1515         ModestMailOperationPrivate *priv;
1516         
1517         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1518         g_return_if_fail (TNY_IS_HEADER (header));
1519         
1520         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1521         folder = tny_header_get_folder (header);
1522
1523         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1524
1525         /* Get message from folder */
1526         if (folder) {
1527                 /* Get account and set it into mail_operation */
1528                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1529
1530                 helper = g_slice_new0 (GetMsgAsyncHelper);
1531                 helper->mail_op = self;
1532                 helper->user_callback = user_callback;
1533                 helper->user_data = user_data;
1534                 helper->header = g_object_ref (header);
1535
1536                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1537
1538                 g_object_unref (G_OBJECT (folder));
1539         } else {
1540                 /* Set status failed and set an error */
1541                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1542                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1543                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1544                              _("Error trying to get a message. No folder found for header"));
1545
1546                 /* Notify the queue */
1547                 modest_mail_operation_notify_end (self);
1548         }
1549 }
1550
1551 static void
1552 get_msg_cb (TnyFolder *folder, 
1553             gboolean cancelled, 
1554             TnyMsg *msg, 
1555             GError **error, 
1556             gpointer user_data)
1557 {
1558         GetMsgAsyncHelper *helper = NULL;
1559         ModestMailOperation *self = NULL;
1560         ModestMailOperationPrivate *priv = NULL;
1561
1562         helper = (GetMsgAsyncHelper *) user_data;
1563         g_return_if_fail (helper != NULL);       
1564         self = helper->mail_op;
1565         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1566         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1567
1568         /* Check errors and cancel */
1569         if (*error) {
1570                 priv->error = g_error_copy (*error);
1571                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1572                 goto out;
1573         }
1574         if (cancelled) {
1575                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1576                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1577                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1578                              _("Error trying to refresh the contents of %s"),
1579                              tny_folder_get_name (folder));
1580                 goto out;
1581         }
1582
1583         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1584
1585         /* If user defined callback function was defined, call it */
1586         if (helper->user_callback) {
1587                 /* This callback is called into an iddle by tinymail,
1588                    and idles are not in the main lock */
1589                 gdk_threads_enter ();
1590                 helper->user_callback (self, helper->header, msg, helper->user_data);
1591                 gdk_threads_leave ();
1592         }
1593
1594  out:
1595         /* Free */
1596         g_object_unref (helper->header);
1597         g_slice_free (GetMsgAsyncHelper, helper);
1598                 
1599         /* Notify about operation end */
1600         modest_mail_operation_notify_end (self);        
1601 }
1602
1603 static void     
1604 get_msg_status_cb (GObject *obj,
1605                    TnyStatus *status,  
1606                    gpointer user_data)
1607 {
1608         GetMsgAsyncHelper *helper = NULL;
1609         ModestMailOperation *self;
1610         ModestMailOperationPrivate *priv;
1611         ModestMailOperationState *state;
1612
1613         g_return_if_fail (status != NULL);
1614         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1615
1616         helper = (GetMsgAsyncHelper *) user_data;
1617         g_return_if_fail (helper != NULL);       
1618
1619         self = helper->mail_op;
1620         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1621
1622         if ((status->position == 1) && (status->of_total == 100))
1623                 return;
1624
1625         priv->done = 1;
1626         priv->total = 1;
1627
1628         state = modest_mail_operation_clone_state (self);
1629         state->bytes_done = status->position;
1630         state->bytes_total = status->of_total;
1631         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1632         g_slice_free (ModestMailOperationState, state);
1633 }
1634
1635 /****************************************************/
1636 typedef struct {
1637         ModestMailOperation *mail_op;
1638         TnyList *headers;
1639         GetMsgAsyncUserCallback user_callback;
1640         gpointer user_data;
1641         GDestroyNotify notify;
1642 } GetFullMsgsInfo;
1643
1644 typedef struct {
1645         GetMsgAsyncUserCallback user_callback;
1646         TnyHeader *header;
1647         TnyMsg *msg;
1648         gpointer user_data;
1649         ModestMailOperation *mail_op;
1650 } NotifyGetMsgsInfo;
1651
1652
1653 /* 
1654  * Used by get_msgs_full_thread to call the user_callback for each
1655  * message that has been read
1656  */
1657 static gboolean
1658 notify_get_msgs_full (gpointer data)
1659 {
1660         gdk_threads_enter ();
1661
1662         NotifyGetMsgsInfo *info;
1663
1664         info = (NotifyGetMsgsInfo *) data;      
1665
1666         /* Call the user callback. Idles are not in the main lock, so
1667            lock it */
1668         gdk_threads_enter ();
1669         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1670         gdk_threads_leave ();
1671
1672         g_slice_free (NotifyGetMsgsInfo, info);
1673
1674         gdk_threads_leave ();
1675
1676         return FALSE;
1677 }
1678
1679 /* 
1680  * Used by get_msgs_full_thread to free al the thread resources and to
1681  * call the destroy function for the passed user_data
1682  */
1683 static gboolean
1684 get_msgs_full_destroyer (gpointer data)
1685 {
1686         gdk_threads_enter ();
1687
1688         GetFullMsgsInfo *info;
1689
1690         info = (GetFullMsgsInfo *) data;
1691
1692         if (info->notify)
1693                 info->notify (info->user_data);
1694
1695         /* free */
1696         g_object_unref (info->headers);
1697         g_slice_free (GetFullMsgsInfo, info);
1698
1699         gdk_threads_leave ();
1700
1701         return FALSE;
1702 }
1703
1704 static gpointer
1705 get_msgs_full_thread (gpointer thr_user_data)
1706 {
1707         GetFullMsgsInfo *info;
1708         ModestMailOperationPrivate *priv = NULL;
1709         TnyIterator *iter = NULL;
1710         
1711         info = (GetFullMsgsInfo *) thr_user_data;       
1712         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1713
1714         iter = tny_list_create_iterator (info->headers);
1715         while (!tny_iterator_is_done (iter)) { 
1716                 TnyHeader *header;
1717                 TnyFolder *folder;
1718                 
1719                 header = TNY_HEADER (tny_iterator_get_current (iter));
1720                 folder = tny_header_get_folder (header);
1721                                 
1722                 /* Get message from folder */
1723                 if (folder) {
1724                         TnyMsg *msg;
1725                         /* The callback will call it per each header */
1726                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1727
1728                         if (msg) {
1729                                 ModestMailOperationState *state;
1730                                 ModestPair *pair;
1731
1732                                 priv->done++;
1733
1734                                 /* notify progress */
1735                                 state = modest_mail_operation_clone_state (info->mail_op);
1736                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1737                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1738                                                  pair, (GDestroyNotify) modest_pair_free);
1739
1740                                 /* The callback is the responsible for
1741                                    freeing the message */
1742                                 if (info->user_callback) {
1743                                         NotifyGetMsgsInfo *info_notify;
1744                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1745                                         info_notify->user_callback = info->user_callback;
1746                                         info_notify->mail_op = info->mail_op;
1747                                         info_notify->header = g_object_ref (header);
1748                                         info_notify->msg = g_object_ref (msg);
1749                                         info_notify->user_data = info->user_data;
1750                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1751                                                          notify_get_msgs_full, 
1752                                                          info_notify, NULL);
1753                                 }
1754                                 g_object_unref (msg);
1755                         }
1756                 } else {
1757                         /* Set status failed and set an error */
1758                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1759                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1760                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1761                                      "Error trying to get a message. No folder found for header");
1762                 }
1763                 g_object_unref (header);                
1764                 tny_iterator_next (iter);
1765         }
1766
1767         /* Set operation status */
1768         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1769                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1770
1771         /* Notify about operation end */
1772         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1773
1774         /* Free thread resources. Will be called after all previous idles */
1775         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1776
1777         return NULL;
1778 }
1779
1780 void 
1781 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1782                                      TnyList *header_list, 
1783                                      GetMsgAsyncUserCallback user_callback,
1784                                      gpointer user_data,
1785                                      GDestroyNotify notify)
1786 {
1787         TnyHeader *header = NULL;
1788         TnyFolder *folder = NULL;
1789         GThread *thread;
1790         ModestMailOperationPrivate *priv = NULL;
1791         GetFullMsgsInfo *info = NULL;
1792         gboolean size_ok = TRUE;
1793         gint max_size;
1794         TnyIterator *iter = NULL;
1795         
1796         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1797         
1798         /* Init mail operation */
1799         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1800         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1801         priv->done = 0;
1802         priv->total = tny_list_get_length(header_list);
1803
1804         /* Get account and set it into mail_operation */
1805         if (tny_list_get_length (header_list) >= 1) {
1806                 iter = tny_list_create_iterator (header_list);
1807                 header = TNY_HEADER (tny_iterator_get_current (iter));
1808                 folder = tny_header_get_folder (header);                
1809                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1810                 g_object_unref (header);
1811                 g_object_unref (folder);
1812
1813                 if (tny_list_get_length (header_list) == 1) {
1814                         g_object_unref (iter);
1815                         iter = NULL;
1816                 }
1817         }
1818
1819         /* Get msg size limit */
1820         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1821                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1822                                          &(priv->error));
1823         if (priv->error) {
1824                 g_clear_error (&(priv->error));
1825                 max_size = G_MAXINT;
1826         } else {
1827                 max_size = max_size * KB;
1828         }
1829
1830         /* Check message size limits. If there is only one message
1831            always retrieve it */
1832         if (iter != NULL) {
1833                 while (!tny_iterator_is_done (iter) && size_ok) {
1834                         header = TNY_HEADER (tny_iterator_get_current (iter));
1835                         if (tny_header_get_message_size (header) >= max_size)
1836                                 size_ok = FALSE;
1837                         g_object_unref (header);
1838                         tny_iterator_next (iter);
1839                 }
1840                 g_object_unref (iter);
1841         }
1842
1843         if (size_ok) {
1844                 /* Create the info */
1845                 info = g_slice_new0 (GetFullMsgsInfo);
1846                 info->mail_op = self;
1847                 info->user_callback = user_callback;
1848                 info->user_data = user_data;
1849                 info->headers = g_object_ref (header_list);
1850                 info->notify = notify;
1851
1852                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1853         } else {
1854                 /* Set status failed and set an error */
1855                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1856                 /* FIXME: the error msg is different for pop */
1857                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1858                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1859                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1860                 /* Remove from queue and free resources */
1861                 modest_mail_operation_notify_end (self);
1862                 if (notify)
1863                         notify (user_data);
1864         }
1865 }
1866
1867
1868 void 
1869 modest_mail_operation_remove_msg (ModestMailOperation *self,
1870                                   TnyHeader *header,
1871                                   gboolean remove_to_trash)
1872 {
1873         TnyFolder *folder;
1874         ModestMailOperationPrivate *priv;
1875
1876         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1877         g_return_if_fail (TNY_IS_HEADER (header));
1878
1879         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1880         folder = tny_header_get_folder (header);
1881
1882         /* Get account and set it into mail_operation */
1883         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1884
1885         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1886
1887         /* Delete or move to trash */
1888         if (remove_to_trash) {
1889                 TnyFolder *trash_folder;
1890                 TnyStoreAccount *store_account;
1891
1892                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1893                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1894                                                                       TNY_FOLDER_TYPE_TRASH);
1895                 if (trash_folder) {
1896                         TnyList *headers;
1897
1898                         /* Create list */
1899                         headers = tny_simple_list_new ();
1900                         tny_list_append (headers, G_OBJECT (header));
1901                         g_object_unref (header);
1902
1903                         /* Move to trash */
1904                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1905                         g_object_unref (headers);
1906 /*                      g_object_unref (trash_folder); */
1907                 } else {
1908                         ModestMailOperationPrivate *priv;
1909
1910                         /* Set status failed and set an error */
1911                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1912                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1913                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1914                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1915                                      _("Error trying to delete a message. Trash folder not found"));
1916                 }
1917
1918                 g_object_unref (G_OBJECT (store_account));
1919         } else {
1920                 tny_folder_remove_msg (folder, header, &(priv->error));
1921                 if (!priv->error)
1922                         tny_folder_sync(folder, TRUE, &(priv->error));
1923         }
1924
1925         /* Set status */
1926         if (!priv->error)
1927                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1928         else
1929                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1930
1931         /* Free */
1932         g_object_unref (G_OBJECT (folder));
1933
1934         /* Notify about operation end */
1935         modest_mail_operation_notify_end (self);
1936 }
1937
1938 static void
1939 transfer_msgs_status_cb (GObject *obj,
1940                          TnyStatus *status,  
1941                          gpointer user_data)
1942 {
1943         XFerMsgAsyncHelper *helper = NULL;
1944         ModestMailOperation *self;
1945         ModestMailOperationPrivate *priv;
1946         ModestMailOperationState *state;
1947
1948
1949         g_return_if_fail (status != NULL);
1950         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1951
1952         helper = (XFerMsgAsyncHelper *) user_data;
1953         g_return_if_fail (helper != NULL);       
1954
1955         self = helper->mail_op;
1956         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1957
1958         if ((status->position == 1) && (status->of_total == 100))
1959                 return;
1960
1961         priv->done = status->position;
1962         priv->total = status->of_total;
1963
1964         state = modest_mail_operation_clone_state (self);
1965         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1966         g_slice_free (ModestMailOperationState, state);
1967 }
1968
1969
1970 static void
1971 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1972 {
1973         XFerMsgAsyncHelper *helper;
1974         ModestMailOperation *self;
1975         ModestMailOperationPrivate *priv;
1976
1977         helper = (XFerMsgAsyncHelper *) user_data;
1978         self = helper->mail_op;
1979
1980         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1981
1982         if (*err) {
1983                 priv->error = g_error_copy (*err);
1984                 priv->done = 0;
1985                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1986         } else if (cancelled) {
1987                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1988                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1989                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1990                              _("Error trying to refresh the contents of %s"),
1991                              tny_folder_get_name (folder));
1992         } else {
1993                 priv->done = 1;
1994                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1995         }
1996
1997         /* If user defined callback function was defined, call it */
1998         if (helper->user_callback) {
1999                 gdk_threads_enter ();
2000                 helper->user_callback (priv->source, helper->user_data);
2001                 gdk_threads_leave ();
2002         }
2003
2004         /* Free */
2005         g_object_unref (helper->headers);
2006         g_object_unref (helper->dest_folder);
2007         g_object_unref (helper->mail_op);
2008         g_slice_free   (XFerMsgAsyncHelper, helper);
2009         g_object_unref (folder);
2010
2011         /* Notify about operation end */
2012         modest_mail_operation_notify_end (self);
2013 }
2014
2015 void
2016 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2017                                  TnyList *headers, 
2018                                  TnyFolder *folder, 
2019                                  gboolean delete_original,
2020                                  XferMsgsAsynUserCallback user_callback,
2021                                  gpointer user_data)
2022 {
2023         ModestMailOperationPrivate *priv;
2024         TnyIterator *iter;
2025         TnyFolder *src_folder;
2026         XFerMsgAsyncHelper *helper;
2027         TnyHeader *header;
2028         ModestTnyFolderRules rules;
2029
2030         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2031         g_return_if_fail (TNY_IS_LIST (headers));
2032         g_return_if_fail (TNY_IS_FOLDER (folder));
2033
2034         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2035         priv->total = 1;
2036         priv->done = 0;
2037         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2038
2039         /* Apply folder rules */
2040         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2041         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2042                 /* Set status failed and set an error */
2043                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2044                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2045                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2046                              _("FIXME: folder does not accept msgs"));
2047                 /* Notify the queue */
2048                 modest_mail_operation_notify_end (self);
2049                 return;
2050         }
2051
2052         /* Create the helper */
2053         helper = g_slice_new0 (XFerMsgAsyncHelper);
2054         helper->mail_op = g_object_ref(self);
2055         helper->dest_folder = g_object_ref(folder);
2056         helper->headers = g_object_ref(headers);
2057         helper->user_callback = user_callback;
2058         helper->user_data = user_data;
2059
2060         /* Get source folder */
2061         iter = tny_list_create_iterator (headers);
2062         header = TNY_HEADER (tny_iterator_get_current (iter));
2063         src_folder = tny_header_get_folder (header);
2064         g_object_unref (header);
2065         g_object_unref (iter);
2066
2067         /* Get account and set it into mail_operation */
2068         priv->account = modest_tny_folder_get_account (src_folder);
2069
2070         /* Transfer messages */
2071         tny_folder_transfer_msgs_async (src_folder, 
2072                                         headers, 
2073                                         folder, 
2074                                         delete_original, 
2075                                         transfer_msgs_cb, 
2076                                         transfer_msgs_status_cb,
2077                                         helper);
2078 }
2079
2080
2081 static void
2082 on_refresh_folder (TnyFolder   *folder, 
2083                    gboolean     cancelled, 
2084                    GError     **error,
2085                    gpointer     user_data)
2086 {
2087         RefreshAsyncHelper *helper = NULL;
2088         ModestMailOperation *self = NULL;
2089         ModestMailOperationPrivate *priv = NULL;
2090
2091         helper = (RefreshAsyncHelper *) user_data;
2092         self = helper->mail_op;
2093         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2094
2095         if (*error) {
2096                 priv->error = g_error_copy (*error);
2097                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2098                 goto out;
2099         }
2100
2101         if (cancelled) {
2102                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2103                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2104                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2105                              _("Error trying to refresh the contents of %s"),
2106                              tny_folder_get_name (folder));
2107                 goto out;
2108         }
2109
2110         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2111
2112  out:
2113         /* Call user defined callback, if it exists */
2114         if (helper->user_callback) {
2115 /*              gdk_threads_enter (); */
2116                 helper->user_callback (priv->source, folder, helper->user_data);
2117 /*              gdk_threads_leave (); */
2118         }
2119
2120         /* Free */
2121         g_object_unref (helper->mail_op);
2122         g_slice_free   (RefreshAsyncHelper, helper);
2123         g_object_unref (folder);
2124
2125         /* Notify about operation end */
2126         modest_mail_operation_notify_end (self);
2127 }
2128
2129 static void
2130 on_refresh_folder_status_update (GObject *obj,
2131                                  TnyStatus *status,
2132                                  gpointer user_data)
2133 {
2134         RefreshAsyncHelper *helper = NULL;
2135         ModestMailOperation *self = NULL;
2136         ModestMailOperationPrivate *priv = NULL;
2137         ModestMailOperationState *state;
2138
2139         g_return_if_fail (user_data != NULL);
2140         g_return_if_fail (status != NULL);
2141         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2142
2143         helper = (RefreshAsyncHelper *) user_data;
2144         self = helper->mail_op;
2145         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2146
2147         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2148
2149         priv->done = status->position;
2150         priv->total = status->of_total;
2151
2152         state = modest_mail_operation_clone_state (self);
2153         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2154         g_slice_free (ModestMailOperationState, state);
2155 }
2156
2157 void 
2158 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2159                                        TnyFolder *folder,
2160                                        RefreshAsyncUserCallback user_callback,
2161                                        gpointer user_data)
2162 {
2163         ModestMailOperationPrivate *priv = NULL;
2164         RefreshAsyncHelper *helper = NULL;
2165
2166         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2167
2168         /* Pick a reference */
2169         g_object_ref (folder);
2170
2171         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2172
2173         /* Get account and set it into mail_operation */
2174         priv->account = modest_tny_folder_get_account  (folder);
2175
2176         /* Create the helper */
2177         helper = g_slice_new0 (RefreshAsyncHelper);
2178         helper->mail_op = g_object_ref(self);
2179         helper->user_callback = user_callback;
2180         helper->user_data = user_data;
2181
2182         /* Refresh the folder. TODO: tinymail could issue a status
2183            updates before the callback call then this could happen. We
2184            must review the design */
2185         tny_folder_refresh_async (folder,
2186                                   on_refresh_folder,
2187                                   on_refresh_folder_status_update,
2188                                   helper);
2189 }
2190
2191 /**
2192  *
2193  * It's used by the mail operation queue to notify the observers
2194  * attached to that signal that the operation finished. We need to use
2195  * that because tinymail does not give us the progress of a given
2196  * operation when it finishes (it directly calls the operation
2197  * callback).
2198  */
2199 static void
2200 modest_mail_operation_notify_end (ModestMailOperation *self)
2201 {
2202         ModestMailOperationState *state;
2203         ModestMailOperationPrivate *priv = NULL;
2204
2205         g_return_if_fail (self);
2206
2207         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2208
2209         if (!priv) {
2210                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2211                 return;
2212         }
2213         
2214         /* Set the account back to not busy */
2215         if (priv->account_name) {
2216                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2217                                                      priv->account_name, FALSE);
2218                 g_free(priv->account_name);
2219                 priv->account_name = NULL;
2220         }
2221         
2222         /* Notify the observers about the mail opertation end */
2223         state = modest_mail_operation_clone_state (self);
2224         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2225         g_slice_free (ModestMailOperationState, state);
2226 }