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