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