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