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