Modified webpage: now tinymail repository is in gitorious.
[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-error.h>
45 #include <tny-folder-observer.h>
46 #include <camel/camel-stream-mem.h>
47 #include <glib/gi18n.h>
48 #include <modest-defs.h>
49 #include "modest-platform.h"
50 #include "modest-account-mgr-helpers.h"
51 #include <modest-tny-account.h>
52 #include <modest-tny-send-queue.h>
53 #include <modest-runtime.h>
54 #include "modest-text-utils.h"
55 #include "modest-tny-msg.h"
56 #include "modest-tny-folder.h"
57 #include "modest-tny-account-store.h"
58 #include "modest-tny-platform-factory.h"
59 #include "modest-marshal.h"
60 #include "modest-error.h"
61 #include "modest-mail-operation.h"
62 #include <modest-count-stream.h>
63 #include <libgnomevfs/gnome-vfs.h>
64 #include "modest-utils.h"
65 #include "modest-debug.h"
66 #ifdef MODEST_USE_LIBTIME
67 #include <clockd/libtime.h>
68 #endif
69 #include "modest-account-protocol.h"
70 #include <camel/camel-stream-null.h>
71 #include <widgets/modest-msg-view-window.h>
72
73 #define KB 1024
74
75 /* 
76  * Remove all these #ifdef stuff when the tinymail's idle calls become
77  * locked
78  */
79 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
80
81 /* 'private'/'protected' functions */
82 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
83 static void modest_mail_operation_init       (ModestMailOperation *obj);
84 static void modest_mail_operation_finalize   (GObject *obj);
85
86 static void     get_msg_async_cb (TnyFolder *folder, 
87                                   gboolean cancelled, 
88                                   TnyMsg *msg, 
89                                   GError *rr, 
90                                   gpointer user_data);
91
92 static void     get_msg_status_cb (GObject *obj,
93                                    TnyStatus *status,  
94                                    gpointer user_data);
95
96 static void     modest_mail_operation_notify_start (ModestMailOperation *self);
97 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
98
99 static void     notify_progress_of_multiple_messages (ModestMailOperation *self,
100                                                       TnyStatus *status,
101                                                       gint *last_total_bytes,
102                                                       gint *sum_total_bytes,
103                                                       gint total_bytes,
104                                                       gboolean increment_done);
105
106 static guint    compute_message_list_size (TnyList *headers, guint num_elements);
107
108 static int      compare_headers_by_date   (gconstpointer a,
109                                            gconstpointer b);
110
111 static void     sync_folder_finish_callback (TnyFolder *self, 
112                                              gboolean cancelled, 
113                                              GError *err, 
114                                              gpointer user_data);
115
116 static gboolean _check_memory_low         (ModestMailOperation *mail_op);
117
118
119 typedef struct {
120         ModestTnySendQueue *queue;
121         ModestMailOperation *self;
122         guint error_handler;
123         guint start_handler;
124         guint stop_handler;
125 } RunQueueHelper;
126
127 static void run_queue_notify_and_destroy (RunQueueHelper *helper,
128                                           ModestMailOperationStatus status);
129
130 /* Helpers for the update account operation (send & receive)*/
131 typedef struct 
132 {
133         ModestMailOperation *mail_op;
134         gchar *account_name;
135         UpdateAccountCallback callback;
136         gpointer user_data;
137         TnyList *folders;
138         gint pending_calls;
139         gboolean poke_all;
140         TnyFolderObserver *inbox_observer;
141         gboolean interactive;
142         gboolean msg_readed;
143         gboolean update_folder_counts;
144 } UpdateAccountInfo;
145
146 static void destroy_update_account_info         (UpdateAccountInfo *info);
147
148 static void update_account_send_mail            (UpdateAccountInfo *info);
149
150 static void update_account_get_msg_async_cb     (TnyFolder *folder, 
151                                                  gboolean canceled, 
152                                                  TnyMsg *msg, 
153                                                  GError *err, 
154                                                  gpointer user_data);
155
156 static void update_account_notify_user_and_free (UpdateAccountInfo *info, 
157                                                  TnyList *new_headers);
158
159 enum _ModestMailOperationSignals 
160 {
161         PROGRESS_CHANGED_SIGNAL,
162         OPERATION_STARTED_SIGNAL,
163         OPERATION_FINISHED_SIGNAL,
164         NUM_SIGNALS
165 };
166
167 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
168 struct _ModestMailOperationPrivate {
169         TnyAccount                *account;
170         guint                      done;
171         guint                      total;
172         GObject                   *source;
173         GError                    *error;
174         ErrorCheckingUserCallback  error_checking;
175         gpointer                   error_checking_user_data;
176         ErrorCheckingUserDataDestroyer error_checking_user_data_destroyer;
177         ModestMailOperationStatus  status;      
178         ModestMailOperationTypeOperation op_type;
179 };
180
181 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
182                                                    MODEST_TYPE_MAIL_OPERATION, \
183                                                    ModestMailOperationPrivate))
184
185 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
186                                                    priv->status = new_status;\
187                                                }
188
189
190 typedef struct {
191         GetMsgAsyncUserCallback user_callback;
192         TnyHeader *header;
193         TnyIterator *more_msgs;
194         gpointer user_data;
195         ModestMailOperation *mail_op;
196         GDestroyNotify destroy_notify;
197         gint last_total_bytes;
198         gint sum_total_bytes;
199         gint total_bytes;
200         TnyIterator *get_parts;
201         TnyMsg *msg;
202 } GetMsgInfo;
203
204 typedef struct _RefreshAsyncHelper {    
205         ModestMailOperation *mail_op;
206         RefreshAsyncUserCallback user_callback; 
207         gpointer user_data;
208 } RefreshAsyncHelper;
209
210 typedef struct _XFerMsgsAsyncHelper
211 {
212         ModestMailOperation *mail_op;
213         TnyList *headers;
214         TnyIterator *more_msgs;
215         TnyFolder *dest_folder;
216         XferMsgsAsyncUserCallback user_callback;        
217         gboolean delete;
218         gpointer user_data;
219         gint last_total_bytes;
220         gint sum_total_bytes;
221         gint total_bytes;
222 } XFerMsgsAsyncHelper;
223
224 typedef struct _XFerFolderAsyncHelper
225 {
226         ModestMailOperation *mail_op;
227         XferFolderAsyncUserCallback user_callback;      
228         gpointer user_data;
229 } XFerFolderAsyncHelper;
230
231 typedef struct _SyncFolderHelper {
232         ModestMailOperation *mail_op;
233         SyncFolderCallback user_callback;
234         gpointer user_data;
235 } SyncFolderHelper;
236
237 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
238                                                       TnyMsg *msg,
239                                                       gpointer userdata);
240
241 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
242                                                        const gchar *from, const gchar *to,
243                                                        const gchar *cc, const gchar *bcc,
244                                                        const gchar *subject, const gchar *plain_body,
245                                                        const gchar *html_body, const GList *attachments_list,
246                                                        const GList *images_list,
247                                                        TnyHeaderFlags priority_flags,
248                                                        const gchar *references, const gchar *in_reply_to,
249                                                        TnyList *header_pairs,
250                                                        ModestMailOperationCreateMsgCallback callback,
251                                                        gpointer userdata);
252
253 static gboolean      idle_notify_queue (gpointer data);
254 typedef struct
255 {
256         ModestMailOperation *mail_op;
257         gchar *from;
258         gchar *to;
259         gchar *cc;
260         gchar *bcc;
261         gchar *subject;
262         gchar *references;
263         gchar *in_reply_to;
264         gchar *plain_body;
265         gchar *html_body;
266         GList *attachments_list;
267         GList *images_list;
268         TnyHeaderFlags priority_flags;
269         TnyList *header_pairs;
270         ModestMailOperationCreateMsgCallback callback;
271         gpointer userdata;
272 } CreateMsgInfo;
273
274 typedef struct
275 {
276         ModestMailOperation *mail_op;
277         TnyMsg *msg;
278         ModestMailOperationCreateMsgCallback callback;
279         gpointer userdata;
280 } CreateMsgIdleInfo;
281
282 /* globals */
283 static GObjectClass *parent_class = NULL;
284
285 static guint signals[NUM_SIGNALS] = {0};
286
287 GType
288 modest_mail_operation_get_type (void)
289 {
290         static GType my_type = 0;
291         if (!my_type) {
292                 static const GTypeInfo my_info = {
293                         sizeof(ModestMailOperationClass),
294                         NULL,           /* base init */
295                         NULL,           /* base finalize */
296                         (GClassInitFunc) modest_mail_operation_class_init,
297                         NULL,           /* class finalize */
298                         NULL,           /* class data */
299                         sizeof(ModestMailOperation),
300                         1,              /* n_preallocs */
301                         (GInstanceInitFunc) modest_mail_operation_init,
302                         NULL
303                 };
304                 my_type = g_type_register_static (G_TYPE_OBJECT,
305                                                   "ModestMailOperation",
306                                                   &my_info, 0);
307         }
308         return my_type;
309 }
310
311 static void
312 modest_mail_operation_class_init (ModestMailOperationClass *klass)
313 {
314         GObjectClass *gobject_class;
315         gobject_class = (GObjectClass*) klass;
316
317         parent_class            = g_type_class_peek_parent (klass);
318         gobject_class->finalize = modest_mail_operation_finalize;
319
320         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
321
322         /**
323          * ModestMailOperation::progress-changed
324          * @self: the #MailOperation that emits the signal
325          * @user_data: user data set when the signal handler was connected
326          *
327          * Emitted when the progress of a mail operation changes
328          */
329         signals[PROGRESS_CHANGED_SIGNAL] = 
330                 g_signal_new ("progress-changed",
331                               G_TYPE_FROM_CLASS (gobject_class),
332                               G_SIGNAL_RUN_FIRST,
333                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
334                               NULL, NULL,
335                               g_cclosure_marshal_VOID__POINTER,
336                               G_TYPE_NONE, 1, G_TYPE_POINTER);
337         /**
338          * operation-started
339          *
340          * This signal is issued whenever a mail operation starts, and
341          * starts mean when the tinymail operation is issued. This
342          * means that it could happen that something wrong happens and
343          * the tinymail function is never called. In this situation a
344          * operation-finished will be issued without any
345          * operation-started
346          */
347         signals[OPERATION_STARTED_SIGNAL] =
348                 g_signal_new ("operation-started",
349                               G_TYPE_FROM_CLASS (gobject_class),
350                               G_SIGNAL_RUN_FIRST,
351                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_started),
352                               NULL, NULL,
353                               g_cclosure_marshal_VOID__VOID,
354                               G_TYPE_NONE, 0);
355         /**
356          * operation-started
357          *
358          * This signal is issued whenever a mail operation
359          * finishes. Note that this signal could be issued without any
360          * previous "operation-started" signal, because this last one
361          * is only issued when the tinymail operation is successfully
362          * started
363          */
364         signals[OPERATION_FINISHED_SIGNAL] =
365                 g_signal_new ("operation-finished",
366                               G_TYPE_FROM_CLASS (gobject_class),
367                               G_SIGNAL_RUN_FIRST,
368                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_finished),
369                               NULL, NULL,
370                               g_cclosure_marshal_VOID__VOID,
371                               G_TYPE_NONE, 0);
372 }
373
374 static void
375 modest_mail_operation_init (ModestMailOperation *obj)
376 {
377         ModestMailOperationPrivate *priv;
378
379         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
380
381         priv->account        = NULL;
382         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
383         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
384         priv->error          = NULL;
385         priv->done           = 0;
386         priv->total          = 0;
387         priv->source         = NULL;
388         priv->error_checking = NULL;
389         priv->error_checking_user_data = NULL;
390 }
391
392 static void
393 modest_mail_operation_finalize (GObject *obj)
394 {
395         ModestMailOperationPrivate *priv;
396
397         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
398
399         
400         
401         if (priv->error) {
402                 g_error_free (priv->error);
403                 priv->error = NULL;
404         }
405         if (priv->source) {
406                 g_object_unref (priv->source);
407                 priv->source = NULL;
408         }
409         if (priv->account) {
410                 g_object_unref (priv->account);
411                 priv->account = NULL;
412         }
413
414
415         G_OBJECT_CLASS(parent_class)->finalize (obj);
416 }
417
418 ModestMailOperation*
419 modest_mail_operation_new (GObject *source)
420 {
421         ModestMailOperation *obj;
422         ModestMailOperationPrivate *priv;
423                 
424         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
425         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
426
427         if (source != NULL)
428                 priv->source = g_object_ref(source);
429
430         return obj;
431 }
432
433 ModestMailOperation*
434 modest_mail_operation_new_with_error_handling (GObject *source,
435                                                ErrorCheckingUserCallback error_handler,
436                                                gpointer user_data,
437                                                ErrorCheckingUserDataDestroyer error_handler_destroyer)
438 {
439         ModestMailOperation *obj;
440         ModestMailOperationPrivate *priv;
441                 
442         obj = modest_mail_operation_new (source);
443         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
444         
445         g_return_val_if_fail (error_handler != NULL, obj);
446         priv->error_checking = error_handler;
447         priv->error_checking_user_data = user_data;
448         priv->error_checking_user_data_destroyer = error_handler_destroyer;
449
450         return obj;
451 }
452
453 void
454 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
455 {
456         ModestMailOperationPrivate *priv;
457
458         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION(self));
459         
460         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
461         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
462
463         /* Call the user callback */
464         if (priv->error_checking != NULL)
465                 priv->error_checking (self, priv->error_checking_user_data);
466 }
467
468
469 ModestMailOperationTypeOperation
470 modest_mail_operation_get_type_operation (ModestMailOperation *self)
471 {
472         ModestMailOperationPrivate *priv;
473
474         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
475                               MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
476         
477         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
478         
479         return priv->op_type;
480 }
481
482 gboolean 
483 modest_mail_operation_is_mine (ModestMailOperation *self, 
484                                GObject *me)
485 {
486         ModestMailOperationPrivate *priv;
487
488         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
489                               FALSE);
490         
491         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
492         if (priv->source == NULL) return FALSE;
493
494         return priv->source == me;
495 }
496
497 GObject *
498 modest_mail_operation_get_source (ModestMailOperation *self)
499 {
500         ModestMailOperationPrivate *priv;
501
502         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
503                               NULL);
504         
505         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
506         if (!priv) {
507                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
508                 return NULL;
509         }
510         
511         return (priv->source) ? g_object_ref (priv->source) : NULL;
512 }
513
514 ModestMailOperationStatus
515 modest_mail_operation_get_status (ModestMailOperation *self)
516 {
517         ModestMailOperationPrivate *priv;
518
519         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
520         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
521                               MODEST_MAIL_OPERATION_STATUS_INVALID);
522
523         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
524         if (!priv) {
525                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
526                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
527         }
528         
529         return priv->status;
530 }
531
532 const GError *
533 modest_mail_operation_get_error (ModestMailOperation *self)
534 {
535         ModestMailOperationPrivate *priv;
536
537         g_return_val_if_fail (self, NULL);
538         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
539
540         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
541
542         if (!priv) {
543                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
544                 return NULL;
545         }
546
547         return priv->error;
548 }
549
550 gboolean 
551 modest_mail_operation_cancel (ModestMailOperation *self)
552 {
553         ModestMailOperationPrivate *priv;
554         gboolean canceled = FALSE;
555         
556         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION (self), FALSE);
557
558         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
559
560         /* Set new status */
561         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
562         
563         /* Cancel the mail operation */
564         g_return_val_if_fail (priv->account, FALSE);
565         tny_account_cancel (priv->account);
566
567         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SEND) {
568                 ModestTnySendQueue *queue;
569                 queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (priv->account),
570                                                        TRUE);
571
572                 /* Cancel the sending of the following next messages */
573                 if (TNY_IS_SEND_QUEUE (queue))
574                         tny_send_queue_cancel (TNY_SEND_QUEUE (queue), TNY_SEND_QUEUE_CANCEL_ACTION_SUSPEND, NULL);
575         }
576         
577         return canceled;
578 }
579
580 guint 
581 modest_mail_operation_get_task_done (ModestMailOperation *self)
582 {
583         ModestMailOperationPrivate *priv;
584
585         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
586                               0);
587         
588         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
589         return priv->done;
590 }
591
592 guint 
593 modest_mail_operation_get_task_total (ModestMailOperation *self)
594 {
595         ModestMailOperationPrivate *priv;
596
597         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
598                               0);
599
600         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
601         return priv->total;
602 }
603
604 gboolean
605 modest_mail_operation_is_finished (ModestMailOperation *self)
606 {
607         ModestMailOperationPrivate *priv;
608         gboolean retval = FALSE;
609
610         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
611                               FALSE);
612         
613         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
614
615         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
616             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
617             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
618             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
619                 retval = TRUE;
620         } else {
621                 retval = FALSE;
622         }
623
624         return retval;
625 }
626
627 /*
628  * Creates an image of the current state of a mail operation, the
629  * caller must free it
630  */
631 static ModestMailOperationState *
632 modest_mail_operation_clone_state (ModestMailOperation *self)
633 {
634         ModestMailOperationState *state;
635         ModestMailOperationPrivate *priv;
636
637         g_return_val_if_fail (self, NULL);
638         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
639         g_return_val_if_fail (priv, NULL);
640
641         if (!priv)
642                 return NULL;
643
644         state = g_slice_new (ModestMailOperationState);
645
646         state->status = priv->status;
647         state->op_type = priv->op_type;
648         state->done = priv->done;
649         state->total = priv->total;
650         state->finished = modest_mail_operation_is_finished (self);
651         state->bytes_done = 0;
652         state->bytes_total = 0;
653
654         return state;
655 }
656
657 /* ******************************************************************* */
658 /* ************************** SEND   ACTIONS ************************* */
659 /* ******************************************************************* */
660
661 typedef struct 
662 {
663         ModestMailOperation *mail_op;
664         gboolean notify;
665 } SendNewMailHelper;
666
667 static void
668 send_mail_on_sync_async_cb (TnyFolder *folder, 
669                             gboolean cancelled, 
670                             GError *err, 
671                             gpointer user_data)
672 {
673         ModestMailOperationPrivate *priv;
674         ModestMailOperation *self;
675         SendNewMailHelper *helper;
676
677         helper = (SendNewMailHelper *) user_data;
678         self = helper->mail_op;
679         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
680
681         if (cancelled)
682                 goto end;
683
684         if (err) {
685                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
686                              MODEST_MAIL_OPERATION_ERROR_SEND_QUEUE_ADD_ERROR,
687                              "Error adding a msg to the send queue\n");
688                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
689         } else {
690                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
691         }
692
693  end:
694         if (helper->notify)
695                 modest_mail_operation_notify_end (self);
696
697         g_object_unref (helper->mail_op);
698         g_slice_free (SendNewMailHelper, helper);
699 }
700
701 static void
702 run_queue_start (TnySendQueue *self,
703                  gpointer user_data)
704 {
705         RunQueueHelper *helper = (RunQueueHelper *) user_data;
706         ModestMailOperation *mail_op;
707                         
708         g_debug ("%s sending queue successfully started", __FUNCTION__);
709
710         /* Wait for the message to be sent */
711         mail_op = modest_mail_operation_new (NULL);
712         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
713                                          mail_op);
714         modest_mail_operation_run_queue (mail_op, helper->queue);
715         g_object_unref (mail_op);
716
717         /* Free the helper and end operation */
718         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
719 }
720
721 static void 
722 run_queue_error_happened (TnySendQueue *queue, 
723                           TnyHeader *header, 
724                           TnyMsg *msg, 
725                           GError *error, 
726                           gpointer user_data)
727 {
728         RunQueueHelper *helper = (RunQueueHelper *) user_data;
729         ModestMailOperationPrivate *priv;
730
731         /* If we are here this means that the send queue could not
732            start to send emails. Shouldn't happen as this means that
733            we could not create the thread */
734         g_debug ("%s sending queue failed to create the thread", __FUNCTION__);
735
736         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
737         priv->error = g_error_copy ((const GError *) error);
738
739         if (error->code != TNY_SYSTEM_ERROR_UNKNOWN) {
740                 /* This code is here for safety reasons. It should
741                    never be called, because that would mean that we
742                    are not controlling some error case */
743                 g_warning ("%s Error %s should not happen", 
744                            __FUNCTION__, error->message);
745         }
746
747         /* Free helper and end operation */
748         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_FAILED);
749 }
750
751 static void
752 send_mail_on_added_to_outbox (TnySendQueue *send_queue, 
753                               gboolean cancelled, 
754                               TnyMsg *msg, 
755                               GError *err,
756                               gpointer user_data)
757 {
758         ModestMailOperationPrivate *priv;
759         ModestMailOperation *self;
760         SendNewMailHelper *helper;
761
762         helper = (SendNewMailHelper *) user_data;
763         self = helper->mail_op;
764         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
765
766         if (cancelled)
767                 goto end;
768
769         if (err) {
770                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
771                              MODEST_MAIL_OPERATION_ERROR_SEND_QUEUE_ADD_ERROR,
772                              "Error adding a msg to the send queue\n");
773                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
774         } else {
775                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
776         }
777
778  end:
779         if (helper->notify) {
780                 TnyTransportAccount *trans_account;
781                 ModestTnySendQueue *queue;
782
783                 trans_account = (TnyTransportAccount *) modest_mail_operation_get_account (self);
784                 if (trans_account) {
785                         queue = modest_runtime_get_send_queue (trans_account, TRUE);
786                         if (queue) {
787                                 RunQueueHelper *helper;
788
789                                 /* Create the helper */
790                                 helper = g_slice_new0 (RunQueueHelper);
791                                 helper->queue = g_object_ref (queue);
792                                 helper->self = g_object_ref (self);
793
794                                 /* if sending is ongoing wait for the queue to
795                                    stop. Otherwise wait for the queue-start
796                                    signal. It could happen that the queue
797                                    could not start, then check also the error
798                                    happened signal */
799                                 if (modest_tny_send_queue_sending_in_progress (queue)) {
800                                         run_queue_start (TNY_SEND_QUEUE (queue), helper);
801                                 } else {
802                                         helper->start_handler = g_signal_connect (queue, "queue-start", 
803                                                                                   G_CALLBACK (run_queue_start), 
804                                                                                   helper);
805                                         helper->error_handler = g_signal_connect (queue, "error-happened", 
806                                                                                   G_CALLBACK (run_queue_error_happened), 
807                                                                                   helper);
808                                 }
809                         } else {
810                                 /* Finalize this mail operation */
811                                 modest_mail_operation_notify_end (self);
812                         }
813                         g_object_unref (trans_account);
814                 } else {
815                         g_warning ("No transport account for the operation");
816                 }
817         }
818
819         g_object_unref (helper->mail_op);
820         g_slice_free (SendNewMailHelper, helper);
821 }
822
823 static gboolean
824 idle_create_msg_cb (gpointer idle_data)
825 {
826         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
827
828         /* This is a GDK lock because we are an idle callback and
829          * info->callback can contain Gtk+ code */
830
831         gdk_threads_enter (); /* CHECKED */
832         info->callback (info->mail_op, info->msg, info->userdata);
833
834         g_object_unref (info->mail_op);
835         if (info->msg)
836                 g_object_unref (info->msg);
837         g_slice_free (CreateMsgIdleInfo, info);
838         gdk_threads_leave (); /* CHECKED */
839
840         return FALSE;
841 }
842
843 static gpointer 
844 create_msg_thread (gpointer thread_data)
845 {
846         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
847         TnyMsg *new_msg = NULL;
848         ModestMailOperationPrivate *priv;
849         gint attached = 0;
850
851         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
852         if (info->html_body == NULL) {
853                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
854                                               info->bcc, info->subject, 
855                                               info->references, info->in_reply_to,
856                                               info->plain_body, 
857                                               info->attachments_list, &attached,
858                                               info->header_pairs,
859                                               &(priv->error));
860         } else {
861                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
862                                                          info->bcc, info->subject, 
863                                                          info->references, info->in_reply_to,
864                                                          info->html_body,
865                                                          info->plain_body, info->attachments_list,
866                                                          info->images_list, &attached,
867                                                          info->header_pairs,
868                                                          &(priv->error));
869         }
870
871         if (new_msg) {
872                 TnyHeader *header = tny_msg_get_header (new_msg);
873
874                 /* Set priority flags in message */
875                 if (info->priority_flags != TNY_HEADER_FLAG_NORMAL_PRIORITY)
876                         tny_header_set_flag (header, info->priority_flags);
877
878                 /* Set attachment flags in message */
879                 if (info->attachments_list != NULL && attached > 0)
880                         tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
881
882                 g_object_unref (G_OBJECT(header));
883         } else {
884                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
885                 if (!priv->error)
886                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
887                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
888                                      "modest: failed to create a new msg\n");
889         }
890
891
892         g_free (info->to);
893         g_free (info->from);
894         g_free (info->cc);
895         g_free (info->bcc);
896         g_free (info->plain_body);
897         g_free (info->html_body);
898         g_free (info->subject);
899         g_free (info->references);
900         g_free (info->in_reply_to);
901         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
902         g_list_free (info->attachments_list);
903         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
904         g_list_free (info->images_list);
905         g_object_unref (info->header_pairs);
906
907         if (info->callback) {
908                 CreateMsgIdleInfo *idle_info;
909                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
910                 idle_info->mail_op = g_object_ref (info->mail_op);
911                 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
912                 idle_info->callback = info->callback;
913                 idle_info->userdata = info->userdata;
914                 g_idle_add (idle_create_msg_cb, idle_info);
915         } else {
916                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
917         }
918
919         g_object_unref (info->mail_op);
920         g_slice_free (CreateMsgInfo, info);
921         if (new_msg) g_object_unref(new_msg);
922         return NULL;
923 }
924
925
926 void
927 modest_mail_operation_create_msg (ModestMailOperation *self,
928                                   const gchar *from, const gchar *to,
929                                   const gchar *cc, const gchar *bcc,
930                                   const gchar *subject, const gchar *plain_body,
931                                   const gchar *html_body,
932                                   const GList *attachments_list,
933                                   const GList *images_list,
934                                   TnyHeaderFlags priority_flags,
935                                   const gchar *references,
936                                   const gchar *in_reply_to,
937                                   TnyList *header_pairs,
938                                   ModestMailOperationCreateMsgCallback callback,
939                                   gpointer userdata)
940 {
941         CreateMsgInfo *info = NULL;
942
943         info = g_slice_new0 (CreateMsgInfo);
944         info->mail_op = g_object_ref (self);
945
946         info->from = g_strdup (from);
947         info->to = g_strdup (to);
948         info->cc = g_strdup (cc);
949         info->bcc  = g_strdup (bcc);
950         info->subject = g_strdup (subject);
951         info->plain_body = g_strdup (plain_body);
952         info->html_body = g_strdup (html_body);
953         info->references = g_strdup (references);
954         info->in_reply_to = g_strdup (in_reply_to);
955         info->attachments_list = g_list_copy ((GList *) attachments_list);
956         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
957         info->images_list = g_list_copy ((GList *) images_list);
958         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
959         info->priority_flags = 0 | priority_flags;
960         info->header_pairs = tny_list_copy (header_pairs);
961
962         info->callback = callback;
963         info->userdata = userdata;
964
965         g_thread_create (create_msg_thread, info, FALSE, NULL);
966 }
967
968 typedef struct
969 {
970         TnyTransportAccount *transport_account;
971         TnyMsg *draft_msg;
972 } SendNewMailInfo;
973
974 static void
975 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
976                                         TnyMsg *msg,
977                                         gpointer userdata)
978 {
979         TnySendQueue *send_queue = NULL;
980         ModestMailOperationPrivate *priv = NULL;
981         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
982         TnyFolder *draft_folder = NULL;
983         TnyFolder *outbox_folder = NULL;
984         TnyHeader *header = NULL;
985
986         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
987
988         if (!msg) {
989                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
990                 modest_mail_operation_notify_end (self);
991                 goto end;
992         }
993
994         if (priv->error && priv->error->code != MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
995                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
996                 modest_mail_operation_notify_end (self);
997                 goto end;
998         }
999
1000         /* Add message to send queue */
1001         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (info->transport_account, TRUE));
1002         if (!TNY_IS_SEND_QUEUE(send_queue)) {
1003                 if (priv->error) {
1004                         g_error_free (priv->error);
1005                         priv->error = NULL;
1006                 }
1007                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1008                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1009                              "modest: could not find send queue for account\n");
1010                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1011                 modest_mail_operation_notify_end (self);
1012                 goto end;
1013         } else {
1014                 SendNewMailHelper *helper = g_slice_new (SendNewMailHelper);
1015                 helper->mail_op = g_object_ref (self);
1016                 helper->notify = (info->draft_msg == NULL);
1017
1018                 /* Add the msg to the queue. The callback will free
1019                    the helper */
1020                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1021                                                                   FALSE);
1022                 tny_send_queue_add_async (send_queue, msg, send_mail_on_added_to_outbox, 
1023                                           NULL, helper);
1024         }
1025
1026         if (info->draft_msg != NULL) {
1027                 TnyList *tmp_headers = NULL;
1028                 TnyFolder *folder = NULL;
1029                 TnyFolder *src_folder = NULL;
1030                 TnyFolderType folder_type;              
1031                 TnyTransportAccount *transport_account = NULL;
1032                 SendNewMailHelper *helper = NULL;
1033
1034                 /* To remove the old mail from its source folder, we need to get the
1035                  * transport account of the original draft message (the transport account
1036                  * might have been changed by the user) */
1037                 header = tny_msg_get_header (info->draft_msg);
1038                 transport_account = modest_tny_account_store_get_transport_account_from_outbox_header(
1039                         modest_runtime_get_account_store(), header);
1040                 if (transport_account == NULL)
1041                         transport_account = g_object_ref(info->transport_account);
1042                 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
1043                                                                       TNY_FOLDER_TYPE_DRAFTS);
1044                 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
1045                                                                        TNY_FOLDER_TYPE_OUTBOX);
1046                 g_object_unref(transport_account);
1047
1048                 if (!draft_folder) {
1049                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL drafts folder",
1050                                    __FUNCTION__);
1051                         modest_mail_operation_notify_end (self);
1052                         goto end;
1053                 }
1054                 if (!outbox_folder) {
1055                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL outbox folder",
1056                                    __FUNCTION__);
1057                         modest_mail_operation_notify_end (self);
1058                         goto end;
1059                 }
1060
1061                 folder = tny_msg_get_folder (info->draft_msg);          
1062                 if (folder == NULL) {
1063                         modest_mail_operation_notify_end (self);
1064                         goto end;
1065                 }
1066                 folder_type = modest_tny_folder_guess_folder_type (folder);
1067
1068                 if (folder_type == TNY_FOLDER_TYPE_INVALID)
1069                         g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1070                 
1071                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
1072                         src_folder = outbox_folder;
1073                 else 
1074                         src_folder = draft_folder;
1075
1076                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
1077                  * because this function requires it to have a UID. */
1078                 helper = g_slice_new (SendNewMailHelper);
1079                 helper->mail_op = g_object_ref (self);
1080                 helper->notify = TRUE;
1081
1082                 tmp_headers = tny_simple_list_new ();
1083                 tny_list_append (tmp_headers, (GObject*) header);
1084                 tny_folder_remove_msgs_async (src_folder, tmp_headers, NULL, NULL, NULL);
1085                 g_object_unref (tmp_headers);
1086                 tny_folder_sync_async (src_folder, TRUE, send_mail_on_sync_async_cb, 
1087                                        NULL, helper);
1088                 g_object_unref (folder);
1089         }
1090
1091 end:
1092         if (header)
1093                 g_object_unref (header);
1094         if (info->draft_msg)
1095                 g_object_unref (info->draft_msg);
1096         if (draft_folder)
1097                 g_object_unref (draft_folder);
1098         if (outbox_folder)
1099                 g_object_unref (outbox_folder);
1100         if (info->transport_account)
1101                 g_object_unref (info->transport_account);
1102         g_slice_free (SendNewMailInfo, info);
1103 }
1104
1105 void
1106 modest_mail_operation_send_mail (ModestMailOperation *self,
1107                                  TnyTransportAccount *transport_account,
1108                                  TnyMsg *msg)
1109 {
1110         TnySendQueue *send_queue = NULL;
1111         ModestMailOperationPrivate *priv = NULL;
1112
1113         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1114
1115         if (!msg) {
1116                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1117                 modest_mail_operation_notify_end (self);
1118                 return;
1119         }
1120
1121         if (priv->error && priv->error->code != MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1122                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1123                 modest_mail_operation_notify_end (self);
1124                 return;
1125         }
1126
1127         /* Add message to send queue */
1128         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account, TRUE));
1129         if (!TNY_IS_SEND_QUEUE(send_queue)) {
1130                 if (priv->error) {
1131                         g_error_free (priv->error);
1132                         priv->error = NULL;
1133                 }
1134                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1135                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1136                              "modest: could not find send queue for account\n");
1137                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1138                 modest_mail_operation_notify_end (self);
1139                 return;
1140         } else {
1141                 SendNewMailHelper *helper = g_slice_new (SendNewMailHelper);
1142                 helper->mail_op = g_object_ref (self);
1143                 helper->notify = TRUE;
1144
1145                 /* Add the msg to the queue. The callback will free
1146                    the helper */
1147                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1148                                                                   FALSE);
1149                 tny_send_queue_add_async (send_queue, msg, send_mail_on_added_to_outbox, 
1150                                           NULL, helper);
1151         }
1152
1153 }
1154
1155 void
1156 modest_mail_operation_send_new_mail (ModestMailOperation *self,
1157                                      TnyTransportAccount *transport_account,
1158                                      TnyMsg *draft_msg,
1159                                      const gchar *from,  const gchar *to,
1160                                      const gchar *cc,  const gchar *bcc,
1161                                      const gchar *subject, const gchar *plain_body,
1162                                      const gchar *html_body,
1163                                      const GList *attachments_list,
1164                                      const GList *images_list,
1165                                      const gchar *references,
1166                                      const gchar *in_reply_to,
1167                                      TnyHeaderFlags priority_flags,
1168                                      TnyList *header_pairs)
1169 {
1170         ModestMailOperationPrivate *priv = NULL;
1171         SendNewMailInfo *info;
1172
1173         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1174         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1175
1176         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1177         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1178         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
1179         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1180
1181         modest_mail_operation_notify_start (self);
1182
1183         /* Check parametters */
1184         if (to == NULL) {
1185                 /* Set status failed and set an error */
1186                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1187                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1188                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1189                              _("Error trying to send a mail. You need to set at least one recipient"));
1190                 modest_mail_operation_notify_end (self);
1191                 return;
1192         }
1193         info = g_slice_new0 (SendNewMailInfo);
1194         info->transport_account = transport_account;
1195         if (transport_account)
1196                 g_object_ref (transport_account);
1197         info->draft_msg = draft_msg;
1198         if (draft_msg)
1199                 g_object_ref (draft_msg);
1200
1201
1202         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1203                                           attachments_list, images_list, priority_flags,
1204                                           references, in_reply_to,
1205                                           header_pairs,
1206                                           modest_mail_operation_send_new_mail_cb, info);
1207
1208 }
1209
1210 typedef struct
1211 {
1212         ModestMailOperation *mailop;
1213         TnyMsg *msg;
1214         SaveToDraftstCallback callback;
1215         gpointer userdata;
1216 } FinishSaveRemoteDraftInfo;
1217
1218 static void
1219 finish_save_remote_draft (ModestAccountProtocol *protocol,
1220                           GError *err,
1221                           const gchar *account_id,
1222                           TnyMsg *new_remote_msg,
1223                           TnyMsg *new_msg,
1224                           TnyMsg *old_msg,
1225                           gpointer userdata)
1226 {
1227         FinishSaveRemoteDraftInfo *info = (FinishSaveRemoteDraftInfo *) userdata;
1228         ModestMailOperationPrivate *priv = NULL;
1229
1230         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1231
1232         if (!priv->error && err != NULL) {
1233                 /* Priority for errors in save to local stage */
1234                 priv->error = g_error_copy (err);
1235                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1236         }
1237
1238         if (info->callback)
1239                 info->callback (info->mailop, info->msg, info->userdata);
1240
1241         if (info->msg)
1242                 g_object_unref (info->msg);
1243
1244         modest_mail_operation_notify_end (info->mailop);
1245         g_object_unref (info->mailop);
1246
1247         g_slice_free (FinishSaveRemoteDraftInfo, info);
1248 }
1249
1250 typedef struct
1251 {
1252         TnyTransportAccount *transport_account;
1253         TnyMsg *draft_msg;
1254         SaveToDraftstCallback callback;
1255         gpointer user_data;
1256         TnyFolder *drafts;
1257         TnyMsg *msg;
1258         ModestMailOperation *mailop;
1259 } SaveToDraftsAddMsgInfo;
1260
1261 static void
1262 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
1263                                                 gboolean canceled,
1264                                                 GError *err,
1265                                                 gpointer userdata)
1266 {
1267         ModestMailOperationPrivate *priv = NULL;
1268         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
1269         GError *io_error = NULL;
1270         gboolean callback_called = FALSE;
1271
1272         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1273
1274         if (priv->error && priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1275                 io_error = priv->error;
1276                 priv->error = NULL;
1277         }
1278         if (priv->error) {
1279                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
1280                 g_error_free(priv->error);
1281         }
1282
1283         priv->error = (err == NULL) ? NULL : g_error_copy(err);
1284
1285         if ((!priv->error) && (info->draft_msg != NULL)) {
1286                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
1287                 TnyFolder *src_folder = tny_header_get_folder (header);
1288
1289                 g_debug ("--- REMOVE AND SYNC");
1290                 /* Remove the old draft */
1291                 tny_folder_remove_msg (src_folder, header, NULL);
1292
1293                 /* Synchronize to expunge and to update the msg counts */
1294                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1295                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1296                 g_debug ("--- REMOVED - SYNCED");
1297
1298                 g_object_unref (G_OBJECT(header));
1299                 g_object_unref (G_OBJECT(src_folder));
1300         }
1301
1302         if (priv->error) {
1303                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1304                 if (io_error) {
1305                         g_error_free (io_error);
1306                         io_error = NULL;
1307                 }
1308         } else if (io_error) {
1309                 priv->error = io_error;
1310                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
1311         } else {
1312                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1313         }
1314
1315         if (info->transport_account) {
1316                 ModestProtocolType transport_protocol_type;
1317                 ModestProtocol *transport_protocol;
1318
1319                 transport_protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (info->transport_account));
1320
1321                 transport_protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1322                                                                                     transport_protocol_type);
1323                 if (transport_protocol && MODEST_IS_ACCOUNT_PROTOCOL (transport_protocol)) {
1324                         FinishSaveRemoteDraftInfo *srd_info = g_slice_new (FinishSaveRemoteDraftInfo);
1325                         srd_info->mailop = info->mailop?g_object_ref (info->mailop):NULL;
1326                         srd_info->msg = info->msg?g_object_ref (info->msg):NULL;
1327                         srd_info->callback = info->callback;
1328                         srd_info->userdata = info->user_data;
1329                         modest_account_protocol_save_remote_draft (MODEST_ACCOUNT_PROTOCOL (transport_protocol), 
1330                                                                    tny_account_get_id (TNY_ACCOUNT (info->transport_account)),
1331                                                                    info->msg, info->draft_msg,
1332                                                                    finish_save_remote_draft,
1333                                                                    srd_info);
1334                                                                    
1335                         callback_called = TRUE;
1336                 }
1337         }
1338
1339         /* Call the user callback */
1340         if (!callback_called && info->callback)
1341                 info->callback (info->mailop, info->msg, info->user_data);
1342
1343         if (info->transport_account)
1344                 g_object_unref (G_OBJECT(info->transport_account));
1345         if (info->draft_msg)
1346                 g_object_unref (G_OBJECT (info->draft_msg));
1347         if (info->drafts)
1348                 g_object_unref (G_OBJECT(info->drafts));
1349         if (info->msg)
1350                 g_object_unref (G_OBJECT (info->msg));
1351
1352         if (!callback_called)
1353                 modest_mail_operation_notify_end (info->mailop);
1354         if (info->mailop)
1355                 g_object_unref(info->mailop);
1356         g_slice_free (SaveToDraftsAddMsgInfo, info);
1357 }
1358
1359 typedef struct
1360 {
1361         TnyTransportAccount *transport_account;
1362         TnyMsg *draft_msg;
1363         SaveToDraftstCallback callback;
1364         gpointer user_data;
1365 } SaveToDraftsInfo;
1366
1367 static void
1368 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1369                                          TnyMsg *msg,
1370                                          gpointer userdata)
1371 {
1372         TnyFolder *drafts = NULL;
1373         ModestMailOperationPrivate *priv = NULL;
1374         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1375
1376         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1377
1378         if (!msg) {
1379                 if (!(priv->error)) {
1380                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1381                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1382                                      "modest: failed to create a new msg\n");
1383                 }
1384         } else {
1385                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1386                                                                 TNY_FOLDER_TYPE_DRAFTS);
1387                 if (!drafts && !(priv->error)) {
1388                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1389                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1390                                      "modest: failed to create a new msg\n");
1391                 }
1392         }
1393
1394         if (!priv->error || priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1395                 if (drafts) {
1396                         SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1397                         cb_info->transport_account = g_object_ref(info->transport_account);
1398                         cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1399                         cb_info->callback = info->callback;
1400                         cb_info->user_data = info->user_data;
1401                         cb_info->drafts = g_object_ref(drafts);
1402                         cb_info->msg = g_object_ref(msg);
1403                         cb_info->mailop = g_object_ref(self);
1404                         tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1405                                                  NULL, cb_info);
1406                 }
1407         } else {
1408                 /* Call the user callback */
1409                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1410                 if (info->callback)
1411                         info->callback (self, msg, info->user_data);
1412                 modest_mail_operation_notify_end (self);
1413         }
1414
1415         if (drafts)
1416                 g_object_unref (G_OBJECT(drafts));
1417         if (info->draft_msg)
1418                 g_object_unref (G_OBJECT (info->draft_msg));
1419         if (info->transport_account)
1420                 g_object_unref (G_OBJECT(info->transport_account));
1421         g_slice_free (SaveToDraftsInfo, info);
1422 }
1423
1424 void
1425 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1426                                       TnyTransportAccount *transport_account,
1427                                       TnyMsg *draft_msg,
1428                                       const gchar *from,  const gchar *to,
1429                                       const gchar *cc,  const gchar *bcc,
1430                                       const gchar *subject, const gchar *plain_body,
1431                                       const gchar *html_body,
1432                                       const GList *attachments_list,
1433                                       const GList *images_list,
1434                                       TnyHeaderFlags priority_flags,
1435                                       const gchar *references,
1436                                       const gchar *in_reply_to,
1437                                       TnyList *header_pairs,
1438                                       SaveToDraftstCallback callback,
1439                                       gpointer user_data)
1440 {
1441         ModestMailOperationPrivate *priv = NULL;
1442         SaveToDraftsInfo *info = NULL;
1443
1444         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1445         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1446
1447         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1448
1449         /* Get account and set it into mail_operation */
1450         priv->account = g_object_ref (transport_account);
1451         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1452
1453         info = g_slice_new0 (SaveToDraftsInfo);
1454         info->transport_account = g_object_ref (transport_account);
1455         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1456         info->callback = callback;
1457         info->user_data = user_data;
1458
1459         g_debug ("--- CREATE MESSAGE");
1460         modest_mail_operation_notify_start (self);
1461         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1462                                           attachments_list, images_list, priority_flags,
1463                                           references, in_reply_to,
1464                                           header_pairs,
1465                                           modest_mail_operation_save_to_drafts_cb, info);
1466 }
1467
1468 typedef struct
1469 {
1470         ModestMailOperation *mail_op;
1471         TnyMimePart *mime_part;
1472         gssize size;
1473         GetMimePartSizeCallback callback;
1474         gpointer userdata;
1475 } GetMimePartSizeInfo;
1476
1477 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1478 /* We use this folder observer to track the headers that have been
1479  * added to a folder */
1480 typedef struct {
1481         GObject parent;
1482         TnyList *new_headers;
1483 } InternalFolderObserver;
1484
1485 typedef struct {
1486         GObjectClass parent;
1487 } InternalFolderObserverClass;
1488
1489 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1490
1491 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1492                          internal_folder_observer,
1493                          G_TYPE_OBJECT,
1494                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1495
1496
1497 static void
1498 foreach_add_item (gpointer header, gpointer user_data)
1499 {
1500         tny_list_append (TNY_LIST (user_data), G_OBJECT (header));
1501 }
1502
1503 /* This is the method that looks for new messages in a folder */
1504 static void
1505 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1506 {
1507         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1508         
1509         TnyFolderChangeChanged changed;
1510
1511         changed = tny_folder_change_get_changed (change);
1512
1513         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1514                 TnyList *list;
1515
1516                 /* Get added headers */
1517                 list = tny_simple_list_new ();
1518                 tny_folder_change_get_added_headers (change, list);
1519
1520                 /* Add them to the folder observer */
1521                 tny_list_foreach (list, foreach_add_item, 
1522                                   derived->new_headers);
1523
1524                 g_object_unref (G_OBJECT (list));
1525         }
1526 }
1527
1528 static void
1529 internal_folder_observer_init (InternalFolderObserver *self) 
1530 {
1531         self->new_headers = tny_simple_list_new ();
1532 }
1533 static void
1534 internal_folder_observer_finalize (GObject *object) 
1535 {
1536         InternalFolderObserver *self;
1537
1538         self = (InternalFolderObserver *) object;
1539         g_object_unref (self->new_headers);
1540
1541         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1542 }
1543 static void
1544 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1545 {
1546         iface->update = internal_folder_observer_update;
1547 }
1548 static void
1549 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1550 {
1551         GObjectClass *object_class;
1552
1553         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1554         object_class = (GObjectClass*) klass;
1555         object_class->finalize = internal_folder_observer_finalize;
1556 }
1557
1558 static void
1559 destroy_update_account_info (UpdateAccountInfo *info)
1560 {
1561         g_free (info->account_name);
1562         g_object_unref (info->folders);
1563         g_object_unref (info->mail_op);
1564         g_slice_free (UpdateAccountInfo, info);
1565 }
1566
1567
1568 static void
1569 update_account_send_mail (UpdateAccountInfo *info)
1570 {
1571         TnyTransportAccount *transport_account = NULL;
1572         ModestTnyAccountStore *account_store;
1573
1574         if (info->update_folder_counts)
1575                 return;
1576
1577         account_store = modest_runtime_get_account_store ();
1578
1579         /* We don't try to send messages while sending mails is blocked */
1580         if (modest_tny_account_store_is_send_mail_blocked (account_store))
1581                 return;
1582
1583         /* Get the transport account */
1584         transport_account = (TnyTransportAccount *) 
1585                 modest_tny_account_store_get_server_account (account_store, info->account_name, 
1586                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1587
1588         if (transport_account) {
1589                 ModestTnySendQueue *send_queue;
1590                 TnyFolder *outbox;
1591                 guint num_messages;
1592
1593                 send_queue = modest_runtime_get_send_queue (transport_account, TRUE);
1594                 g_object_unref (transport_account);
1595
1596                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1597                         /* Get outbox folder */
1598                         outbox = tny_send_queue_get_outbox (TNY_SEND_QUEUE (send_queue));
1599                         if (outbox) { /* this could fail in some cases */
1600                                 num_messages = tny_folder_get_all_count (outbox);
1601                                 g_object_unref (outbox);
1602                         } else {
1603                                 g_warning ("%s: could not get outbox", __FUNCTION__);
1604                                 num_messages = 0;
1605                         }
1606
1607                         if (num_messages != 0) {
1608                                 ModestMailOperation *mail_op;
1609                                 /* Reenable suspended items */
1610                                 mail_op = modest_mail_operation_new (NULL);
1611                                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1612                                                                  mail_op);
1613                                 modest_mail_operation_queue_wakeup (mail_op, MODEST_TNY_SEND_QUEUE (send_queue));
1614
1615                                 /* Try to send */
1616                                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1617                                                                                   info->interactive);
1618                         }
1619                 }
1620         }
1621 }
1622
1623 static void
1624 update_account_get_msg_async_cb (TnyFolder *folder, 
1625                                  gboolean canceled, 
1626                                  TnyMsg *msg, 
1627                                  GError *err, 
1628                                  gpointer user_data)
1629 {
1630         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1631         ModestMailOperationPrivate *priv;
1632
1633         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (msg_info->mail_op);
1634         priv->done++;
1635
1636         if (TNY_IS_MSG (msg)) {
1637                 TnyHeader *header = tny_msg_get_header (msg);
1638
1639                 if (header) {
1640                         ModestMailOperationState *state;
1641                         state = modest_mail_operation_clone_state (msg_info->mail_op);
1642                         msg_info->sum_total_bytes += tny_header_get_message_size (header);
1643                         state->bytes_done = msg_info->sum_total_bytes;
1644                         state->bytes_total = msg_info->total_bytes;
1645
1646                         /* Notify the status change. Only notify about changes
1647                            referred to bytes */
1648                         g_signal_emit (G_OBJECT (msg_info->mail_op), 
1649                                        signals[PROGRESS_CHANGED_SIGNAL], 
1650                                        0, state, NULL);
1651
1652                         g_object_unref (header);
1653                         g_slice_free (ModestMailOperationState, state);
1654                 }
1655         }
1656
1657         if (priv->done == priv->total) {
1658                 TnyList *new_headers;
1659                 UpdateAccountInfo *info;
1660
1661                 /* After getting all the messages send the ones in the
1662                    outboxes */
1663                 info = (UpdateAccountInfo *) msg_info->user_data;
1664                 update_account_send_mail (info);
1665
1666                 /* Check if the operation was a success */
1667                 if (!priv->error)
1668                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1669                 
1670                 /* Call the user callback and free */
1671                 new_headers = tny_iterator_get_list (msg_info->more_msgs);
1672                 update_account_notify_user_and_free (info, new_headers);
1673
1674                 /* Delete the helper */
1675                 if (msg_info->msg)
1676                         g_object_unref (msg_info->msg);
1677                 g_object_unref (msg_info->more_msgs);
1678                 g_object_unref (msg_info->mail_op);
1679                 g_slice_free (GetMsgInfo, msg_info);
1680         }
1681 }
1682
1683 static void
1684 update_account_notify_user_and_free (UpdateAccountInfo *info, 
1685                                      TnyList *new_headers)
1686 {
1687         /* Set the account back to not busy */
1688         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), 
1689                                              info->account_name, FALSE);
1690         
1691         /* User callback */
1692         if (info->callback)
1693                 info->callback (info->mail_op, new_headers, info->user_data);
1694         
1695         /* Mail operation end */
1696         modest_mail_operation_notify_end (info->mail_op);
1697
1698         /* Frees */
1699         if (new_headers)
1700                 g_object_unref (new_headers);
1701         destroy_update_account_info (info);
1702 }
1703
1704 static void
1705 inbox_refreshed_cb (TnyFolder *inbox, 
1706                     gboolean canceled, 
1707                     GError *err, 
1708                     gpointer user_data)
1709 {       
1710         UpdateAccountInfo *info;
1711         ModestMailOperationPrivate *priv;
1712         TnyIterator *new_headers_iter;
1713         GPtrArray *new_headers_array = NULL;
1714         gint max_size = G_MAXINT, retrieve_limit, i;
1715         ModestAccountMgr *mgr;
1716         ModestAccountRetrieveType retrieve_type;
1717         TnyList *new_headers = NULL;
1718         gboolean headers_only;
1719         time_t time_to_store;
1720
1721         info = (UpdateAccountInfo *) user_data;
1722         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1723         mgr = modest_runtime_get_account_mgr ();
1724
1725         if (canceled || err) {
1726                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1727                 if (err)
1728                         priv->error = g_error_copy (err);
1729                 else
1730                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1731                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1732                                      "canceled");
1733
1734                 if (inbox)
1735                         tny_folder_remove_observer (inbox, info->inbox_observer);
1736                 g_object_unref (info->inbox_observer);
1737                 info->inbox_observer = NULL;
1738
1739                 /* Notify the user about the error and then exit */
1740                 update_account_notify_user_and_free (info, NULL);
1741                 return;
1742         }
1743
1744         if (!inbox) {
1745                 /* Try to send anyway */
1746                 goto send_mail;
1747         }
1748
1749         if (!info->update_folder_counts) {
1750                 /* Set the last updated as the current time */
1751 #ifdef MODEST_USE_LIBTIME
1752                 struct tm utc_tm;
1753                 time_get_utc (&utc_tm);
1754                 time_to_store = time_mktime (&utc_tm, "GMT");
1755 #else
1756                 time_to_store = time (NULL);
1757 #endif
1758                 modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time_to_store);
1759
1760                 /* Get the message max size */
1761                 max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1762                                                  MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1763                 if (max_size == 0)
1764                         max_size = G_MAXINT;
1765                 else
1766                         max_size = max_size * KB;
1767         }
1768
1769         /* Create the new headers array. We need it to sort the
1770            new headers by date */
1771         new_headers_array = g_ptr_array_new ();
1772         if (info->inbox_observer) {
1773                 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1774                 if (!tny_iterator_is_done (new_headers_iter)) {
1775                         modest_platform_emit_folder_updated_signal (info->account_name, tny_folder_get_id (TNY_FOLDER (inbox)));
1776                         modest_account_mgr_set_has_new_mails (modest_runtime_get_account_mgr (),
1777                                                               info->account_name, TRUE);
1778                 }
1779                 while (!tny_iterator_is_done (new_headers_iter)) {
1780                         TnyHeader *header = NULL;
1781
1782                         header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1783                         /* Apply per-message size limits */
1784                         if (tny_header_get_message_size (header) < max_size)
1785                                 g_ptr_array_add (new_headers_array, g_object_ref (header));
1786
1787                         g_object_unref (header);
1788                         tny_iterator_next (new_headers_iter);
1789                 }
1790                 g_object_unref (new_headers_iter);
1791
1792                 tny_folder_remove_observer (inbox, info->inbox_observer);
1793                 g_object_unref (info->inbox_observer);
1794                 info->inbox_observer = NULL;
1795         }
1796
1797         if (new_headers_array->len == 0) {
1798                 g_ptr_array_free (new_headers_array, FALSE);
1799                 goto send_mail;
1800         }
1801
1802         /* Get per-account message amount retrieval limit */
1803         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1804         if (retrieve_limit == 0)
1805                 retrieve_limit = G_MAXINT;
1806
1807         /* Get per-account retrieval type */
1808         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1809         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1810
1811         /* Order by date */
1812         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1813
1814         /* Copy the headers to a list and free the array */
1815         new_headers = tny_simple_list_new ();
1816         for (i=0; i < new_headers_array->len; i++) {
1817                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1818                 /* We want the first element to be the most recent
1819                    one, that's why we reverse the list */
1820                 tny_list_prepend (new_headers, G_OBJECT (header));
1821         }
1822         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1823         g_ptr_array_free (new_headers_array, FALSE);
1824
1825         if (!headers_only && (tny_list_get_length (new_headers) > 0)) {
1826                 gint msg_num = 0;
1827                 TnyIterator *iter;
1828                 GetMsgInfo *msg_info;
1829
1830                 priv->done = 0;
1831                 priv->total = MIN (tny_list_get_length (new_headers), retrieve_limit);
1832
1833                 iter = tny_list_create_iterator (new_headers);
1834
1835                 /* Create the message info */
1836                 msg_info = g_slice_new0 (GetMsgInfo);
1837                 msg_info->mail_op = g_object_ref (info->mail_op);
1838                 msg_info->total_bytes = compute_message_list_size (new_headers, priv->total);
1839                 msg_info->more_msgs = g_object_ref (iter);
1840                 msg_info->msg = NULL;
1841                 msg_info->user_data = info;
1842
1843                 while ((msg_num < priv->total ) && !tny_iterator_is_done (iter)) {
1844                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1845                         TnyFolder *folder = tny_header_get_folder (header);
1846
1847                         /* Get message in an async way */
1848                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb,
1849                                                   NULL, msg_info);
1850
1851                         g_object_unref (folder);
1852                         g_object_unref (header);
1853
1854                         msg_num++;
1855                         tny_iterator_next (iter);
1856                 }
1857                 g_object_unref (iter);
1858                 g_object_unref (new_headers);
1859
1860                 /* The mail operation will finish when the last
1861                    message is retrieved */
1862                 return;
1863         }
1864  send_mail:
1865         /* If we don't have to retrieve the new messages then
1866            simply send mail */
1867         update_account_send_mail (info);
1868
1869         /* Check if the operation was a success */
1870         if (!priv->error)
1871                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1872
1873         /* Call the user callback and free */
1874         update_account_notify_user_and_free (info, new_headers);
1875 }
1876
1877 static void
1878 inbox_refresh_status_update (GObject *obj,
1879                              TnyStatus *status,
1880                              gpointer user_data)
1881 {
1882         UpdateAccountInfo *info = NULL;
1883         ModestMailOperation *self = NULL;
1884         ModestMailOperationPrivate *priv = NULL;
1885         ModestMailOperationState *state;
1886
1887         g_return_if_fail (user_data != NULL);
1888         g_return_if_fail (status != NULL);
1889
1890         /* Show only the status information we want */
1891         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
1892                 return;
1893
1894         info = (UpdateAccountInfo *) user_data;
1895         self = info->mail_op;
1896         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1897
1898         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1899
1900         priv->done = status->position;
1901         priv->total = status->of_total;
1902
1903         state = modest_mail_operation_clone_state (self);
1904
1905         /* This is not a GDK lock because we are a Tinymail callback and
1906          * Tinymail already acquires the Gdk lock */
1907         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1908
1909         g_slice_free (ModestMailOperationState, state);
1910 }
1911
1912 static void 
1913 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1914                           gboolean canceled,
1915                           TnyList *list, 
1916                           GError *err, 
1917                           gpointer user_data)
1918 {
1919         UpdateAccountInfo *info;
1920         ModestMailOperationPrivate *priv;
1921     
1922         info = (UpdateAccountInfo *) user_data;
1923         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1924
1925         if (err || canceled) {
1926                 /* If the error was previosly set by another callback
1927                    don't set it again */
1928                 if (!priv->error) {
1929                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1930                         if (err)
1931                                 priv->error = g_error_copy (err);
1932                         else
1933                                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1934                                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1935                                              "canceled");
1936                 }
1937         } else { 
1938                 /* We're not getting INBOX children if we don't want to poke all */
1939                 TnyIterator *iter = tny_list_create_iterator (list);
1940                 while (!tny_iterator_is_done (iter)) {
1941                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1942
1943                         /* Add to the list of all folders */
1944                         tny_list_append (info->folders, (GObject *) folder);
1945                         
1946                         if (info->poke_all) {
1947                                 TnyList *folders = tny_simple_list_new ();
1948                                 /* Add pending call */
1949                                 info->pending_calls++;
1950                                 
1951                                 tny_folder_store_get_folders_async (folder, folders, NULL, FALSE,
1952                                                                     recurse_folders_async_cb, 
1953                                                                     NULL, info);
1954                                 g_object_unref (folders);
1955                         }
1956                         
1957                         g_object_unref (G_OBJECT (folder));
1958                         
1959                         tny_iterator_next (iter);           
1960                 }
1961                 g_object_unref (G_OBJECT (iter));
1962         }
1963
1964         /* Remove my own pending call */
1965         info->pending_calls--;
1966
1967         /* This means that we have all the folders */
1968         if (info->pending_calls == 0) {
1969                 TnyIterator *iter_all_folders;
1970                 TnyFolder *inbox = NULL;
1971
1972                 /* If there was any error do not continue */
1973                 if (priv->error) {
1974                         update_account_notify_user_and_free (info, NULL);
1975                         return;
1976                 }
1977
1978                 iter_all_folders = tny_list_create_iterator (info->folders);
1979
1980                 /* Do a poke status over all folders */
1981                 while (!tny_iterator_is_done (iter_all_folders) &&
1982                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1983                         TnyFolder *folder = NULL;
1984
1985                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1986
1987                         if (!info->update_folder_counts && tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1988                                 /* Get a reference to the INBOX */
1989                                 inbox = g_object_ref (folder);
1990                         } else {
1991                                 /* Issue a poke status over the folder */
1992                                 if (info->poke_all)
1993                                         tny_folder_poke_status (folder);
1994                         }
1995
1996                         /* Free and go to next */
1997                         g_object_unref (folder);
1998                         tny_iterator_next (iter_all_folders);
1999                 }
2000                 g_object_unref (iter_all_folders);
2001
2002                 /* Refresh the INBOX */
2003                 if (inbox) {
2004                         /* Refresh the folder. Our observer receives
2005                          * the new emails during folder refreshes, so
2006                          * we can use observer->new_headers
2007                          */
2008                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
2009                         tny_folder_add_observer (inbox, info->inbox_observer);
2010
2011                         /* Refresh the INBOX */
2012                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, inbox_refresh_status_update, info);
2013                         g_object_unref (inbox);
2014                 } else {
2015                         /* We could not perform the inbox refresh but
2016                            we'll try to send mails anyway */
2017                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
2018                 }
2019         }
2020 }
2021
2022 void
2023 modest_mail_operation_update_account (ModestMailOperation *self,
2024                                       const gchar *account_name,
2025                                       gboolean poke_all,
2026                                       gboolean interactive,
2027                                       UpdateAccountCallback callback,
2028                                       gpointer user_data)
2029 {
2030         UpdateAccountInfo *info = NULL;
2031         ModestMailOperationPrivate *priv = NULL;
2032         ModestTnyAccountStore *account_store = NULL;
2033         TnyList *folders;
2034         ModestMailOperationState *state;
2035
2036         /* Init mail operation */
2037         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2038         priv->total = 0;
2039         priv->done  = 0;
2040         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2041         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
2042
2043         /* Get the store account */
2044         account_store = modest_runtime_get_account_store ();
2045         priv->account =
2046                 modest_tny_account_store_get_server_account (account_store,
2047                                                              account_name,
2048                                                              TNY_ACCOUNT_TYPE_STORE);
2049
2050         /* The above function could return NULL */
2051         if (!priv->account) {
2052                 /* Check if the operation was a success */
2053                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2054                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2055                              "no account");
2056                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2057
2058                 /* Call the user callback */
2059                 if (callback)
2060                         callback (self, NULL, user_data);
2061
2062                 /* Notify about operation end */
2063                 modest_mail_operation_notify_end (self);
2064
2065                 return;
2066         }
2067         
2068         /* We have once seen priv->account getting finalized during this code,
2069          * therefore adding a reference (bug #82296) */
2070         
2071         g_object_ref (priv->account);
2072
2073         /* Create the helper object */
2074         info = g_slice_new0 (UpdateAccountInfo);
2075         info->pending_calls = 1;
2076         info->folders = tny_simple_list_new ();
2077         info->mail_op = g_object_ref (self);
2078         info->poke_all = poke_all;
2079         info->interactive = interactive;
2080         info->update_folder_counts = FALSE;
2081         info->account_name = g_strdup (account_name);
2082         info->callback = callback;
2083         info->user_data = user_data;
2084
2085         /* Set account busy */
2086         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2087         modest_mail_operation_notify_start (self);
2088
2089         /* notify about the start of the operation */ 
2090         state = modest_mail_operation_clone_state (self);
2091         state->done = 0;
2092         state->total = 0;
2093
2094         /* Start notifying progress */
2095         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2096         g_slice_free (ModestMailOperationState, state);
2097         
2098         /* Get all folders and continue in the callback */ 
2099         folders = tny_simple_list_new ();
2100         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2101                                             folders, NULL, TRUE,
2102                                             recurse_folders_async_cb, 
2103                                             NULL, info);
2104         g_object_unref (folders);
2105         
2106         g_object_unref (priv->account);
2107         
2108 }
2109
2110 void
2111 modest_mail_operation_update_folder_counts (ModestMailOperation *self,
2112                                       const gchar *account_name)
2113 {
2114         UpdateAccountInfo *info = NULL;
2115         ModestMailOperationPrivate *priv = NULL;
2116         ModestTnyAccountStore *account_store = NULL;
2117         TnyList *folders;
2118         ModestMailOperationState *state;
2119
2120         /* Init mail operation */
2121         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2122         priv->total = 0;
2123         priv->done  = 0;
2124         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2125         priv->op_type = MODEST_MAIL_OPERATION_TYPE_UPDATE_FOLDER_COUNTS;
2126
2127         /* Get the store account */
2128         account_store = modest_runtime_get_account_store ();
2129         priv->account =
2130                 modest_tny_account_store_get_server_account (account_store,
2131                                                              account_name,
2132                                                              TNY_ACCOUNT_TYPE_STORE);
2133
2134         /* The above function could return NULL */
2135         if (!priv->account) {
2136                 /* Check if the operation was a success */
2137                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2138                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2139                              "no account");
2140                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2141
2142                 /* Notify about operation end */
2143                 modest_mail_operation_notify_end (self);
2144
2145                 return;
2146         }
2147         
2148         /* We have once seen priv->account getting finalized during this code,
2149          * therefore adding a reference (bug #82296) */
2150         
2151         g_object_ref (priv->account);
2152
2153         /* Create the helper object */
2154         info = g_slice_new0 (UpdateAccountInfo);
2155         info->pending_calls = 1;
2156         info->folders = tny_simple_list_new ();
2157         info->mail_op = g_object_ref (self);
2158         info->poke_all = TRUE;
2159         info->interactive = FALSE;
2160         info->update_folder_counts = TRUE;
2161         info->account_name = g_strdup (account_name);
2162         info->callback = NULL;
2163         info->user_data = NULL;
2164
2165         /* Set account busy */
2166         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2167         modest_mail_operation_notify_start (self);
2168
2169         /* notify about the start of the operation */ 
2170         state = modest_mail_operation_clone_state (self);
2171         state->done = 0;
2172         state->total = 0;
2173
2174         /* Start notifying progress */
2175         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2176         g_slice_free (ModestMailOperationState, state);
2177         
2178         /* Get all folders and continue in the callback */ 
2179         folders = tny_simple_list_new ();
2180         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2181                                             folders, NULL, TRUE,
2182                                             recurse_folders_async_cb, 
2183                                             NULL, info);
2184         g_object_unref (folders);
2185         
2186         g_object_unref (priv->account);
2187         
2188 }
2189
2190 /*
2191  * Used to notify the queue from the main
2192  * loop. We call it inside an idle call to achieve that
2193  */
2194 static gboolean
2195 idle_notify_queue (gpointer data)
2196 {
2197         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
2198
2199         gdk_threads_enter ();
2200         modest_mail_operation_notify_end (mail_op);
2201         gdk_threads_leave ();
2202         g_object_unref (mail_op);
2203
2204         return FALSE;
2205 }
2206
2207 static int
2208 compare_headers_by_date (gconstpointer a,
2209                          gconstpointer b)
2210 {
2211         TnyHeader **header1, **header2;
2212         time_t sent1, sent2;
2213
2214         header1 = (TnyHeader **) a;
2215         header2 = (TnyHeader **) b;
2216
2217         sent1 = tny_header_get_date_sent (*header1);
2218         sent2 = tny_header_get_date_sent (*header2);
2219
2220         /* We want the most recent ones (greater time_t) at the
2221            beginning */
2222         if (sent1 < sent2)
2223                 return -1;
2224         else
2225                 return 1;
2226 }
2227
2228
2229 /* ******************************************************************* */
2230 /* ************************** STORE  ACTIONS ************************* */
2231 /* ******************************************************************* */
2232
2233 typedef struct {
2234         ModestMailOperation *mail_op;
2235         CreateFolderUserCallback callback;
2236         gpointer user_data;
2237 } CreateFolderInfo;
2238
2239
2240 static void
2241 create_folder_cb (TnyFolderStore *parent_folder, 
2242                   gboolean canceled, 
2243                   TnyFolder *new_folder, 
2244                   GError *err, 
2245                   gpointer user_data)
2246 {
2247         ModestMailOperationPrivate *priv;
2248         CreateFolderInfo *info;
2249
2250         info = (CreateFolderInfo *) user_data;
2251         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2252
2253         if (canceled || err) {
2254                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2255                 if (err)
2256                         priv->error = g_error_copy (err);
2257                 else
2258                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2259                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2260                                      "canceled");               
2261         } else {
2262                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2263         }
2264
2265         /* The user will unref the new_folder */
2266         if (info->callback)
2267                 info->callback (info->mail_op, parent_folder, 
2268                                 new_folder, info->user_data);
2269         
2270         /* Notify about operation end */
2271         modest_mail_operation_notify_end (info->mail_op);
2272
2273         /* Frees */
2274         g_object_unref (info->mail_op);
2275         g_slice_free (CreateFolderInfo, info);
2276 }
2277
2278 void
2279 modest_mail_operation_create_folder (ModestMailOperation *self,
2280                                      TnyFolderStore *parent,
2281                                      const gchar *name,
2282                                      CreateFolderUserCallback callback,
2283                                      gpointer user_data)
2284 {
2285         ModestMailOperationPrivate *priv;
2286
2287         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2288         g_return_if_fail (name);
2289         
2290         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2291         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2292         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
2293                 g_object_ref (parent) : 
2294                 modest_tny_folder_get_account (TNY_FOLDER (parent));
2295
2296         /* Check for already existing folder */
2297         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
2298                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2299                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2300                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2301                              _CS_FOLDER_ALREADY_EXISTS);
2302         }
2303
2304         /* Check parent */
2305         if (TNY_IS_FOLDER (parent)) {
2306                 /* Check folder rules */
2307                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2308                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2309                         /* Set status failed and set an error */
2310                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2311                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2312                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2313                                      _("mail_in_ui_folder_create_error"));
2314                 }
2315         }
2316
2317         if (!priv->error && (!strcmp (name, " ") || strchr (name, '/'))) {
2318                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2319                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2320                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2321                              _("mail_in_ui_folder_create_error"));
2322         }
2323
2324         if (!priv->error) {
2325                 CreateFolderInfo *info;
2326
2327                 info = g_slice_new0 (CreateFolderInfo);
2328                 info->mail_op = g_object_ref (self);
2329                 info->callback = callback;
2330                 info->user_data = user_data;
2331
2332                 modest_mail_operation_notify_start (self);
2333
2334                 /* Create the folder */
2335                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
2336                                                       NULL, info);
2337         } else {
2338                 /* Call the user callback anyway */
2339                 if (callback)
2340                         callback (self, parent, NULL, user_data);
2341                 /* Notify about operation end */
2342                 modest_mail_operation_notify_end (self);
2343         }
2344 }
2345
2346 void
2347 modest_mail_operation_remove_folder (ModestMailOperation *self,
2348                                      TnyFolder           *folder,
2349                                      gboolean             remove_to_trash)
2350 {
2351         ModestMailOperationPrivate *priv;
2352         ModestTnyFolderRules rules;
2353
2354         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2355         g_return_if_fail (TNY_IS_FOLDER (folder));
2356         
2357         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2358         
2359         /* Check folder rules */
2360         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2361         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
2362                 /* Set status failed and set an error */
2363                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2364                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2365                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2366                              _("mail_in_ui_folder_delete_error"));
2367                 goto end;
2368         }
2369
2370         /* Get the account */
2371         priv->account = modest_tny_folder_get_account (folder);
2372         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2373
2374         /* Delete folder or move to trash */
2375         if (remove_to_trash) {
2376                 TnyFolder *trash_folder = NULL;
2377                 trash_folder = modest_tny_account_get_special_folder (priv->account,
2378                                                                       TNY_FOLDER_TYPE_TRASH);
2379                 /* TODO: error_handling */
2380                 if (trash_folder) {
2381                         modest_mail_operation_notify_start (self);
2382                         modest_mail_operation_xfer_folder (self, folder,
2383                                                     TNY_FOLDER_STORE (trash_folder), 
2384                                                     TRUE, NULL, NULL);
2385                         g_object_unref (trash_folder);
2386                 } else {
2387                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
2388                 }
2389         } else {
2390                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
2391                 if (parent) {
2392                         modest_mail_operation_notify_start (self);
2393                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
2394                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
2395                         
2396                         if (!priv->error)
2397                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2398
2399                         g_object_unref (parent);
2400                 } else
2401                         g_warning ("%s: could not get parent folder", __FUNCTION__);
2402         }
2403
2404  end:
2405         /* Notify about operation end */
2406         modest_mail_operation_notify_end (self);
2407 }
2408
2409 static void
2410 transfer_folder_status_cb (GObject *obj,
2411                            TnyStatus *status,
2412                            gpointer user_data)
2413 {
2414         ModestMailOperation *self;
2415         ModestMailOperationPrivate *priv;
2416         ModestMailOperationState *state;
2417         XFerFolderAsyncHelper *helper;
2418
2419         g_return_if_fail (status != NULL);
2420
2421         /* Show only the status information we want */
2422         if (status->code != TNY_FOLDER_STATUS_CODE_COPY_FOLDER)
2423                 return;
2424
2425         helper = (XFerFolderAsyncHelper *) user_data;
2426         g_return_if_fail (helper != NULL);
2427
2428         self = helper->mail_op;
2429         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2430
2431         priv->done = status->position;
2432         priv->total = status->of_total;
2433
2434         state = modest_mail_operation_clone_state (self);
2435
2436         /* This is not a GDK lock because we are a Tinymail callback
2437          * which is already GDK locked by Tinymail */
2438
2439         /* no gdk_threads_enter (), CHECKED */
2440
2441         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
2442
2443         /* no gdk_threads_leave (), CHECKED */
2444
2445         g_slice_free (ModestMailOperationState, state);
2446 }
2447
2448 static void
2449 transfer_folder_cb (TnyFolder *folder, 
2450                     gboolean cancelled, 
2451                     TnyFolderStore *into, 
2452                     TnyFolder *new_folder, 
2453                     GError *err, 
2454                     gpointer user_data)
2455 {
2456         XFerFolderAsyncHelper *helper;
2457         ModestMailOperation *self = NULL;
2458         ModestMailOperationPrivate *priv = NULL;
2459
2460         helper = (XFerFolderAsyncHelper *) user_data;
2461         g_return_if_fail (helper != NULL);
2462
2463         self = helper->mail_op;
2464         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2465
2466         if (err) {
2467                 priv->error = g_error_copy (err);
2468                 priv->done = 0;
2469                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2470         } else if (cancelled) {
2471                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2472                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2473                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2474                              _("Transference of %s was cancelled."),
2475                              tny_folder_get_name (folder));
2476         } else {
2477                 priv->done = 1;
2478                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2479         }
2480
2481         /* Update state of new folder */
2482         if (new_folder) {
2483                 tny_folder_refresh_async (new_folder, NULL, NULL, NULL);
2484                 tny_folder_poke_status (new_folder);
2485         }
2486
2487         /* Notify about operation end */
2488         modest_mail_operation_notify_end (self);
2489
2490         /* If user defined callback function was defined, call it */
2491         if (helper->user_callback) {
2492
2493                 /* This is not a GDK lock because we are a Tinymail callback
2494                  * which is already GDK locked by Tinymail */
2495
2496                 /* no gdk_threads_enter (), CHECKED */
2497                 helper->user_callback (self, new_folder, helper->user_data);
2498                 /* no gdk_threads_leave () , CHECKED */
2499         }
2500
2501         /* Free */
2502         g_object_unref (helper->mail_op);
2503         g_slice_free   (XFerFolderAsyncHelper, helper);
2504 }
2505
2506 /**
2507  *
2508  * This function checks if the new name is a valid name for our local
2509  * folders account. The new name could not be the same than then name
2510  * of any of the mandatory local folders
2511  *
2512  * We can not rely on tinymail because tinymail does not check the
2513  * name of the virtual folders that the account could have in the case
2514  * that we're doing a rename (because it directly calls Camel which
2515  * knows nothing about our virtual folders). 
2516  *
2517  * In the case of an actual copy/move (i.e. move/copy a folder between
2518  * accounts) tinymail uses the tny_folder_store_create_account which
2519  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2520  * checks the new name of the folder, so this call in that case
2521  * wouldn't be needed. *But* NOTE that if tinymail changes its
2522  * implementation (if folder transfers within the same account is no
2523  * longer implemented as a rename) this call will allow Modest to work
2524  * perfectly
2525  *
2526  * If the new name is not valid, this function will set the status to
2527  * failed and will set also an error in the mail operation
2528  */
2529 static gboolean
2530 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2531                                  TnyFolderStore *into,
2532                                  const gchar *new_name)
2533 {
2534         if (TNY_IS_ACCOUNT (into) && 
2535             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2536             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2537                                                                  new_name)) {
2538                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2539                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2540                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2541                              _CS_FOLDER_ALREADY_EXISTS);
2542                 return FALSE;
2543         } else
2544                 return TRUE;
2545 }
2546
2547 void
2548 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2549                                    TnyFolder *folder,
2550                                    TnyFolderStore *parent,
2551                                    gboolean delete_original,
2552                                    XferFolderAsyncUserCallback user_callback,
2553                                    gpointer user_data)
2554 {
2555         ModestMailOperationPrivate *priv = NULL;
2556         ModestTnyFolderRules parent_rules = 0, rules; 
2557         XFerFolderAsyncHelper *helper = NULL;
2558         const gchar *folder_name = NULL;
2559         const gchar *error_msg;
2560
2561         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2562         g_return_if_fail (TNY_IS_FOLDER (folder));
2563         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2564
2565         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2566         folder_name = tny_folder_get_name (folder);
2567
2568         /* Set the error msg */
2569         error_msg = _("mail_in_ui_folder_move_target_error");
2570
2571         /* Get account and set it into mail_operation */
2572         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2573         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2574         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2575
2576         /* Get folder rules */
2577         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2578         if (TNY_IS_FOLDER (parent))
2579                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2580         
2581         /* Apply operation constraints */
2582         if ((gpointer) parent == (gpointer) folder ||
2583             (!TNY_IS_FOLDER_STORE (parent)) || 
2584             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2585                 /* Folder rules */
2586                 goto error;
2587         } else if (TNY_IS_FOLDER (parent) && 
2588                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2589                 /* Folder rules */
2590                 goto error;
2591
2592         } else if (TNY_IS_FOLDER (parent) &&
2593                    TNY_IS_FOLDER_STORE (folder) &&
2594                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2595                                                   TNY_FOLDER_STORE (folder))) {
2596                 /* Do not move a parent into a child */
2597                 goto error;
2598         } else if (TNY_IS_FOLDER_STORE (parent) &&
2599                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2600                 /* Check that the new folder name is not used by any
2601                    parent subfolder */
2602                 goto error;     
2603         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2604                 /* Check that the new folder name is not used by any
2605                    special local folder */
2606                 goto error;
2607         } else {
2608                 /* Create the helper */
2609                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2610                 helper->mail_op = g_object_ref (self);
2611                 helper->user_callback = user_callback;
2612                 helper->user_data = user_data;
2613                 
2614                 /* Move/Copy folder */
2615                 modest_mail_operation_notify_start (self);
2616                 tny_folder_copy_async (folder,
2617                                        parent,
2618                                        tny_folder_get_name (folder),
2619                                        delete_original,
2620                                        transfer_folder_cb,
2621                                        transfer_folder_status_cb,
2622                                        helper);
2623                 return;
2624         }
2625
2626  error:
2627         /* Set status failed and set an error */
2628         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2629         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2630                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2631                      "%s", error_msg);
2632
2633         /* Call the user callback if exists */
2634         if (user_callback)
2635                 user_callback (self, NULL, user_data);
2636
2637         /* Notify the queue */
2638         modest_mail_operation_notify_end (self);
2639 }
2640
2641 void
2642 modest_mail_operation_rename_folder (ModestMailOperation *self,
2643                                      TnyFolder *folder,
2644                                      const gchar *name,
2645                                      XferFolderAsyncUserCallback user_callback,
2646                                      gpointer user_data)
2647 {
2648         ModestMailOperationPrivate *priv;
2649         ModestTnyFolderRules rules;
2650         XFerFolderAsyncHelper *helper;
2651
2652         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2653         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2654         g_return_if_fail (name);
2655         
2656         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2657
2658         /* Get account and set it into mail_operation */
2659         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2660         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2661
2662         /* Check folder rules */
2663         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2664         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2665                 goto error;
2666         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2667                 goto error;
2668         } else {
2669                 TnyFolderStore *into;
2670
2671                 into = tny_folder_get_folder_store (folder);    
2672
2673                 /* Check that the new folder name is not used by any
2674                    special local folder */
2675                 if (new_name_valid_if_local_account (priv, into, name)) {
2676                         /* Create the helper */
2677                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2678                         helper->mail_op = g_object_ref(self);
2679                         helper->user_callback = user_callback;
2680                         helper->user_data = user_data;
2681                 
2682                         /* Rename. Camel handles folder subscription/unsubscription */
2683                         modest_mail_operation_notify_start (self);
2684                         tny_folder_copy_async (folder, into, name, TRUE,
2685                                                transfer_folder_cb,
2686                                                transfer_folder_status_cb,
2687                                                helper);
2688                         g_object_unref (into);
2689                 } else {
2690                         g_object_unref (into);
2691                         goto error;
2692                 }
2693
2694                 return;
2695         }
2696  error:
2697         /* Set status failed and set an error */
2698         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2699         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2700                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2701                      _("FIXME: unable to rename"));
2702         
2703         if (user_callback)
2704                 user_callback (self, NULL, user_data);
2705
2706         /* Notify about operation end */
2707         modest_mail_operation_notify_end (self);
2708 }
2709
2710 /* ******************************************************************* */
2711 /* **************************  MSG  ACTIONS  ************************* */
2712 /* ******************************************************************* */
2713
2714 void 
2715 modest_mail_operation_find_msg (ModestMailOperation *self,
2716                                 TnyFolder *folder,
2717                                 const gchar *msg_uid,
2718                                 gboolean progress_feedback,
2719                                 GetMsgAsyncUserCallback user_callback,
2720                                 gpointer user_data)
2721 {
2722         GetMsgInfo *helper = NULL;
2723         ModestMailOperationPrivate *priv;
2724         
2725         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2726         g_return_if_fail (msg_uid != NULL);
2727         
2728         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2729         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2730         priv->total = 1;
2731         priv->done = 0;
2732
2733         /* Check memory low */
2734         if (_check_memory_low (self)) {
2735                 if (user_callback)
2736                         user_callback (self, NULL, FALSE, NULL, priv->error, user_data);
2737                 modest_mail_operation_notify_end (self);
2738                 return;
2739         }
2740
2741         /* Get account and set it into mail_operation */
2742         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2743         
2744         /* Check for cached messages */
2745         if (progress_feedback) {
2746                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2747         } else {
2748                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2749         }
2750         
2751         /* Create the helper */
2752         helper = g_slice_new0 (GetMsgInfo);
2753         helper->header = NULL;
2754         helper->mail_op = g_object_ref (self);
2755         helper->user_callback = user_callback;
2756         helper->user_data = user_data;
2757         helper->destroy_notify = NULL;
2758         helper->last_total_bytes = 0;
2759         helper->sum_total_bytes = 0;
2760         helper->total_bytes = 0;
2761         helper->more_msgs = NULL;
2762         helper->get_parts = NULL;
2763         helper->msg = NULL;
2764
2765         modest_mail_operation_notify_start (self);
2766         
2767         /* notify about the start of the operation */ 
2768         ModestMailOperationState *state;
2769         state = modest_mail_operation_clone_state (self);
2770         state->done = 0;
2771         state->total = 0;
2772         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2773                                 0, state, NULL);
2774         g_slice_free (ModestMailOperationState, state);
2775         
2776         tny_folder_find_msg_async (folder, msg_uid, get_msg_async_cb, get_msg_status_cb, helper);
2777 }
2778
2779 void 
2780 modest_mail_operation_get_msg (ModestMailOperation *self,
2781                                TnyHeader *header,
2782                                gboolean progress_feedback,
2783                                GetMsgAsyncUserCallback user_callback,
2784                                gpointer user_data)
2785 {
2786         GetMsgInfo *helper = NULL;
2787         TnyFolder *folder;
2788         ModestMailOperationPrivate *priv;
2789         
2790         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2791         g_return_if_fail (TNY_IS_HEADER (header));
2792         
2793         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2794         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2795         priv->total = 1;
2796         priv->done = 0;
2797
2798         /* Check memory low */
2799         if (_check_memory_low (self)) {
2800                 if (user_callback)
2801                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2802                 modest_mail_operation_notify_end (self);
2803                 return;
2804         }
2805
2806         /* Get account and set it into mail_operation */
2807         folder = tny_header_get_folder (header);
2808         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2809         
2810         /* Check for cached messages */
2811         if (progress_feedback) {
2812                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2813                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2814                 else 
2815                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2816         } else {
2817                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2818         }
2819         
2820         /* Create the helper */
2821         helper = g_slice_new0 (GetMsgInfo);
2822         helper->header = g_object_ref (header);
2823         helper->mail_op = g_object_ref (self);
2824         helper->user_callback = user_callback;
2825         helper->user_data = user_data;
2826         helper->destroy_notify = NULL;
2827         helper->last_total_bytes = 0;
2828         helper->sum_total_bytes = 0;
2829         helper->total_bytes = tny_header_get_message_size (header);
2830         helper->more_msgs = NULL;
2831         helper->get_parts = NULL;
2832         helper->msg = NULL;
2833
2834         modest_mail_operation_notify_start (self);
2835         
2836         /* notify about the start of the operation */ 
2837         ModestMailOperationState *state;
2838         state = modest_mail_operation_clone_state (self);
2839         state->done = 0;
2840         state->total = 0;
2841         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2842                                 0, state, NULL);
2843         g_slice_free (ModestMailOperationState, state);
2844         
2845         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2846
2847         g_object_unref (G_OBJECT (folder));
2848 }
2849
2850 void 
2851 modest_mail_operation_get_msg_and_parts (ModestMailOperation *self,
2852                                          TnyHeader *header,
2853                                          TnyList *parts,
2854                                          gboolean progress_feedback,
2855                                          GetMsgAsyncUserCallback user_callback,
2856                                          gpointer user_data)
2857 {
2858         GetMsgInfo *helper = NULL;
2859         TnyFolder *folder;
2860         ModestMailOperationPrivate *priv;
2861         
2862         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2863         g_return_if_fail (TNY_IS_HEADER (header));
2864         
2865         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2866         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2867         priv->total = 1;
2868         priv->done = 0;
2869
2870         /* Check memory low */
2871         if (_check_memory_low (self)) {
2872                 if (user_callback)
2873                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2874                 modest_mail_operation_notify_end (self);
2875                 return;
2876         }
2877
2878         /* Get account and set it into mail_operation */
2879         folder = tny_header_get_folder (header);
2880         if (folder == NULL && MODEST_IS_MSG_VIEW_WINDOW (priv->source)) {
2881                 const gchar *acc_name;
2882                 acc_name = modest_window_get_active_account (MODEST_WINDOW (priv->source));
2883                 priv->account = modest_tny_account_store_get_server_account
2884                         (modest_runtime_get_account_store (),
2885                          acc_name,
2886                          TNY_ACCOUNT_TYPE_STORE);
2887                 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (priv->account), 
2888                                                                        modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (priv->source)));
2889         } else {
2890                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2891         }
2892         
2893         /* Check for cached messages */
2894         if (progress_feedback) {
2895                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2896                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2897                 else 
2898                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2899         } else {
2900                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2901         }
2902         
2903         /* Create the helper */
2904         helper = g_slice_new0 (GetMsgInfo);
2905         helper->header = g_object_ref (header);
2906         helper->mail_op = g_object_ref (self);
2907         helper->user_callback = user_callback;
2908         helper->user_data = user_data;
2909         helper->destroy_notify = NULL;
2910         helper->last_total_bytes = 0;
2911         helper->sum_total_bytes = 0;
2912         helper->total_bytes = tny_header_get_message_size (header);
2913         helper->more_msgs = NULL;
2914         helper->get_parts = tny_list_create_iterator (parts);
2915         helper->msg = NULL;
2916
2917         modest_mail_operation_notify_start (self);
2918         
2919         /* notify about the start of the operation */ 
2920         ModestMailOperationState *state;
2921         state = modest_mail_operation_clone_state (self);
2922         state->done = 0;
2923         state->total = 0;
2924         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2925                                 0, state, NULL);
2926         g_slice_free (ModestMailOperationState, state);
2927         
2928         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2929
2930         g_object_unref (G_OBJECT (folder));
2931 }
2932
2933 static void     
2934 get_msg_status_cb (GObject *obj,
2935                    TnyStatus *status,  
2936                    gpointer user_data)
2937 {
2938         GetMsgInfo *helper = NULL;
2939
2940         g_return_if_fail (status != NULL);
2941
2942         /* Show only the status information we want */
2943         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2944                 return;
2945
2946         helper = (GetMsgInfo *) user_data;
2947         g_return_if_fail (helper != NULL);       
2948
2949         /* Notify progress */
2950         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2951                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2952 }
2953
2954 static void
2955 get_msg_async_get_part_cb (TnyMimePart *self, gboolean cancelled, TnyStream *stream, GError *err, gpointer user_data)
2956 {
2957         GetMsgInfo *helper;
2958         TnyFolder *folder = NULL;
2959
2960         helper = (GetMsgInfo *) user_data;
2961
2962         if (helper->header) {
2963                 folder = tny_header_get_folder (helper->header);
2964         }
2965
2966         get_msg_async_cb (folder, cancelled, helper->msg, err, user_data);
2967
2968         if (folder) g_object_unref (folder);
2969 }
2970
2971 static void
2972 get_msg_async_cb (TnyFolder *folder, 
2973                   gboolean canceled, 
2974                   TnyMsg *msg, 
2975                   GError *err, 
2976                   gpointer user_data)
2977 {
2978         GetMsgInfo *info = NULL;
2979         ModestMailOperationPrivate *priv = NULL;
2980         gboolean finished;
2981
2982         info = (GetMsgInfo *) user_data;
2983
2984         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2985         priv->done++;
2986
2987         if (info->more_msgs) {
2988                 tny_iterator_next (info->more_msgs);
2989                 finished = (tny_iterator_is_done (info->more_msgs));
2990         } else if (info->get_parts) {
2991                 tny_iterator_next (info->get_parts);
2992                 finished = (tny_iterator_is_done (info->get_parts));
2993         } else {
2994                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2995         }
2996
2997         /* If canceled by the user, ignore the error given by Tinymail */
2998         if (canceled) {
2999                 canceled = TRUE;
3000                 finished = TRUE;
3001                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3002         } else if (err) {
3003                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3004                 priv->error = g_error_copy ((const GError *) err);
3005                 if (priv->error) {
3006                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3007                 } else {
3008                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3009                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3010                                      "%s", err->message);
3011                 }
3012         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
3013                 /* Set the success status before calling the user callback */
3014                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3015         }
3016
3017         if (info->header == NULL && msg)
3018                 info->header = tny_msg_get_header (msg);
3019
3020         /* Call the user callback */
3021         if (info->user_callback && (finished || (info->get_parts == NULL)))
3022                 info->user_callback (info->mail_op, info->header, canceled, 
3023                                      msg, err, info->user_data);
3024
3025         /* Notify about operation end if this is the last callback */
3026         if (finished) {
3027                 /* Free user data */
3028                 if (info->destroy_notify)
3029                         info->destroy_notify (info->user_data);
3030
3031                 /* Notify about operation end */
3032                 modest_mail_operation_notify_end (info->mail_op);
3033
3034                 /* Clean */
3035                 if (info->msg)
3036                         g_object_unref (info->msg);
3037                 if (info->more_msgs)
3038                         g_object_unref (info->more_msgs);
3039                 if (info->header)
3040                         g_object_unref (info->header);
3041                 g_object_unref (info->mail_op);
3042                 g_slice_free (GetMsgInfo, info);
3043         } else if (info->get_parts) {
3044                 CamelStream *null_stream;
3045                 TnyStream *tny_null_stream;
3046                 TnyMimePart *part;
3047
3048                 if (info->msg == NULL && msg != NULL)
3049                         info->msg = g_object_ref (msg);
3050
3051                 null_stream = camel_stream_null_new ();
3052                 tny_null_stream = tny_camel_stream_new (null_stream);
3053                 
3054                 part = TNY_MIME_PART (tny_iterator_get_current (info->get_parts));
3055                 tny_mime_part_decode_to_stream_async (part, tny_null_stream, 
3056                                                       get_msg_async_get_part_cb,
3057                                                       get_msg_status_cb,
3058                                                       info);
3059                 g_object_unref (tny_null_stream);
3060                 g_object_unref (part);
3061
3062         } else if (info->more_msgs) {
3063                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
3064                 TnyFolder *folder = tny_header_get_folder (header);
3065
3066                 g_object_unref (info->header);
3067                 info->header = g_object_ref (header);
3068
3069                 /* Retrieve the next message */
3070                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
3071
3072                 g_object_unref (header);
3073                 g_object_unref (folder);
3074         } else {
3075                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
3076         }
3077 }
3078
3079 void 
3080 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
3081                                      TnyList *header_list, 
3082                                      GetMsgAsyncUserCallback user_callback,
3083                                      gpointer user_data,
3084                                      GDestroyNotify notify)
3085 {
3086         ModestMailOperationPrivate *priv = NULL;
3087         gint msg_list_size;
3088         TnyIterator *iter = NULL;
3089         gboolean has_uncached_messages;
3090         
3091         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3092
3093         /* Init mail operation */
3094         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3095         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3096         priv->done = 0;
3097         priv->total = tny_list_get_length(header_list);
3098
3099         /* Check memory low */
3100         if (_check_memory_low (self)) {
3101                 if (user_callback) {
3102                         TnyHeader *header = NULL;
3103                         TnyIterator *iter;
3104
3105                         if (tny_list_get_length (header_list) > 0) {
3106                                 iter = tny_list_create_iterator (header_list);
3107                                 header = (TnyHeader *) tny_iterator_get_current (iter);
3108                                 g_object_unref (iter);
3109                         }
3110                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
3111                         if (header)
3112                                 g_object_unref (header);
3113                 }
3114                 if (notify)
3115                         notify (user_data);
3116                 /* Notify about operation end */
3117                 modest_mail_operation_notify_end (self);
3118                 return;
3119         }
3120
3121         /* Check uncached messages */
3122         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
3123              !has_uncached_messages && !tny_iterator_is_done (iter); 
3124              tny_iterator_next (iter)) {
3125                 TnyHeader *header;
3126
3127                 header = (TnyHeader *) tny_iterator_get_current (iter);
3128                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
3129                         has_uncached_messages = TRUE;
3130                 g_object_unref (header);
3131         }       
3132         g_object_unref (iter);
3133         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
3134
3135         /* Get account and set it into mail_operation */
3136         if (tny_list_get_length (header_list) >= 1) {
3137                 TnyIterator *iterator = tny_list_create_iterator (header_list);
3138                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
3139                 if (header) {
3140                         TnyFolder *folder = tny_header_get_folder (header);
3141                         if (folder) {           
3142                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
3143                                 g_object_unref (folder);
3144                         }
3145                         g_object_unref (header);
3146                 }
3147                 g_object_unref (iterator);
3148         }
3149
3150         msg_list_size = compute_message_list_size (header_list, 0);
3151
3152         modest_mail_operation_notify_start (self);
3153         iter = tny_list_create_iterator (header_list);
3154         if (!tny_iterator_is_done (iter)) {
3155                 /* notify about the start of the operation */
3156                 ModestMailOperationState *state;
3157                 state = modest_mail_operation_clone_state (self);
3158                 state->done = 0;
3159                 state->total = 0;
3160                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
3161                                0, state, NULL);
3162
3163                 GetMsgInfo *msg_info = NULL;
3164                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3165                 TnyFolder *folder = tny_header_get_folder (header);
3166
3167                 /* Create the message info */
3168                 msg_info = g_slice_new0 (GetMsgInfo);
3169                 msg_info->mail_op = g_object_ref (self);
3170                 msg_info->header = g_object_ref (header);
3171                 msg_info->more_msgs = g_object_ref (iter);
3172                 msg_info->user_callback = user_callback;
3173                 msg_info->user_data = user_data;
3174                 msg_info->destroy_notify = notify;
3175                 msg_info->last_total_bytes = 0;
3176                 msg_info->sum_total_bytes = 0;
3177                 msg_info->total_bytes = msg_list_size;
3178                 msg_info->msg = NULL;
3179
3180                 /* The callback will call it per each header */
3181                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
3182
3183                 /* Free and go on */
3184                 g_object_unref (header);
3185                 g_object_unref (folder);
3186                 g_slice_free (ModestMailOperationState, state);
3187         }
3188         g_object_unref (iter);
3189 }
3190
3191
3192 static void
3193 remove_msgs_async_cb (TnyFolder *folder, 
3194                       gboolean canceled, 
3195                       GError *err, 
3196                       gpointer user_data)
3197 {
3198         gboolean expunge;
3199         const gchar *account_name;
3200         TnyAccount *account;
3201         ModestProtocolType account_proto = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
3202         ModestMailOperation *self;
3203         ModestMailOperationPrivate *priv;
3204         ModestProtocolRegistry *protocol_registry;
3205         SyncFolderHelper *helper;
3206
3207         self = (ModestMailOperation *) user_data;
3208         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3209         protocol_registry = modest_runtime_get_protocol_registry ();
3210
3211         if (canceled || err) {
3212                 /* If canceled by the user, ignore the error given by Tinymail */
3213                 if (canceled) {
3214                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3215                 } else if (err) {
3216                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3217                         priv->error = g_error_copy ((const GError *) err);
3218                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3219                 }
3220                 /* Exit */
3221                 modest_mail_operation_notify_end (self);
3222                 g_object_unref (self);
3223                 return;
3224         }
3225
3226         account = modest_tny_folder_get_account (folder);
3227         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3228         account_proto = modest_tny_account_get_protocol_type (account);
3229         g_object_unref (account);
3230
3231         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_proto)) {
3232                 if (modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3233                                                             account_name))
3234                         expunge = FALSE;
3235                 else
3236                         expunge = TRUE;
3237         } else {
3238                 expunge = TRUE;
3239         }
3240
3241         /* Create helper */
3242         helper = g_slice_new0 (SyncFolderHelper);
3243         helper->mail_op = g_object_ref (self);
3244         helper->user_callback = NULL;
3245         helper->user_data = NULL;
3246
3247         /* Sync folder */
3248         tny_folder_sync_async(folder, expunge, sync_folder_finish_callback, NULL, helper);
3249
3250         /* Remove the extra reference */
3251         g_object_unref (self);
3252 }
3253
3254 void 
3255 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
3256                                    TnyList *headers,
3257                                    gboolean remove_to_trash /*ignored*/)
3258 {
3259         TnyFolder *folder = NULL;
3260         ModestMailOperationPrivate *priv;
3261         TnyIterator *iter = NULL;
3262         TnyHeader *header = NULL;
3263         TnyList *remove_headers = NULL;
3264         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
3265         ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
3266
3267         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3268         g_return_if_fail (TNY_IS_LIST (headers));
3269
3270         if (remove_to_trash)
3271                 g_warning ("remove to trash is not implemented");
3272
3273         if (tny_list_get_length(headers) == 0) {
3274                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
3275                 goto cleanup; /* nothing to do */
3276         }
3277
3278         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3279
3280         /* Get folder from first header and sync it */
3281         iter = tny_list_create_iterator (headers);
3282         header = TNY_HEADER (tny_iterator_get_current (iter));
3283         g_object_unref (iter);
3284
3285         folder = tny_header_get_folder (header);
3286         if (!TNY_IS_FOLDER(folder)) {
3287                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
3288                 goto cleanup;
3289         }
3290
3291         /* Use the merged folder if we're removing messages from outbox */
3292         if (modest_tny_folder_is_local_folder (folder)) {
3293                 ModestTnyLocalFoldersAccount *local_account;
3294
3295                 local_account = (ModestTnyLocalFoldersAccount *)
3296                         modest_tny_account_store_get_local_folders_account (accstore);
3297                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
3298                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
3299                         g_object_unref (folder);
3300                         folder = modest_tny_local_folders_account_get_merged_outbox (local_account);
3301                 }
3302                 g_object_unref (local_account);
3303         }
3304
3305         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
3306                 TnyIterator *headers_iter = tny_list_create_iterator (headers);
3307
3308                 while (!tny_iterator_is_done (headers_iter)) {
3309                         TnyTransportAccount *traccount = NULL;
3310                         TnyHeader *hdr = NULL;
3311
3312                         hdr = TNY_HEADER (tny_iterator_get_current (headers_iter));
3313                         traccount = modest_tny_account_store_get_transport_account_from_outbox_header (accstore,
3314                                                                                                        header);
3315                         if (traccount) {
3316                                 ModestTnySendQueueStatus status;
3317                                 ModestTnySendQueue *send_queue;
3318
3319                                 send_queue = modest_runtime_get_send_queue(traccount, TRUE);
3320                                 if (TNY_IS_SEND_QUEUE (send_queue)) {
3321                                         char *msg_id;
3322
3323                                         msg_id = modest_tny_send_queue_get_msg_id (hdr);
3324                                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
3325                                         if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
3326                                                 if (G_UNLIKELY (remove_headers == NULL))
3327                                                         remove_headers = tny_simple_list_new ();
3328                                                 tny_list_append(remove_headers, G_OBJECT(hdr));
3329                                         }
3330                                         g_free(msg_id);
3331                                 }
3332                                 g_object_unref(traccount);
3333                         }
3334                         g_object_unref(hdr);
3335                         tny_iterator_next (headers_iter);
3336                 }
3337                 g_object_unref(headers_iter);
3338         }
3339
3340         /* Get account and set it into mail_operation */
3341         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
3342         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
3343         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3344
3345         if (!remove_headers)
3346                 remove_headers = g_object_ref (headers);
3347
3348         /* Notify messages are "read" */
3349         iter = tny_list_create_iterator (remove_headers);
3350         while (!tny_iterator_is_done (iter)) {
3351                 gchar *msg_uid;
3352                 TnyHeader *header;
3353
3354                 header = TNY_HEADER (tny_iterator_get_current (iter));
3355                 msg_uid =  modest_tny_folder_get_header_unique_id (header);
3356                 if (msg_uid) {
3357                         modest_platform_emit_msg_read_changed_signal (msg_uid, TRUE);
3358                         g_free (msg_uid);
3359                 }
3360                 g_object_unref (header);
3361                 tny_iterator_next (iter);
3362         }
3363         g_object_unref (iter);
3364
3365         /* remove message from folder */
3366         modest_mail_operation_notify_start (self);
3367         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
3368                                       NULL, g_object_ref (self));
3369
3370 cleanup:
3371         if (remove_headers)
3372                 g_object_unref (remove_headers);
3373         if (header)
3374                 g_object_unref (header);
3375         if (folder)
3376                 g_object_unref (folder);
3377 }
3378
3379 static void
3380 notify_progress_of_multiple_messages (ModestMailOperation *self,
3381                                       TnyStatus *status,
3382                                       gint *last_total_bytes,
3383                                       gint *sum_total_bytes,
3384                                       gint total_bytes, 
3385                                       gboolean increment_done)
3386 {
3387         ModestMailOperationPrivate *priv;
3388         ModestMailOperationState *state;
3389         gboolean is_num_bytes = FALSE;
3390
3391         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3392
3393         /* We know that tinymail sends us information about
3394          *  transferred bytes with this particular message
3395          */
3396         if (status->message)
3397                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
3398
3399         state = modest_mail_operation_clone_state (self);
3400         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
3401                 /* We know that we're in a different message when the
3402                    total number of bytes to transfer is different. Of
3403                    course it could fail if we're transferring messages
3404                    of the same size, but this is a workarround */
3405                 if (status->of_total != *last_total_bytes) {
3406                         /* We need to increment the done when there is
3407                            no information about each individual
3408                            message, we need to do this in message
3409                            transfers, and we don't do it for getting
3410                            messages */
3411                         if (increment_done)
3412                                 priv->done++;
3413                         *sum_total_bytes += *last_total_bytes;
3414                         *last_total_bytes = status->of_total;
3415                 }
3416                 state->bytes_done += status->position + *sum_total_bytes;
3417                 state->bytes_total = total_bytes;
3418
3419                 /* Notify the status change. Only notify about changes
3420                    referred to bytes */
3421                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3422                                0, state, NULL);
3423         }
3424
3425         g_slice_free (ModestMailOperationState, state);
3426 }
3427
3428 static void
3429 transfer_msgs_status_cb (GObject *obj,
3430                          TnyStatus *status,  
3431                          gpointer user_data)
3432 {
3433         XFerMsgsAsyncHelper *helper;
3434
3435         g_return_if_fail (status != NULL);
3436
3437         /* Show only the status information we want */
3438         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
3439                 return;
3440
3441         helper = (XFerMsgsAsyncHelper *) user_data;
3442         g_return_if_fail (helper != NULL);       
3443
3444         /* Notify progress */
3445         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
3446                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
3447 }
3448
3449 static void
3450 transfer_msgs_sync_folder_cb (TnyFolder *self, 
3451                               gboolean cancelled, 
3452                               GError *err, 
3453                               gpointer user_data)
3454 {
3455         XFerMsgsAsyncHelper *helper;
3456         /* We don't care here about the results of the
3457            synchronization */
3458         helper = (XFerMsgsAsyncHelper *) user_data;
3459
3460         /* Notify about operation end */
3461         modest_mail_operation_notify_end (helper->mail_op);
3462
3463         /* If user defined callback function was defined, call it */
3464         if (helper->user_callback)
3465                 helper->user_callback (helper->mail_op, helper->user_data);
3466         
3467         /* Free */
3468         if (helper->more_msgs)
3469                 g_object_unref (helper->more_msgs);
3470         if (helper->headers)
3471                 g_object_unref (helper->headers);
3472         if (helper->dest_folder)
3473                 g_object_unref (helper->dest_folder);
3474         if (helper->mail_op)
3475                 g_object_unref (helper->mail_op);
3476         g_slice_free (XFerMsgsAsyncHelper, helper);
3477 }
3478
3479 static void
3480 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
3481 {
3482         XFerMsgsAsyncHelper *helper;
3483         ModestMailOperation *self;
3484         ModestMailOperationPrivate *priv;
3485         gboolean finished = TRUE;
3486
3487         helper = (XFerMsgsAsyncHelper *) user_data;
3488         self = helper->mail_op;
3489
3490         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3491
3492         if (cancelled) {
3493                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3494         } else if (err) {
3495                 priv->error = g_error_copy (err);
3496                 priv->done = 0;
3497                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
3498         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
3499                 if (helper->more_msgs) {
3500                         /* We'll transfer the next message in the list */
3501                         tny_iterator_next (helper->more_msgs);
3502                         if (!tny_iterator_is_done (helper->more_msgs)) {
3503                                 GObject *next_header;
3504                                 g_object_unref (helper->headers);
3505                                 helper->headers = tny_simple_list_new ();
3506                                 next_header = tny_iterator_get_current (helper->more_msgs);
3507                                 tny_list_append (helper->headers, next_header);
3508                                 g_object_unref (next_header);
3509                                 finished = FALSE;
3510                         }
3511                 }
3512                 if (finished) {
3513                         priv->done = 1;
3514                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3515                 }
3516         }
3517
3518         if (finished) {
3519                 TnyAccount *src_account;
3520                 TnyAccount *dest_account;
3521
3522                 /* send the notification that the source folder might have changed */
3523                 src_account = modest_tny_folder_get_account (folder);
3524                 if (src_account) {
3525                         const gchar *src_folder_id;
3526                         const gchar *src_account_name;
3527
3528                         src_folder_id = tny_folder_get_id (folder);
3529                         src_account_name =
3530                                 modest_tny_account_get_parent_modest_account_name_for_server_account (src_account);
3531                         modest_platform_emit_folder_updated_signal (src_account_name, src_folder_id);
3532                         g_object_unref (src_account);
3533                 }
3534                 /* send the notification that the destination folder might have changed */
3535                 dest_account = modest_tny_folder_get_account (helper->dest_folder);
3536                 if (dest_account) {
3537                         const gchar *dest_folder_id;
3538                         const gchar *dest_account_name;
3539
3540                         dest_folder_id = tny_folder_get_id (helper->dest_folder);
3541                         dest_account_name =
3542                                 modest_tny_account_get_parent_modest_account_name_for_server_account (dest_account);
3543                         modest_platform_emit_folder_updated_signal (dest_account_name, dest_folder_id);
3544                         g_object_unref (dest_account);
3545                 }
3546
3547                 /* Synchronize the source folder contents. This should
3548                    be done by tinymail but the camel_folder_sync it's
3549                    actually disabled in transfer_msgs_thread_clean
3550                    because it's supposed to cause hangs */
3551                 tny_folder_sync_async (folder, helper->delete, 
3552                                        transfer_msgs_sync_folder_cb, 
3553                                        NULL, helper);
3554         } else {
3555                 /* Transfer more messages */
3556                 tny_folder_transfer_msgs_async (folder,
3557                                                 helper->headers,
3558                                                 helper->dest_folder,
3559                                                 helper->delete,
3560                                                 transfer_msgs_cb,
3561                                                 transfer_msgs_status_cb,
3562                                                 helper);
3563         }
3564 }
3565
3566 /* Computes the size of the messages the headers in the list belongs
3567    to. If num_elements is different from 0 then it only takes into
3568    account the first num_elements for the calculation */
3569 static guint
3570 compute_message_list_size (TnyList *headers, 
3571                            guint num_elements)
3572 {
3573         TnyIterator *iter;
3574         guint size = 0, element = 0;
3575
3576         /* If num_elements is not valid then take all into account */
3577         if ((num_elements <= 0) || (num_elements > tny_list_get_length (headers)))
3578                 num_elements = tny_list_get_length (headers);
3579
3580         iter = tny_list_create_iterator (headers);
3581         while (!tny_iterator_is_done (iter) && element < num_elements) {
3582                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3583                 size += tny_header_get_message_size (header);
3584                 g_object_unref (header);
3585                 tny_iterator_next (iter);
3586                 element++;
3587         }
3588         g_object_unref (iter);
3589
3590         return size;
3591 }
3592
3593 void
3594 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
3595                                  TnyList *headers, 
3596                                  TnyFolder *folder, 
3597                                  gboolean delete_original,
3598                                  XferMsgsAsyncUserCallback user_callback,
3599                                  gpointer user_data)
3600 {
3601         ModestMailOperationPrivate *priv = NULL;
3602         TnyIterator *iter = NULL;
3603         TnyFolder *src_folder = NULL;
3604         XFerMsgsAsyncHelper *helper = NULL;
3605         TnyHeader *header = NULL;
3606         ModestTnyFolderRules rules = 0;
3607         TnyAccount *dst_account = NULL;
3608         gboolean leave_on_server;
3609         ModestMailOperationState *state;
3610         ModestProtocolRegistry *protocol_registry;
3611         ModestProtocolType account_protocol;
3612
3613         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
3614         g_return_if_fail (headers && TNY_IS_LIST (headers));
3615         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
3616
3617         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3618         protocol_registry = modest_runtime_get_protocol_registry ();
3619
3620         priv->total = tny_list_get_length (headers);
3621         priv->done = 0;
3622         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3623         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3624
3625         /* Apply folder rules */
3626         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3627         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3628                 /* Set status failed and set an error */
3629                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3630                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3631                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
3632                              _CS_UNABLE_TO_PASTE_HERE);
3633                 /* Notify the queue */
3634                 modest_mail_operation_notify_end (self);
3635                 return;
3636         }
3637                 
3638         /* Get source folder */
3639         iter = tny_list_create_iterator (headers);
3640         header = TNY_HEADER (tny_iterator_get_current (iter));
3641         if (header) {
3642                 src_folder = tny_header_get_folder (header);
3643                 g_object_unref (header);
3644         }
3645         g_object_unref (iter);
3646
3647         if (src_folder == NULL) {
3648                 /* Notify the queue */
3649                 modest_mail_operation_notify_end (self);
3650
3651                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
3652                 return;
3653         }
3654
3655         
3656         /* Check folder source and destination */
3657         if (src_folder == folder) {
3658                 /* Set status failed and set an error */
3659                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3660                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3661                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
3662                              _("mail_in_ui_folder_copy_target_error"));
3663                 
3664                 /* Notify the queue */
3665                 modest_mail_operation_notify_end (self);
3666                 
3667                 /* Free */
3668                 g_object_unref (src_folder);            
3669                 return;
3670         }
3671
3672         /* Create the helper */
3673         helper = g_slice_new0 (XFerMsgsAsyncHelper);
3674         helper->mail_op = g_object_ref(self);
3675         helper->dest_folder = g_object_ref(folder);
3676         helper->user_callback = user_callback;
3677         helper->user_data = user_data;
3678         helper->last_total_bytes = 0;
3679         helper->sum_total_bytes = 0;
3680         helper->total_bytes = compute_message_list_size (headers, 0);
3681
3682         /* Get account and set it into mail_operation */
3683         priv->account = modest_tny_folder_get_account (src_folder);
3684         dst_account = modest_tny_folder_get_account (folder);
3685
3686         if (priv->account == dst_account) {
3687                 /* Transfer all messages at once using the fast
3688                  * method. Note that depending on the server this
3689                  * might not be that fast, and might not be
3690                  * user-cancellable either */
3691                 helper->headers = g_object_ref (headers);
3692                 helper->more_msgs = NULL;
3693         } else {
3694                 /* Transfer messages one by one so the user can cancel
3695                  * the operation */
3696                 GObject *hdr;
3697                 helper->headers = tny_simple_list_new ();
3698                 helper->more_msgs = tny_list_create_iterator (headers);
3699                 hdr = tny_iterator_get_current (helper->more_msgs);
3700                 tny_list_append (helper->headers, hdr);
3701                 g_object_unref (hdr);
3702         }
3703
3704         /* If leave_on_server is set to TRUE then don't use
3705            delete_original, we always pass FALSE. This is because
3706            otherwise tinymail will try to sync the source folder and
3707            this could cause an error if we're offline while
3708            transferring an already downloaded message from a POP
3709            account */
3710         account_protocol = modest_tny_account_get_protocol_type (priv->account);
3711         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_protocol)) {
3712                 const gchar *account_name;
3713
3714                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
3715                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3716                                                                           account_name);
3717         } else {
3718                 leave_on_server = FALSE;
3719         }
3720
3721         /* Do not delete messages if leave on server is TRUE */
3722         helper->delete = (leave_on_server) ? FALSE : delete_original;
3723
3724         modest_mail_operation_notify_start (self);
3725
3726         /* Start notifying progress */
3727         state = modest_mail_operation_clone_state (self);
3728         state->done = 0;
3729         state->total = 0;
3730         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3731         g_slice_free (ModestMailOperationState, state);
3732
3733         tny_folder_transfer_msgs_async (src_folder, 
3734                                         helper->headers, 
3735                                         folder, 
3736                                         helper->delete, 
3737                                         transfer_msgs_cb, 
3738                                         transfer_msgs_status_cb,
3739                                         helper);
3740         g_object_unref (src_folder);
3741         g_object_unref (dst_account);
3742 }
3743
3744
3745 static void
3746 on_refresh_folder (TnyFolder   *folder, 
3747                    gboolean     cancelled, 
3748                    GError     *error,
3749                    gpointer     user_data)
3750 {
3751         RefreshAsyncHelper *helper = NULL;
3752         ModestMailOperation *self = NULL;
3753         ModestMailOperationPrivate *priv = NULL;
3754
3755         helper = (RefreshAsyncHelper *) user_data;
3756         self = helper->mail_op;
3757         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3758
3759         g_return_if_fail(priv!=NULL);
3760
3761         if (error) {
3762                 priv->error = g_error_copy (error);
3763                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3764                 goto out;
3765         }
3766
3767         if (cancelled) {
3768                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3769                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3770                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3771                              _("Error trying to refresh the contents of %s"),
3772                              tny_folder_get_name (folder));
3773                 goto out;
3774         }
3775
3776         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3777  out:
3778
3779         /* Call user defined callback, if it exists */
3780         if (helper->user_callback) {
3781
3782                 /* This is not a GDK lock because we are a Tinymail callback and
3783                  * Tinymail already acquires the Gdk lock */
3784                 helper->user_callback (self, folder, helper->user_data);
3785         }
3786
3787         /* Free */
3788         g_slice_free (RefreshAsyncHelper, helper);
3789
3790         /* Notify about operation end */
3791         modest_mail_operation_notify_end (self);
3792         g_object_unref(self);
3793 }
3794
3795 static void
3796 on_refresh_folder_status_update (GObject *obj,
3797                                  TnyStatus *status,
3798                                  gpointer user_data)
3799 {
3800         RefreshAsyncHelper *helper = NULL;
3801         ModestMailOperation *self = NULL;
3802         ModestMailOperationPrivate *priv = NULL;
3803         ModestMailOperationState *state;
3804
3805         g_return_if_fail (user_data != NULL);
3806         g_return_if_fail (status != NULL);
3807
3808         /* Show only the status information we want */
3809         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
3810                 return;
3811
3812         helper = (RefreshAsyncHelper *) user_data;
3813         self = helper->mail_op;
3814         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3815
3816         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3817
3818         priv->done = status->position;
3819         priv->total = status->of_total;
3820
3821         state = modest_mail_operation_clone_state (self);
3822
3823         /* This is not a GDK lock because we are a Tinymail callback and
3824          * Tinymail already acquires the Gdk lock */
3825         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3826
3827         g_slice_free (ModestMailOperationState, state);
3828 }
3829
3830 void 
3831 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3832                                        TnyFolder *folder,
3833                                        RefreshAsyncUserCallback user_callback,
3834                                        gpointer user_data)
3835 {
3836         ModestMailOperationPrivate *priv = NULL;
3837         RefreshAsyncHelper *helper = NULL;
3838
3839         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3840
3841         /* Check memory low */
3842         if (_check_memory_low (self)) {
3843                 if (user_callback)
3844                         user_callback (self, folder, user_data);
3845                 /* Notify about operation end */
3846                 modest_mail_operation_notify_end (self);
3847                 return;
3848         }
3849
3850         /* Get account and set it into mail_operation */
3851         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3852         priv->account = modest_tny_folder_get_account  (folder);
3853         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3854
3855         /* Create the helper */
3856         helper = g_slice_new0 (RefreshAsyncHelper);
3857         helper->mail_op = g_object_ref(self);
3858         helper->user_callback = user_callback;
3859         helper->user_data = user_data;
3860
3861         modest_mail_operation_notify_start (self);
3862         
3863         /* notify that the operation was started */
3864         ModestMailOperationState *state;
3865         state = modest_mail_operation_clone_state (self);
3866         state->done = 0;
3867         state->total = 0;
3868         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3869                         0, state, NULL);
3870         g_slice_free (ModestMailOperationState, state);
3871         
3872         tny_folder_refresh_async (folder,
3873                                   on_refresh_folder,
3874                                   on_refresh_folder_status_update,
3875                                   helper);
3876 }
3877
3878 static void
3879 run_queue_notify_and_destroy (RunQueueHelper *helper,
3880                               ModestMailOperationStatus status)
3881 {
3882         ModestMailOperationPrivate *priv;
3883
3884         /* Disconnect */
3885         if (helper->error_handler &&
3886             g_signal_handler_is_connected (helper->queue, helper->error_handler))
3887                 g_signal_handler_disconnect (helper->queue, helper->error_handler);
3888         if (helper->start_handler &&
3889             g_signal_handler_is_connected (helper->queue, helper->start_handler))
3890                 g_signal_handler_disconnect (helper->queue, helper->start_handler);
3891         if (helper->stop_handler &&
3892             g_signal_handler_is_connected (helper->queue, helper->stop_handler))
3893                 g_signal_handler_disconnect (helper->queue, helper->stop_handler);
3894
3895         /* Set status */
3896         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
3897         priv->status = status;
3898
3899         /* Notify end */
3900         modest_mail_operation_notify_end (helper->self);
3901
3902         /* Free data */
3903         g_object_unref (helper->queue);
3904         g_object_unref (helper->self);
3905         g_slice_free (RunQueueHelper, helper);
3906 }
3907
3908 static void
3909 run_queue_stop (ModestTnySendQueue *queue,
3910                 gpointer user_data)
3911 {
3912         RunQueueHelper *helper;
3913
3914         g_debug ("%s sending queue stopped", __FUNCTION__);
3915
3916         helper = (RunQueueHelper *) user_data;
3917         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
3918 }
3919
3920 void
3921 modest_mail_operation_run_queue (ModestMailOperation *self,
3922                                  ModestTnySendQueue *queue)
3923 {
3924         ModestMailOperationPrivate *priv;
3925         RunQueueHelper *helper;
3926
3927         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3928         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3929         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3930
3931         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3932         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3933         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3934
3935         /* Create the helper */
3936         helper = g_slice_new0 (RunQueueHelper);
3937         helper->queue = g_object_ref (queue);
3938         helper->self = g_object_ref (self);
3939         helper->stop_handler = g_signal_connect (queue, "queue-stop", 
3940                                                  G_CALLBACK (run_queue_stop), 
3941                                                  helper);
3942
3943         /* Notify operation has started */
3944         modest_mail_operation_notify_start (self);
3945         g_debug ("%s, run queue started", __FUNCTION__);
3946 }
3947
3948 static void
3949 queue_wakeup_callback (ModestTnySendQueue *queue,
3950                        gboolean cancelled,
3951                        GError *error,
3952                        gpointer userdata)
3953 {
3954         ModestMailOperation *mail_op;
3955         ModestMailOperationPrivate *priv;
3956
3957         mail_op = (ModestMailOperation *) userdata;
3958         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3959
3960         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3961         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (queue));
3962
3963         /* Notify end */
3964         modest_mail_operation_notify_end (mail_op);
3965         g_object_unref (mail_op);
3966 }
3967
3968 void
3969 modest_mail_operation_queue_wakeup (ModestMailOperation *self,
3970                                     ModestTnySendQueue *queue)
3971 {
3972         ModestMailOperationPrivate *priv;
3973
3974         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3975         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3976         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3977
3978         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3979         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3980         priv->op_type = MODEST_MAIL_OPERATION_TYPE_QUEUE_WAKEUP;
3981
3982         g_object_ref (self);
3983
3984         modest_tny_send_queue_wakeup (queue, queue_wakeup_callback, self);
3985         modest_mail_operation_notify_start (self);
3986 }
3987
3988 static void
3989 shutdown_callback (ModestTnyAccountStore *account_store, gpointer userdata)
3990 {
3991         ModestMailOperation *self = (ModestMailOperation *) userdata;
3992         ModestMailOperationPrivate *priv;
3993
3994         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3995         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3996         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3997
3998         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3999
4000         modest_mail_operation_notify_end (self);
4001         g_object_unref (self);
4002 }
4003
4004 void
4005 modest_mail_operation_shutdown (ModestMailOperation *self, ModestTnyAccountStore *account_store)
4006 {
4007         ModestMailOperationPrivate *priv;
4008
4009         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
4010         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
4011         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
4012
4013         modest_mail_operation_queue_set_running_shutdown (modest_runtime_get_mail_operation_queue ());
4014
4015         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
4016         priv->account = NULL;
4017         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SHUTDOWN;
4018
4019         modest_mail_operation_notify_start (self);
4020         g_object_ref (self);
4021         modest_tny_account_store_shutdown (account_store, shutdown_callback, self);
4022 }
4023
4024 static void
4025 sync_folder_finish_callback (TnyFolder *self,
4026                              gboolean cancelled,
4027                              GError *err,
4028                              gpointer user_data)
4029
4030 {
4031         ModestMailOperationPrivate *priv;
4032         SyncFolderHelper *helper;
4033
4034         helper = (SyncFolderHelper *) user_data;
4035         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->mail_op);
4036
4037         /* If canceled by the user, ignore the error given by Tinymail */
4038         if (cancelled) {
4039                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
4040         } else if (err) {
4041                 /* If the operation was a sync then the status is
4042                    failed, but if it's part of another operation then
4043                    just set it as finished with errors */
4044                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
4045                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
4046                 else
4047                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
4048                 priv->error = g_error_copy ((const GError *) err);
4049                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
4050         } else {
4051                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
4052         }
4053
4054         /* User callback */
4055         if (helper->user_callback)
4056                 helper->user_callback (helper->mail_op, self, helper->user_data);
4057
4058         modest_mail_operation_notify_end (helper->mail_op);
4059
4060         /* Frees */
4061         g_object_unref (helper->mail_op);
4062         g_slice_free (SyncFolderHelper, helper);
4063 }
4064
4065 void
4066 modest_mail_operation_sync_folder (ModestMailOperation *self,
4067                                    TnyFolder *folder,
4068                                    gboolean expunge,
4069                                    SyncFolderCallback callback,
4070                                    gpointer user_data)
4071 {
4072         ModestMailOperationPrivate *priv;
4073         SyncFolderHelper *helper;
4074
4075         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
4076         g_return_if_fail (TNY_IS_FOLDER (folder));
4077         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
4078
4079         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
4080         priv->account = modest_tny_folder_get_account (folder);
4081         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
4082
4083         /* Create helper */
4084         helper = g_slice_new0 (SyncFolderHelper);
4085         helper->mail_op = g_object_ref (self);
4086         helper->user_callback = callback;
4087         helper->user_data = user_data;
4088
4089         modest_mail_operation_notify_start (self);
4090         tny_folder_sync_async (folder, expunge,
4091                                (TnyFolderCallback) sync_folder_finish_callback,
4092                                NULL, helper);
4093 }
4094
4095 static void
4096 modest_mail_operation_notify_start (ModestMailOperation *self)
4097 {
4098         ModestMailOperationPrivate *priv = NULL;
4099
4100         g_return_if_fail (self);
4101
4102         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4103
4104         /* Ensure that all the fields are filled correctly */
4105         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
4106
4107         /* Notify the observers about the mail operation. We do not
4108            wrapp this emission because we assume that this function is
4109            always called from within the main lock */
4110         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
4111 }
4112
4113 /**
4114  *
4115  * It's used by the mail operation queue to notify the observers
4116  * attached to that signal that the operation finished. We need to use
4117  * that because tinymail does not give us the progress of a given
4118  * operation when it finishes (it directly calls the operation
4119  * callback).
4120  */
4121 static void
4122 modest_mail_operation_notify_end (ModestMailOperation *self)
4123 {
4124         ModestMailOperationPrivate *priv = NULL;
4125
4126         g_return_if_fail (self);
4127
4128         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4129
4130         /* Notify the observers about the mail operation end. We do
4131            not wrapp this emission because we assume that this
4132            function is always called from within the main lock */
4133         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
4134
4135         /* Remove the error user data */
4136         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
4137                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
4138 }
4139
4140 TnyAccount *
4141 modest_mail_operation_get_account (ModestMailOperation *self)
4142 {
4143         ModestMailOperationPrivate *priv = NULL;
4144
4145         g_return_val_if_fail (self, NULL);
4146
4147         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4148
4149         return (priv->account) ? g_object_ref (priv->account) : NULL;
4150 }
4151
4152 void
4153 modest_mail_operation_noop (ModestMailOperation *self)
4154 {
4155         ModestMailOperationPrivate *priv = NULL;
4156
4157         g_return_if_fail (self);
4158
4159         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4160         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
4161         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
4162         priv->done = 0;
4163         priv->total = 0;
4164
4165         /* This mail operation does nothing actually */
4166         modest_mail_operation_notify_start (self);
4167         modest_mail_operation_notify_end (self);
4168 }
4169
4170
4171 gchar*
4172 modest_mail_operation_to_string (ModestMailOperation *self)
4173 {
4174         const gchar *type, *status, *account_id;
4175         ModestMailOperationPrivate *priv = NULL;
4176         
4177         g_return_val_if_fail (self, NULL);
4178
4179         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4180
4181         /* new operations don't have anything interesting */
4182         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
4183                 return g_strdup_printf ("%p <new operation>", self);
4184         
4185         switch (priv->op_type) {
4186         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
4187         case MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE:    type= "SEND-AND-RECEIVE";    break;
4188         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
4189         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
4190         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
4191         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
4192         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
4193         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
4194         case MODEST_MAIL_OPERATION_TYPE_SHUTDOWN: type= "SHUTDOWN"; break;
4195         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
4196         default: type = "UNEXPECTED"; break;
4197         }
4198
4199         switch (priv->status) {
4200         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
4201         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
4202         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
4203         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
4204         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
4205         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
4206         default:                                                status= "UNEXPECTED"; break;
4207         } 
4208
4209         account_id = priv->account ? tny_account_get_id (priv->account) : "";
4210
4211         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
4212                                 priv->done, priv->total,
4213                                 priv->error && priv->error->message ? priv->error->message : "");
4214 }
4215
4216 /* 
4217  * Once the mail operations were objects this will be no longer
4218  * needed. I don't like it, but we need it for the moment
4219  */
4220 static gboolean
4221 _check_memory_low (ModestMailOperation *mail_op)
4222 {
4223         if (modest_platform_check_memory_low (NULL, FALSE)) {
4224                 ModestMailOperationPrivate *priv;
4225
4226                 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
4227                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
4228                 g_set_error (&(priv->error),
4229                              MODEST_MAIL_OPERATION_ERROR,
4230                              MODEST_MAIL_OPERATION_ERROR_LOW_MEMORY,
4231                              "Not enough memory to complete the operation");
4232                 return TRUE;
4233         } else {
4234                 return FALSE;
4235         }
4236 }