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