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