d9e31bcf271281d2662fbefd75164bc58c3f4903
[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 typedef struct {
1643         ModestMailOperation *mail_op;
1644         CreateFolderUserCallback callback;
1645         gpointer user_data;
1646 } CreateFolderInfo;
1647
1648
1649 static void
1650 create_folder_cb (TnyFolderStore *parent_folder, 
1651                   gboolean canceled, 
1652                   TnyFolder *new_folder, 
1653                   GError *err, 
1654                   gpointer user_data)
1655 {
1656         ModestMailOperationPrivate *priv;
1657         CreateFolderInfo *info;
1658
1659         info = (CreateFolderInfo *) user_data;
1660         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1661
1662         if (canceled || err) {
1663                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1664                 if (err)
1665                         priv->error = g_error_copy (err);
1666                 else
1667                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1668                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1669                                      "canceled");               
1670         } else {
1671                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1672         }
1673
1674         /* The user will unref the new_folder */
1675         if (info->callback)
1676                 info->callback (info->mail_op, parent_folder, 
1677                                 new_folder, info->user_data);
1678         
1679         /* Notify about operation end */
1680         modest_mail_operation_notify_end (info->mail_op);
1681
1682         /* Frees */
1683         g_object_unref (info->mail_op);
1684         g_slice_free (CreateFolderInfo, info);
1685 }
1686
1687 void
1688 modest_mail_operation_create_folder (ModestMailOperation *self,
1689                                      TnyFolderStore *parent,
1690                                      const gchar *name,
1691                                      CreateFolderUserCallback callback,
1692                                      gpointer user_data)
1693 {
1694         ModestMailOperationPrivate *priv;
1695
1696         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1697         g_return_if_fail (name);
1698         
1699         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1700         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1701         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1702                 g_object_ref (parent) : 
1703                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1704
1705         /* Check for already existing folder */
1706         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1707                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1708                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1709                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1710                              _CS("ckdg_ib_folder_already_exists"));
1711         }
1712
1713         /* Check parent */
1714         if (TNY_IS_FOLDER (parent)) {
1715                 /* Check folder rules */
1716                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1717                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1718                         /* Set status failed and set an error */
1719                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1720                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1721                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1722                                      _("mail_in_ui_folder_create_error"));
1723                 }
1724         }
1725
1726         if (!strcmp (name, " ") || strchr (name, '/')) {
1727                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1728                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1729                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1730                              _("mail_in_ui_folder_create_error"));
1731         }
1732
1733         if (!priv->error) {
1734                 CreateFolderInfo *info;
1735
1736                 info = g_slice_new0 (CreateFolderInfo);
1737                 info->mail_op = g_object_ref (self);
1738                 info->callback = callback;
1739                 info->user_data = user_data;
1740
1741                 modest_mail_operation_notify_start (self);
1742
1743                 /* Create the folder */
1744                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
1745                                                       NULL, info);
1746         } else {
1747                 /* Call the user callback anyway */
1748                 if (callback)
1749                         callback (self, parent, NULL, user_data);
1750                 /* Notify about operation end */
1751                 modest_mail_operation_notify_end (self);
1752         }
1753 }
1754
1755 void
1756 modest_mail_operation_remove_folder (ModestMailOperation *self,
1757                                      TnyFolder           *folder,
1758                                      gboolean             remove_to_trash)
1759 {
1760         TnyAccount *account;
1761         ModestMailOperationPrivate *priv;
1762         ModestTnyFolderRules rules;
1763
1764         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1765         g_return_if_fail (TNY_IS_FOLDER (folder));
1766         
1767         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1768         
1769         /* Check folder rules */
1770         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1771         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1772                 /* Set status failed and set an error */
1773                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1774                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1775                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1776                              _("mail_in_ui_folder_delete_error"));
1777                 goto end;
1778         }
1779
1780         /* Get the account */
1781         account = modest_tny_folder_get_account (folder);
1782         priv->account = g_object_ref(account);
1783         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1784
1785         /* Delete folder or move to trash */
1786         if (remove_to_trash) {
1787                 TnyFolder *trash_folder = NULL;
1788                 trash_folder = modest_tny_account_get_special_folder (account,
1789                                                                       TNY_FOLDER_TYPE_TRASH);
1790                 /* TODO: error_handling */
1791                 if (trash_folder) {
1792                         modest_mail_operation_notify_start (self);
1793                         modest_mail_operation_xfer_folder (self, folder,
1794                                                     TNY_FOLDER_STORE (trash_folder), 
1795                                                     TRUE, NULL, NULL);
1796                         g_object_unref (trash_folder);
1797                 }
1798         } else {
1799                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1800                 if (parent) {
1801                         modest_mail_operation_notify_start (self);
1802                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
1803                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1804                         
1805                         if (!priv->error)
1806                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1807
1808                         g_object_unref (parent);
1809                 } else
1810                         g_warning ("%s: could not get parent folder", __FUNCTION__);
1811         }
1812         g_object_unref (G_OBJECT (account));
1813
1814  end:
1815         /* Notify about operation end */
1816         modest_mail_operation_notify_end (self);
1817 }
1818
1819 static void
1820 transfer_folder_status_cb (GObject *obj,
1821                            TnyStatus *status,
1822                            gpointer user_data)
1823 {
1824         ModestMailOperation *self;
1825         ModestMailOperationPrivate *priv;
1826         ModestMailOperationState *state;
1827         XFerMsgAsyncHelper *helper;
1828
1829         g_return_if_fail (status != NULL);
1830         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1831
1832         helper = (XFerMsgAsyncHelper *) user_data;
1833         g_return_if_fail (helper != NULL);
1834
1835         self = helper->mail_op;
1836         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1837
1838         priv->done = status->position;
1839         priv->total = status->of_total;
1840
1841         state = modest_mail_operation_clone_state (self);
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
1848         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1849
1850         /* no gdk_threads_leave (), CHECKED */
1851
1852         g_slice_free (ModestMailOperationState, state);
1853 }
1854
1855
1856 static void
1857 transfer_folder_cb (TnyFolder *folder, 
1858                     gboolean cancelled, 
1859                     TnyFolderStore *into, 
1860                     TnyFolder *new_folder, 
1861                     GError *err, 
1862                     gpointer user_data)
1863 {
1864         XFerMsgAsyncHelper *helper;
1865         ModestMailOperation *self = NULL;
1866         ModestMailOperationPrivate *priv = NULL;
1867
1868         helper = (XFerMsgAsyncHelper *) user_data;
1869         g_return_if_fail (helper != NULL);       
1870
1871         self = helper->mail_op;
1872         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1873
1874         if (err) {
1875                 priv->error = g_error_copy (err);
1876                 priv->done = 0;
1877                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1878         } else if (cancelled) {
1879                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1880                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1881                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1882                              _("Transference of %s was cancelled."),
1883                              tny_folder_get_name (folder));
1884         } else {
1885                 priv->done = 1;
1886                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1887         }
1888                 
1889         /* Notify about operation end */
1890         modest_mail_operation_notify_end (self);
1891
1892         /* If user defined callback function was defined, call it */
1893         if (helper->user_callback) {
1894
1895                 /* This is not a GDK lock because we are a Tinymail callback
1896                  * which is already GDK locked by Tinymail */
1897
1898                 /* no gdk_threads_enter (), CHECKED */
1899                 helper->user_callback (self, helper->user_data);
1900                 /* no gdk_threads_leave () , CHECKED */
1901         }
1902
1903         /* Free */
1904         g_object_unref (helper->mail_op);
1905         g_slice_free   (XFerMsgAsyncHelper, helper);
1906 }
1907
1908 /**
1909  *
1910  * This function checks if the new name is a valid name for our local
1911  * folders account. The new name could not be the same than then name
1912  * of any of the mandatory local folders
1913  *
1914  * We can not rely on tinymail because tinymail does not check the
1915  * name of the virtual folders that the account could have in the case
1916  * that we're doing a rename (because it directly calls Camel which
1917  * knows nothing about our virtual folders). 
1918  *
1919  * In the case of an actual copy/move (i.e. move/copy a folder between
1920  * accounts) tinymail uses the tny_folder_store_create_account which
1921  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1922  * checks the new name of the folder, so this call in that case
1923  * wouldn't be needed. *But* NOTE that if tinymail changes its
1924  * implementation (if folder transfers within the same account is no
1925  * longer implemented as a rename) this call will allow Modest to work
1926  * perfectly
1927  *
1928  * If the new name is not valid, this function will set the status to
1929  * failed and will set also an error in the mail operation
1930  */
1931 static gboolean
1932 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1933                                  TnyFolderStore *into,
1934                                  const gchar *new_name)
1935 {
1936         if (TNY_IS_ACCOUNT (into) && 
1937             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1938             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1939                                                                  new_name)) {
1940                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1941                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1942                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1943                              _CS("ckdg_ib_folder_already_exists"));
1944                 return FALSE;
1945         } else
1946                 return TRUE;
1947 }
1948
1949 void
1950 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1951                                    TnyFolder *folder,
1952                                    TnyFolderStore *parent,
1953                                    gboolean delete_original,
1954                                    XferAsyncUserCallback user_callback,
1955                                    gpointer user_data)
1956 {
1957         ModestMailOperationPrivate *priv = NULL;
1958         ModestTnyFolderRules parent_rules = 0, rules; 
1959         XFerMsgAsyncHelper *helper = NULL;
1960         const gchar *folder_name = NULL;
1961         const gchar *error_msg;
1962
1963         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1964         g_return_if_fail (TNY_IS_FOLDER (folder));
1965         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1966
1967         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1968         folder_name = tny_folder_get_name (folder);
1969
1970         /* Set the error msg */
1971         error_msg = _("mail_in_ui_folder_move_target_error");
1972
1973         /* Get account and set it into mail_operation */
1974         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1975         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1976         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1977
1978         /* Get folder rules */
1979         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1980         if (TNY_IS_FOLDER (parent))
1981                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1982         
1983         /* Apply operation constraints */
1984         if ((gpointer) parent == (gpointer) folder ||
1985             (!TNY_IS_FOLDER_STORE (parent)) || 
1986             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1987                 /* Folder rules */
1988                 goto error;
1989         } else if (TNY_IS_FOLDER (parent) && 
1990                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1991                 /* Folder rules */
1992                 goto error;
1993
1994         } else if (TNY_IS_FOLDER (parent) &&
1995                    TNY_IS_FOLDER_STORE (folder) &&
1996                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
1997                                                   TNY_FOLDER_STORE (folder))) {
1998                 /* Do not move a parent into a child */
1999                 goto error;
2000         } else if (TNY_IS_FOLDER_STORE (parent) &&
2001                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2002                 /* Check that the new folder name is not used by any
2003                    parent subfolder */
2004                 goto error;     
2005         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2006                 /* Check that the new folder name is not used by any
2007                    special local folder */
2008                 goto error;
2009         } else {
2010                 /* Create the helper */
2011                 helper = g_slice_new0 (XFerMsgAsyncHelper);
2012                 helper->mail_op = g_object_ref (self);
2013                 helper->dest_folder = NULL;
2014                 helper->headers = NULL;
2015                 helper->user_callback = user_callback;
2016                 helper->user_data = user_data;
2017                 
2018                 /* Move/Copy folder */
2019                 modest_mail_operation_notify_start (self);
2020                 tny_folder_copy_async (folder,
2021                                        parent,
2022                                        tny_folder_get_name (folder),
2023                                        delete_original,
2024                                        transfer_folder_cb,
2025                                        transfer_folder_status_cb,
2026                                        helper);
2027                 return;
2028         }
2029
2030  error:
2031         /* Set status failed and set an error */
2032         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2033         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2034                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2035                      error_msg);
2036
2037         /* Call the user callback if exists */
2038         if (user_callback)
2039                 user_callback (self, user_data);
2040
2041         /* Notify the queue */
2042         modest_mail_operation_notify_end (self);
2043 }
2044
2045 void
2046 modest_mail_operation_rename_folder (ModestMailOperation *self,
2047                                      TnyFolder *folder,
2048                                      const gchar *name)
2049 {
2050         ModestMailOperationPrivate *priv;
2051         ModestTnyFolderRules rules;
2052         XFerMsgAsyncHelper *helper;
2053
2054         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2055         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2056         g_return_if_fail (name);
2057         
2058         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2059
2060         /* Get account and set it into mail_operation */
2061         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2062         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2063
2064         /* Check folder rules */
2065         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2066         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2067                 /* Set status failed and set an error */
2068                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2069                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2070                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2071                              _("FIXME: unable to rename"));
2072
2073                 /* Notify about operation end */
2074                 modest_mail_operation_notify_end (self);
2075         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2076                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2077                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2078                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2079                              _("FIXME: unable to rename"));
2080                 /* Notify about operation end */
2081                 modest_mail_operation_notify_end (self);
2082         } else {
2083                 TnyFolderStore *into;
2084
2085                 into = tny_folder_get_folder_store (folder);    
2086
2087                 /* Check that the new folder name is not used by any
2088                    special local folder */
2089                 if (new_name_valid_if_local_account (priv, into, name)) {
2090                         /* Create the helper */
2091                         helper = g_slice_new0 (XFerMsgAsyncHelper);
2092                         helper->mail_op = g_object_ref(self);
2093                         helper->dest_folder = NULL;
2094                         helper->headers = NULL;
2095                         helper->user_callback = NULL;
2096                         helper->user_data = NULL;
2097                 
2098                         /* Rename. Camel handles folder subscription/unsubscription */
2099                         modest_mail_operation_notify_start (self);
2100                         tny_folder_copy_async (folder, into, name, TRUE,
2101                                                transfer_folder_cb,
2102                                                transfer_folder_status_cb,
2103                                                helper);
2104                 } else {
2105                         modest_mail_operation_notify_end (self);
2106                 }
2107                 g_object_unref (into);
2108         }
2109 }
2110
2111 /* ******************************************************************* */
2112 /* **************************  MSG  ACTIONS  ************************* */
2113 /* ******************************************************************* */
2114
2115 void 
2116 modest_mail_operation_get_msg (ModestMailOperation *self,
2117                                TnyHeader *header,
2118                                GetMsgAsyncUserCallback user_callback,
2119                                gpointer user_data)
2120 {
2121         GetMsgInfo *helper = NULL;
2122         TnyFolder *folder;
2123         ModestMailOperationPrivate *priv;
2124         
2125         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2126         g_return_if_fail (TNY_IS_HEADER (header));
2127         
2128         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2129         folder = tny_header_get_folder (header);
2130
2131         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2132         priv->total = 1;
2133         priv->done = 0;
2134
2135         /* Get account and set it into mail_operation */
2136         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2137         
2138         /* Check for cached messages */
2139         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2140                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2141         else 
2142                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2143         
2144         /* Create the helper */
2145         helper = g_slice_new0 (GetMsgInfo);
2146         helper->header = g_object_ref (header);
2147         helper->mail_op = g_object_ref (self);
2148         helper->user_callback = user_callback;
2149         helper->user_data = user_data;
2150         helper->destroy_notify = NULL;
2151         helper->last_total_bytes = 0;
2152         helper->sum_total_bytes = 0;
2153         helper->total_bytes = tny_header_get_message_size (header);
2154
2155         modest_mail_operation_notify_start (self);
2156         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2157
2158         g_object_unref (G_OBJECT (folder));
2159 }
2160
2161 static void     
2162 get_msg_status_cb (GObject *obj,
2163                    TnyStatus *status,  
2164                    gpointer user_data)
2165 {
2166         GetMsgInfo *helper = NULL;
2167
2168         g_return_if_fail (status != NULL);
2169         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2170
2171         helper = (GetMsgInfo *) user_data;
2172         g_return_if_fail (helper != NULL);       
2173
2174         /* Notify progress */
2175         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2176                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2177 }
2178
2179 static void
2180 get_msg_async_cb (TnyFolder *folder, 
2181                   gboolean canceled, 
2182                   TnyMsg *msg, 
2183                   GError *err, 
2184                   gpointer user_data)
2185 {
2186         GetMsgInfo *info = NULL;
2187         ModestMailOperationPrivate *priv = NULL;
2188         gboolean finished;
2189
2190         info = (GetMsgInfo *) user_data;
2191
2192         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2193         priv->done++;
2194         finished = (priv->done == priv->total) ? TRUE : FALSE;
2195
2196         /* Check errors */
2197         if (canceled || err) {
2198                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2199                 if (!priv->error)
2200                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2201                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2202                                      err->message);
2203         } else {
2204                 /* Set the success status before calling the user callback */
2205                 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2206                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2207         }
2208
2209
2210         /* Call the user callback */
2211         if (info->user_callback)
2212                 info->user_callback (info->mail_op, info->header, canceled, 
2213                                      msg, err, info->user_data);
2214
2215         /* Notify about operation end if this is the last callback */
2216         if (finished) {
2217                 /* Free user data */
2218                 if (info->destroy_notify)
2219                         info->destroy_notify (info->user_data);
2220
2221                 /* Notify about operation end */
2222                 modest_mail_operation_notify_end (info->mail_op);
2223         }
2224
2225         /* Clean */
2226         g_object_unref (info->header);
2227         g_object_unref (info->mail_op);
2228         g_slice_free (GetMsgInfo, info);
2229 }
2230
2231 void 
2232 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2233                                      TnyList *header_list, 
2234                                      GetMsgAsyncUserCallback user_callback,
2235                                      gpointer user_data,
2236                                      GDestroyNotify notify)
2237 {
2238         ModestMailOperationPrivate *priv = NULL;
2239         gboolean size_ok = TRUE;
2240         gint max_size;
2241         TnyIterator *iter = NULL;
2242         
2243         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2244         
2245         /* Init mail operation */
2246         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2247         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2248         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2249         priv->done = 0;
2250         priv->total = tny_list_get_length(header_list);
2251
2252         /* Get account and set it into mail_operation */
2253         if (tny_list_get_length (header_list) >= 1) {
2254                 iter = tny_list_create_iterator (header_list);
2255                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2256                 if (header) {
2257                         TnyFolder *folder = tny_header_get_folder (header);
2258                         if (folder) {           
2259                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2260                                 g_object_unref (folder);
2261                         }
2262                         g_object_unref (header);
2263                 }
2264
2265                 if (tny_list_get_length (header_list) == 1) {
2266                         g_object_unref (iter);
2267                         iter = NULL;
2268                 }
2269         }
2270
2271         /* Get msg size limit */
2272         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2273                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2274                                          &(priv->error));
2275         if (priv->error) {
2276                 g_clear_error (&(priv->error));
2277                 max_size = G_MAXINT;
2278         } else {
2279                 max_size = max_size * KB;
2280         }
2281
2282         /* Check message size limits. If there is only one message
2283            always retrieve it */
2284         if (iter != NULL) {
2285                 while (!tny_iterator_is_done (iter) && size_ok) {
2286                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2287                         if (header) {
2288                                 if (tny_header_get_message_size (header) >= max_size)
2289                                         size_ok = FALSE;
2290                                 g_object_unref (header);
2291                         }
2292
2293                         tny_iterator_next (iter);
2294                 }
2295                 g_object_unref (iter);
2296         }
2297
2298         if (size_ok) {
2299                 const gint msg_list_size = compute_message_list_size (header_list);
2300
2301                 modest_mail_operation_notify_start (self);
2302                 iter = tny_list_create_iterator (header_list);
2303                 while (!tny_iterator_is_done (iter)) { 
2304                         GetMsgInfo *msg_info = NULL;
2305                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2306                         TnyFolder *folder = tny_header_get_folder (header);
2307                         
2308                         /* Create the message info */
2309                         msg_info = g_slice_new0 (GetMsgInfo);
2310                         msg_info->mail_op = g_object_ref (self);
2311                         msg_info->header = g_object_ref (header);
2312                         msg_info->user_callback = user_callback;
2313                         msg_info->user_data = user_data;
2314                         msg_info->destroy_notify = notify;
2315                         msg_info->last_total_bytes = 0;
2316                         msg_info->sum_total_bytes = 0;
2317                         msg_info->total_bytes = msg_list_size;
2318                         
2319                         /* The callback will call it per each header */
2320                         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2321                         
2322                         /* Free and go on */
2323                         g_object_unref (header);
2324                         g_object_unref (folder);
2325                         tny_iterator_next (iter);
2326                 }
2327                 g_object_unref (iter);
2328         } else {
2329                 /* Set status failed and set an error */
2330                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2331                 /* FIXME: the error msg is different for pop */
2332                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2333                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2334                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2335                 /* Remove from queue and free resources */
2336                 modest_mail_operation_notify_end (self);
2337                 if (notify)
2338                         notify (user_data);
2339         }
2340 }
2341
2342
2343 void 
2344 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2345                                   TnyHeader *header,
2346                                   gboolean remove_to_trash /*ignored*/)
2347 {
2348         TnyFolder *folder;
2349         ModestMailOperationPrivate *priv;
2350
2351         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2352         g_return_if_fail (TNY_IS_HEADER (header));
2353
2354         if (remove_to_trash)
2355                 g_warning ("remove to trash is not implemented");
2356
2357         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2358         folder = tny_header_get_folder (header);
2359
2360         /* Get account and set it into mail_operation */
2361         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2362         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2363         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2364
2365         /* remove message from folder */
2366         tny_folder_remove_msg (folder, header, &(priv->error));
2367         if (!priv->error) {
2368                 gboolean expunge, leave_on_server;
2369                 const gchar *account_name;
2370                 TnyAccount *account;
2371
2372                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2373                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2374
2375                 modest_mail_operation_notify_start (self);
2376
2377                 /* Get leave on server setting */
2378                 account = tny_folder_get_account (folder);
2379                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2380                 leave_on_server =
2381                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2382                                                                 account_name);
2383
2384                 if (TNY_IS_CAMEL_POP_FOLDER (folder) && !leave_on_server)
2385                         expunge = TRUE;
2386                 else
2387                         expunge = FALSE;
2388
2389                 /* Sync folder */
2390                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2391
2392                 /* Unref */
2393                 g_object_unref (account);
2394         }
2395         
2396         
2397         /* Set status */
2398         if (!priv->error)
2399                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2400         else
2401                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2402
2403         /* Free */
2404         g_object_unref (G_OBJECT (folder));
2405
2406         /* Notify about operation end */
2407         modest_mail_operation_notify_end (self);
2408 }
2409
2410 void 
2411 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2412                                    TnyList *headers,
2413                                   gboolean remove_to_trash /*ignored*/)
2414 {
2415         TnyFolder *folder;
2416         ModestMailOperationPrivate *priv;
2417         TnyIterator *iter = NULL;
2418         TnyHeader *header = NULL;
2419         TnyList *remove_headers = NULL;
2420         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2421
2422         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2423         g_return_if_fail (TNY_IS_LIST (headers));
2424
2425         if (remove_to_trash)
2426                 g_warning ("remove to trash is not implemented");
2427
2428         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2429
2430         remove_headers = g_object_ref(headers);
2431
2432         /* Get folder from first header and sync it */
2433         iter = tny_list_create_iterator (headers);
2434         header = TNY_HEADER (tny_iterator_get_current (iter));
2435         folder = tny_header_get_folder (header);
2436
2437         /* Don't remove messages that are being sent */
2438         if (modest_tny_folder_is_local_folder (folder)) {
2439                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2440         }
2441         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2442                 TnyTransportAccount *traccount = NULL;
2443                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2444                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2445                 if (traccount) {
2446                         ModestTnySendQueueStatus status;
2447                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2448                         TnyIterator *iter = tny_list_create_iterator(headers);
2449                         g_object_unref(remove_headers);
2450                         remove_headers = TNY_LIST(tny_simple_list_new());
2451                         while (!tny_iterator_is_done(iter)) {
2452                                 char *msg_id;
2453                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2454                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2455                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2456                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2457                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2458                                 }
2459                                 g_object_unref(hdr);
2460                                 g_free(msg_id);
2461                                 tny_iterator_next(iter);
2462                         }
2463                         g_object_unref(iter);
2464                         g_object_unref(traccount);
2465                 }
2466         }
2467
2468         /* Get account and set it into mail_operation */
2469         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2470         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2471         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2472
2473         /* remove message from folder */
2474         modest_mail_operation_notify_start (self);
2475
2476         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2477         if (!priv->error) {
2478                 gboolean expunge, leave_on_server;
2479                 const gchar *account_name;
2480                 TnyAccount *account;
2481                 
2482                 account = tny_folder_get_account (folder);
2483                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2484                 leave_on_server =
2485                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2486                                         account_name);
2487
2488                 if (TNY_IS_CAMEL_POP_FOLDER (folder) && !leave_on_server)
2489                         expunge = TRUE;
2490                 else
2491                         expunge = FALSE;
2492
2493                 /* Sync folder */
2494                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2495                 
2496                 g_object_unref (account);
2497         }
2498         
2499         
2500         /* Set status */
2501         if (!priv->error)
2502                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2503         else
2504                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2505
2506         /* Free */
2507         g_object_unref (remove_headers);
2508         g_object_unref (header);
2509         g_object_unref (iter);
2510         g_object_unref (G_OBJECT (folder));
2511
2512         /* Notify about operation end */
2513         modest_mail_operation_notify_end (self);
2514 }
2515
2516 static void
2517 notify_progress_of_multiple_messages (ModestMailOperation *self,
2518                                       TnyStatus *status,
2519                                       gint *last_total_bytes,
2520                                       gint *sum_total_bytes,
2521                                       gint total_bytes, 
2522                                       gboolean increment_done)
2523 {
2524         ModestMailOperationPrivate *priv;
2525         ModestMailOperationState *state;
2526         gboolean is_num_bytes;
2527
2528         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2529
2530         /* We know that tinymail sends us information about
2531            transferred bytes with this particular message */
2532         is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2533
2534         state = modest_mail_operation_clone_state (self);
2535         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2536                 /* We know that we're in a different message when the
2537                    total number of bytes to transfer is different. Of
2538                    course it could fail if we're transferring messages
2539                    of the same size, but this is a workarround */
2540                 if (status->of_total != *last_total_bytes) {
2541                         /* We need to increment the done when there is
2542                            no information about each individual
2543                            message, we need to do this in message
2544                            transfers, and we don't do it for getting
2545                            messages */
2546                         if (increment_done)
2547                                 priv->done++;
2548                         *sum_total_bytes += *last_total_bytes;
2549                         *last_total_bytes = status->of_total;
2550                 }
2551                 state->bytes_done += status->position + *sum_total_bytes;
2552                 state->bytes_total = total_bytes;
2553
2554                 /* Notify the status change. Only notify about changes
2555                    referred to bytes */
2556                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2557                                0, state, NULL);
2558         }
2559
2560         g_slice_free (ModestMailOperationState, state);
2561 }
2562
2563 static void
2564 transfer_msgs_status_cb (GObject *obj,
2565                          TnyStatus *status,  
2566                          gpointer user_data)
2567 {
2568         XFerMsgAsyncHelper *helper;
2569
2570         g_return_if_fail (status != NULL);
2571         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2572
2573         helper = (XFerMsgAsyncHelper *) user_data;
2574         g_return_if_fail (helper != NULL);       
2575
2576         /* Notify progress */
2577         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2578                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2579 }
2580
2581
2582 static void
2583 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2584 {
2585         XFerMsgAsyncHelper *helper;
2586         ModestMailOperation *self;
2587         ModestMailOperationPrivate *priv;
2588         TnyIterator *iter = NULL;
2589         TnyHeader *header = NULL;
2590
2591         helper = (XFerMsgAsyncHelper *) user_data;
2592         self = helper->mail_op;
2593
2594         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2595
2596         if (err) {
2597                 priv->error = g_error_copy (err);
2598                 priv->done = 0;
2599                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2600         } else if (cancelled) {
2601                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2602                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2603                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2604                              _("Error trying to refresh the contents of %s"),
2605                              tny_folder_get_name (folder));
2606         } else {
2607                 priv->done = 1;
2608                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2609
2610                 /* Update folder counts */
2611                 tny_folder_poke_status (folder);                
2612                 tny_folder_poke_status (helper->dest_folder);           
2613         }
2614
2615         
2616         /* Mark headers as deleted and seen */
2617         if ((helper->delete) && 
2618             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2619                 iter = tny_list_create_iterator (helper->headers);
2620                 while (!tny_iterator_is_done (iter)) {
2621                         header = TNY_HEADER (tny_iterator_get_current (iter));
2622                         tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2623                         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2624                         g_object_unref (header);
2625
2626                         tny_iterator_next (iter);
2627                 }
2628
2629         }
2630                 
2631
2632         /* Notify about operation end */
2633         modest_mail_operation_notify_end (self);
2634
2635         /* If user defined callback function was defined, call it */
2636         if (helper->user_callback) {
2637                 /* This is not a GDK lock because we are a Tinymail callback and
2638                  * Tinymail already acquires the Gdk lock */
2639
2640                 /* no gdk_threads_enter (), CHECKED */
2641                 helper->user_callback (self, helper->user_data);
2642                 /* no gdk_threads_leave (), CHECKED */
2643         }
2644
2645         /* Free */
2646         if (helper->headers)
2647                 g_object_unref (helper->headers);
2648         if (helper->dest_folder)
2649                 g_object_unref (helper->dest_folder);
2650         if (helper->mail_op)
2651                 g_object_unref (helper->mail_op);
2652         if (folder)
2653                 g_object_unref (folder);
2654         if (iter)
2655                 g_object_unref (iter);
2656         g_slice_free (XFerMsgAsyncHelper, helper);
2657 }
2658
2659 static guint
2660 compute_message_list_size (TnyList *headers)
2661 {
2662         TnyIterator *iter;
2663         guint size = 0;
2664
2665         iter = tny_list_create_iterator (headers);
2666         while (!tny_iterator_is_done (iter)) {
2667                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2668                 size += tny_header_get_message_size (header);
2669                 g_object_unref (header);
2670                 tny_iterator_next (iter);
2671         }
2672         g_object_unref (iter);
2673
2674         return size;
2675 }
2676
2677 static guint
2678 compute_message_array_size (GPtrArray *headers)
2679 {
2680         guint size = 0;
2681         gint i;
2682
2683         for (i = 0; i < headers->len; i++) {
2684                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2685                 size += tny_header_get_message_size (header);
2686         }
2687
2688         return size;
2689 }
2690
2691
2692 void
2693 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2694                                  TnyList *headers, 
2695                                  TnyFolder *folder, 
2696                                  gboolean delete_original,
2697                                  XferAsyncUserCallback user_callback,
2698                                  gpointer user_data)
2699 {
2700         ModestMailOperationPrivate *priv = NULL;
2701         TnyIterator *iter = NULL;
2702         TnyFolder *src_folder = NULL;
2703         XFerMsgAsyncHelper *helper = NULL;
2704         TnyHeader *header = NULL;
2705         ModestTnyFolderRules rules = 0;
2706
2707         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2708         g_return_if_fail (headers && TNY_IS_LIST (headers));
2709         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2710
2711         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2712         priv->total = tny_list_get_length (headers);
2713         priv->done = 0;
2714         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2715         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2716
2717         /* Apply folder rules */
2718         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2719         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2720                 /* Set status failed and set an error */
2721                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2722                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2723                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2724                              _CS("ckct_ib_unable_to_paste_here"));
2725                 /* Notify the queue */
2726                 modest_mail_operation_notify_end (self);
2727                 return;
2728         }
2729                 
2730         /* Get source folder */
2731         iter = tny_list_create_iterator (headers);
2732         header = TNY_HEADER (tny_iterator_get_current (iter));
2733         if (header) {
2734                 src_folder = tny_header_get_folder (header);
2735                 g_object_unref (header);
2736         }
2737         g_object_unref (iter);
2738
2739         if (src_folder == NULL) {
2740                 /* Notify the queue */
2741                 modest_mail_operation_notify_end (self);
2742
2743                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2744                 return;
2745         }
2746
2747         
2748         /* Check folder source and destination */
2749         if (src_folder == folder) {
2750                 /* Set status failed and set an error */
2751                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2752                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2753                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2754                              _("mail_in_ui_folder_copy_target_error"));
2755                 
2756                 /* Notify the queue */
2757                 modest_mail_operation_notify_end (self);
2758                 
2759                 /* Free */
2760                 g_object_unref (src_folder);            
2761                 return;
2762         }
2763
2764         /* Create the helper */
2765         helper = g_slice_new0 (XFerMsgAsyncHelper);
2766         helper->mail_op = g_object_ref(self);
2767         helper->dest_folder = g_object_ref(folder);
2768         helper->headers = g_object_ref(headers);
2769         helper->user_callback = user_callback;
2770         helper->user_data = user_data;
2771         helper->delete = delete_original;
2772         helper->last_total_bytes = 0;
2773         helper->sum_total_bytes = 0;
2774         helper->total_bytes = compute_message_list_size (headers);
2775
2776         /* Get account and set it into mail_operation */
2777         priv->account = modest_tny_folder_get_account (src_folder);
2778
2779         /* Transfer messages */
2780         modest_mail_operation_notify_start (self);
2781         tny_folder_transfer_msgs_async (src_folder, 
2782                                         headers, 
2783                                         folder, 
2784                                         delete_original, 
2785                                         transfer_msgs_cb, 
2786                                         transfer_msgs_status_cb,
2787                                         helper);
2788 }
2789
2790
2791 static void
2792 on_refresh_folder (TnyFolder   *folder, 
2793                    gboolean     cancelled, 
2794                    GError     *error,
2795                    gpointer     user_data)
2796 {
2797         RefreshAsyncHelper *helper = NULL;
2798         ModestMailOperation *self = NULL;
2799         ModestMailOperationPrivate *priv = NULL;
2800
2801         helper = (RefreshAsyncHelper *) user_data;
2802         self = helper->mail_op;
2803         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2804
2805         g_return_if_fail(priv!=NULL);
2806
2807         if (error) {
2808                 priv->error = g_error_copy (error);
2809                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2810                 goto out;
2811         }
2812
2813         if (cancelled) {
2814                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2815                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2816                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2817                              _("Error trying to refresh the contents of %s"),
2818                              tny_folder_get_name (folder));
2819                 goto out;
2820         }
2821
2822         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2823  out:
2824
2825         /* Call user defined callback, if it exists */
2826         if (helper->user_callback) {
2827
2828                 /* This is not a GDK lock because we are a Tinymail callback and
2829                  * Tinymail already acquires the Gdk lock */
2830                 helper->user_callback (self, folder, helper->user_data);
2831         }
2832
2833         /* Free */
2834         g_slice_free (RefreshAsyncHelper, helper);
2835
2836         /* Notify about operation end */
2837         modest_mail_operation_notify_end (self);
2838         g_object_unref(self);
2839 }
2840
2841 static void
2842 on_refresh_folder_status_update (GObject *obj,
2843                                  TnyStatus *status,
2844                                  gpointer user_data)
2845 {
2846         RefreshAsyncHelper *helper = NULL;
2847         ModestMailOperation *self = NULL;
2848         ModestMailOperationPrivate *priv = NULL;
2849         ModestMailOperationState *state;
2850
2851         g_return_if_fail (user_data != NULL);
2852         g_return_if_fail (status != NULL);
2853         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2854
2855         helper = (RefreshAsyncHelper *) user_data;
2856         self = helper->mail_op;
2857         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2858
2859         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2860
2861         priv->done = status->position;
2862         priv->total = status->of_total;
2863
2864         state = modest_mail_operation_clone_state (self);
2865
2866         /* This is not a GDK lock because we are a Tinymail callback and
2867          * Tinymail already acquires the Gdk lock */
2868         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2869
2870         g_slice_free (ModestMailOperationState, state);
2871 }
2872
2873 void 
2874 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2875                                        TnyFolder *folder,
2876                                        RefreshAsyncUserCallback user_callback,
2877                                        gpointer user_data)
2878 {
2879         ModestMailOperationPrivate *priv = NULL;
2880         RefreshAsyncHelper *helper = NULL;
2881
2882         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2883
2884         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2885
2886         /* Get account and set it into mail_operation */
2887         priv->account = modest_tny_folder_get_account  (folder);
2888         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2889
2890         /* Create the helper */
2891         helper = g_slice_new0 (RefreshAsyncHelper);
2892         helper->mail_op = g_object_ref(self);
2893         helper->user_callback = user_callback;
2894         helper->user_data = user_data;
2895
2896         /* Refresh the folder. TODO: tinymail could issue a status
2897            updates before the callback call then this could happen. We
2898            must review the design */
2899         modest_mail_operation_notify_start (self);
2900         tny_folder_refresh_async (folder,
2901                                   on_refresh_folder,
2902                                   on_refresh_folder_status_update,
2903                                   helper);
2904 }
2905
2906
2907 static void
2908 modest_mail_operation_notify_start (ModestMailOperation *self)
2909 {
2910         ModestMailOperationPrivate *priv = NULL;
2911
2912         g_return_if_fail (self);
2913
2914         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2915
2916         /* Ensure that all the fields are filled correctly */
2917         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2918
2919         /* Notify the observers about the mail operation. We do not
2920            wrapp this emission because we assume that this function is
2921            always called from within the main lock */
2922         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2923 }
2924
2925 /**
2926  *
2927  * It's used by the mail operation queue to notify the observers
2928  * attached to that signal that the operation finished. We need to use
2929  * that because tinymail does not give us the progress of a given
2930  * operation when it finishes (it directly calls the operation
2931  * callback).
2932  */
2933 static void
2934 modest_mail_operation_notify_end (ModestMailOperation *self)
2935 {
2936         ModestMailOperationPrivate *priv = NULL;
2937
2938         g_return_if_fail (self);
2939
2940         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2941
2942         /* Notify the observers about the mail operation end. We do
2943            not wrapp this emission because we assume that this
2944            function is always called from within the main lock */
2945         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2946
2947         /* Remove the error user data */
2948         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2949                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2950 }
2951
2952 TnyAccount *
2953 modest_mail_operation_get_account (ModestMailOperation *self)
2954 {
2955         ModestMailOperationPrivate *priv = NULL;
2956
2957         g_return_val_if_fail (self, NULL);
2958
2959         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2960
2961         return (priv->account) ? g_object_ref (priv->account) : NULL;
2962 }
2963
2964 void
2965 modest_mail_operation_noop (ModestMailOperation *self)
2966 {
2967         ModestMailOperationPrivate *priv = NULL;
2968
2969         g_return_if_fail (self);
2970
2971         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2972         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2973         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2974         priv->done = 0;
2975         priv->total = 0;
2976
2977         /* This mail operation does nothing actually */
2978         modest_mail_operation_notify_start (self);
2979         modest_mail_operation_notify_end (self);
2980 }