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