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