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