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