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