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