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