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