0d8447edf0525a45079deac56ce693794a23b95d
[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
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                 tny_iterator_next (iter);
1008         }
1009          g_object_unref (G_OBJECT (iter));
1010          g_object_unref (G_OBJECT (folders));
1011 }
1012
1013 /* 
1014  * Issues the "progress-changed" signal. The timer won't be removed,
1015  * so you must call g_source_remove to stop the signal emission
1016  */
1017 static gboolean
1018 idle_notify_progress (gpointer data)
1019 {
1020         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1021         ModestMailOperationState *state;
1022
1023         state = modest_mail_operation_clone_state (mail_op);
1024 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1025         gdk_threads_enter ();
1026 #endif
1027         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1028 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1029         gdk_threads_leave ();
1030 #endif
1031         g_slice_free (ModestMailOperationState, state);
1032         
1033         return TRUE;
1034 }
1035
1036 /* 
1037  * Issues the "progress-changed" signal and removes the timer. It uses
1038  * a lock to ensure that the progress information of the mail
1039  * operation is not modified while there are notifications pending
1040  */
1041 static gboolean
1042 idle_notify_progress_once (gpointer data)
1043 {
1044         ModestPair *pair;
1045
1046         pair = (ModestPair *) data;
1047
1048 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1049         gdk_threads_enter ();
1050 #endif
1051         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1052 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1053         gdk_threads_leave ();
1054 #endif
1055
1056         /* Free the state and the reference to the mail operation */
1057         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1058         g_object_unref (pair->first);
1059
1060         return FALSE;
1061 }
1062
1063 /* 
1064  * Used to notify the queue from the main
1065  * loop. We call it inside an idle call to achieve that
1066  */
1067 static gboolean
1068 idle_notify_queue (gpointer data)
1069 {
1070         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1071
1072         /* Do not need to block, the notify end will do it for us */    
1073         modest_mail_operation_notify_end (mail_op);
1074         g_object_unref (mail_op);
1075
1076         return FALSE;
1077 }
1078
1079 static int
1080 compare_headers_by_date (gconstpointer a, 
1081                          gconstpointer b)
1082 {
1083         TnyHeader **header1, **header2;
1084         time_t sent1, sent2;
1085
1086         header1 = (TnyHeader **) a;
1087         header2 = (TnyHeader **) b;
1088
1089         sent1 = tny_header_get_date_sent (*header1);
1090         sent2 = tny_header_get_date_sent (*header2);
1091
1092         /* We want the most recent ones (greater time_t) at the
1093            beginning */
1094         if (sent1 < sent2)
1095                 return 1;
1096         else
1097                 return -1;
1098 }
1099
1100 static gboolean 
1101 set_last_updated_idle (gpointer data)
1102 {
1103         gdk_threads_enter ();
1104
1105         /* It does not matter if the time is not exactly the same than
1106            the time when this idle was called, it's just an
1107            approximation and it won't be very different */
1108         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
1109                                     (gchar *) data, 
1110                                     MODEST_ACCOUNT_LAST_UPDATED, 
1111                                     time(NULL), 
1112                                     TRUE);
1113
1114         gdk_threads_leave ();
1115
1116         return FALSE;
1117 }
1118
1119 static gboolean
1120 idle_update_account_cb (gpointer data)
1121 {
1122         UpdateAccountInfo *idle_info;
1123
1124         idle_info = (UpdateAccountInfo *) data;
1125
1126         gdk_threads_enter ();
1127         idle_info->callback (idle_info->mail_op,
1128                              idle_info->new_headers,
1129                              idle_info->user_data);
1130         gdk_threads_leave ();
1131
1132         /* Frees */
1133         g_object_unref (idle_info->mail_op);
1134         g_free (idle_info);
1135
1136         return FALSE;
1137 }
1138
1139
1140 static gpointer
1141 update_account_thread (gpointer thr_user_data)
1142 {
1143         static gboolean first_time = TRUE;
1144         UpdateAccountInfo *info = NULL;
1145         TnyList *all_folders = NULL;
1146         GPtrArray *new_headers = NULL;
1147         TnyIterator *iter = NULL;
1148         TnyFolderStoreQuery *query = NULL;
1149         ModestMailOperationPrivate *priv = NULL;
1150         ModestTnySendQueue *send_queue = NULL;
1151         gint num_new_headers = 0;
1152
1153         info = (UpdateAccountInfo *) thr_user_data;
1154         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1155
1156         /* Get account and set it into mail_operation */
1157         priv->account = g_object_ref (info->account);
1158
1159         /*
1160          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
1161          * show any updates unless we do that
1162          */
1163         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account)) 
1164                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1165
1166         /* Get all the folders. We can do it synchronously because
1167            we're already running in a different thread than the UI */
1168         all_folders = tny_simple_list_new ();
1169         query = tny_folder_store_query_new ();
1170         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1171         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1172                                       all_folders,
1173                                       query,
1174                                       &(priv->error));
1175         if (priv->error) {
1176                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1177                 goto out;
1178         }
1179
1180         iter = tny_list_create_iterator (all_folders);
1181         while (!tny_iterator_is_done (iter)) {
1182                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1183
1184                 recurse_folders (folder, query, all_folders);
1185                 tny_iterator_next (iter);
1186         }
1187         g_object_unref (G_OBJECT (iter));
1188
1189         /* Update status and notify. We need to call the notification
1190            with a source function in order to call it from the main
1191            loop. We need that in order not to get into trouble with
1192            Gtk+. We use a timeout in order to provide more status
1193            information, because the sync tinymail call does not
1194            provide it for the moment */
1195         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1196
1197         /* Refresh folders */
1198         num_new_headers = 0;
1199         new_headers = g_ptr_array_new ();
1200         iter = tny_list_create_iterator (all_folders);
1201
1202         while (!tny_iterator_is_done (iter) && !priv->error && 
1203                priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1204
1205                 InternalFolderObserver *observer;
1206                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1207
1208                 /* Refresh the folder */
1209                 /* Our observer receives notification of new emails during folder refreshes,
1210                  * so we can use observer->new_headers.
1211                  */
1212                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1213                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1214                 
1215                 /* This gets the status information (headers) from the server.
1216                  * We use the blocking version, because we are already in a separate 
1217                  * thread.
1218                  */
1219
1220                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
1221                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1222                         TnyIterator *iter;
1223
1224                         /* If the retrieve type is full messages, refresh and get the messages */
1225                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1226
1227                         iter = tny_list_create_iterator (observer->new_headers);
1228                         while (!tny_iterator_is_done (iter)) {
1229                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1230                                  
1231                                 /* Apply per-message size limits */
1232                                 if (tny_header_get_message_size (header) < info->max_size)
1233                                         g_ptr_array_add (new_headers, g_object_ref (header));
1234
1235                                 g_object_unref (header);
1236                                 tny_iterator_next (iter);
1237                         }
1238                         g_object_unref (iter);
1239                 } else {
1240                         /* We do not need to do it the first time
1241                            because it's automatically done by the tree
1242                            model */
1243                         if (G_UNLIKELY (!first_time))
1244                                 tny_folder_poke_status (TNY_FOLDER (folder));
1245                 }
1246                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1247                 g_object_unref (observer);
1248                 observer = NULL;                        
1249
1250                 g_object_unref (G_OBJECT (folder));
1251                 if (priv->error)
1252                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1253
1254                 tny_iterator_next (iter);
1255         }
1256
1257         g_object_unref (G_OBJECT (iter));
1258         g_source_remove (timeout);
1259
1260         if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED && 
1261             priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1262             new_headers->len > 0) {
1263                 gint msg_num = 0;
1264
1265                 /* Order by date */
1266                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1267
1268                 /* Apply message count limit */
1269                 /* If the number of messages exceeds the maximum, ask the
1270                  * user to download them all,
1271                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1272                  */
1273                 if (new_headers->len > info->retrieve_limit) {
1274                         /* TODO: Ask the user, instead of just
1275                          * failing, showing
1276                          * mail_nc_msg_count_limit_exceeded, with 'Get
1277                          * all' and 'Newest only' buttons. */
1278                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1279                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1280                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1281                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1282                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1283                         goto out;
1284                 }
1285                 
1286                 priv->done = 0;
1287                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1288                 while (msg_num < priv->total) {
1289
1290                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1291                         TnyFolder *folder = tny_header_get_folder (header);
1292                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1293                         ModestMailOperationState *state;
1294                         ModestPair* pair;
1295
1296                         priv->done++;
1297                         /* We can not just use the mail operation because the
1298                            values of done and total could change before the
1299                            idle is called */
1300                         state = modest_mail_operation_clone_state (info->mail_op);
1301                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1302                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1303                                          pair, (GDestroyNotify) modest_pair_free);
1304
1305                         g_object_unref (msg);
1306                         g_object_unref (folder);
1307
1308                         msg_num++;
1309                 }
1310         }
1311
1312         /* Get the number of new headers and free them */
1313         num_new_headers = new_headers->len;
1314         g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1315         g_ptr_array_free (new_headers, FALSE);
1316         
1317         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1318                 goto out;
1319
1320         /* Perform send (if operation was not cancelled) */
1321 /*      priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND; */
1322         priv->done = 0;
1323         priv->total = 0;
1324         if (priv->account != NULL) 
1325                 g_object_unref (priv->account);
1326         priv->account = g_object_ref (info->transport_account);
1327         
1328         send_queue = modest_runtime_get_send_queue (info->transport_account);
1329         if (send_queue) {
1330 /*              timeout = g_timeout_add (250, idle_notify_progress, info->mail_op); */
1331                 modest_tny_send_queue_try_to_send (send_queue);
1332 /*              g_source_remove (timeout); */
1333         } else {
1334                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1335                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1336                              "cannot create a send queue for %s\n", 
1337                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1338                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1339         }
1340         
1341         /* Check if the operation was a success */
1342         if (!priv->error) {
1343                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1344
1345                 /* Update the last updated key */
1346                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1347                                  set_last_updated_idle, 
1348                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1349                                  (GDestroyNotify) g_free);
1350         }
1351
1352  out:
1353
1354         if (info->callback) {
1355                 UpdateAccountInfo *idle_info;
1356
1357                 /* This thread is not in the main lock */
1358                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1359                 idle_info->mail_op = g_object_ref (info->mail_op);
1360                 idle_info->new_headers = num_new_headers;
1361                 idle_info->callback = info->callback;
1362                 g_idle_add (idle_update_account_cb, idle_info);
1363         }
1364
1365         /* Notify about operation end. Note that the info could be
1366            freed before this idle happens, but the mail operation will
1367            be still alive */
1368         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1369
1370         /* Frees */
1371         g_object_unref (query);
1372         g_object_unref (all_folders);
1373         g_object_unref (info->account);
1374         g_object_unref (info->transport_account);
1375         g_free (info->retrieve_type);
1376         g_slice_free (UpdateAccountInfo, info);
1377
1378         first_time = FALSE;
1379
1380         return NULL;
1381 }
1382
1383 gboolean
1384 modest_mail_operation_update_account (ModestMailOperation *self,
1385                                       const gchar *account_name,
1386                                       UpdateAccountCallback callback,
1387                                       gpointer user_data)
1388 {
1389         GThread *thread = NULL;
1390         UpdateAccountInfo *info = NULL;
1391         ModestMailOperationPrivate *priv = NULL;
1392         ModestAccountMgr *mgr = NULL;
1393         TnyStoreAccount *store_account = NULL;
1394         TnyTransportAccount *transport_account = NULL;
1395
1396         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1397         g_return_val_if_fail (account_name, FALSE);
1398
1399         /* Init mail operation. Set total and done to 0, and do not
1400            update them, this way the progress objects will know that
1401            we have no clue about the number of the objects */
1402         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1403         priv->total = 0;
1404         priv->done  = 0;
1405         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1406
1407         /* Get the Modest account */
1408         store_account = (TnyStoreAccount *)
1409                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1410                                                                      account_name,
1411                                                                      TNY_ACCOUNT_TYPE_STORE);
1412                                                                      
1413         /* Make sure that we have a connection, and request one 
1414          * if necessary:
1415          * TODO: Is there some way to trigger this for every attempt to 
1416          * use the network? */
1417         if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1418                 goto error;
1419
1420         if (!store_account) {
1421                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1422                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1423                              "cannot get tny store account for %s\n", account_name);
1424                 goto error;
1425         }
1426
1427         
1428         /* Get the transport account, we can not do it in the thread
1429            due to some problems with dbus */
1430         transport_account = (TnyTransportAccount *)
1431                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1432                                                                                     account_name);
1433         if (!transport_account) {
1434                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1435                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1436                              "cannot get tny transport account for %s\n", account_name);
1437                 goto error;
1438         }
1439
1440         /* Create the helper object */
1441         info = g_slice_new (UpdateAccountInfo);
1442         info->mail_op = self;
1443         info->account = store_account;
1444         info->transport_account = transport_account;
1445         info->callback = callback;
1446         info->user_data = user_data;
1447
1448         /* Get the message size limit */
1449         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1450                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1451         if (info->max_size == 0)
1452                 info->max_size = G_MAXINT;
1453         else
1454                 info->max_size = info->max_size * KB;
1455
1456         /* Get per-account retrieval type */
1457         mgr = modest_runtime_get_account_mgr ();
1458         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1459                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1460
1461         /* Get per-account message amount retrieval limit */
1462         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1463                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1464         if (info->retrieve_limit == 0)
1465                 info->retrieve_limit = G_MAXINT;
1466                 
1467         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1468
1469         /* Set account busy */
1470         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1471         priv->account_name = g_strdup(account_name);
1472         
1473         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1474
1475         return TRUE;
1476
1477  error:
1478         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1479         if (callback) 
1480                 callback (self, 0, user_data);
1481         modest_mail_operation_notify_end (self);
1482         return FALSE;
1483 }
1484
1485 /* ******************************************************************* */
1486 /* ************************** STORE  ACTIONS ************************* */
1487 /* ******************************************************************* */
1488
1489
1490 TnyFolder *
1491 modest_mail_operation_create_folder (ModestMailOperation *self,
1492                                      TnyFolderStore *parent,
1493                                      const gchar *name)
1494 {
1495         ModestMailOperationPrivate *priv;
1496         TnyFolder *new_folder = NULL;
1497
1498         TnyList *list = tny_simple_list_new ();
1499         TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1500
1501         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1502         g_return_val_if_fail (name, NULL);
1503         
1504         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1505
1506         /* Check for already existing folder */
1507         tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1508         tny_folder_store_get_folders (parent, list, query, NULL);
1509         g_object_unref (G_OBJECT (query));
1510
1511         if (tny_list_get_length (list) > 0) {
1512                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1513                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1514                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1515                              _CS("ckdg_ib_folder_already_exists"));
1516         }
1517
1518         g_object_unref (G_OBJECT (list));
1519
1520         /* Check parent */
1521         if (TNY_IS_FOLDER (parent)) {
1522                 /* Check folder rules */
1523                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1524                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1525                         /* Set status failed and set an error */
1526                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1527                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1528                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1529                                      _("mail_in_ui_folder_create_error"));
1530                 }
1531         }
1532
1533         if (!strcmp (name, " ") || strchr (name, '/')) {
1534                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1535                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1536                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1537                              _("mail_in_ui_folder_create_error"));
1538         }
1539
1540         if (!priv->error) {
1541                 /* Create the folder */
1542                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1543                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1544                 if (!priv->error)
1545                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1546         }
1547
1548         /* Notify about operation end */
1549         modest_mail_operation_notify_end (self);
1550
1551         return new_folder;
1552 }
1553
1554 void
1555 modest_mail_operation_remove_folder (ModestMailOperation *self,
1556                                      TnyFolder           *folder,
1557                                      gboolean             remove_to_trash)
1558 {
1559         TnyAccount *account;
1560         ModestMailOperationPrivate *priv;
1561         ModestTnyFolderRules rules;
1562
1563         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1564         g_return_if_fail (TNY_IS_FOLDER (folder));
1565         
1566         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1567         
1568         /* Check folder rules */
1569         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1570         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1571                 /* Set status failed and set an error */
1572                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1573                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1574                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1575                              _("mail_in_ui_folder_delete_error"));
1576                 goto end;
1577         }
1578
1579         /* Get the account */
1580         account = modest_tny_folder_get_account (folder);
1581         priv->account = g_object_ref(account);
1582
1583         /* Delete folder or move to trash */
1584         if (remove_to_trash) {
1585                 TnyFolder *trash_folder = NULL;
1586                 trash_folder = modest_tny_account_get_special_folder (account,
1587                                                                       TNY_FOLDER_TYPE_TRASH);
1588                 /* TODO: error_handling */
1589                 if (trash_folder) {
1590                         modest_mail_operation_xfer_folder (self, folder,
1591                                                     TNY_FOLDER_STORE (trash_folder), 
1592                                                     TRUE, NULL, NULL);
1593                         g_object_unref (trash_folder);
1594                 }
1595         } else {
1596                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1597
1598                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1599                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1600
1601                 if (parent)
1602                         g_object_unref (G_OBJECT (parent));
1603         }
1604         g_object_unref (G_OBJECT (account));
1605
1606  end:
1607         /* Notify about operation end */
1608         modest_mail_operation_notify_end (self);
1609 }
1610
1611 static void
1612 transfer_folder_status_cb (GObject *obj,
1613                            TnyStatus *status,
1614                            gpointer user_data)
1615 {
1616         ModestMailOperation *self;
1617         ModestMailOperationPrivate *priv;
1618         ModestMailOperationState *state;
1619         XFerMsgAsyncHelper *helper;
1620
1621         g_return_if_fail (status != NULL);
1622         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1623
1624         helper = (XFerMsgAsyncHelper *) user_data;
1625         g_return_if_fail (helper != NULL);
1626
1627         self = helper->mail_op;
1628         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1629
1630         priv->done = status->position;
1631         priv->total = status->of_total;
1632
1633         state = modest_mail_operation_clone_state (self);
1634 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1635         gdk_threads_enter ();
1636 #endif
1637         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1638 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1639         gdk_threads_leave ();
1640 #endif
1641         g_slice_free (ModestMailOperationState, state);
1642 }
1643
1644
1645 static void
1646 transfer_folder_cb (TnyFolder *folder, 
1647                     TnyFolderStore *into, 
1648                     gboolean cancelled, 
1649                     TnyFolder *new_folder, 
1650                     GError **err, 
1651                     gpointer user_data)
1652 {
1653         XFerMsgAsyncHelper *helper;
1654         ModestMailOperation *self = NULL;
1655         ModestMailOperationPrivate *priv = NULL;
1656
1657         helper = (XFerMsgAsyncHelper *) user_data;
1658         g_return_if_fail (helper != NULL);       
1659
1660         self = helper->mail_op;
1661         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1662
1663         if (*err) {
1664                 priv->error = g_error_copy (*err);
1665                 priv->done = 0;
1666                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1667         } else if (cancelled) {
1668                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1669                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1670                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1671                              _("Transference of %s was cancelled."),
1672                              tny_folder_get_name (folder));
1673         } else {
1674                 priv->done = 1;
1675                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1676         }
1677                 
1678         /* Notify about operation end */
1679         modest_mail_operation_notify_end (self);
1680
1681         /* If user defined callback function was defined, call it */
1682         if (helper->user_callback) {
1683                 gdk_threads_enter ();
1684                 helper->user_callback (priv->source, helper->user_data);
1685                 gdk_threads_leave ();
1686         }
1687
1688         /* Free */
1689         g_object_unref (helper->mail_op);
1690         g_slice_free   (XFerMsgAsyncHelper, helper);
1691 }
1692
1693 /**
1694  *
1695  * This function checks if the new name is a valid name for our local
1696  * folders account. The new name could not be the same than then name
1697  * of any of the mandatory local folders
1698  *
1699  * We can not rely on tinymail because tinymail does not check the
1700  * name of the virtual folders that the account could have in the case
1701  * that we're doing a rename (because it directly calls Camel which
1702  * knows nothing about our virtual folders). 
1703  *
1704  * In the case of an actual copy/move (i.e. move/copy a folder between
1705  * accounts) tinymail uses the tny_folder_store_create_account which
1706  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1707  * checks the new name of the folder, so this call in that case
1708  * wouldn't be needed. *But* NOTE that if tinymail changes its
1709  * implementation (if folder transfers within the same account is no
1710  * longer implemented as a rename) this call will allow Modest to work
1711  * perfectly
1712  *
1713  * If the new name is not valid, this function will set the status to
1714  * failed and will set also an error in the mail operation
1715  */
1716 static gboolean
1717 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1718                                  TnyFolderStore *into,
1719                                  const gchar *new_name)
1720 {
1721         if (TNY_IS_ACCOUNT (into) && 
1722             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1723             modest_tny_local_folders_account_extra_folder_exists (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1724                                                                   new_name)) {
1725                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1726                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1727                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1728                              _("FIXME: folder name already in use"));
1729                 return FALSE;
1730         } else
1731                 return TRUE;
1732 }
1733
1734 void
1735 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1736                                    TnyFolder *folder,
1737                                    TnyFolderStore *parent,
1738                                    gboolean delete_original,
1739                                    XferMsgsAsynUserCallback user_callback,
1740                                    gpointer user_data)
1741 {
1742         ModestMailOperationPrivate *priv = NULL;
1743         ModestTnyFolderRules parent_rules = 0, rules; 
1744         XFerMsgAsyncHelper *helper = NULL;
1745
1746         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1747         g_return_if_fail (TNY_IS_FOLDER (folder));
1748
1749         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1750
1751         /* Get account and set it into mail_operation */
1752         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1753         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1754
1755         /* Get folder rules */
1756         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1757         if (TNY_IS_FOLDER (parent))
1758                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1759         
1760         /* The moveable restriction is applied also to copy operation */
1761         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1762                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1763                 /* Set status failed and set an error */
1764                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1765                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1766                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1767                              _("mail_in_ui_folder_move_target_error"));
1768
1769                 /* Notify the queue */
1770                 modest_mail_operation_notify_end (self);
1771         } else if (TNY_IS_FOLDER (parent) && 
1772                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1773                 /* Set status failed and set an error */
1774                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1775                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1776                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1777                              _("FIXME: parent folder does not accept new folders"));
1778
1779                 /* Notify the queue */
1780                 modest_mail_operation_notify_end (self);
1781         } else {
1782
1783
1784                 /* Check that the new folder name is not used by any
1785                    special local folder */
1786                 if (new_name_valid_if_local_account (priv, parent, 
1787                                                      tny_folder_get_name (folder))) {
1788                         /* Create the helper */
1789                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1790                         helper->mail_op = g_object_ref(self);
1791                         helper->dest_folder = NULL;
1792                         helper->headers = NULL;
1793                         helper->user_callback = user_callback;
1794                         helper->user_data = user_data;
1795                         
1796                         /* Move/Copy folder */          
1797                         tny_folder_copy_async (folder,
1798                                                parent,
1799                                                tny_folder_get_name (folder),
1800                                                delete_original,
1801                                                transfer_folder_cb,
1802                                                transfer_folder_status_cb,
1803                                                helper);
1804                 } else {
1805                         modest_mail_operation_notify_end (self);
1806                 }
1807         }
1808 }
1809
1810 void
1811 modest_mail_operation_rename_folder (ModestMailOperation *self,
1812                                      TnyFolder *folder,
1813                                      const gchar *name)
1814 {
1815         ModestMailOperationPrivate *priv;
1816         ModestTnyFolderRules rules;
1817         XFerMsgAsyncHelper *helper;
1818
1819         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1820         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1821         g_return_if_fail (name);
1822         
1823         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1824
1825         /* Get account and set it into mail_operation */
1826         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1827
1828         /* Check folder rules */
1829         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1830         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1831                 /* Set status failed and set an error */
1832                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1833                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1834                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1835                              _("FIXME: unable to rename"));
1836
1837                 /* Notify about operation end */
1838                 modest_mail_operation_notify_end (self);
1839         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1840                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1841                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1842                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1843                              _("FIXME: unable to rename"));
1844                 /* Notify about operation end */
1845                 modest_mail_operation_notify_end (self);
1846         } else {
1847                 TnyFolderStore *into;
1848
1849                 into = tny_folder_get_folder_store (folder);    
1850
1851                 /* Check that the new folder name is not used by any
1852                    special local folder */
1853                 if (new_name_valid_if_local_account (priv, into, name)) {
1854                         /* Create the helper */
1855                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1856                         helper->mail_op = g_object_ref(self);
1857                         helper->dest_folder = NULL;
1858                         helper->headers = NULL;
1859                         helper->user_callback = NULL;
1860                         helper->user_data = NULL;
1861                 
1862                         /* Rename. Camel handles folder subscription/unsubscription */
1863                         tny_folder_copy_async (folder, into, name, TRUE,
1864                                                transfer_folder_cb,
1865                                                transfer_folder_status_cb,
1866                                                helper);
1867                 } else {
1868                         modest_mail_operation_notify_end (self);
1869                 }
1870                 g_object_unref (into);
1871         }
1872 }
1873
1874 /* ******************************************************************* */
1875 /* **************************  MSG  ACTIONS  ************************* */
1876 /* ******************************************************************* */
1877
1878 void modest_mail_operation_get_msg (ModestMailOperation *self,
1879                                     TnyHeader *header,
1880                                     GetMsgAsyncUserCallback user_callback,
1881                                     gpointer user_data)
1882 {
1883         GetMsgAsyncHelper *helper = NULL;
1884         TnyFolder *folder;
1885         ModestMailOperationPrivate *priv;
1886         
1887         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1888         g_return_if_fail (TNY_IS_HEADER (header));
1889         
1890         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1891         folder = tny_header_get_folder (header);
1892
1893         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1894
1895         /* Get message from folder */
1896         if (folder) {
1897                 /* Get account and set it into mail_operation */
1898                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1899
1900                 helper = g_slice_new0 (GetMsgAsyncHelper);
1901                 helper->mail_op = self;
1902                 helper->user_callback = user_callback;
1903                 helper->user_data = user_data;
1904                 helper->header = g_object_ref (header);
1905
1906                 // The callback's reference so that the mail op is not
1907                 // finalized until the async operation is completed even if
1908                 // the user canceled the request meanwhile.
1909                 g_object_ref (G_OBJECT (helper->mail_op));
1910
1911                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1912
1913                 g_object_unref (G_OBJECT (folder));
1914         } else {
1915                 /* Set status failed and set an error */
1916                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1917                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1918                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1919                              _("Error trying to get a message. No folder found for header"));
1920
1921                 /* Notify the queue */
1922                 modest_mail_operation_notify_end (self);
1923         }
1924 }
1925
1926 static gboolean
1927 idle_get_mime_part_size_cb (gpointer userdata)
1928 {
1929         GetMimePartSizeInfo *idle_info;
1930
1931         idle_info = (GetMimePartSizeInfo *) userdata;
1932
1933         gdk_threads_enter ();
1934         idle_info->callback (idle_info->mail_op,
1935                              idle_info->size,
1936                              idle_info->userdata);
1937         gdk_threads_leave ();
1938
1939         g_object_unref (idle_info->mail_op);
1940         g_slice_free (GetMimePartSizeInfo, idle_info);
1941
1942         return FALSE;
1943 }
1944
1945 static gpointer
1946 get_mime_part_size_thread (gpointer thr_user_data)
1947 {
1948         GetMimePartSizeInfo *info;
1949         gchar read_buffer[GET_SIZE_BUFFER_SIZE];
1950         TnyStream *stream;
1951         gssize readed_size;
1952         gssize total = 0;
1953         ModestMailOperationPrivate *priv;
1954
1955         info = (GetMimePartSizeInfo *) thr_user_data;
1956         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1957
1958         stream = tny_camel_mem_stream_new ();
1959         tny_mime_part_decode_to_stream (info->mime_part, stream);
1960         tny_stream_reset (stream);
1961         if (tny_stream_is_eos (stream)) {
1962                 tny_stream_close (stream);
1963                 stream = tny_mime_part_get_stream (info->mime_part);
1964         }
1965         
1966         while (!tny_stream_is_eos (stream)) {
1967                 readed_size = tny_stream_read (stream, read_buffer, GET_SIZE_BUFFER_SIZE);
1968                 total += readed_size;
1969         }
1970
1971         if (info->callback) {
1972                 GetMimePartSizeInfo *idle_info;
1973
1974                 idle_info = g_slice_new0 (GetMimePartSizeInfo);
1975                 idle_info->mail_op = g_object_ref (info->mail_op);
1976                 idle_info->size = total;
1977                 idle_info->callback = info->callback;
1978                 idle_info->userdata = info->userdata;
1979                 g_idle_add (idle_get_mime_part_size_cb, idle_info);
1980         }
1981
1982         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1983
1984         g_object_unref (info->mail_op);
1985         g_object_unref (stream);
1986         g_object_unref (info->mime_part);
1987         g_slice_free  (GetMimePartSizeInfo, info);
1988
1989         return NULL;
1990 }
1991
1992 void          
1993 modest_mail_operation_get_mime_part_size (ModestMailOperation *self,
1994                                           TnyMimePart *part,
1995                                           GetMimePartSizeCallback user_callback,
1996                                           gpointer user_data,
1997                                           GDestroyNotify notify)
1998 {
1999         GetMimePartSizeInfo *info;
2000         ModestMailOperationPrivate *priv;
2001         GThread *thread;
2002         
2003         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2004         g_return_if_fail (TNY_IS_MIME_PART (part));
2005         
2006         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2007
2008         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2009         info = g_slice_new0 (GetMimePartSizeInfo);
2010         info->mail_op = g_object_ref (self);
2011         info->mime_part = g_object_ref (part);
2012         info->callback = user_callback;
2013         info->userdata = user_data;
2014
2015         thread = g_thread_create (get_mime_part_size_thread, info, FALSE, NULL);
2016
2017 }
2018
2019 static void
2020 get_msg_cb (TnyFolder *folder, 
2021             gboolean cancelled, 
2022             TnyMsg *msg, 
2023             GError **error, 
2024             gpointer user_data)
2025 {
2026         GetMsgAsyncHelper *helper = NULL;
2027         ModestMailOperation *self = NULL;
2028         ModestMailOperationPrivate *priv = NULL;
2029
2030         helper = (GetMsgAsyncHelper *) user_data;
2031         g_return_if_fail (helper != NULL);       
2032         self = helper->mail_op;
2033         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2034         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2035
2036         /* Check errors and cancel */
2037         if (*error) {
2038                 priv->error = g_error_copy (*error);
2039                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2040         } else if (cancelled) {
2041                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2042                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2043                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2044                              _("Error trying to refresh the contents of %s"),
2045                              tny_folder_get_name (folder));
2046         } else {
2047                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2048         }
2049
2050         /* If user defined callback function was defined, call it even
2051            if the operation failed*/
2052         if (helper->user_callback) {
2053                 /* This callback is called into an iddle by tinymail,
2054                    and idles are not in the main lock */
2055                 gdk_threads_enter ();
2056                 helper->user_callback (self, helper->header, msg, helper->user_data);
2057                 gdk_threads_leave ();   
2058         }
2059
2060         /* Notify about operation end */
2061         modest_mail_operation_notify_end (self);
2062         /* Free */
2063         g_object_unref (helper->mail_op);
2064         g_object_unref (helper->header);
2065         g_slice_free (GetMsgAsyncHelper, helper);
2066                 
2067 }
2068
2069 static void     
2070 get_msg_status_cb (GObject *obj,
2071                    TnyStatus *status,  
2072                    gpointer user_data)
2073 {
2074         GetMsgAsyncHelper *helper = NULL;
2075         ModestMailOperation *self;
2076         ModestMailOperationPrivate *priv;
2077         ModestMailOperationState *state;
2078
2079         g_return_if_fail (status != NULL);
2080         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2081
2082         helper = (GetMsgAsyncHelper *) user_data;
2083         g_return_if_fail (helper != NULL);       
2084
2085         self = helper->mail_op;
2086         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2087
2088         priv->done = 1;
2089         priv->total = 1;
2090
2091         state = modest_mail_operation_clone_state (self);
2092         state->bytes_done = status->position;
2093         state->bytes_total = status->of_total;
2094 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2095         gdk_threads_enter ();
2096 #endif
2097         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2098 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2099         gdk_threads_leave ();
2100 #endif
2101         g_slice_free (ModestMailOperationState, state);
2102 }
2103
2104 /****************************************************/
2105 typedef struct {
2106         ModestMailOperation *mail_op;
2107         TnyList *headers;
2108         GetMsgAsyncUserCallback user_callback;
2109         gpointer user_data;
2110         GDestroyNotify notify;
2111 } GetFullMsgsInfo;
2112
2113 typedef struct {
2114         GetMsgAsyncUserCallback user_callback;
2115         TnyHeader *header;
2116         TnyMsg *msg;
2117         gpointer user_data;
2118         ModestMailOperation *mail_op;
2119 } NotifyGetMsgsInfo;
2120
2121
2122 /* 
2123  * Used by get_msgs_full_thread to call the user_callback for each
2124  * message that has been read
2125  */
2126 static gboolean
2127 notify_get_msgs_full (gpointer data)
2128 {
2129         NotifyGetMsgsInfo *info;
2130
2131         info = (NotifyGetMsgsInfo *) data;      
2132
2133         /* Call the user callback. Idles are not in the main lock, so
2134            lock it */
2135         gdk_threads_enter ();
2136         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2137         gdk_threads_leave ();
2138
2139         g_slice_free (NotifyGetMsgsInfo, info);
2140
2141         return FALSE;
2142 }
2143
2144 /* 
2145  * Used by get_msgs_full_thread to free al the thread resources and to
2146  * call the destroy function for the passed user_data
2147  */
2148 static gboolean
2149 get_msgs_full_destroyer (gpointer data)
2150 {
2151         GetFullMsgsInfo *info;
2152
2153         info = (GetFullMsgsInfo *) data;
2154
2155         if (info->notify) {
2156                 gdk_threads_enter ();   
2157                 info->notify (info->user_data);
2158                 gdk_threads_leave ();
2159         }
2160
2161         /* free */
2162         g_object_unref (info->headers);
2163         g_slice_free (GetFullMsgsInfo, info);
2164
2165         return FALSE;
2166 }
2167
2168 static gpointer
2169 get_msgs_full_thread (gpointer thr_user_data)
2170 {
2171         GetFullMsgsInfo *info;
2172         ModestMailOperationPrivate *priv = NULL;
2173         TnyIterator *iter = NULL;
2174         
2175         info = (GetFullMsgsInfo *) thr_user_data;       
2176         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2177
2178         iter = tny_list_create_iterator (info->headers);
2179         while (!tny_iterator_is_done (iter)) { 
2180                 TnyHeader *header;
2181                 TnyFolder *folder;
2182                 
2183                 header = TNY_HEADER (tny_iterator_get_current (iter));
2184                 folder = tny_header_get_folder (header);
2185                                 
2186                 /* Get message from folder */
2187                 if (folder) {
2188                         TnyMsg *msg;
2189                         /* The callback will call it per each header */
2190                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2191
2192                         if (msg) {
2193                                 ModestMailOperationState *state;
2194                                 ModestPair *pair;
2195
2196                                 priv->done++;
2197
2198                                 /* notify progress */
2199                                 state = modest_mail_operation_clone_state (info->mail_op);
2200                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2201                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2202                                                  pair, (GDestroyNotify) modest_pair_free);
2203
2204                                 /* The callback is the responsible for
2205                                    freeing the message */
2206                                 if (info->user_callback) {
2207                                         NotifyGetMsgsInfo *info_notify;
2208                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2209                                         info_notify->user_callback = info->user_callback;
2210                                         info_notify->mail_op = info->mail_op;
2211                                         info_notify->header = g_object_ref (header);
2212                                         info_notify->msg = g_object_ref (msg);
2213                                         info_notify->user_data = info->user_data;
2214                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2215                                                          notify_get_msgs_full, 
2216                                                          info_notify, NULL);
2217                                 }
2218                                 g_object_unref (msg);
2219                         } 
2220                 } else {
2221                         /* Set status failed and set an error */
2222                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2223                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2224                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2225                                      "Error trying to get a message. No folder found for header");
2226                 }
2227                 g_object_unref (header);                
2228                 tny_iterator_next (iter);
2229         }
2230
2231         /* Set operation status */
2232         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2233                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2234
2235         /* Notify about operation end */
2236         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2237
2238         /* Free thread resources. Will be called after all previous idles */
2239         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2240
2241         return NULL;
2242 }
2243
2244 void 
2245 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2246                                      TnyList *header_list, 
2247                                      GetMsgAsyncUserCallback user_callback,
2248                                      gpointer user_data,
2249                                      GDestroyNotify notify)
2250 {
2251         TnyHeader *header = NULL;
2252         TnyFolder *folder = NULL;
2253         GThread *thread;
2254         ModestMailOperationPrivate *priv = NULL;
2255         GetFullMsgsInfo *info = NULL;
2256         gboolean size_ok = TRUE;
2257         gint max_size;
2258         TnyIterator *iter = NULL;
2259         
2260         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2261         
2262         /* Init mail operation */
2263         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2264         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2265         priv->done = 0;
2266         priv->total = tny_list_get_length(header_list);
2267
2268         /* Get account and set it into mail_operation */
2269         if (tny_list_get_length (header_list) >= 1) {
2270                 iter = tny_list_create_iterator (header_list);
2271                 header = TNY_HEADER (tny_iterator_get_current (iter));
2272                 folder = tny_header_get_folder (header);                
2273                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2274                 g_object_unref (header);
2275                 g_object_unref (folder);
2276
2277                 if (tny_list_get_length (header_list) == 1) {
2278                         g_object_unref (iter);
2279                         iter = NULL;
2280                 }
2281         }
2282
2283         /* Get msg size limit */
2284         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2285                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2286                                          &(priv->error));
2287         if (priv->error) {
2288                 g_clear_error (&(priv->error));
2289                 max_size = G_MAXINT;
2290         } else {
2291                 max_size = max_size * KB;
2292         }
2293
2294         /* Check message size limits. If there is only one message
2295            always retrieve it */
2296         if (iter != NULL) {
2297                 while (!tny_iterator_is_done (iter) && size_ok) {
2298                         header = TNY_HEADER (tny_iterator_get_current (iter));
2299                         if (tny_header_get_message_size (header) >= max_size)
2300                                 size_ok = FALSE;
2301                         g_object_unref (header);
2302                         tny_iterator_next (iter);
2303                 }
2304                 g_object_unref (iter);
2305         }
2306
2307         if (size_ok) {
2308                 /* Create the info */
2309                 info = g_slice_new0 (GetFullMsgsInfo);
2310                 info->mail_op = self;
2311                 info->user_callback = user_callback;
2312                 info->user_data = user_data;
2313                 info->headers = g_object_ref (header_list);
2314                 info->notify = notify;
2315
2316                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2317         } else {
2318                 /* Set status failed and set an error */
2319                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2320                 /* FIXME: the error msg is different for pop */
2321                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2322                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2323                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2324                 /* Remove from queue and free resources */
2325                 modest_mail_operation_notify_end (self);
2326                 if (notify)
2327                         notify (user_data);
2328         }
2329 }
2330
2331
2332 void 
2333 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
2334                                   gboolean remove_to_trash /*ignored*/)
2335 {
2336         TnyFolder *folder;
2337         ModestMailOperationPrivate *priv;
2338
2339         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2340         g_return_if_fail (TNY_IS_HEADER (header));
2341
2342         if (remove_to_trash)
2343                 g_warning ("remove to trash is not implemented");
2344
2345         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2346         folder = tny_header_get_folder (header);
2347
2348         /* Get account and set it into mail_operation */
2349         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2350
2351         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2352
2353
2354         tny_folder_remove_msg (folder, header, &(priv->error));
2355         if (!priv->error) {
2356                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2357                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2358
2359                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2360                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2361                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2362                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2363                 else
2364                         /* lcoal folders */
2365                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2366         }
2367         
2368         
2369         /* Set status */
2370         if (!priv->error)
2371                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2372         else
2373                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2374
2375         /* Free */
2376         g_object_unref (G_OBJECT (folder));
2377
2378         /* Notify about operation end */
2379         modest_mail_operation_notify_end (self);
2380 }
2381
2382 static void
2383 transfer_msgs_status_cb (GObject *obj,
2384                          TnyStatus *status,  
2385                          gpointer user_data)
2386 {
2387         XFerMsgAsyncHelper *helper = NULL;
2388         ModestMailOperation *self;
2389         ModestMailOperationPrivate *priv;
2390         ModestMailOperationState *state;
2391
2392
2393         g_return_if_fail (status != NULL);
2394         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2395
2396         helper = (XFerMsgAsyncHelper *) user_data;
2397         g_return_if_fail (helper != NULL);       
2398
2399         self = helper->mail_op;
2400         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2401
2402         priv->done = status->position;
2403         priv->total = status->of_total;
2404
2405         state = modest_mail_operation_clone_state (self);
2406 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2407         gdk_threads_enter ();
2408 #endif
2409         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2410 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2411         gdk_threads_leave ();
2412 #endif
2413         g_slice_free (ModestMailOperationState, state);
2414 }
2415
2416
2417 static void
2418 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2419 {
2420         XFerMsgAsyncHelper *helper;
2421         ModestMailOperation *self;
2422         ModestMailOperationPrivate *priv;
2423
2424         helper = (XFerMsgAsyncHelper *) user_data;
2425         self = helper->mail_op;
2426
2427         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2428
2429         if (*err) {
2430                 priv->error = g_error_copy (*err);
2431                 priv->done = 0;
2432                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2433         } else if (cancelled) {
2434                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2435                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2436                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2437                              _("Error trying to refresh the contents of %s"),
2438                              tny_folder_get_name (folder));
2439         } else {
2440                 priv->done = 1;
2441                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2442         }
2443
2444         /* Notify about operation end */
2445         modest_mail_operation_notify_end (self);
2446
2447         /* If user defined callback function was defined, call it */
2448         if (helper->user_callback) {
2449                 gdk_threads_enter ();
2450                 helper->user_callback (priv->source, helper->user_data);
2451                 gdk_threads_leave ();
2452         }
2453
2454         /* Free */
2455         g_object_unref (helper->headers);
2456         g_object_unref (helper->dest_folder);
2457         g_object_unref (helper->mail_op);
2458         g_slice_free   (XFerMsgAsyncHelper, helper);
2459         g_object_unref (folder);
2460
2461 }
2462
2463 void
2464 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2465                                  TnyList *headers, 
2466                                  TnyFolder *folder, 
2467                                  gboolean delete_original,
2468                                  XferMsgsAsynUserCallback user_callback,
2469                                  gpointer user_data)
2470 {
2471         ModestMailOperationPrivate *priv;
2472         TnyIterator *iter;
2473         TnyFolder *src_folder;
2474         XFerMsgAsyncHelper *helper;
2475         TnyHeader *header;
2476         ModestTnyFolderRules rules;
2477         const gchar *id1 = NULL;
2478         const gchar *id2 = NULL;
2479         gboolean same_folder = FALSE;
2480
2481         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2482         g_return_if_fail (TNY_IS_LIST (headers));
2483         g_return_if_fail (TNY_IS_FOLDER (folder));
2484
2485         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2486         priv->total = 1;
2487         priv->done = 0;
2488         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2489
2490         /* Apply folder rules */
2491         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2492         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2493                 /* Set status failed and set an error */
2494                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2495                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2496                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2497                              _CS("ckct_ib_unable_to_paste_here"));
2498                 /* Notify the queue */
2499                 modest_mail_operation_notify_end (self);
2500                 return;
2501         }
2502                 
2503         /* Get source folder */
2504         iter = tny_list_create_iterator (headers);
2505         header = TNY_HEADER (tny_iterator_get_current (iter));
2506         src_folder = tny_header_get_folder (header);
2507         g_object_unref (header);
2508         g_object_unref (iter);
2509
2510         /* Check folder source and destination */
2511         id1 = tny_folder_get_id (src_folder);
2512         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2513         same_folder = !g_ascii_strcasecmp (id1, id2);
2514         if (same_folder) {
2515                 /* Set status failed and set an error */
2516                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2517                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2518                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2519                              _("mcen_ib_unable_to_copy_samefolder"));
2520                 
2521                 /* Notify the queue */
2522                 modest_mail_operation_notify_end (self);
2523                 
2524                 /* Free */
2525                 g_object_unref (src_folder);            
2526                 return;
2527         }
2528
2529         /* Create the helper */
2530         helper = g_slice_new0 (XFerMsgAsyncHelper);
2531         helper->mail_op = g_object_ref(self);
2532         helper->dest_folder = g_object_ref(folder);
2533         helper->headers = g_object_ref(headers);
2534         helper->user_callback = user_callback;
2535         helper->user_data = user_data;
2536
2537         /* Get account and set it into mail_operation */
2538         priv->account = modest_tny_folder_get_account (src_folder);
2539
2540         /* Transfer messages */
2541         tny_folder_transfer_msgs_async (src_folder, 
2542                                         headers, 
2543                                         folder, 
2544                                         delete_original, 
2545                                         transfer_msgs_cb, 
2546                                         transfer_msgs_status_cb,
2547                                         helper);
2548 }
2549
2550
2551 static void
2552 on_refresh_folder (TnyFolder   *folder, 
2553                    gboolean     cancelled, 
2554                    GError     **error,
2555                    gpointer     user_data)
2556 {
2557         RefreshAsyncHelper *helper = NULL;
2558         ModestMailOperation *self = NULL;
2559         ModestMailOperationPrivate *priv = NULL;
2560
2561         helper = (RefreshAsyncHelper *) user_data;
2562         self = helper->mail_op;
2563         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2564
2565         if (*error) {
2566                 priv->error = g_error_copy (*error);
2567                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2568                 goto out;
2569         }
2570
2571         if (cancelled) {
2572                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2573                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2574                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2575                              _("Error trying to refresh the contents of %s"),
2576                              tny_folder_get_name (folder));
2577                 goto out;
2578         }
2579
2580         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2581  out:
2582         /* Call user defined callback, if it exists */
2583         if (helper->user_callback) {
2584                 gdk_threads_enter ();
2585                 helper->user_callback (self, folder, helper->user_data);
2586                 gdk_threads_leave ();
2587         }
2588
2589         /* Free */
2590 /*      g_object_unref (helper->mail_op); */
2591         g_slice_free   (RefreshAsyncHelper, helper);
2592
2593         /* Notify about operation end */
2594         modest_mail_operation_notify_end (self);
2595 }
2596
2597 static void
2598 on_refresh_folder_status_update (GObject *obj,
2599                                  TnyStatus *status,
2600                                  gpointer user_data)
2601 {
2602         RefreshAsyncHelper *helper = NULL;
2603         ModestMailOperation *self = NULL;
2604         ModestMailOperationPrivate *priv = NULL;
2605         ModestMailOperationState *state;
2606
2607         g_return_if_fail (user_data != NULL);
2608         g_return_if_fail (status != NULL);
2609         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2610
2611         helper = (RefreshAsyncHelper *) user_data;
2612         self = helper->mail_op;
2613         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2614
2615         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2616
2617         priv->done = status->position;
2618         priv->total = status->of_total;
2619
2620         state = modest_mail_operation_clone_state (self);
2621 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2622         gdk_threads_enter ();
2623 #endif
2624         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2625 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2626         gdk_threads_leave ();
2627 #endif
2628         g_slice_free (ModestMailOperationState, state);
2629 }
2630
2631 void 
2632 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2633                                        TnyFolder *folder,
2634                                        RefreshAsyncUserCallback user_callback,
2635                                        gpointer user_data)
2636 {
2637         ModestMailOperationPrivate *priv = NULL;
2638         RefreshAsyncHelper *helper = NULL;
2639
2640         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2641
2642         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2643
2644         /* Get account and set it into mail_operation */
2645         priv->account = modest_tny_folder_get_account  (folder);
2646
2647         /* Create the helper */
2648         helper = g_slice_new0 (RefreshAsyncHelper);
2649         helper->mail_op = g_object_ref (self);
2650         helper->user_callback = user_callback;
2651         helper->user_data = user_data;
2652
2653         /* Refresh the folder. TODO: tinymail could issue a status
2654            updates before the callback call then this could happen. We
2655            must review the design */
2656         tny_folder_refresh_async (folder,
2657                                   on_refresh_folder,
2658                                   on_refresh_folder_status_update,
2659                                   helper);
2660 }
2661
2662 /**
2663  *
2664  * It's used by the mail operation queue to notify the observers
2665  * attached to that signal that the operation finished. We need to use
2666  * that because tinymail does not give us the progress of a given
2667  * operation when it finishes (it directly calls the operation
2668  * callback).
2669  */
2670 static void
2671 modest_mail_operation_notify_end (ModestMailOperation *self)
2672 {
2673         ModestMailOperationState *state;
2674         ModestMailOperationPrivate *priv = NULL;
2675
2676         g_return_if_fail (self);
2677
2678         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2679
2680         /* Set the account back to not busy */
2681         if (priv->account_name) {
2682                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2683                                                      priv->account_name, FALSE);
2684                 g_free(priv->account_name);
2685                 priv->account_name = NULL;
2686         }
2687         
2688         /* Notify the observers about the mail operation end */
2689         /* We do not wrapp this emission because we assume that this
2690            function is always called from within the main lock */
2691         state = modest_mail_operation_clone_state (self);
2692         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2693         g_slice_free (ModestMailOperationState, state);
2694 }