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