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