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