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