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