2007-06-26 Murray Cumming <murrayc@murrayc.com>
[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, GError **err, 
1364                     gpointer user_data)
1365 {
1366         ModestMailOperation *self = NULL;
1367         ModestMailOperationPrivate *priv = NULL;
1368
1369         self = MODEST_MAIL_OPERATION (user_data);
1370
1371         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1372
1373         if (*err) {
1374                 priv->error = g_error_copy (*err);
1375                 priv->done = 0;
1376                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1377         } else if (cancelled) {
1378                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1379                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1380                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1381                              _("Transference of %s was cancelled."),
1382                              tny_folder_get_name (folder));
1383         } else {
1384                 priv->done = 1;
1385                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1386         }
1387                 
1388         /* Free */
1389         g_object_unref (folder);
1390         g_object_unref (into);
1391         if (new_folder != NULL)
1392                 g_object_unref (new_folder);
1393
1394         /* Notify about operation end */
1395         modest_mail_operation_notify_end (self);
1396 }
1397
1398 void
1399 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1400                                    TnyFolder *folder,
1401                                    TnyFolderStore *parent,
1402                                    gboolean delete_original)
1403 {
1404         ModestMailOperationPrivate *priv = NULL;
1405         ModestTnyFolderRules parent_rules, rules;
1406
1407         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1408         g_return_if_fail (TNY_IS_FOLDER (folder));
1409         g_return_if_fail (TNY_IS_FOLDER (parent));
1410
1411         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1412
1413         /* Get account and set it into mail_operation */
1414         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1415         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1416
1417         /* Get folder rules */
1418         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1419         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1420
1421         if (!TNY_IS_FOLDER_STORE (parent)) {
1422                 
1423         }
1424         
1425         /* The moveable restriction is applied also to copy operation */
1426         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1427                 /* Set status failed and set an error */
1428                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1429                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1430                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1431                              _("mail_in_ui_folder_move_target_error"));
1432
1433                 /* Notify the queue */
1434                 modest_mail_operation_notify_end (self);
1435         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1436                 /* Set status failed and set an error */
1437                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1438                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1439                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1440                              _("FIXME: parent folder does not accept new folders"));
1441
1442                 /* Notify the queue */
1443                 modest_mail_operation_notify_end (self);
1444         } else {
1445                 /* Pick references for async calls */
1446                 g_object_ref (folder);
1447                 g_object_ref (parent);
1448
1449                 /* Move/Copy folder */          
1450                 tny_folder_copy_async (folder,
1451                                        parent,
1452                                        tny_folder_get_name (folder),
1453                                        delete_original,
1454                                        transfer_folder_cb,
1455                                        transfer_folder_status_cb,
1456                                        self);
1457         }
1458 }
1459
1460 void
1461 modest_mail_operation_rename_folder (ModestMailOperation *self,
1462                                      TnyFolder *folder,
1463                                      const gchar *name)
1464 {
1465         ModestMailOperationPrivate *priv;
1466         ModestTnyFolderRules rules;
1467
1468         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1469         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1470         g_return_if_fail (name);
1471         
1472         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1473
1474         /* Get account and set it into mail_operation */
1475         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1476
1477         /* Check folder rules */
1478         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1479         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1480                 /* Set status failed and set an error */
1481                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1482                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1483                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1484                              _("FIXME: unable to rename"));
1485
1486                 /* Notify about operation end */
1487                 modest_mail_operation_notify_end (self);
1488         } else {
1489                 /* Rename. Camel handles folder subscription/unsubscription */
1490                 TnyFolderStore *into;
1491
1492                 into = tny_folder_get_folder_store (folder);
1493                 tny_folder_copy_async (folder, into, name, TRUE,
1494                                  transfer_folder_cb,
1495                                  transfer_folder_status_cb,
1496                                  self);
1497                 if (into)
1498                         g_object_unref (into);
1499                 
1500         }
1501  }
1502
1503 /* ******************************************************************* */
1504 /* **************************  MSG  ACTIONS  ************************* */
1505 /* ******************************************************************* */
1506
1507 void modest_mail_operation_get_msg (ModestMailOperation *self,
1508                                     TnyHeader *header,
1509                                     GetMsgAsyncUserCallback user_callback,
1510                                     gpointer user_data)
1511 {
1512         GetMsgAsyncHelper *helper = NULL;
1513         TnyFolder *folder;
1514         ModestMailOperationPrivate *priv;
1515         
1516         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1517         g_return_if_fail (TNY_IS_HEADER (header));
1518         
1519         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1520         folder = tny_header_get_folder (header);
1521
1522         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1523
1524         /* Get message from folder */
1525         if (folder) {
1526                 /* Get account and set it into mail_operation */
1527                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1528
1529                 helper = g_slice_new0 (GetMsgAsyncHelper);
1530                 helper->mail_op = self;
1531                 helper->user_callback = user_callback;
1532                 helper->user_data = user_data;
1533                 helper->header = g_object_ref (header);
1534
1535                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1536
1537                 g_object_unref (G_OBJECT (folder));
1538         } else {
1539                 /* Set status failed and set an error */
1540                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1541                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1542                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1543                              _("Error trying to get a message. No folder found for header"));
1544
1545                 /* Notify the queue */
1546                 modest_mail_operation_notify_end (self);
1547         }
1548 }
1549
1550 static void
1551 get_msg_cb (TnyFolder *folder, 
1552             gboolean cancelled, 
1553             TnyMsg *msg, 
1554             GError **error, 
1555             gpointer user_data)
1556 {
1557         GetMsgAsyncHelper *helper = NULL;
1558         ModestMailOperation *self = NULL;
1559         ModestMailOperationPrivate *priv = NULL;
1560
1561         helper = (GetMsgAsyncHelper *) user_data;
1562         g_return_if_fail (helper != NULL);       
1563         self = helper->mail_op;
1564         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1565         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1566
1567         /* Check errors and cancel */
1568         if (*error) {
1569                 priv->error = g_error_copy (*error);
1570                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1571                 goto out;
1572         }
1573         if (cancelled) {
1574                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1575                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1576                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1577                              _("Error trying to refresh the contents of %s"),
1578                              tny_folder_get_name (folder));
1579                 goto out;
1580         }
1581
1582         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1583
1584         /* If user defined callback function was defined, call it */
1585         if (helper->user_callback) {
1586                 /* This callback is called into an iddle by tinymail,
1587                    and idles are not in the main lock */
1588                 gdk_threads_enter ();
1589                 helper->user_callback (self, helper->header, msg, helper->user_data);
1590                 gdk_threads_leave ();
1591         }
1592
1593  out:
1594         /* Free */
1595         g_object_unref (helper->header);
1596         g_slice_free (GetMsgAsyncHelper, helper);
1597                 
1598         /* Notify about operation end */
1599         modest_mail_operation_notify_end (self);        
1600 }
1601
1602 static void     
1603 get_msg_status_cb (GObject *obj,
1604                    TnyStatus *status,  
1605                    gpointer user_data)
1606 {
1607         GetMsgAsyncHelper *helper = NULL;
1608         ModestMailOperation *self;
1609         ModestMailOperationPrivate *priv;
1610         ModestMailOperationState *state;
1611
1612         g_return_if_fail (status != NULL);
1613         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1614
1615         helper = (GetMsgAsyncHelper *) user_data;
1616         g_return_if_fail (helper != NULL);       
1617
1618         self = helper->mail_op;
1619         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1620
1621         if ((status->position == 1) && (status->of_total == 100))
1622                 return;
1623
1624         priv->done = 1;
1625         priv->total = 1;
1626
1627         state = modest_mail_operation_clone_state (self);
1628         state->bytes_done = status->position;
1629         state->bytes_total = status->of_total;
1630         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1631         g_slice_free (ModestMailOperationState, state);
1632 }
1633
1634 /****************************************************/
1635 typedef struct {
1636         ModestMailOperation *mail_op;
1637         TnyList *headers;
1638         GetMsgAsyncUserCallback user_callback;
1639         gpointer user_data;
1640         GDestroyNotify notify;
1641 } GetFullMsgsInfo;
1642
1643 typedef struct {
1644         GetMsgAsyncUserCallback user_callback;
1645         TnyHeader *header;
1646         TnyMsg *msg;
1647         gpointer user_data;
1648         ModestMailOperation *mail_op;
1649 } NotifyGetMsgsInfo;
1650
1651
1652 /* 
1653  * Used by get_msgs_full_thread to call the user_callback for each
1654  * message that has been read
1655  */
1656 static gboolean
1657 notify_get_msgs_full (gpointer data)
1658 {
1659         gdk_threads_enter ();
1660
1661         NotifyGetMsgsInfo *info;
1662
1663         info = (NotifyGetMsgsInfo *) data;      
1664
1665         /* Call the user callback. Idles are not in the main lock, so
1666            lock it */
1667         gdk_threads_enter ();
1668         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1669         gdk_threads_leave ();
1670
1671         g_slice_free (NotifyGetMsgsInfo, info);
1672
1673         gdk_threads_leave ();
1674
1675         return FALSE;
1676 }
1677
1678 /* 
1679  * Used by get_msgs_full_thread to free al the thread resources and to
1680  * call the destroy function for the passed user_data
1681  */
1682 static gboolean
1683 get_msgs_full_destroyer (gpointer data)
1684 {
1685         gdk_threads_enter ();
1686
1687         GetFullMsgsInfo *info;
1688
1689         info = (GetFullMsgsInfo *) data;
1690
1691         if (info->notify)
1692                 info->notify (info->user_data);
1693
1694         /* free */
1695         g_object_unref (info->headers);
1696         g_slice_free (GetFullMsgsInfo, info);
1697
1698         gdk_threads_leave ();
1699
1700         return FALSE;
1701 }
1702
1703 static gpointer
1704 get_msgs_full_thread (gpointer thr_user_data)
1705 {
1706         GetFullMsgsInfo *info;
1707         ModestMailOperationPrivate *priv = NULL;
1708         TnyIterator *iter = NULL;
1709         
1710         info = (GetFullMsgsInfo *) thr_user_data;       
1711         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1712
1713         iter = tny_list_create_iterator (info->headers);
1714         while (!tny_iterator_is_done (iter)) { 
1715                 TnyHeader *header;
1716                 TnyFolder *folder;
1717                 
1718                 header = TNY_HEADER (tny_iterator_get_current (iter));
1719                 folder = tny_header_get_folder (header);
1720                                 
1721                 /* Get message from folder */
1722                 if (folder) {
1723                         TnyMsg *msg;
1724                         /* The callback will call it per each header */
1725                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1726
1727                         if (msg) {
1728                                 ModestMailOperationState *state;
1729                                 ModestPair *pair;
1730
1731                                 priv->done++;
1732
1733                                 /* notify progress */
1734                                 state = modest_mail_operation_clone_state (info->mail_op);
1735                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1736                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1737                                                  pair, (GDestroyNotify) modest_pair_free);
1738
1739                                 /* The callback is the responsible for
1740                                    freeing the message */
1741                                 if (info->user_callback) {
1742                                         NotifyGetMsgsInfo *info_notify;
1743                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1744                                         info_notify->user_callback = info->user_callback;
1745                                         info_notify->mail_op = info->mail_op;
1746                                         info_notify->header = g_object_ref (header);
1747                                         info_notify->msg = g_object_ref (msg);
1748                                         info_notify->user_data = info->user_data;
1749                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1750                                                          notify_get_msgs_full, 
1751                                                          info_notify, NULL);
1752                                 }
1753                                 g_object_unref (msg);
1754                         }
1755                 } else {
1756                         /* Set status failed and set an error */
1757                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1758                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1759                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1760                                      "Error trying to get a message. No folder found for header");
1761                 }
1762                 g_object_unref (header);                
1763                 tny_iterator_next (iter);
1764         }
1765
1766         /* Set operation status */
1767         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1768                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1769
1770         /* Notify about operation end */
1771         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1772
1773         /* Free thread resources. Will be called after all previous idles */
1774         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1775
1776         return NULL;
1777 }
1778
1779 void 
1780 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1781                                      TnyList *header_list, 
1782                                      GetMsgAsyncUserCallback user_callback,
1783                                      gpointer user_data,
1784                                      GDestroyNotify notify)
1785 {
1786         TnyHeader *header = NULL;
1787         TnyFolder *folder = NULL;
1788         GThread *thread;
1789         ModestMailOperationPrivate *priv = NULL;
1790         GetFullMsgsInfo *info = NULL;
1791         gboolean size_ok = TRUE;
1792         gint max_size;
1793         TnyIterator *iter = NULL;
1794         
1795         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1796         
1797         /* Init mail operation */
1798         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1799         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1800         priv->done = 0;
1801         priv->total = tny_list_get_length(header_list);
1802
1803         /* Get account and set it into mail_operation */
1804         if (tny_list_get_length (header_list) >= 1) {
1805                 iter = tny_list_create_iterator (header_list);
1806                 header = TNY_HEADER (tny_iterator_get_current (iter));
1807                 folder = tny_header_get_folder (header);                
1808                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1809                 g_object_unref (header);
1810                 g_object_unref (folder);
1811
1812                 if (tny_list_get_length (header_list) == 1) {
1813                         g_object_unref (iter);
1814                         iter = NULL;
1815                 }
1816         }
1817
1818         /* Get msg size limit */
1819         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1820                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1821                                          &(priv->error));
1822         if (priv->error) {
1823                 g_clear_error (&(priv->error));
1824                 max_size = G_MAXINT;
1825         } else {
1826                 max_size = max_size * KB;
1827         }
1828
1829         /* Check message size limits. If there is only one message
1830            always retrieve it */
1831         if (iter != NULL) {
1832                 while (!tny_iterator_is_done (iter) && size_ok) {
1833                         header = TNY_HEADER (tny_iterator_get_current (iter));
1834                         if (tny_header_get_message_size (header) >= max_size)
1835                                 size_ok = FALSE;
1836                         g_object_unref (header);
1837                         tny_iterator_next (iter);
1838                 }
1839                 g_object_unref (iter);
1840         }
1841
1842         if (size_ok) {
1843                 /* Create the info */
1844                 info = g_slice_new0 (GetFullMsgsInfo);
1845                 info->mail_op = self;
1846                 info->user_callback = user_callback;
1847                 info->user_data = user_data;
1848                 info->headers = g_object_ref (header_list);
1849                 info->notify = notify;
1850
1851                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1852         } else {
1853                 /* Set status failed and set an error */
1854                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1855                 /* FIXME: the error msg is different for pop */
1856                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1857                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1858                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1859                 /* Remove from queue and free resources */
1860                 modest_mail_operation_notify_end (self);
1861                 if (notify)
1862                         notify (user_data);
1863         }
1864 }
1865
1866
1867 void 
1868 modest_mail_operation_remove_msg (ModestMailOperation *self,
1869                                   TnyHeader *header,
1870                                   gboolean remove_to_trash)
1871 {
1872         TnyFolder *folder;
1873         ModestMailOperationPrivate *priv;
1874
1875         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1876         g_return_if_fail (TNY_IS_HEADER (header));
1877
1878         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1879         folder = tny_header_get_folder (header);
1880
1881         /* Get account and set it into mail_operation */
1882         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1883
1884         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1885
1886         /* Delete or move to trash */
1887         if (remove_to_trash) {
1888                 TnyFolder *trash_folder;
1889                 TnyStoreAccount *store_account;
1890
1891                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1892                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1893                                                                       TNY_FOLDER_TYPE_TRASH);
1894                 if (trash_folder) {
1895                         TnyList *headers;
1896
1897                         /* Create list */
1898                         headers = tny_simple_list_new ();
1899                         tny_list_append (headers, G_OBJECT (header));
1900                         g_object_unref (header);
1901
1902                         /* Move to trash */
1903                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1904                         g_object_unref (headers);
1905 /*                      g_object_unref (trash_folder); */
1906                 } else {
1907                         ModestMailOperationPrivate *priv;
1908
1909                         /* Set status failed and set an error */
1910                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1911                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1912                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1913                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1914                                      _("Error trying to delete a message. Trash folder not found"));
1915                 }
1916
1917                 g_object_unref (G_OBJECT (store_account));
1918         } else {
1919                 tny_folder_remove_msg (folder, header, &(priv->error));
1920                 if (!priv->error)
1921                         tny_folder_sync(folder, TRUE, &(priv->error));
1922         }
1923
1924         /* Set status */
1925         if (!priv->error)
1926                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1927         else
1928                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1929
1930         /* Free */
1931         g_object_unref (G_OBJECT (folder));
1932
1933         /* Notify about operation end */
1934         modest_mail_operation_notify_end (self);
1935 }
1936
1937 static void
1938 transfer_msgs_status_cb (GObject *obj,
1939                          TnyStatus *status,  
1940                          gpointer user_data)
1941 {
1942         XFerMsgAsyncHelper *helper = NULL;
1943         ModestMailOperation *self;
1944         ModestMailOperationPrivate *priv;
1945         ModestMailOperationState *state;
1946
1947
1948         g_return_if_fail (status != NULL);
1949         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1950
1951         helper = (XFerMsgAsyncHelper *) user_data;
1952         g_return_if_fail (helper != NULL);       
1953
1954         self = helper->mail_op;
1955         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1956
1957         if ((status->position == 1) && (status->of_total == 100))
1958                 return;
1959
1960         priv->done = status->position;
1961         priv->total = status->of_total;
1962
1963         state = modest_mail_operation_clone_state (self);
1964         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1965         g_slice_free (ModestMailOperationState, state);
1966 }
1967
1968
1969 static void
1970 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1971 {
1972         XFerMsgAsyncHelper *helper;
1973         ModestMailOperation *self;
1974         ModestMailOperationPrivate *priv;
1975
1976         helper = (XFerMsgAsyncHelper *) user_data;
1977         self = helper->mail_op;
1978
1979         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1980
1981         if (*err) {
1982                 priv->error = g_error_copy (*err);
1983                 priv->done = 0;
1984                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1985         } else if (cancelled) {
1986                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1987                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1988                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1989                              _("Error trying to refresh the contents of %s"),
1990                              tny_folder_get_name (folder));
1991         } else {
1992                 priv->done = 1;
1993                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1994         }
1995
1996         /* If user defined callback function was defined, call it */
1997         if (helper->user_callback) {
1998                 gdk_threads_enter ();
1999                 helper->user_callback (priv->source, helper->user_data);
2000                 gdk_threads_leave ();
2001         }
2002
2003         /* Free */
2004         g_object_unref (helper->headers);
2005         g_object_unref (helper->dest_folder);
2006         g_object_unref (helper->mail_op);
2007         g_slice_free   (XFerMsgAsyncHelper, helper);
2008         g_object_unref (folder);
2009
2010         /* Notify about operation end */
2011         modest_mail_operation_notify_end (self);
2012 }
2013
2014 void
2015 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2016                                  TnyList *headers, 
2017                                  TnyFolder *folder, 
2018                                  gboolean delete_original,
2019                                  XferMsgsAsynUserCallback user_callback,
2020                                  gpointer user_data)
2021 {
2022         ModestMailOperationPrivate *priv;
2023         TnyIterator *iter;
2024         TnyFolder *src_folder;
2025         XFerMsgAsyncHelper *helper;
2026         TnyHeader *header;
2027         ModestTnyFolderRules rules;
2028
2029         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2030         g_return_if_fail (TNY_IS_LIST (headers));
2031         g_return_if_fail (TNY_IS_FOLDER (folder));
2032
2033         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2034         priv->total = 1;
2035         priv->done = 0;
2036         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2037
2038         /* Apply folder rules */
2039         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2040         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2041                 /* Set status failed and set an error */
2042                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2043                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2044                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2045                              _("FIXME: folder does not accept msgs"));
2046                 /* Notify the queue */
2047                 modest_mail_operation_notify_end (self);
2048                 return;
2049         }
2050
2051         /* Create the helper */
2052         helper = g_slice_new0 (XFerMsgAsyncHelper);
2053         helper->mail_op = g_object_ref(self);
2054         helper->dest_folder = g_object_ref(folder);
2055         helper->headers = g_object_ref(headers);
2056         helper->user_callback = user_callback;
2057         helper->user_data = user_data;
2058
2059         /* Get source folder */
2060         iter = tny_list_create_iterator (headers);
2061         header = TNY_HEADER (tny_iterator_get_current (iter));
2062         src_folder = tny_header_get_folder (header);
2063         g_object_unref (header);
2064         g_object_unref (iter);
2065
2066         /* Get account and set it into mail_operation */
2067         priv->account = modest_tny_folder_get_account (src_folder);
2068
2069         /* Transfer messages */
2070         tny_folder_transfer_msgs_async (src_folder, 
2071                                         headers, 
2072                                         folder, 
2073                                         delete_original, 
2074                                         transfer_msgs_cb, 
2075                                         transfer_msgs_status_cb,
2076                                         helper);
2077 }
2078
2079
2080 static void
2081 on_refresh_folder (TnyFolder   *folder, 
2082                    gboolean     cancelled, 
2083                    GError     **error,
2084                    gpointer     user_data)
2085 {
2086         RefreshAsyncHelper *helper = NULL;
2087         ModestMailOperation *self = NULL;
2088         ModestMailOperationPrivate *priv = NULL;
2089
2090         helper = (RefreshAsyncHelper *) user_data;
2091         self = helper->mail_op;
2092         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2093
2094         if (*error) {
2095                 priv->error = g_error_copy (*error);
2096                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2097                 goto out;
2098         }
2099
2100         if (cancelled) {
2101                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2102                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2103                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2104                              _("Error trying to refresh the contents of %s"),
2105                              tny_folder_get_name (folder));
2106                 goto out;
2107         }
2108
2109         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2110
2111  out:
2112         /* Call user defined callback, if it exists */
2113         if (helper->user_callback) {
2114 /*              gdk_threads_enter (); */
2115                 helper->user_callback (priv->source, folder, helper->user_data);
2116 /*              gdk_threads_leave (); */
2117         }
2118
2119         /* Free */
2120         g_object_unref (helper->mail_op);
2121         g_slice_free   (RefreshAsyncHelper, helper);
2122         g_object_unref (folder);
2123
2124         /* Notify about operation end */
2125         modest_mail_operation_notify_end (self);
2126 }
2127
2128 static void
2129 on_refresh_folder_status_update (GObject *obj,
2130                                  TnyStatus *status,
2131                                  gpointer user_data)
2132 {
2133         RefreshAsyncHelper *helper = NULL;
2134         ModestMailOperation *self = NULL;
2135         ModestMailOperationPrivate *priv = NULL;
2136         ModestMailOperationState *state;
2137
2138         g_return_if_fail (user_data != NULL);
2139         g_return_if_fail (status != NULL);
2140         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2141
2142         helper = (RefreshAsyncHelper *) user_data;
2143         self = helper->mail_op;
2144         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2145
2146         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2147
2148         priv->done = status->position;
2149         priv->total = status->of_total;
2150
2151         state = modest_mail_operation_clone_state (self);
2152         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2153         g_slice_free (ModestMailOperationState, state);
2154 }
2155
2156 void 
2157 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2158                                        TnyFolder *folder,
2159                                        RefreshAsyncUserCallback user_callback,
2160                                        gpointer user_data)
2161 {
2162         ModestMailOperationPrivate *priv = NULL;
2163         RefreshAsyncHelper *helper = NULL;
2164
2165         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2166
2167         /* Pick a reference */
2168         g_object_ref (folder);
2169
2170         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2171
2172         /* Get account and set it into mail_operation */
2173         priv->account = modest_tny_folder_get_account  (folder);
2174
2175         /* Create the helper */
2176         helper = g_slice_new0 (RefreshAsyncHelper);
2177         helper->mail_op = g_object_ref(self);
2178         helper->user_callback = user_callback;
2179         helper->user_data = user_data;
2180
2181         /* Refresh the folder. TODO: tinymail could issue a status
2182            updates before the callback call then this could happen. We
2183            must review the design */
2184         tny_folder_refresh_async (folder,
2185                                   on_refresh_folder,
2186                                   on_refresh_folder_status_update,
2187                                   helper);
2188 }
2189
2190 /**
2191  *
2192  * It's used by the mail operation queue to notify the observers
2193  * attached to that signal that the operation finished. We need to use
2194  * that because tinymail does not give us the progress of a given
2195  * operation when it finishes (it directly calls the operation
2196  * callback).
2197  */
2198 static void
2199 modest_mail_operation_notify_end (ModestMailOperation *self)
2200 {
2201         ModestMailOperationState *state;
2202         ModestMailOperationPrivate *priv = NULL;
2203
2204         g_return_if_fail (self);
2205
2206         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2207
2208         if (!priv) {
2209                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2210                 return;
2211         }
2212         
2213         /* Set the account back to not busy */
2214         if (priv->account_name) {
2215                 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2216                                                                                                                                                                 FALSE);
2217                 g_free(priv->account_name);
2218                 priv->account_name = NULL;
2219         }
2220         
2221         /* Notify the observers about the mail opertation end */
2222         state = modest_mail_operation_clone_state (self);
2223         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2224         g_slice_free (ModestMailOperationState, state);
2225 }