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