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