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