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