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