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