* Changed the update_account_thread it now refreshes the INBOX and pokes the other...
[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-camel-mem-stream.h>
41 #include <tny-simple-list.h>
42 #include <tny-send-queue.h>
43 #include <tny-status.h>
44 #include <tny-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include <modest-tny-account.h>
49 #include <modest-tny-send-queue.h>
50 #include <modest-runtime.h>
51 #include "modest-text-utils.h"
52 #include "modest-tny-msg.h"
53 #include "modest-tny-folder.h"
54 #include "modest-tny-account-store.h"
55 #include "modest-tny-platform-factory.h"
56 #include "modest-marshal.h"
57 #include "modest-error.h"
58 #include "modest-mail-operation.h"
59
60 #define KB 1024
61
62 /* 
63  * Remove all these #ifdef stuff when the tinymail's idle calls become
64  * locked
65  */
66 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
67
68 /* 'private'/'protected' functions */
69 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
70 static void modest_mail_operation_init       (ModestMailOperation *obj);
71 static void modest_mail_operation_finalize   (GObject *obj);
72
73 static void     get_msg_cb (TnyFolder *folder, 
74                             gboolean cancelled, 
75                             TnyMsg *msg, 
76                             GError *rr, 
77                             gpointer user_data);
78
79 static void     get_msg_status_cb (GObject *obj,
80                                    TnyStatus *status,  
81                                    gpointer user_data);
82
83 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
84
85 enum _ModestMailOperationSignals 
86 {
87         PROGRESS_CHANGED_SIGNAL,
88
89         NUM_SIGNALS
90 };
91
92 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
93 struct _ModestMailOperationPrivate {
94         TnyAccount                 *account;
95         gchar                                                                            *account_name;
96         guint                      done;
97         guint                      total;
98         GObject                   *source;
99         GError                    *error;
100         ErrorCheckingUserCallback  error_checking;
101         gpointer                   error_checking_user_data;
102         ModestMailOperationStatus  status;      
103         ModestMailOperationTypeOperation op_type;
104 };
105
106 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
107                                                    MODEST_TYPE_MAIL_OPERATION, \
108                                                    ModestMailOperationPrivate))
109
110 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
111                                                    priv->status = new_status;\
112                                                }
113
114 typedef struct _GetMsgAsyncHelper {     
115         ModestMailOperation *mail_op;
116         TnyHeader *header;
117         GetMsgAsyncUserCallback user_callback;  
118         gpointer user_data;
119 } GetMsgAsyncHelper;
120
121 typedef struct _RefreshAsyncHelper {    
122         ModestMailOperation *mail_op;
123         RefreshAsyncUserCallback user_callback; 
124         gpointer user_data;
125 } RefreshAsyncHelper;
126
127 typedef struct _XFerMsgAsyncHelper
128 {
129         ModestMailOperation *mail_op;
130         TnyList *headers;
131         TnyFolder *dest_folder;
132         XferMsgsAsynUserCallback user_callback; 
133         gboolean delete;
134         gpointer user_data;
135 } XFerMsgAsyncHelper;
136
137 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
138                                                       TnyMsg *msg,
139                                                       gpointer userdata);
140
141 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
142                                                        const gchar *from, const gchar *to,
143                                                        const gchar *cc, const gchar *bcc,
144                                                        const gchar *subject, const gchar *plain_body,
145                                                        const gchar *html_body, const GList *attachments_list,
146                                                        TnyHeaderFlags priority_flags,
147                                                        ModestMailOperationCreateMsgCallback callback,
148                                                        gpointer userdata);
149
150 static gboolean      idle_notify_queue (gpointer data);
151 typedef struct
152 {
153         ModestMailOperation *mail_op;
154         gchar *from;
155         gchar *to;
156         gchar *cc;
157         gchar *bcc;
158         gchar *subject;
159         gchar *plain_body;
160         gchar *html_body;
161         GList *attachments_list;
162         TnyHeaderFlags priority_flags;
163         ModestMailOperationCreateMsgCallback callback;
164         gpointer userdata;
165 } CreateMsgInfo;
166
167 typedef struct
168 {
169         ModestMailOperation *mail_op;
170         TnyMsg *msg;
171         ModestMailOperationCreateMsgCallback callback;
172         gpointer userdata;
173 } CreateMsgIdleInfo;
174
175 /* globals */
176 static GObjectClass *parent_class = NULL;
177
178 static guint signals[NUM_SIGNALS] = {0};
179
180 GType
181 modest_mail_operation_get_type (void)
182 {
183         static GType my_type = 0;
184         if (!my_type) {
185                 static const GTypeInfo my_info = {
186                         sizeof(ModestMailOperationClass),
187                         NULL,           /* base init */
188                         NULL,           /* base finalize */
189                         (GClassInitFunc) modest_mail_operation_class_init,
190                         NULL,           /* class finalize */
191                         NULL,           /* class data */
192                         sizeof(ModestMailOperation),
193                         1,              /* n_preallocs */
194                         (GInstanceInitFunc) modest_mail_operation_init,
195                         NULL
196                 };
197                 my_type = g_type_register_static (G_TYPE_OBJECT,
198                                                   "ModestMailOperation",
199                                                   &my_info, 0);
200         }
201         return my_type;
202 }
203
204 static void
205 modest_mail_operation_class_init (ModestMailOperationClass *klass)
206 {
207         GObjectClass *gobject_class;
208         gobject_class = (GObjectClass*) klass;
209
210         parent_class            = g_type_class_peek_parent (klass);
211         gobject_class->finalize = modest_mail_operation_finalize;
212
213         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
214
215         /**
216          * ModestMailOperation::progress-changed
217          * @self: the #MailOperation that emits the signal
218          * @user_data: user data set when the signal handler was connected
219          *
220          * Emitted when the progress of a mail operation changes
221          */
222         signals[PROGRESS_CHANGED_SIGNAL] = 
223                 g_signal_new ("progress-changed",
224                               G_TYPE_FROM_CLASS (gobject_class),
225                               G_SIGNAL_RUN_FIRST,
226                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
227                               NULL, NULL,
228                               g_cclosure_marshal_VOID__POINTER,
229                               G_TYPE_NONE, 1, G_TYPE_POINTER);
230
231 }
232
233 static void
234 modest_mail_operation_init (ModestMailOperation *obj)
235 {
236         ModestMailOperationPrivate *priv;
237
238         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
239
240         priv->account        = NULL;
241         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
242         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
243         priv->error          = NULL;
244         priv->done           = 0;
245         priv->total          = 0;
246         priv->source         = NULL;
247         priv->error_checking = NULL;
248         priv->error_checking_user_data = NULL;
249 }
250
251 static void
252 modest_mail_operation_finalize (GObject *obj)
253 {
254         ModestMailOperationPrivate *priv;
255
256         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
257
258         
259         
260         if (priv->error) {
261                 g_error_free (priv->error);
262                 priv->error = NULL;
263         }
264         if (priv->source) {
265                 g_object_unref (priv->source);
266                 priv->source = NULL;
267         }
268         if (priv->account) {
269                 g_object_unref (priv->account);
270                 priv->account = NULL;
271         }
272
273
274         G_OBJECT_CLASS(parent_class)->finalize (obj);
275 }
276
277 ModestMailOperation*
278 modest_mail_operation_new (ModestMailOperationTypeOperation op_type, 
279                            GObject *source)
280 {
281         ModestMailOperation *obj;
282         ModestMailOperationPrivate *priv;
283                 
284         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
285         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
286
287         priv->op_type = op_type;
288         if (source != NULL)
289                 priv->source = g_object_ref(source);
290
291         return obj;
292 }
293
294 ModestMailOperation*
295 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
296                                                GObject *source,
297                                                ErrorCheckingUserCallback error_handler,
298                                                gpointer user_data)
299 {
300         ModestMailOperation *obj;
301         ModestMailOperationPrivate *priv;
302                 
303         obj = modest_mail_operation_new (op_type, source);
304         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
305         
306         g_return_val_if_fail (error_handler != NULL, obj);
307         priv->error_checking = error_handler;
308         priv->error_checking_user_data = user_data;
309
310         return obj;
311 }
312
313 void
314 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
315 {
316         ModestMailOperationPrivate *priv;
317         
318         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
319         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
320
321         if (priv->error_checking != NULL)
322                 priv->error_checking (self, priv->error_checking_user_data);
323 }
324
325
326 ModestMailOperationTypeOperation
327 modest_mail_operation_get_type_operation (ModestMailOperation *self)
328 {
329         ModestMailOperationPrivate *priv;
330
331         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
332         
333         return priv->op_type;
334 }
335
336 gboolean 
337 modest_mail_operation_is_mine (ModestMailOperation *self, 
338                                GObject *me)
339 {
340         ModestMailOperationPrivate *priv;
341
342         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
343         if (priv->source == NULL) return FALSE;
344
345         return priv->source == me;
346 }
347
348 GObject *
349 modest_mail_operation_get_source (ModestMailOperation *self)
350 {
351         ModestMailOperationPrivate *priv;
352
353         g_return_val_if_fail (self, NULL);
354         
355         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
356         if (!priv) {
357                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
358                 return NULL;
359         }
360         
361         return g_object_ref (priv->source);
362 }
363
364 ModestMailOperationStatus
365 modest_mail_operation_get_status (ModestMailOperation *self)
366 {
367         ModestMailOperationPrivate *priv;
368
369         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
370         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
371                               MODEST_MAIL_OPERATION_STATUS_INVALID);
372
373         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
374         if (!priv) {
375                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
376                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
377         }
378         
379         return priv->status;
380 }
381
382 const GError *
383 modest_mail_operation_get_error (ModestMailOperation *self)
384 {
385         ModestMailOperationPrivate *priv;
386
387         g_return_val_if_fail (self, NULL);
388         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
389
390         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
391
392         if (!priv) {
393                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
394                 return NULL;
395         }
396
397         return priv->error;
398 }
399
400 gboolean 
401 modest_mail_operation_cancel (ModestMailOperation *self)
402 {
403         ModestMailOperationPrivate *priv;
404         gboolean canceled = FALSE;
405
406         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
407
408         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
409
410         /* Note that if we call cancel with an already canceled mail
411            operation the progress changed signal won't be emitted */
412         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
413                 return FALSE;
414
415         /* Set new status */
416         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
417         
418         /* Cancel the mail operation. We need to wrap it between this
419            start/stop operations to allow following calls to the
420            account */
421         g_return_val_if_fail (priv->account, FALSE);
422         tny_account_cancel (priv->account);
423
424         return canceled;
425 }
426
427 guint 
428 modest_mail_operation_get_task_done (ModestMailOperation *self)
429 {
430         ModestMailOperationPrivate *priv;
431
432         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
433
434         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
435         return priv->done;
436 }
437
438 guint 
439 modest_mail_operation_get_task_total (ModestMailOperation *self)
440 {
441         ModestMailOperationPrivate *priv;
442
443         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
444
445         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
446         return priv->total;
447 }
448
449 gboolean
450 modest_mail_operation_is_finished (ModestMailOperation *self)
451 {
452         ModestMailOperationPrivate *priv;
453         gboolean retval = FALSE;
454
455         if (!MODEST_IS_MAIL_OPERATION (self)) {
456                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
457                 return retval;
458         }
459
460         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
461
462         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
463             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
464             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
465             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
466                 retval = TRUE;
467         } else {
468                 retval = FALSE;
469         }
470
471         return retval;
472 }
473
474 guint
475 modest_mail_operation_get_id (ModestMailOperation *self)
476 {
477         ModestMailOperationPrivate *priv;
478
479         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
480
481         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
482         return priv->done;
483 }
484
485 guint 
486 modest_mail_operation_set_id (ModestMailOperation *self,
487                               guint id)
488 {
489         ModestMailOperationPrivate *priv;
490
491         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
492
493         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
494         return priv->done;
495 }
496
497 /*
498  * Creates an image of the current state of a mail operation, the
499  * caller must free it
500  */
501 static ModestMailOperationState *
502 modest_mail_operation_clone_state (ModestMailOperation *self)
503 {
504         ModestMailOperationState *state;
505         ModestMailOperationPrivate *priv;
506
507         /* FIXME: this should be fixed properly
508          * 
509          * in some cases, priv was NULL, so checking here to
510          * make sure.
511          */
512         g_return_val_if_fail (self, NULL);
513         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
514         g_return_val_if_fail (priv, NULL);
515
516         if (!priv)
517                 return NULL;
518
519         state = g_slice_new (ModestMailOperationState);
520
521         state->status = priv->status;
522         state->op_type = priv->op_type;
523         state->done = priv->done;
524         state->total = priv->total;
525         state->finished = modest_mail_operation_is_finished (self);
526         state->bytes_done = 0;
527         state->bytes_total = 0;
528
529         return state;
530 }
531
532 /* ******************************************************************* */
533 /* ************************** SEND   ACTIONS ************************* */
534 /* ******************************************************************* */
535
536 void
537 modest_mail_operation_send_mail (ModestMailOperation *self,
538                                  TnyTransportAccount *transport_account,
539                                  TnyMsg* msg)
540 {
541         TnySendQueue *send_queue = NULL;
542         ModestMailOperationPrivate *priv;
543         
544         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
545         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
546         g_return_if_fail (TNY_IS_MSG (msg));
547         
548         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
549
550         /* Get account and set it into mail_operation */
551         priv->account = g_object_ref (transport_account);
552         priv->done = 1;
553         priv->total = 1;
554
555         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
556         if (!TNY_IS_SEND_QUEUE(send_queue)) {
557                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
558                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
559                              "modest: could not find send queue for account\n");
560                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
561
562         } else {
563                 /* TODO: connect to the msg-sent in order to know when
564                    the mail operation is finished */
565
566 /*              tny_send_queue_add (send_queue, msg, &(priv->error)); */
567                 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue), 
568                                            msg, 
569                                            &(priv->error));
570
571                 /* TODO: we're setting always success, do the check in
572                    the handler */
573                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
574         }
575
576         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)
577                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_outbox_waiting_to_be_sent"));
578
579         /* TODO: do this in the handler of the "msg-sent"
580            signal.Notify about operation end */
581         modest_mail_operation_notify_end (self);
582 }
583
584 static gboolean
585 idle_create_msg_cb (gpointer idle_data)
586 {
587         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
588
589         /* This is a GDK lock because we are an idle callback and
590          * info->callback can contain Gtk+ code */
591
592         gdk_threads_enter (); /* CHECKED */
593         info->callback (info->mail_op, info->msg, info->userdata);
594
595         g_object_unref (info->mail_op);
596         if (info->msg)
597                 g_object_unref (info->msg);
598         g_slice_free (CreateMsgIdleInfo, info);
599         gdk_threads_leave (); /* CHECKED */
600
601         return FALSE;
602 }
603
604 static gpointer 
605 create_msg_thread (gpointer thread_data)
606 {
607         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
608         TnyMsg *new_msg = NULL;
609         ModestMailOperationPrivate *priv;
610
611         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
612         if (info->html_body == NULL) {
613                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
614                                               info->bcc, info->subject, info->plain_body, 
615                                               info->attachments_list); /* FIXME: attachments */
616         } else {
617                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
618                                                          info->bcc, info->subject, info->html_body,
619                                                          info->plain_body, info->attachments_list);
620         }
621
622         if (new_msg) {
623                 TnyHeader *header;
624                 TnyHeaderFlags flags = 0;
625
626                 /* Set priority flags in message */
627                 header = tny_msg_get_header (new_msg);
628                 if (info->priority_flags != 0)
629                         flags |= info->priority_flags;
630
631                 /* Set attachment flags in message */
632                 if (info->attachments_list != NULL)
633                         flags |= TNY_HEADER_FLAG_ATTACHMENTS;
634
635                 tny_header_set_flags (header, flags);
636                 g_object_unref (G_OBJECT(header));
637         } else {
638                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
639                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
640                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
641                              "modest: failed to create a new msg\n");
642         }
643
644
645         g_free (info->to);
646         g_free (info->from);
647         g_free (info->cc);
648         g_free (info->bcc);
649         g_free (info->plain_body);
650         g_free (info->html_body);
651         g_free (info->subject);
652         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
653         g_list_free (info->attachments_list);
654
655         if (info->callback) {
656                 CreateMsgIdleInfo *idle_info;
657                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
658                 idle_info->mail_op = info->mail_op;
659                 g_object_ref (info->mail_op);
660                 idle_info->msg = new_msg;
661                 if (new_msg)
662                         g_object_ref (new_msg);
663                 idle_info->callback = info->callback;
664                 idle_info->userdata = info->userdata;
665                 g_idle_add (idle_create_msg_cb, idle_info);
666         } else {
667                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
668         }
669
670         g_object_unref (info->mail_op);
671         g_slice_free (CreateMsgInfo, info);
672         return NULL;
673 }
674
675 void
676 modest_mail_operation_create_msg (ModestMailOperation *self,
677                                   const gchar *from, const gchar *to,
678                                   const gchar *cc, const gchar *bcc,
679                                   const gchar *subject, const gchar *plain_body,
680                                   const gchar *html_body,
681                                   const GList *attachments_list,
682                                   TnyHeaderFlags priority_flags,
683                                   ModestMailOperationCreateMsgCallback callback,
684                                   gpointer userdata)
685 {
686         CreateMsgInfo *info = NULL;
687
688         info = g_slice_new0 (CreateMsgInfo);
689         info->mail_op = self;
690         g_object_ref (self);
691
692         info->from = g_strdup (from);
693         info->to = g_strdup (to);
694         info->cc = g_strdup (cc);
695         info->bcc  = g_strdup (bcc);
696         info->subject = g_strdup (subject);
697         info->plain_body = g_strdup (plain_body);
698         info->html_body = g_strdup (html_body);
699         info->attachments_list = g_list_copy ((GList *) attachments_list);
700         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
701         info->priority_flags = priority_flags;
702
703         info->callback = callback;
704         info->userdata = userdata;
705
706         g_thread_create (create_msg_thread, info, FALSE, NULL);
707 }
708
709 typedef struct
710 {
711         TnyTransportAccount *transport_account;
712         TnyMsg *draft_msg;
713 } SendNewMailInfo;
714
715 static void
716 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
717                                         TnyMsg *msg,
718                                         gpointer userdata)
719 {
720         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
721         TnyFolder *draft_folder = NULL;
722         TnyFolder *outbox_folder = NULL;
723         TnyHeader *header;
724         GError *err = NULL;
725
726         if (!msg) {
727                 goto end;
728         }
729
730         /* Call mail operation */
731         modest_mail_operation_send_mail (self, info->transport_account, msg);
732
733         /* Remove old mail from its source folder */
734         draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
735         outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
736         if (info->draft_msg != NULL) {
737                 TnyFolder *folder = NULL;
738                 TnyFolder *src_folder = NULL;
739                 TnyFolderType folder_type;              
740                 folder = tny_msg_get_folder (info->draft_msg);          
741                 if (folder == NULL) goto end;
742                 folder_type = modest_tny_folder_guess_folder_type (folder);
743                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
744                         src_folder = outbox_folder;
745                 else 
746                         src_folder = draft_folder;
747
748                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
749                  * because this function requires it to have a UID. */          
750                 header = tny_msg_get_header (info->draft_msg);
751                 tny_folder_remove_msg (src_folder, header, NULL);
752
753                 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
754 /*              tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);  /\* expunge *\/ */
755                 
756                 g_object_unref (header);
757                 g_object_unref (folder);
758         }
759
760 end:
761         if (err != NULL)
762                 g_error_free(err);      
763         if (info->draft_msg)
764                 g_object_unref (info->draft_msg);
765         if (draft_folder)
766                 g_object_unref (draft_folder);
767         if (outbox_folder)
768                 g_object_unref (outbox_folder);
769         if (info->transport_account)
770                 g_object_unref (info->transport_account);
771         g_slice_free (SendNewMailInfo, info);
772         modest_mail_operation_notify_end (self);
773 }
774
775 void
776 modest_mail_operation_send_new_mail (ModestMailOperation *self,
777                                      TnyTransportAccount *transport_account,
778                                      TnyMsg *draft_msg,
779                                      const gchar *from,  const gchar *to,
780                                      const gchar *cc,  const gchar *bcc,
781                                      const gchar *subject, const gchar *plain_body,
782                                      const gchar *html_body,
783                                      const GList *attachments_list,
784                                      TnyHeaderFlags priority_flags)
785 {
786         ModestMailOperationPrivate *priv = NULL;
787         SendNewMailInfo *info;
788
789         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
790         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
791
792         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
793
794         /* Check parametters */
795         if (to == NULL) {
796                 /* Set status failed and set an error */
797                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
798                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
799                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
800                              _("Error trying to send a mail. You need to set at least one recipient"));
801                 return;
802         }
803         info = g_slice_new0 (SendNewMailInfo);
804         info->transport_account = transport_account;
805         if (transport_account)
806                 g_object_ref (transport_account);
807         info->draft_msg = draft_msg;
808         if (draft_msg)
809                 g_object_ref (draft_msg);
810         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
811                                           attachments_list, priority_flags,
812                                           modest_mail_operation_send_new_mail_cb, info);
813
814 }
815
816 typedef struct
817 {
818         TnyTransportAccount *transport_account;
819         TnyMsg *draft_msg;
820         ModestMsgEditWindow *edit_window;
821 } SaveToDraftsInfo;
822
823 static void
824 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
825                                          TnyMsg *msg,
826                                          gpointer userdata)
827 {
828         TnyFolder *src_folder = NULL;
829         TnyFolder *folder = NULL;
830         TnyHeader *header = NULL;
831         ModestMailOperationPrivate *priv = NULL;
832         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
833
834         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
835         if (!msg) {
836                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
837                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
838                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
839                              "modest: failed to create a new msg\n");
840                 goto end;
841         }
842
843         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
844         if (!folder) {
845                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
846                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
847                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
848                              "modest: failed to create a new msg\n");
849                 goto end;
850         }
851
852         if (!priv->error)
853                 tny_folder_add_msg (folder, msg, &(priv->error));
854
855         if ((!priv->error) && (info->draft_msg != NULL)) {
856                 header = tny_msg_get_header (info->draft_msg);
857                 src_folder = tny_header_get_folder (header); 
858
859                 /* Remove the old draft expunging it */
860                 tny_folder_remove_msg (src_folder, header, NULL);
861 /*              tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
862 /*              tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
863
864                 tny_folder_sync (folder, TRUE, &(priv->error)); /* FALSE --> don't expunge */
865                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);  /* expunge */
866
867                 g_object_unref (header);
868         }
869         
870         if (!priv->error)
871                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
872         else
873                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
874
875         if (info->edit_window)
876                 modest_msg_edit_window_set_draft (info->edit_window, msg);
877
878
879 end:
880         if (folder)
881                 g_object_unref (G_OBJECT(folder));
882         if (src_folder)
883                 g_object_unref (G_OBJECT(src_folder));
884         if (info->edit_window)
885                 g_object_unref (G_OBJECT(info->edit_window));
886         if (info->draft_msg)
887                 g_object_unref (G_OBJECT (info->draft_msg));
888         if (info->transport_account)
889                 g_object_unref (G_OBJECT(info->transport_account));
890         g_slice_free (SaveToDraftsInfo, info);
891
892         modest_mail_operation_notify_end (self);
893 }
894
895 void
896 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
897                                       TnyTransportAccount *transport_account,
898                                       TnyMsg *draft_msg,
899                                       ModestMsgEditWindow *edit_window,
900                                       const gchar *from,  const gchar *to,
901                                       const gchar *cc,  const gchar *bcc,
902                                       const gchar *subject, const gchar *plain_body,
903                                       const gchar *html_body,
904                                       const GList *attachments_list,
905                                       TnyHeaderFlags priority_flags)
906 {
907         ModestMailOperationPrivate *priv = NULL;
908         SaveToDraftsInfo *info = NULL;
909
910         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
911         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
912
913         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
914
915         /* Get account and set it into mail_operation */
916         priv->account = g_object_ref (transport_account);
917
918         info = g_slice_new0 (SaveToDraftsInfo);
919         info->transport_account = g_object_ref (transport_account);
920         info->draft_msg = draft_msg;
921         if (draft_msg)
922                 g_object_ref (draft_msg);
923         info->edit_window = edit_window;
924         if (edit_window)
925                 g_object_ref (edit_window);
926
927         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
928                                           attachments_list, priority_flags,
929                                           modest_mail_operation_save_to_drafts_cb, info);
930
931 }
932
933 typedef struct 
934 {
935         ModestMailOperation *mail_op;
936         TnyStoreAccount *account;
937         TnyTransportAccount *transport_account;
938         gint max_size;
939         gint retrieve_limit;
940         gchar *retrieve_type;
941         gchar *account_name;
942         UpdateAccountCallback callback;
943         gpointer user_data;
944         TnyList *new_headers;
945 } UpdateAccountInfo;
946
947 typedef struct
948 {
949         ModestMailOperation *mail_op;
950         TnyMimePart *mime_part;
951         gssize size;
952         GetMimePartSizeCallback callback;
953         gpointer userdata;
954 } GetMimePartSizeInfo;
955
956 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
957 /* We use this folder observer to track the headers that have been
958  * added to a folder */
959 typedef struct {
960         GObject parent;
961         TnyList *new_headers;
962 } InternalFolderObserver;
963
964 typedef struct {
965         GObjectClass parent;
966 } InternalFolderObserverClass;
967
968 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
969
970 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
971                          internal_folder_observer,
972                          G_TYPE_OBJECT,
973                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
974
975
976 static void
977 foreach_add_item (gpointer header, gpointer user_data)
978 {
979         tny_list_prepend (TNY_LIST (user_data), 
980                           g_object_ref (G_OBJECT (header)));
981 }
982
983 /* This is the method that looks for new messages in a folder */
984 static void
985 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
986 {
987         InternalFolderObserver *derived = (InternalFolderObserver *)self;
988         
989         TnyFolderChangeChanged changed;
990
991         changed = tny_folder_change_get_changed (change);
992
993         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
994                 TnyList *list;
995
996                 /* Get added headers */
997                 list = tny_simple_list_new ();
998                 tny_folder_change_get_added_headers (change, list);
999
1000                 /* Add them to the folder observer */
1001                 tny_list_foreach (list, foreach_add_item, 
1002                                   derived->new_headers);
1003
1004                 g_object_unref (G_OBJECT (list));
1005         }
1006 }
1007
1008 static void
1009 internal_folder_observer_init (InternalFolderObserver *self) 
1010 {
1011         self->new_headers = tny_simple_list_new ();
1012 }
1013 static void
1014 internal_folder_observer_finalize (GObject *object) 
1015 {
1016         InternalFolderObserver *self;
1017
1018         self = (InternalFolderObserver *) object;
1019         g_object_unref (self->new_headers);
1020
1021         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1022 }
1023 static void
1024 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1025 {
1026         iface->update_func = internal_folder_observer_update;
1027 }
1028 static void
1029 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1030 {
1031         GObjectClass *object_class;
1032
1033         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1034         object_class = (GObjectClass*) klass;
1035         object_class->finalize = internal_folder_observer_finalize;
1036 }
1037
1038 /*****************/
1039
1040 static void
1041 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1042 {
1043         TnyIterator *iter;
1044         TnyList *folders = tny_simple_list_new ();
1045
1046         tny_folder_store_get_folders (store, folders, query, NULL);
1047         iter = tny_list_create_iterator (folders);
1048
1049         while (!tny_iterator_is_done (iter)) {
1050
1051                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1052                 if (folder) {
1053                         tny_list_prepend (all_folders, G_OBJECT (folder));
1054                         recurse_folders (folder, query, all_folders);    
1055                         g_object_unref (G_OBJECT (folder));
1056                 }
1057
1058                 tny_iterator_next (iter);
1059         }
1060          g_object_unref (G_OBJECT (iter));
1061          g_object_unref (G_OBJECT (folders));
1062 }
1063
1064 /* 
1065  * Issues the "progress-changed" signal. The timer won't be removed,
1066  * so you must call g_source_remove to stop the signal emission
1067  */
1068 static gboolean
1069 idle_notify_progress (gpointer data)
1070 {
1071         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1072         ModestMailOperationState *state;
1073
1074         state = modest_mail_operation_clone_state (mail_op);
1075
1076         /* This is a GDK lock because we are an idle callback and
1077          * the handlers of this signal can contain Gtk+ code */
1078
1079         gdk_threads_enter (); /* CHECKED */
1080         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1081         gdk_threads_leave (); /* CHECKED */
1082
1083         g_slice_free (ModestMailOperationState, state);
1084         
1085         return TRUE;
1086 }
1087
1088 /* 
1089  * Issues the "progress-changed" signal and removes the timer. It uses
1090  * a lock to ensure that the progress information of the mail
1091  * operation is not modified while there are notifications pending
1092  */
1093 static gboolean
1094 idle_notify_progress_once (gpointer data)
1095 {
1096         ModestPair *pair;
1097
1098         pair = (ModestPair *) data;
1099
1100         /* This is a GDK lock because we are an idle callback and
1101          * the handlers of this signal can contain Gtk+ code */
1102
1103         gdk_threads_enter (); /* CHECKED */
1104         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1105         gdk_threads_leave (); /* CHECKED */
1106
1107         /* Free the state and the reference to the mail operation */
1108         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1109         g_object_unref (pair->first);
1110
1111         return FALSE;
1112 }
1113
1114 /* 
1115  * Used to notify the queue from the main
1116  * loop. We call it inside an idle call to achieve that
1117  */
1118 static gboolean
1119 idle_notify_queue (gpointer data)
1120 {
1121         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1122
1123         /* Do not need to block, the notify end will do it for us */    
1124         modest_mail_operation_notify_end (mail_op);
1125         g_object_unref (mail_op);
1126
1127         return FALSE;
1128 }
1129
1130 static int
1131 compare_headers_by_date (gconstpointer a, 
1132                          gconstpointer b)
1133 {
1134         TnyHeader **header1, **header2;
1135         time_t sent1, sent2;
1136
1137         header1 = (TnyHeader **) a;
1138         header2 = (TnyHeader **) b;
1139
1140         sent1 = tny_header_get_date_sent (*header1);
1141         sent2 = tny_header_get_date_sent (*header2);
1142
1143         /* We want the most recent ones (greater time_t) at the
1144            beginning */
1145         if (sent1 < sent2)
1146                 return 1;
1147         else
1148                 return -1;
1149 }
1150
1151 static gboolean 
1152 set_last_updated_idle (gpointer data)
1153 {
1154
1155         /* This is a GDK lock because we are an idle callback and
1156          * modest_account_mgr_set_int can contain Gtk+ code */
1157
1158         gdk_threads_enter (); /* CHECKED - please recheck */
1159
1160         /* It does not matter if the time is not exactly the same than
1161            the time when this idle was called, it's just an
1162            approximation and it won't be very different */
1163
1164         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
1165                                     (gchar *) data, 
1166                                     MODEST_ACCOUNT_LAST_UPDATED, 
1167                                     time(NULL), 
1168                                     TRUE);
1169
1170         gdk_threads_leave (); /* CHECKED - please recheck */
1171
1172         return FALSE;
1173 }
1174
1175 static gboolean
1176 idle_update_account_cb (gpointer data)
1177 {
1178         UpdateAccountInfo *idle_info;
1179
1180         idle_info = (UpdateAccountInfo *) data;
1181
1182         /* This is a GDK lock because we are an idle callback and
1183          * idle_info->callback can contain Gtk+ code */
1184
1185         gdk_threads_enter (); /* CHECKED */
1186         idle_info->callback (idle_info->mail_op,
1187                              idle_info->new_headers,
1188                              idle_info->user_data);
1189         gdk_threads_leave (); /* CHECKED */
1190
1191         /* Frees */
1192         g_object_unref (idle_info->mail_op);
1193         if (idle_info->new_headers)
1194                 g_object_unref (idle_info->new_headers);
1195         g_free (idle_info);
1196
1197         return FALSE;
1198 }
1199
1200 static TnyList *
1201 get_all_folders_from_account (TnyStoreAccount *account,
1202                               GError **error)
1203 {
1204         TnyList *all_folders = NULL;
1205         TnyIterator *iter = NULL;
1206         TnyFolderStoreQuery *query = NULL;
1207
1208         all_folders = tny_simple_list_new ();
1209         query = tny_folder_store_query_new ();
1210         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1211         tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
1212                                       all_folders,
1213                                       query,
1214                                       error);
1215
1216         if (*error) {
1217                 if (all_folders)
1218                         g_object_unref (all_folders);
1219                 return NULL;
1220         }
1221
1222         iter = tny_list_create_iterator (all_folders);
1223         while (!tny_iterator_is_done (iter)) {
1224                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1225                 if (folder) {
1226                         recurse_folders (folder, query, all_folders);
1227                         g_object_unref (folder);
1228                 }
1229                 tny_iterator_next (iter);
1230         }
1231         g_object_unref (G_OBJECT (iter));
1232
1233         return all_folders;
1234 }
1235
1236
1237 static gpointer
1238 update_account_thread (gpointer thr_user_data)
1239 {
1240         static gboolean first_time = TRUE;
1241         UpdateAccountInfo *info = NULL;
1242         TnyList *all_folders = NULL, *new_headers = NULL;
1243         GPtrArray *new_headers_array = NULL;
1244         TnyIterator *iter = NULL;
1245         ModestMailOperationPrivate *priv = NULL;
1246         ModestTnySendQueue *send_queue = NULL;
1247         gint i = 0, timeout = 0;
1248
1249         info = (UpdateAccountInfo *) thr_user_data;
1250         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1251
1252         /* Get account and set it into mail_operation */
1253         priv->account = g_object_ref (info->account);
1254
1255         /* Get all the folders. We can do it synchronously because
1256            we're already running in a different thread than the UI */
1257         all_folders = get_all_folders_from_account (info->account, &(priv->error));
1258         if (!all_folders) {
1259                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1260                 goto out;
1261         }
1262
1263         /* Update status and notify. We need to call the notification
1264            with a source function in order to call it from the main
1265            loop. We need that in order not to get into trouble with
1266            Gtk+. We use a timeout in order to provide more status
1267            information, because the sync tinymail call does not
1268            provide it for the moment */
1269         timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1270
1271         new_headers_array = g_ptr_array_new ();
1272         iter = tny_list_create_iterator (all_folders);
1273
1274         while (!tny_iterator_is_done (iter) && !priv->error && 
1275                priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1276
1277                 TnyFolderType folder_type;
1278                 TnyFolder *folder = NULL;
1279
1280                 folder = TNY_FOLDER (tny_iterator_get_current (iter));
1281                 folder_type = tny_folder_get_folder_type (folder);
1282
1283                 /* Refresh it only if it's the INBOX */
1284                 if (folder_type == TNY_FOLDER_TYPE_INBOX) {
1285                         InternalFolderObserver *observer = NULL;
1286                         TnyIterator *new_headers_iter = NULL;
1287
1288                         /* Refresh the folder. Our observer receives
1289                          * the new emails during folder refreshes, so
1290                          * we can use observer->new_headers
1291                          */
1292                         observer = g_object_new (internal_folder_observer_get_type (), NULL);
1293                         tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1294                 
1295                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1296
1297                         new_headers_iter = tny_list_create_iterator (observer->new_headers);
1298                         while (!tny_iterator_is_done (new_headers_iter)) {
1299                                 TnyHeader *header = NULL;
1300
1301                                 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1302                                 /* Apply per-message size limits */
1303                                 if (tny_header_get_message_size (header) < info->max_size)
1304                                         g_ptr_array_add (new_headers_array, g_object_ref (header));
1305                                 
1306                                 g_object_unref (header);
1307                                 tny_iterator_next (new_headers_iter);
1308                         }
1309                         g_object_unref (new_headers_iter);
1310
1311                         tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1312                         g_object_unref (observer);
1313                 } else {
1314                         /* We no not need to do it the first time,
1315                            because it's automatically done by the tree
1316                            model */
1317                         if (G_LIKELY (!first_time))
1318                                 tny_folder_poke_status (folder);
1319                 }
1320                 g_object_unref (folder);
1321
1322                 tny_iterator_next (iter);
1323         }
1324         g_object_unref (G_OBJECT (iter));
1325         g_source_remove (timeout);
1326
1327         if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED && 
1328             priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1329             new_headers_array->len > 0) {
1330                 gint msg_num = 0;
1331
1332                 /* Order by date */
1333                 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1334
1335                 /* TODO: Ask the user, instead of just failing,
1336                  * showing mail_nc_msg_count_limit_exceeded, with 'Get
1337                  * all' and 'Newest only' buttons. */
1338                 if (new_headers_array->len > info->retrieve_limit) {
1339                         /* TODO */
1340                 }
1341
1342                 /* Should be get only the headers or the message as well? */
1343                 if (g_ascii_strcasecmp (info->retrieve_type, 
1344                                         MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {     
1345                         priv->done = 0;
1346                         priv->total = MIN (new_headers_array->len, info->retrieve_limit);
1347                         while (msg_num < priv->total) {
1348
1349                                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1350                                 TnyFolder *folder = tny_header_get_folder (header);
1351                                 TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1352                                 ModestMailOperationState *state;
1353                                 ModestPair* pair;
1354
1355                                 priv->done++;
1356                                 /* We can not just use the mail operation because the
1357                                    values of done and total could change before the
1358                                    idle is called */
1359                                 state = modest_mail_operation_clone_state (info->mail_op);
1360                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1361                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1362                                                  pair, (GDestroyNotify) modest_pair_free);
1363
1364                                 g_object_unref (msg);
1365                                 g_object_unref (folder);
1366
1367                                 msg_num++;
1368                         }
1369                 }
1370         }
1371
1372         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1373                 goto out;
1374
1375         /* Copy the headers to a list and free the array */
1376         new_headers = tny_simple_list_new ();
1377         for (i=0; i < new_headers_array->len; i++) {
1378                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1379                 tny_list_append (new_headers, G_OBJECT (header));
1380         }
1381         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1382         g_ptr_array_free (new_headers_array, FALSE);
1383         
1384
1385         /* Perform send (if operation was not cancelled) */
1386         priv->done = 0;
1387         priv->total = 0;
1388         if (priv->account != NULL) 
1389                 g_object_unref (priv->account);
1390         priv->account = g_object_ref (info->transport_account);
1391         
1392         send_queue = modest_runtime_get_send_queue (info->transport_account);
1393         if (send_queue) {
1394                 modest_tny_send_queue_try_to_send (send_queue);
1395         } else {
1396                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1397                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1398                              "cannot create a send queue for %s\n", 
1399                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1400                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1401         }
1402         
1403         /* Check if the operation was a success */
1404         if (!priv->error) {
1405                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1406
1407                 /* Update the last updated key */
1408                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1409                                  set_last_updated_idle, 
1410                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1411                                  (GDestroyNotify) g_free);
1412         }
1413
1414  out:
1415
1416         if (info->callback) {
1417                 UpdateAccountInfo *idle_info;
1418
1419                 /* This thread is not in the main lock */
1420                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1421                 idle_info->mail_op = g_object_ref (info->mail_op);
1422                 idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL;
1423                 idle_info->callback = info->callback;
1424                 idle_info->user_data = info->user_data;
1425                 g_idle_add (idle_update_account_cb, idle_info);
1426         }
1427
1428         /* Notify about operation end. Note that the info could be
1429            freed before this idle happens, but the mail operation will
1430            be still alive */
1431         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1432
1433         /* Frees */
1434         if (new_headers)
1435                 g_object_unref (new_headers);
1436         g_object_unref (all_folders);
1437         g_object_unref (info->account);
1438         g_object_unref (info->transport_account);
1439         g_free (info->retrieve_type);
1440         g_slice_free (UpdateAccountInfo, info);
1441
1442         first_time = FALSE;
1443
1444         return NULL;
1445 }
1446
1447 gboolean
1448 modest_mail_operation_update_account (ModestMailOperation *self,
1449                                       const gchar *account_name,
1450                                       UpdateAccountCallback callback,
1451                                       gpointer user_data)
1452 {
1453         GThread *thread = NULL;
1454         UpdateAccountInfo *info = NULL;
1455         ModestMailOperationPrivate *priv = NULL;
1456         ModestAccountMgr *mgr = NULL;
1457         TnyStoreAccount *store_account = NULL;
1458         TnyTransportAccount *transport_account = NULL;
1459
1460         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1461         g_return_val_if_fail (account_name, FALSE);
1462
1463         /* Init mail operation. Set total and done to 0, and do not
1464            update them, this way the progress objects will know that
1465            we have no clue about the number of the objects */
1466         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1467         priv->total = 0;
1468         priv->done  = 0;
1469         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1470
1471         /* Get the Modest account */
1472         store_account = (TnyStoreAccount *)
1473                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1474                                                                      account_name,
1475                                                                      TNY_ACCOUNT_TYPE_STORE);
1476                                                                      
1477         /* Make sure that we have a connection, and request one 
1478          * if necessary:
1479          * TODO: Is there some way to trigger this for every attempt to 
1480          * use the network? */
1481         if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1482                 goto error;
1483
1484         if (!store_account) {
1485                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1486                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1487                              "cannot get tny store account for %s\n", account_name);
1488                 goto error;
1489         }
1490
1491         
1492         /* Get the transport account, we can not do it in the thread
1493            due to some problems with dbus */
1494         transport_account = (TnyTransportAccount *)
1495                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1496                                                                                     account_name);
1497         if (!transport_account) {
1498                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1499                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1500                              "cannot get tny transport account for %s\n", account_name);
1501                 goto error;
1502         }
1503
1504         /* Create the helper object */
1505         info = g_slice_new (UpdateAccountInfo);
1506         info->mail_op = self;
1507         info->account = store_account;
1508         info->transport_account = transport_account;
1509         info->callback = callback;
1510         info->user_data = user_data;
1511
1512         /* Get the message size limit */
1513         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1514                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1515         if (info->max_size == 0)
1516                 info->max_size = G_MAXINT;
1517         else
1518                 info->max_size = info->max_size * KB;
1519
1520         /* Get per-account retrieval type */
1521         mgr = modest_runtime_get_account_mgr ();
1522         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1523                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1524
1525         /* Get per-account message amount retrieval limit */
1526         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1527                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1528         if (info->retrieve_limit == 0)
1529                 info->retrieve_limit = G_MAXINT;
1530                 
1531         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1532
1533         /* Set account busy */
1534         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1535         priv->account_name = g_strdup(account_name);
1536         
1537         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1538
1539         return TRUE;
1540
1541  error:
1542         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1543         if (callback) 
1544                 callback (self, 0, user_data);
1545         modest_mail_operation_notify_end (self);
1546         return FALSE;
1547 }
1548
1549 /* ******************************************************************* */
1550 /* ************************** STORE  ACTIONS ************************* */
1551 /* ******************************************************************* */
1552
1553
1554 TnyFolder *
1555 modest_mail_operation_create_folder (ModestMailOperation *self,
1556                                      TnyFolderStore *parent,
1557                                      const gchar *name)
1558 {
1559         ModestMailOperationPrivate *priv;
1560         TnyFolder *new_folder = NULL;
1561
1562         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1563         g_return_val_if_fail (name, NULL);
1564         
1565         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1566
1567         /* Check for already existing folder */
1568         if (modest_tny_folder_has_subfolder_with_name (parent, name)) {
1569                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1570                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1571                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1572                              _CS("ckdg_ib_folder_already_exists"));
1573         }
1574
1575         /* Check parent */
1576         if (TNY_IS_FOLDER (parent)) {
1577                 /* Check folder rules */
1578                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1579                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1580                         /* Set status failed and set an error */
1581                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1582                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1583                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1584                                      _("mail_in_ui_folder_create_error"));
1585                 }
1586         }
1587
1588         if (!strcmp (name, " ") || strchr (name, '/')) {
1589                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1590                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1591                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1592                              _("mail_in_ui_folder_create_error"));
1593         }
1594
1595         if (!priv->error) {
1596                 /* Create the folder */
1597                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1598                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1599                 if (!priv->error)
1600                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1601         }
1602
1603         /* Notify about operation end */
1604         modest_mail_operation_notify_end (self);
1605
1606         return new_folder;
1607 }
1608
1609 void
1610 modest_mail_operation_remove_folder (ModestMailOperation *self,
1611                                      TnyFolder           *folder,
1612                                      gboolean             remove_to_trash)
1613 {
1614         TnyAccount *account;
1615         ModestMailOperationPrivate *priv;
1616         ModestTnyFolderRules rules;
1617
1618         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1619         g_return_if_fail (TNY_IS_FOLDER (folder));
1620         
1621         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1622         
1623         /* Check folder rules */
1624         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1625         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1626                 /* Set status failed and set an error */
1627                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1628                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1629                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1630                              _("mail_in_ui_folder_delete_error"));
1631                 goto end;
1632         }
1633
1634         /* Get the account */
1635         account = modest_tny_folder_get_account (folder);
1636         priv->account = g_object_ref(account);
1637
1638         /* Delete folder or move to trash */
1639         if (remove_to_trash) {
1640                 TnyFolder *trash_folder = NULL;
1641                 trash_folder = modest_tny_account_get_special_folder (account,
1642                                                                       TNY_FOLDER_TYPE_TRASH);
1643                 /* TODO: error_handling */
1644                 if (trash_folder) {
1645                         modest_mail_operation_xfer_folder (self, folder,
1646                                                     TNY_FOLDER_STORE (trash_folder), 
1647                                                     TRUE, NULL, NULL);
1648                         g_object_unref (trash_folder);
1649                 }
1650         } else {
1651                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1652
1653                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1654                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1655
1656                 if (!priv->error)
1657                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1658
1659                 if (parent)
1660                         g_object_unref (G_OBJECT (parent));
1661         }
1662         g_object_unref (G_OBJECT (account));
1663
1664  end:
1665         /* Notify about operation end */
1666         modest_mail_operation_notify_end (self);
1667 }
1668
1669 static void
1670 transfer_folder_status_cb (GObject *obj,
1671                            TnyStatus *status,
1672                            gpointer user_data)
1673 {
1674         ModestMailOperation *self;
1675         ModestMailOperationPrivate *priv;
1676         ModestMailOperationState *state;
1677         XFerMsgAsyncHelper *helper;
1678
1679         g_return_if_fail (status != NULL);
1680         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1681
1682         helper = (XFerMsgAsyncHelper *) user_data;
1683         g_return_if_fail (helper != NULL);
1684
1685         self = helper->mail_op;
1686         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1687
1688         priv->done = status->position;
1689         priv->total = status->of_total;
1690
1691         state = modest_mail_operation_clone_state (self);
1692
1693         /* This is not a GDK lock because we are a Tinymail callback
1694          * which is already GDK locked by Tinymail */
1695
1696         /* no gdk_threads_enter (), CHECKED */
1697
1698         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1699
1700         /* no gdk_threads_leave (), CHECKED */
1701
1702         g_slice_free (ModestMailOperationState, state);
1703 }
1704
1705
1706 static void
1707 transfer_folder_cb (TnyFolder *folder, 
1708                     gboolean cancelled, 
1709                     TnyFolderStore *into, 
1710                     TnyFolder *new_folder, 
1711                     GError *err, 
1712                     gpointer user_data)
1713 {
1714         XFerMsgAsyncHelper *helper;
1715         ModestMailOperation *self = NULL;
1716         ModestMailOperationPrivate *priv = NULL;
1717
1718         helper = (XFerMsgAsyncHelper *) user_data;
1719         g_return_if_fail (helper != NULL);       
1720
1721         self = helper->mail_op;
1722         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1723
1724         if (err) {
1725                 priv->error = g_error_copy (err);
1726                 priv->done = 0;
1727                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1728         } else if (cancelled) {
1729                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1730                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1731                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1732                              _("Transference of %s was cancelled."),
1733                              tny_folder_get_name (folder));
1734         } else {
1735                 priv->done = 1;
1736                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1737         }
1738                 
1739         /* Notify about operation end */
1740         modest_mail_operation_notify_end (self);
1741
1742         /* If user defined callback function was defined, call it */
1743         if (helper->user_callback) {
1744
1745                 /* This is not a GDK lock because we are a Tinymail callback
1746                  * which is already GDK locked by Tinymail */
1747
1748                 /* no gdk_threads_enter (), CHECKED */
1749                 helper->user_callback (priv->source, helper->user_data);
1750                 /* no gdk_threads_leave () , CHECKED */
1751         }
1752
1753         /* Free */
1754         g_object_unref (helper->mail_op);
1755         g_slice_free   (XFerMsgAsyncHelper, helper);
1756 }
1757
1758 /**
1759  *
1760  * This function checks if the new name is a valid name for our local
1761  * folders account. The new name could not be the same than then name
1762  * of any of the mandatory local folders
1763  *
1764  * We can not rely on tinymail because tinymail does not check the
1765  * name of the virtual folders that the account could have in the case
1766  * that we're doing a rename (because it directly calls Camel which
1767  * knows nothing about our virtual folders). 
1768  *
1769  * In the case of an actual copy/move (i.e. move/copy a folder between
1770  * accounts) tinymail uses the tny_folder_store_create_account which
1771  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1772  * checks the new name of the folder, so this call in that case
1773  * wouldn't be needed. *But* NOTE that if tinymail changes its
1774  * implementation (if folder transfers within the same account is no
1775  * longer implemented as a rename) this call will allow Modest to work
1776  * perfectly
1777  *
1778  * If the new name is not valid, this function will set the status to
1779  * failed and will set also an error in the mail operation
1780  */
1781 static gboolean
1782 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1783                                  TnyFolderStore *into,
1784                                  const gchar *new_name)
1785 {
1786         if (TNY_IS_ACCOUNT (into) && 
1787             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1788             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1789                                                                  new_name)) {
1790                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1791                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1792                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1793                              _("ckdg_ib_folder_already_exists"));
1794                 return FALSE;
1795         } else
1796                 return TRUE;
1797 }
1798
1799 /**
1800  * This function checks if @ancestor is an acestor of @folder and
1801  * returns TRUE in that case
1802  */
1803 static gboolean
1804 folder_is_ancestor (TnyFolder *folder,
1805                     TnyFolderStore *ancestor)
1806 {
1807         TnyFolder *tmp = NULL;
1808         gboolean found = FALSE;
1809
1810         tmp = folder;
1811         while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1812                 TnyFolderStore *folder_store;
1813
1814                 folder_store = tny_folder_get_folder_store (tmp);
1815                 if (ancestor == folder_store)
1816                         found = TRUE;
1817                 else
1818                         tmp = g_object_ref (folder_store);
1819                 g_object_unref (folder_store);
1820         }
1821         return found;
1822 }
1823
1824 void
1825 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1826                                    TnyFolder *folder,
1827                                    TnyFolderStore *parent,
1828                                    gboolean delete_original,
1829                                    XferMsgsAsynUserCallback user_callback,
1830                                    gpointer user_data)
1831 {
1832         ModestMailOperationPrivate *priv = NULL;
1833         ModestTnyFolderRules parent_rules = 0, rules; 
1834         XFerMsgAsyncHelper *helper = NULL;
1835         const gchar *folder_name = NULL;
1836         const gchar *error_msg;
1837
1838         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1839         g_return_if_fail (TNY_IS_FOLDER (folder));
1840         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1841
1842         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1843         folder_name = tny_folder_get_name (folder);
1844
1845         /* Set the error msg */
1846         error_msg = _("mail_in_ui_folder_move_target_error");
1847
1848         /* Get account and set it into mail_operation */
1849         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1850         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1851
1852         /* Get folder rules */
1853         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1854         if (TNY_IS_FOLDER (parent))
1855                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1856         
1857         /* The moveable restriction is applied also to copy operation */
1858         if ((gpointer) parent == (gpointer) folder ||
1859             (!TNY_IS_FOLDER_STORE (parent)) || 
1860             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1861
1862                 /* Set status failed and set an error */
1863                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1864                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1865                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1866                              error_msg);
1867
1868                 /* Notify the queue */
1869                 modest_mail_operation_notify_end (self);
1870
1871         } else if (TNY_IS_FOLDER (parent) && 
1872                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1873
1874                 /* Set status failed and set an error */
1875                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1876                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1877                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1878                              error_msg);
1879
1880                 /* Notify the queue */
1881                 modest_mail_operation_notify_end (self);
1882
1883         } else if (TNY_IS_FOLDER (parent) &&
1884                    TNY_IS_FOLDER_STORE (folder) &&
1885                    folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1886                 /* Set status failed and set an error */
1887                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1888                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1889                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1890                              error_msg);
1891
1892                 /* Notify the queue */
1893                 modest_mail_operation_notify_end (self);
1894
1895         } else if (TNY_IS_FOLDER_STORE (parent) &&
1896                    modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1897                 /* Check that the new folder name is not used by any
1898                     parent subfolder */
1899
1900                 /* Set status failed and set an error */
1901                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1902                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1903                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1904                              error_msg);
1905
1906                 /* Notify the queue */
1907                 modest_mail_operation_notify_end (self);
1908                 
1909         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1910                 /* Check that the new folder name is not used by any
1911                    special local folder */
1912
1913                 /* Set status failed and set an error */
1914                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1915                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1916                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1917                              error_msg);
1918
1919                 /* Notify the queue */
1920                 modest_mail_operation_notify_end (self);
1921         } else {
1922                 /* Create the helper */
1923                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1924                 helper->mail_op = g_object_ref(self);
1925                 helper->dest_folder = NULL;
1926                 helper->headers = NULL;
1927                 helper->user_callback = user_callback;
1928                 helper->user_data = user_data;
1929                 
1930                 /* Move/Copy folder */          
1931                 tny_folder_copy_async (folder,
1932                                        parent,
1933                                        tny_folder_get_name (folder),
1934                                        delete_original,
1935                                        transfer_folder_cb,
1936                                        transfer_folder_status_cb,
1937                                        helper);         
1938         } 
1939         
1940 }
1941
1942 void
1943 modest_mail_operation_rename_folder (ModestMailOperation *self,
1944                                      TnyFolder *folder,
1945                                      const gchar *name)
1946 {
1947         ModestMailOperationPrivate *priv;
1948         ModestTnyFolderRules rules;
1949         XFerMsgAsyncHelper *helper;
1950
1951         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1952         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1953         g_return_if_fail (name);
1954         
1955         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1956
1957         /* Get account and set it into mail_operation */
1958         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1959
1960         /* Check folder rules */
1961         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1962         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1963                 /* Set status failed and set an error */
1964                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1965                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1966                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1967                              _("FIXME: unable to rename"));
1968
1969                 /* Notify about operation end */
1970                 modest_mail_operation_notify_end (self);
1971         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1972                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1973                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1974                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1975                              _("FIXME: unable to rename"));
1976                 /* Notify about operation end */
1977                 modest_mail_operation_notify_end (self);
1978         } else {
1979                 TnyFolderStore *into;
1980
1981                 into = tny_folder_get_folder_store (folder);    
1982
1983                 /* Check that the new folder name is not used by any
1984                    special local folder */
1985                 if (new_name_valid_if_local_account (priv, into, name)) {
1986                         /* Create the helper */
1987                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1988                         helper->mail_op = g_object_ref(self);
1989                         helper->dest_folder = NULL;
1990                         helper->headers = NULL;
1991                         helper->user_callback = NULL;
1992                         helper->user_data = NULL;
1993                 
1994                         /* Rename. Camel handles folder subscription/unsubscription */
1995                         tny_folder_copy_async (folder, into, name, TRUE,
1996                                                transfer_folder_cb,
1997                                                transfer_folder_status_cb,
1998                                                helper);
1999                 } else {
2000                         modest_mail_operation_notify_end (self);
2001                 }
2002                 g_object_unref (into);
2003         }
2004 }
2005
2006 /* ******************************************************************* */
2007 /* **************************  MSG  ACTIONS  ************************* */
2008 /* ******************************************************************* */
2009
2010 void modest_mail_operation_get_msg (ModestMailOperation *self,
2011                                     TnyHeader *header,
2012                                     GetMsgAsyncUserCallback user_callback,
2013                                     gpointer user_data)
2014 {
2015         GetMsgAsyncHelper *helper = NULL;
2016         TnyFolder *folder;
2017         ModestMailOperationPrivate *priv;
2018         
2019         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2020         g_return_if_fail (TNY_IS_HEADER (header));
2021         
2022         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2023         folder = tny_header_get_folder (header);
2024
2025         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2026
2027         /* Get message from folder */
2028         if (folder) {
2029                 /* Get account and set it into mail_operation */
2030                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2031                 
2032                 /* Check for cached messages */
2033                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2034                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2035                 else 
2036                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2037
2038                 helper = g_slice_new0 (GetMsgAsyncHelper);
2039                 helper->mail_op = self;
2040                 helper->user_callback = user_callback;
2041                 helper->user_data = user_data;
2042                 helper->header = g_object_ref (header);
2043
2044                 // The callback's reference so that the mail op is not
2045                 // finalized until the async operation is completed even if
2046                 // the user canceled the request meanwhile.
2047                 g_object_ref (G_OBJECT (helper->mail_op));
2048
2049                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2050
2051                 g_object_unref (G_OBJECT (folder));
2052         } else {
2053                 /* Set status failed and set an error */
2054                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2055                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2056                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2057                              _("Error trying to get a message. No folder found for header"));
2058
2059                 /* Notify the queue */
2060                 modest_mail_operation_notify_end (self);
2061         }
2062 }
2063
2064 static void
2065 get_msg_cb (TnyFolder *folder, 
2066             gboolean cancelled, 
2067             TnyMsg *msg, 
2068             GError *error, 
2069             gpointer user_data)
2070 {
2071         GetMsgAsyncHelper *helper = NULL;
2072         ModestMailOperation *self = NULL;
2073         ModestMailOperationPrivate *priv = NULL;
2074
2075         helper = (GetMsgAsyncHelper *) user_data;
2076         g_return_if_fail (helper != NULL);       
2077         self = helper->mail_op;
2078         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2079         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2080
2081         /* Check errors and cancel */
2082         if (error) {
2083                 priv->error = g_error_copy (error);
2084                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2085         } else if (cancelled) {
2086                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2087                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2088                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2089                              _("Error trying to refresh the contents of %s"),
2090                              tny_folder_get_name (folder));
2091         } else {
2092                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2093         }
2094
2095         /* If user defined callback function was defined, call it even
2096            if the operation failed*/
2097         if (helper->user_callback) {
2098                 /* This is not a GDK lock because we are a Tinymail callback
2099                  * which is already GDK locked by Tinymail */
2100
2101                 /* no gdk_threads_enter (), CHECKED */
2102                 helper->user_callback (self, helper->header, msg, helper->user_data);
2103                 /* no gdk_threads_leave (), CHECKED */
2104         }
2105
2106         /* Notify about operation end */
2107         modest_mail_operation_notify_end (self);
2108         /* Free */
2109         g_object_unref (helper->mail_op);
2110         g_object_unref (helper->header);
2111         g_slice_free (GetMsgAsyncHelper, helper);
2112                 
2113 }
2114
2115 static void     
2116 get_msg_status_cb (GObject *obj,
2117                    TnyStatus *status,  
2118                    gpointer user_data)
2119 {
2120         GetMsgAsyncHelper *helper = NULL;
2121         ModestMailOperation *self;
2122         ModestMailOperationPrivate *priv;
2123         ModestMailOperationState *state;
2124
2125         g_return_if_fail (status != NULL);
2126         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2127
2128         helper = (GetMsgAsyncHelper *) user_data;
2129         g_return_if_fail (helper != NULL);       
2130
2131         self = helper->mail_op;
2132         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2133
2134         priv->done = 1;
2135         priv->total = 1;
2136
2137         state = modest_mail_operation_clone_state (self);
2138         state->bytes_done = status->position;
2139         state->bytes_total = status->of_total;
2140
2141         /* This is not a GDK lock because we are a Tinymail callback
2142          * which is already GDK locked by Tinymail */
2143
2144         /* no gdk_threads_enter (), CHECKED */
2145         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2146         /* no gdk_threads_leave (), CHECKED */
2147
2148         g_slice_free (ModestMailOperationState, state);
2149 }
2150
2151 /****************************************************/
2152 typedef struct {
2153         ModestMailOperation *mail_op;
2154         TnyList *headers;
2155         GetMsgAsyncUserCallback user_callback;
2156         gpointer user_data;
2157         GDestroyNotify notify;
2158 } GetFullMsgsInfo;
2159
2160 typedef struct {
2161         GetMsgAsyncUserCallback user_callback;
2162         TnyHeader *header;
2163         TnyMsg *msg;
2164         gpointer user_data;
2165         ModestMailOperation *mail_op;
2166 } NotifyGetMsgsInfo;
2167
2168
2169 /* 
2170  * Used by get_msgs_full_thread to call the user_callback for each
2171  * message that has been read
2172  */
2173 static gboolean
2174 notify_get_msgs_full (gpointer data)
2175 {
2176         NotifyGetMsgsInfo *info;
2177
2178         info = (NotifyGetMsgsInfo *) data;      
2179
2180         /* This is a GDK lock because we are an idle callback and
2181          * because info->user_callback can contain Gtk+ code */
2182
2183         gdk_threads_enter (); /* CHECKED */
2184         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2185         gdk_threads_leave (); /* CHECKED */
2186
2187         g_slice_free (NotifyGetMsgsInfo, info);
2188
2189         return FALSE;
2190 }
2191
2192 /* 
2193  * Used by get_msgs_full_thread to free al the thread resources and to
2194  * call the destroy function for the passed user_data
2195  */
2196 static gboolean
2197 get_msgs_full_destroyer (gpointer data)
2198 {
2199         GetFullMsgsInfo *info;
2200
2201         info = (GetFullMsgsInfo *) data;
2202
2203         if (info->notify) {
2204
2205                 /* This is a GDK lock because we are an idle callback and
2206                  * because info->notify can contain Gtk+ code */
2207
2208                 gdk_threads_enter (); /* CHECKED */
2209                 info->notify (info->user_data);
2210                 gdk_threads_leave (); /* CHECKED */
2211         }
2212
2213         /* free */
2214         g_object_unref (info->headers);
2215         g_slice_free (GetFullMsgsInfo, info);
2216
2217         return FALSE;
2218 }
2219
2220 static gpointer
2221 get_msgs_full_thread (gpointer thr_user_data)
2222 {
2223         GetFullMsgsInfo *info;
2224         ModestMailOperationPrivate *priv = NULL;
2225         TnyIterator *iter = NULL;
2226         
2227         info = (GetFullMsgsInfo *) thr_user_data;       
2228         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2229
2230         iter = tny_list_create_iterator (info->headers);
2231         while (!tny_iterator_is_done (iter)) { 
2232                 TnyHeader *header;
2233                 TnyFolder *folder;
2234                 
2235                 header = TNY_HEADER (tny_iterator_get_current (iter));
2236                 folder = tny_header_get_folder (header);
2237                                 
2238                 /* Check for cached messages */
2239                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2240                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2241                 else 
2242                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2243
2244                 /* Get message from folder */
2245                 if (folder) {
2246                         TnyMsg *msg;
2247                         /* The callback will call it per each header */
2248                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2249
2250                         if (msg) {
2251                                 ModestMailOperationState *state;
2252                                 ModestPair *pair;
2253
2254                                 priv->done++;
2255
2256                                 /* notify progress */
2257                                 state = modest_mail_operation_clone_state (info->mail_op);
2258                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2259                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2260                                                  pair, (GDestroyNotify) modest_pair_free);
2261
2262                                 /* The callback is the responsible for
2263                                    freeing the message */
2264                                 if (info->user_callback) {
2265                                         NotifyGetMsgsInfo *info_notify;
2266                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2267                                         info_notify->user_callback = info->user_callback;
2268                                         info_notify->mail_op = info->mail_op;
2269                                         info_notify->header = g_object_ref (header);
2270                                         info_notify->msg = g_object_ref (msg);
2271                                         info_notify->user_data = info->user_data;
2272                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2273                                                          notify_get_msgs_full, 
2274                                                          info_notify, NULL);
2275                                 }
2276                                 g_object_unref (msg);
2277                         } 
2278                 } else {
2279                         /* Set status failed and set an error */
2280                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2281                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2282                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2283                                      "Error trying to get a message. No folder found for header");
2284                 }
2285
2286                 if (header)
2287                         g_object_unref (header);
2288                 
2289                 tny_iterator_next (iter);
2290         }
2291
2292         /* Set operation status */
2293         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2294                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2295
2296         /* Notify about operation end */
2297         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2298
2299         /* Free thread resources. Will be called after all previous idles */
2300         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2301
2302         return NULL;
2303 }
2304
2305 void 
2306 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2307                                      TnyList *header_list, 
2308                                      GetMsgAsyncUserCallback user_callback,
2309                                      gpointer user_data,
2310                                      GDestroyNotify notify)
2311 {
2312         TnyHeader *header = NULL;
2313         TnyFolder *folder = NULL;
2314         GThread *thread;
2315         ModestMailOperationPrivate *priv = NULL;
2316         GetFullMsgsInfo *info = NULL;
2317         gboolean size_ok = TRUE;
2318         gint max_size;
2319         TnyIterator *iter = NULL;
2320         
2321         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2322         
2323         /* Init mail operation */
2324         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2325         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2326         priv->done = 0;
2327         priv->total = tny_list_get_length(header_list);
2328
2329         /* Get account and set it into mail_operation */
2330         if (tny_list_get_length (header_list) >= 1) {
2331                 iter = tny_list_create_iterator (header_list);
2332                 header = TNY_HEADER (tny_iterator_get_current (iter));
2333                 if (header) {
2334                         folder = tny_header_get_folder (header);
2335                         if (folder) {           
2336                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2337
2338                                 g_object_unref (folder);
2339                         }
2340
2341                         g_object_unref (header);
2342                 }
2343
2344                 if (tny_list_get_length (header_list) == 1) {
2345                         g_object_unref (iter);
2346                         iter = NULL;
2347                 }
2348         }
2349
2350         /* Get msg size limit */
2351         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2352                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2353                                          &(priv->error));
2354         if (priv->error) {
2355                 g_clear_error (&(priv->error));
2356                 max_size = G_MAXINT;
2357         } else {
2358                 max_size = max_size * KB;
2359         }
2360
2361         /* Check message size limits. If there is only one message
2362            always retrieve it */
2363         if (iter != NULL) {
2364                 while (!tny_iterator_is_done (iter) && size_ok) {
2365                         header = TNY_HEADER (tny_iterator_get_current (iter));
2366                         if (header) {
2367                                 if (tny_header_get_message_size (header) >= max_size)
2368                                         size_ok = FALSE;
2369                                 g_object_unref (header);
2370                         }
2371
2372                         tny_iterator_next (iter);
2373                 }
2374                 g_object_unref (iter);
2375         }
2376
2377         if (size_ok) {
2378                 /* Create the info */
2379                 info = g_slice_new0 (GetFullMsgsInfo);
2380                 info->mail_op = self;
2381                 info->user_callback = user_callback;
2382                 info->user_data = user_data;
2383                 info->headers = g_object_ref (header_list);
2384                 info->notify = notify;
2385
2386                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2387         } else {
2388                 /* Set status failed and set an error */
2389                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2390                 /* FIXME: the error msg is different for pop */
2391                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2392                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2393                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2394                 /* Remove from queue and free resources */
2395                 modest_mail_operation_notify_end (self);
2396                 if (notify)
2397                         notify (user_data);
2398         }
2399 }
2400
2401
2402 void 
2403 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2404                                   TnyHeader *header,
2405                                   gboolean remove_to_trash /*ignored*/)
2406 {
2407         TnyFolder *folder;
2408         ModestMailOperationPrivate *priv;
2409
2410         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2411         g_return_if_fail (TNY_IS_HEADER (header));
2412
2413         if (remove_to_trash)
2414                 g_warning ("remove to trash is not implemented");
2415
2416         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2417         folder = tny_header_get_folder (header);
2418
2419         /* Get account and set it into mail_operation */
2420         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2421
2422         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2423
2424         /* remove message from folder */
2425         tny_folder_remove_msg (folder, header, &(priv->error));
2426         if (!priv->error) {
2427                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2428                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2429
2430                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2431 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2432                         tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2433                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2434 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2435                         tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2436                 else
2437                         /* local folders */
2438 /*                      tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2439                         tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2440         }
2441         
2442         
2443         /* Set status */
2444         if (!priv->error)
2445                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2446         else
2447                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2448
2449         /* Free */
2450         g_object_unref (G_OBJECT (folder));
2451
2452         /* Notify about operation end */
2453         modest_mail_operation_notify_end (self);
2454 }
2455
2456 void 
2457 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2458                                    TnyList *headers,
2459                                   gboolean remove_to_trash /*ignored*/)
2460 {
2461         TnyFolder *folder;
2462         ModestMailOperationPrivate *priv;
2463         TnyIterator *iter = NULL;
2464         TnyHeader *header = NULL;
2465
2466         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2467         g_return_if_fail (TNY_IS_LIST (headers));
2468
2469         if (remove_to_trash)
2470                 g_warning ("remove to trash is not implemented");
2471
2472         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2473
2474         /* Get folder from first header and sync it */
2475         iter = tny_list_create_iterator (headers);
2476         header = TNY_HEADER (tny_iterator_get_current (iter));
2477         folder = tny_header_get_folder (header);
2478         
2479         /* Get account and set it into mail_operation */
2480         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2481
2482         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2483
2484         /* remove message from folder */
2485         tny_folder_remove_msgs (folder, headers, &(priv->error));
2486         if (!priv->error) {
2487                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2488 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2489                         tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2490                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2491 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2492                         tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2493                 else
2494                         /* local folders */
2495 /*                      tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2496                         tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2497         }
2498         
2499         
2500         /* Set status */
2501         if (!priv->error)
2502                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2503         else
2504                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2505
2506         /* Free */
2507         g_object_unref (header);
2508         g_object_unref (iter);
2509         g_object_unref (G_OBJECT (folder));
2510
2511         /* Notify about operation end */
2512         modest_mail_operation_notify_end (self);
2513 }
2514
2515
2516 static void
2517 transfer_msgs_status_cb (GObject *obj,
2518                          TnyStatus *status,  
2519                          gpointer user_data)
2520 {
2521         XFerMsgAsyncHelper *helper = NULL;
2522         ModestMailOperation *self;
2523         ModestMailOperationPrivate *priv;
2524         ModestMailOperationState *state;
2525
2526
2527         g_return_if_fail (status != NULL);
2528         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2529
2530         helper = (XFerMsgAsyncHelper *) user_data;
2531         g_return_if_fail (helper != NULL);       
2532
2533         self = helper->mail_op;
2534         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2535
2536         priv->done = status->position;
2537         priv->total = status->of_total;
2538
2539         state = modest_mail_operation_clone_state (self);
2540
2541         /* This is not a GDK lock because we are a Tinymail callback and
2542          * Tinymail already acquires the Gdk lock */
2543
2544         /* no gdk_threads_enter (), CHECKED */
2545
2546         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2547
2548         /* no gdk_threads_leave (), CHECKED */
2549
2550         g_slice_free (ModestMailOperationState, state);
2551 }
2552
2553
2554 static void
2555 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2556 {
2557         XFerMsgAsyncHelper *helper;
2558         ModestMailOperation *self;
2559         ModestMailOperationPrivate *priv;
2560         TnyIterator *iter = NULL;
2561         TnyHeader *header = NULL;
2562
2563         helper = (XFerMsgAsyncHelper *) user_data;
2564         self = helper->mail_op;
2565
2566         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2567
2568         if (err) {
2569                 priv->error = g_error_copy (err);
2570                 priv->done = 0;
2571                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2572         } else if (cancelled) {
2573                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2574                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2575                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2576                              _("Error trying to refresh the contents of %s"),
2577                              tny_folder_get_name (folder));
2578         } else {
2579                 priv->done = 1;
2580                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2581
2582                 /* Update folder counts */
2583                 tny_folder_poke_status (folder);                
2584                 tny_folder_poke_status (helper->dest_folder);           
2585         }
2586
2587         
2588         /* Mark headers as deleted and seen */
2589         if ((helper->delete) && 
2590             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2591                 iter = tny_list_create_iterator (helper->headers);
2592                 while (!tny_iterator_is_done (iter)) {
2593                         header = TNY_HEADER (tny_iterator_get_current (iter));
2594                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2595                         tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2596                         g_object_unref (header);
2597
2598                         tny_iterator_next (iter);
2599                 }
2600
2601         }
2602                 
2603
2604         /* Notify about operation end */
2605         modest_mail_operation_notify_end (self);
2606
2607         /* If user defined callback function was defined, call it */
2608         if (helper->user_callback) {
2609                 /* This is not a GDK lock because we are a Tinymail callback and
2610                  * Tinymail already acquires the Gdk lock */
2611
2612                 /* no gdk_threads_enter (), CHECKED */
2613                 helper->user_callback (priv->source, helper->user_data);
2614                 /* no gdk_threads_leave (), CHECKED */
2615         }
2616
2617         /* Free */
2618         if (helper->headers)
2619                 g_object_unref (helper->headers);
2620         if (helper->dest_folder)
2621                 g_object_unref (helper->dest_folder);
2622         if (helper->mail_op)
2623                 g_object_unref (helper->mail_op);
2624         if (folder)
2625                 g_object_unref (folder);
2626         if (iter)
2627                 g_object_unref (iter);
2628         g_slice_free   (XFerMsgAsyncHelper, helper);
2629
2630 }
2631
2632 void
2633 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2634                                  TnyList *headers, 
2635                                  TnyFolder *folder, 
2636                                  gboolean delete_original,
2637                                  XferMsgsAsynUserCallback user_callback,
2638                                  gpointer user_data)
2639 {
2640         ModestMailOperationPrivate *priv = NULL;
2641         TnyIterator *iter = NULL;
2642         TnyFolder *src_folder = NULL;
2643         XFerMsgAsyncHelper *helper = NULL;
2644         TnyHeader *header = NULL;
2645         ModestTnyFolderRules rules = 0;
2646         const gchar *id1 = NULL;
2647         const gchar *id2 = NULL;
2648         gboolean same_folder = FALSE;
2649
2650         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2651         g_return_if_fail (TNY_IS_LIST (headers));
2652         g_return_if_fail (TNY_IS_FOLDER (folder));
2653
2654         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2655         priv->total = 1;
2656         priv->done = 0;
2657         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2658
2659         /* Apply folder rules */
2660         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2661         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2662                 /* Set status failed and set an error */
2663                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2664                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2665                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2666                              _CS("ckct_ib_unable_to_paste_here"));
2667                 /* Notify the queue */
2668                 modest_mail_operation_notify_end (self);
2669                 return;
2670         }
2671                 
2672         /* Get source folder */
2673         iter = tny_list_create_iterator (headers);
2674         header = TNY_HEADER (tny_iterator_get_current (iter));
2675         if (header) {
2676                 src_folder = tny_header_get_folder (header);
2677                 g_object_unref (header);
2678         }
2679
2680         g_object_unref (iter);
2681
2682         /* Check folder source and destination */
2683         id1 = tny_folder_get_id (src_folder);
2684         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2685         same_folder = !g_ascii_strcasecmp (id1, id2);
2686         if (same_folder) {
2687                 /* Set status failed and set an error */
2688                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2689                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2690                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2691                              _("mcen_ib_unable_to_copy_samefolder"));
2692                 
2693                 /* Notify the queue */
2694                 modest_mail_operation_notify_end (self);
2695                 
2696                 /* Free */
2697                 g_object_unref (src_folder);            
2698                 return;
2699         }
2700
2701         /* Create the helper */
2702         helper = g_slice_new0 (XFerMsgAsyncHelper);
2703         helper->mail_op = g_object_ref(self);
2704         helper->dest_folder = g_object_ref(folder);
2705         helper->headers = g_object_ref(headers);
2706         helper->user_callback = user_callback;
2707         helper->user_data = user_data;
2708         helper->delete = delete_original;
2709
2710         /* Get account and set it into mail_operation */
2711         priv->account = modest_tny_folder_get_account (src_folder);
2712
2713         /* Transfer messages */
2714         tny_folder_transfer_msgs_async (src_folder, 
2715                                         headers, 
2716                                         folder, 
2717                                         delete_original, 
2718                                         transfer_msgs_cb, 
2719                                         transfer_msgs_status_cb,
2720                                         helper);
2721 }
2722
2723
2724 static void
2725 on_refresh_folder (TnyFolder   *folder, 
2726                    gboolean     cancelled, 
2727                    GError     *error,
2728                    gpointer     user_data)
2729 {
2730         RefreshAsyncHelper *helper = NULL;
2731         ModestMailOperation *self = NULL;
2732         ModestMailOperationPrivate *priv = NULL;
2733
2734         helper = (RefreshAsyncHelper *) user_data;
2735         self = helper->mail_op;
2736         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2737
2738         g_return_if_fail(priv!=NULL);
2739
2740         if (error) {
2741                 priv->error = g_error_copy (error);
2742                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2743                 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2744                        error->message);
2745                 goto out;
2746         }
2747
2748         if (cancelled) {
2749                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2750                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2751                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2752                              _("Error trying to refresh the contents of %s"),
2753                              tny_folder_get_name (folder));
2754                 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2755                 goto out;
2756         }
2757
2758         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2759
2760         /* Call user defined callback, if it exists */
2761         if (helper->user_callback) {
2762
2763                 /* This is not a GDK lock because we are a Tinymail callback and
2764                  * Tinymail already acquires the Gdk lock */
2765                 helper->user_callback (self, folder, helper->user_data);
2766         }
2767
2768  out:
2769         /* Free */
2770         g_slice_free   (RefreshAsyncHelper, helper);
2771
2772         /* Notify about operation end */
2773         modest_mail_operation_notify_end (self);
2774         g_object_unref(self);
2775 }
2776
2777 static void
2778 on_refresh_folder_status_update (GObject *obj,
2779                                  TnyStatus *status,
2780                                  gpointer user_data)
2781 {
2782         RefreshAsyncHelper *helper = NULL;
2783         ModestMailOperation *self = NULL;
2784         ModestMailOperationPrivate *priv = NULL;
2785         ModestMailOperationState *state;
2786
2787         g_return_if_fail (user_data != NULL);
2788         g_return_if_fail (status != NULL);
2789         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2790
2791         helper = (RefreshAsyncHelper *) user_data;
2792         self = helper->mail_op;
2793         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2794
2795         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2796
2797         priv->done = status->position;
2798         priv->total = status->of_total;
2799
2800         state = modest_mail_operation_clone_state (self);
2801
2802         /* This is not a GDK lock because we are a Tinymail callback and
2803          * Tinymail already acquires the Gdk lock */
2804         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2805
2806         g_slice_free (ModestMailOperationState, state);
2807 }
2808
2809 void 
2810 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2811                                        TnyFolder *folder,
2812                                        RefreshAsyncUserCallback user_callback,
2813                                        gpointer user_data)
2814 {
2815         ModestMailOperationPrivate *priv = NULL;
2816         RefreshAsyncHelper *helper = NULL;
2817
2818         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2819
2820         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2821
2822         /* Get account and set it into mail_operation */
2823         priv->account = modest_tny_folder_get_account  (folder);
2824
2825         /* Create the helper */
2826         helper = g_slice_new0 (RefreshAsyncHelper);
2827         helper->mail_op = g_object_ref(self);
2828         helper->user_callback = user_callback;
2829         helper->user_data = user_data;
2830
2831         /* Refresh the folder. TODO: tinymail could issue a status
2832            updates before the callback call then this could happen. We
2833            must review the design */
2834         tny_folder_refresh_async (folder,
2835                                   on_refresh_folder,
2836                                   on_refresh_folder_status_update,
2837                                   helper);
2838 }
2839
2840 /**
2841  *
2842  * It's used by the mail operation queue to notify the observers
2843  * attached to that signal that the operation finished. We need to use
2844  * that because tinymail does not give us the progress of a given
2845  * operation when it finishes (it directly calls the operation
2846  * callback).
2847  */
2848 static void
2849 modest_mail_operation_notify_end (ModestMailOperation *self)
2850 {
2851         ModestMailOperationState *state;
2852         ModestMailOperationPrivate *priv = NULL;
2853
2854         g_return_if_fail (self);
2855
2856         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2857
2858         /* Set the account back to not busy */
2859         if (priv->account_name) {
2860                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2861                                                      priv->account_name, FALSE);
2862                 g_free(priv->account_name);
2863                 priv->account_name = NULL;
2864         }
2865         
2866         /* Notify the observers about the mail operation end */
2867         /* We do not wrapp this emission because we assume that this
2868            function is always called from within the main lock */
2869         state = modest_mail_operation_clone_state (self);
2870         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2871         g_slice_free (ModestMailOperationState, state);
2872 }