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