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