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