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