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