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