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