* Now we block sending mails (from any account) when we're changing
[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
716         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
717         if (info->html_body == NULL) {
718                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
719                                               info->bcc, info->subject, info->plain_body, 
720                                               info->attachments_list,
721                                               &(priv->error));
722         } else {
723                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
724                                                          info->bcc, info->subject, info->html_body,
725                                                          info->plain_body, info->attachments_list,
726                                                          info->images_list,
727                                                          &(priv->error));
728         }
729
730         if (new_msg) {
731                 TnyHeader *header;
732
733                 /* Set priority flags in message */
734                 header = tny_msg_get_header (new_msg);
735                 tny_header_set_flag (header, info->priority_flags);
736
737                 /* Set attachment flags in message */
738                 if (info->attachments_list != NULL)
739                         tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
740
741                 g_object_unref (G_OBJECT(header));
742         } else {
743                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
744                 if (!priv->error)
745                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
746                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
747                                      "modest: failed to create a new msg\n");
748         }
749
750
751         g_free (info->to);
752         g_free (info->from);
753         g_free (info->cc);
754         g_free (info->bcc);
755         g_free (info->plain_body);
756         g_free (info->html_body);
757         g_free (info->subject);
758         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
759         g_list_free (info->attachments_list);
760         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
761         g_list_free (info->images_list);
762
763         if (info->callback) {
764                 CreateMsgIdleInfo *idle_info;
765                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
766                 idle_info->mail_op = g_object_ref (info->mail_op);
767                 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
768                 idle_info->callback = info->callback;
769                 idle_info->userdata = info->userdata;
770                 g_idle_add (idle_create_msg_cb, idle_info);
771         } else {
772                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
773         }
774
775         g_object_unref (info->mail_op);
776         g_slice_free (CreateMsgInfo, info);
777         if (new_msg) g_object_unref(new_msg);
778         return NULL;
779 }
780
781
782 void
783 modest_mail_operation_create_msg (ModestMailOperation *self,
784                                   const gchar *from, const gchar *to,
785                                   const gchar *cc, const gchar *bcc,
786                                   const gchar *subject, const gchar *plain_body,
787                                   const gchar *html_body,
788                                   const GList *attachments_list,
789                                   const GList *images_list,
790                                   TnyHeaderFlags priority_flags,
791                                   ModestMailOperationCreateMsgCallback callback,
792                                   gpointer userdata)
793 {
794         CreateMsgInfo *info = NULL;
795
796         info = g_slice_new0 (CreateMsgInfo);
797         info->mail_op = g_object_ref (self);
798
799         info->from = g_strdup (from);
800         info->to = g_strdup (to);
801         info->cc = g_strdup (cc);
802         info->bcc  = g_strdup (bcc);
803         info->subject = g_strdup (subject);
804         info->plain_body = g_strdup (plain_body);
805         info->html_body = g_strdup (html_body);
806         info->attachments_list = g_list_copy ((GList *) attachments_list);
807         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
808         info->images_list = g_list_copy ((GList *) images_list);
809         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
810         info->priority_flags = priority_flags;
811
812         info->callback = callback;
813         info->userdata = userdata;
814
815         g_thread_create (create_msg_thread, info, FALSE, NULL);
816 }
817
818 typedef struct
819 {
820         TnyTransportAccount *transport_account;
821         TnyMsg *draft_msg;
822 } SendNewMailInfo;
823
824 static void
825 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
826                                         TnyMsg *msg,
827                                         gpointer userdata)
828 {
829         TnySendQueue *send_queue = NULL;
830         ModestMailOperationPrivate *priv = NULL;
831         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
832         TnyFolder *draft_folder = NULL;
833         TnyFolder *outbox_folder = NULL;
834         TnyHeader *header = NULL;
835
836         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
837
838         if (!msg) {
839                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
840                 modest_mail_operation_notify_end (self);
841                 goto end;
842         }
843
844         if (priv->error && priv->error->code != MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
845                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
846                 modest_mail_operation_notify_end (self);
847                 goto end;
848         }
849
850         /* Add message to send queue */
851         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (info->transport_account, TRUE));
852         if (!TNY_IS_SEND_QUEUE(send_queue)) {
853                 if (priv->error) {
854                         g_error_free (priv->error);
855                         priv->error = NULL;
856                 }
857                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
858                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
859                              "modest: could not find send queue for account\n");
860                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
861                 modest_mail_operation_notify_end (self);
862                 goto end;
863         } else {
864                 SendNewMailHelper *helper = g_slice_new (SendNewMailHelper);
865                 helper->mail_op = g_object_ref (self);
866                 helper->notify = (info->draft_msg == NULL);
867
868                 /* Add the msg to the queue. The callback will free
869                    the helper */
870                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
871                                                                   FALSE);
872                 tny_send_queue_add_async (send_queue, msg, send_mail_on_added_to_outbox, 
873                                           NULL, helper);
874         }
875
876         if (info->draft_msg != NULL) {
877                 TnyList *tmp_headers = NULL;
878                 TnyFolder *folder = NULL;
879                 TnyFolder *src_folder = NULL;
880                 TnyFolderType folder_type;              
881                 TnyTransportAccount *transport_account = NULL;
882                 SendNewMailHelper *helper = NULL;
883
884                 /* To remove the old mail from its source folder, we need to get the
885                  * transport account of the original draft message (the transport account
886                  * might have been changed by the user) */
887                 header = tny_msg_get_header (info->draft_msg);
888                 transport_account = modest_tny_account_store_get_transport_account_from_outbox_header(
889                         modest_runtime_get_account_store(), header);
890                 if (transport_account == NULL)
891                         transport_account = g_object_ref(info->transport_account);
892                 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
893                                                                       TNY_FOLDER_TYPE_DRAFTS);
894                 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
895                                                                        TNY_FOLDER_TYPE_OUTBOX);
896                 g_object_unref(transport_account);
897
898                 if (!draft_folder) {
899                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL drafts folder",
900                                    __FUNCTION__);
901                         modest_mail_operation_notify_end (self);
902                         goto end;
903                 }
904                 if (!outbox_folder) {
905                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL outbox folder",
906                                    __FUNCTION__);
907                         modest_mail_operation_notify_end (self);
908                         goto end;
909                 }
910
911                 folder = tny_msg_get_folder (info->draft_msg);          
912                 if (folder == NULL) {
913                         modest_mail_operation_notify_end (self);
914                         goto end;
915                 }
916                 folder_type = modest_tny_folder_guess_folder_type (folder);
917
918                 if (folder_type == TNY_FOLDER_TYPE_INVALID)
919                         g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
920                 
921                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
922                         src_folder = outbox_folder;
923                 else 
924                         src_folder = draft_folder;
925
926                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
927                  * because this function requires it to have a UID. */
928                 helper = g_slice_new (SendNewMailHelper);
929                 helper->mail_op = g_object_ref (self);
930                 helper->notify = TRUE;
931
932                 tmp_headers = tny_simple_list_new ();
933                 tny_list_append (tmp_headers, (GObject*) header);
934                 tny_folder_remove_msgs_async (src_folder, tmp_headers, NULL, NULL, NULL);
935                 g_object_unref (tmp_headers);
936                 tny_folder_sync_async (src_folder, TRUE, send_mail_on_sync_async_cb, 
937                                        NULL, helper);
938                 g_object_unref (folder);
939         }
940
941 end:
942         if (header)
943                 g_object_unref (header);
944         if (info->draft_msg)
945                 g_object_unref (info->draft_msg);
946         if (draft_folder)
947                 g_object_unref (draft_folder);
948         if (outbox_folder)
949                 g_object_unref (outbox_folder);
950         if (info->transport_account)
951                 g_object_unref (info->transport_account);
952         g_slice_free (SendNewMailInfo, info);
953 }
954
955 void
956 modest_mail_operation_send_new_mail (ModestMailOperation *self,
957                                      TnyTransportAccount *transport_account,
958                                      TnyMsg *draft_msg,
959                                      const gchar *from,  const gchar *to,
960                                      const gchar *cc,  const gchar *bcc,
961                                      const gchar *subject, const gchar *plain_body,
962                                      const gchar *html_body,
963                                      const GList *attachments_list,
964                                      const GList *images_list,
965                                      TnyHeaderFlags priority_flags)
966 {
967         ModestMailOperationPrivate *priv = NULL;
968         SendNewMailInfo *info;
969
970         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
971         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
972
973         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
974         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
975         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
976         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
977
978         modest_mail_operation_notify_start (self);
979
980         /* Check parametters */
981         if (to == NULL) {
982                 /* Set status failed and set an error */
983                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
984                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
985                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
986                              _("Error trying to send a mail. You need to set at least one recipient"));
987                 modest_mail_operation_notify_end (self);
988                 return;
989         }
990         info = g_slice_new0 (SendNewMailInfo);
991         info->transport_account = transport_account;
992         if (transport_account)
993                 g_object_ref (transport_account);
994         info->draft_msg = draft_msg;
995         if (draft_msg)
996                 g_object_ref (draft_msg);
997
998
999         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1000                                           attachments_list, images_list, priority_flags,
1001                                           modest_mail_operation_send_new_mail_cb, info);
1002
1003 }
1004
1005 typedef struct
1006 {
1007         TnyTransportAccount *transport_account;
1008         TnyMsg *draft_msg;
1009         SaveToDraftstCallback callback;
1010         gpointer user_data;
1011         TnyFolder *drafts;
1012         TnyMsg *msg;
1013         ModestMailOperation *mailop;
1014 } SaveToDraftsAddMsgInfo;
1015
1016 static void
1017 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
1018                                                 gboolean canceled,
1019                                                 GError *err,
1020                                                 gpointer userdata)
1021 {
1022         ModestMailOperationPrivate *priv = NULL;
1023         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
1024         GError *io_error = NULL;
1025
1026         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1027
1028         if (priv->error && priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1029                 io_error = priv->error;
1030                 priv->error = NULL;
1031         }
1032         if (priv->error) {
1033                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
1034                 g_error_free(priv->error);
1035         }
1036
1037         priv->error = (err == NULL) ? NULL : g_error_copy(err);
1038
1039         if ((!priv->error) && (info->draft_msg != NULL)) {
1040                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
1041                 TnyFolder *src_folder = tny_header_get_folder (header);
1042
1043                 /* Remove the old draft */
1044                 tny_folder_remove_msg (src_folder, header, NULL);
1045
1046                 /* Synchronize to expunge and to update the msg counts */
1047                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1048                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1049
1050                 g_object_unref (G_OBJECT(header));
1051                 g_object_unref (G_OBJECT(src_folder));
1052         }
1053
1054         if (priv->error) {
1055                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1056                 if (io_error) {
1057                         g_error_free (io_error);
1058                         io_error = NULL;
1059                 }
1060         } else if (io_error) {
1061                 priv->error = io_error;
1062                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
1063         } else {
1064                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1065         }
1066
1067         /* Call the user callback */
1068         if (info->callback)
1069                 info->callback (info->mailop, info->msg, info->user_data);
1070
1071         if (info->transport_account)
1072                 g_object_unref (G_OBJECT(info->transport_account));
1073         if (info->draft_msg)
1074                 g_object_unref (G_OBJECT (info->draft_msg));
1075         if (info->drafts)
1076                 g_object_unref (G_OBJECT(info->drafts));
1077         if (info->msg)
1078                 g_object_unref (G_OBJECT (info->msg));
1079
1080         modest_mail_operation_notify_end (info->mailop);
1081         g_object_unref(info->mailop);
1082         g_slice_free (SaveToDraftsAddMsgInfo, info);
1083 }
1084
1085 typedef struct
1086 {
1087         TnyTransportAccount *transport_account;
1088         TnyMsg *draft_msg;
1089         SaveToDraftstCallback callback;
1090         gpointer user_data;
1091 } SaveToDraftsInfo;
1092
1093 static void
1094 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1095                                          TnyMsg *msg,
1096                                          gpointer userdata)
1097 {
1098         TnyFolder *drafts = NULL;
1099         ModestMailOperationPrivate *priv = NULL;
1100         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1101
1102         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1103
1104         if (!msg) {
1105                 if (!(priv->error)) {
1106                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1107                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1108                                      "modest: failed to create a new msg\n");
1109                 }
1110         } else {
1111                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1112                                                                 TNY_FOLDER_TYPE_DRAFTS);
1113                 if (!drafts && !(priv->error)) {
1114                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1115                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1116                                      "modest: failed to create a new msg\n");
1117                 }
1118         }
1119
1120         if (!priv->error || priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1121                 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1122                 cb_info->transport_account = g_object_ref(info->transport_account);
1123                 cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1124                 cb_info->callback = info->callback;
1125                 cb_info->user_data = info->user_data;
1126                 cb_info->drafts = g_object_ref(drafts);
1127                 cb_info->msg = g_object_ref(msg);
1128                 cb_info->mailop = g_object_ref(self);
1129                 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1130                                          NULL, cb_info);
1131         } else {
1132                 /* Call the user callback */
1133                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1134                 if (info->callback)
1135                         info->callback (self, msg, info->user_data);
1136                 modest_mail_operation_notify_end (self);
1137         }
1138
1139         if (drafts)
1140                 g_object_unref (G_OBJECT(drafts));
1141         if (info->draft_msg)
1142                 g_object_unref (G_OBJECT (info->draft_msg));
1143         if (info->transport_account)
1144                 g_object_unref (G_OBJECT(info->transport_account));
1145         g_slice_free (SaveToDraftsInfo, info);
1146 }
1147
1148 void
1149 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1150                                       TnyTransportAccount *transport_account,
1151                                       TnyMsg *draft_msg,
1152                                       const gchar *from,  const gchar *to,
1153                                       const gchar *cc,  const gchar *bcc,
1154                                       const gchar *subject, const gchar *plain_body,
1155                                       const gchar *html_body,
1156                                       const GList *attachments_list,
1157                                       const GList *images_list,
1158                                       TnyHeaderFlags priority_flags,
1159                                       SaveToDraftstCallback callback,
1160                                       gpointer user_data)
1161 {
1162         ModestMailOperationPrivate *priv = NULL;
1163         SaveToDraftsInfo *info = NULL;
1164
1165         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1166         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1167
1168         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1169
1170         /* Get account and set it into mail_operation */
1171         priv->account = g_object_ref (transport_account);
1172         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1173
1174         info = g_slice_new0 (SaveToDraftsInfo);
1175         info->transport_account = g_object_ref (transport_account);
1176         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1177         info->callback = callback;
1178         info->user_data = user_data;
1179
1180         modest_mail_operation_notify_start (self);
1181         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1182                                           attachments_list, images_list, priority_flags,
1183                                           modest_mail_operation_save_to_drafts_cb, info);
1184 }
1185
1186 typedef struct
1187 {
1188         ModestMailOperation *mail_op;
1189         TnyMimePart *mime_part;
1190         gssize size;
1191         GetMimePartSizeCallback callback;
1192         gpointer userdata;
1193 } GetMimePartSizeInfo;
1194
1195 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1196 /* We use this folder observer to track the headers that have been
1197  * added to a folder */
1198 typedef struct {
1199         GObject parent;
1200         TnyList *new_headers;
1201 } InternalFolderObserver;
1202
1203 typedef struct {
1204         GObjectClass parent;
1205 } InternalFolderObserverClass;
1206
1207 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1208
1209 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1210                          internal_folder_observer,
1211                          G_TYPE_OBJECT,
1212                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1213
1214
1215 static void
1216 foreach_add_item (gpointer header, gpointer user_data)
1217 {
1218         tny_list_prepend (TNY_LIST (user_data), 
1219                           G_OBJECT (header));
1220 }
1221
1222 /* This is the method that looks for new messages in a folder */
1223 static void
1224 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1225 {
1226         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1227         
1228         TnyFolderChangeChanged changed;
1229
1230         changed = tny_folder_change_get_changed (change);
1231
1232         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1233                 TnyList *list;
1234
1235                 /* Get added headers */
1236                 list = tny_simple_list_new ();
1237                 tny_folder_change_get_added_headers (change, list);
1238
1239                 /* Add them to the folder observer */
1240                 tny_list_foreach (list, foreach_add_item, 
1241                                   derived->new_headers);
1242
1243                 g_object_unref (G_OBJECT (list));
1244         }
1245 }
1246
1247 static void
1248 internal_folder_observer_init (InternalFolderObserver *self) 
1249 {
1250         self->new_headers = tny_simple_list_new ();
1251 }
1252 static void
1253 internal_folder_observer_finalize (GObject *object) 
1254 {
1255         InternalFolderObserver *self;
1256
1257         self = (InternalFolderObserver *) object;
1258         g_object_unref (self->new_headers);
1259
1260         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1261 }
1262 static void
1263 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1264 {
1265         iface->update = internal_folder_observer_update;
1266 }
1267 static void
1268 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1269 {
1270         GObjectClass *object_class;
1271
1272         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1273         object_class = (GObjectClass*) klass;
1274         object_class->finalize = internal_folder_observer_finalize;
1275 }
1276
1277 static void
1278 destroy_update_account_info (UpdateAccountInfo *info)
1279 {
1280         g_free (info->account_name);
1281         g_object_unref (info->folders);
1282         g_object_unref (info->mail_op);
1283         g_slice_free (UpdateAccountInfo, info);
1284 }
1285
1286
1287 static void
1288 update_account_send_mail (UpdateAccountInfo *info)
1289 {
1290         TnyTransportAccount *transport_account = NULL;
1291         ModestTnyAccountStore *account_store;
1292
1293         account_store = modest_runtime_get_account_store ();
1294
1295         /* We don't try to send messages while sending mails is blocked */
1296         if (modest_tny_account_store_is_send_mail_blocked (account_store))
1297                 return;
1298
1299         /* Get the transport account */
1300         transport_account = (TnyTransportAccount *)
1301                 modest_tny_account_store_get_transport_account_for_open_connection (account_store,
1302                                                                                     info->account_name);
1303
1304         if (transport_account) {
1305                 ModestTnySendQueue *send_queue;
1306                 TnyFolder *outbox;
1307                 guint num_messages;
1308
1309                 send_queue = modest_runtime_get_send_queue (transport_account, TRUE);
1310                 g_object_unref (transport_account);
1311
1312                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1313                         /* Get outbox folder */
1314                         outbox = tny_send_queue_get_outbox (TNY_SEND_QUEUE (send_queue));
1315                         if (outbox) { /* this could fail in some cases */
1316                                 num_messages = tny_folder_get_all_count (outbox);
1317                                 g_object_unref (outbox);
1318                         } else {
1319                                 g_warning ("%s: could not get outbox", __FUNCTION__);
1320                                 num_messages = 0;
1321                         }
1322                 
1323                         if (num_messages != 0) {
1324                                 /* Reenable suspended items */
1325                                 modest_tny_send_queue_wakeup (MODEST_TNY_SEND_QUEUE (send_queue));
1326                                 
1327                                 /* Try to send */
1328                                 tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (send_queue));
1329                                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1330                                                                                   info->interactive);
1331                         }
1332                 }
1333         }
1334 }
1335
1336 static void
1337 update_account_get_msg_async_cb (TnyFolder *folder, 
1338                                  gboolean canceled, 
1339                                  TnyMsg *msg, 
1340                                  GError *err, 
1341                                  gpointer user_data)
1342 {
1343         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1344         ModestMailOperationPrivate *priv;
1345
1346         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (msg_info->mail_op);
1347         priv->done++;
1348
1349         if (TNY_IS_MSG (msg)) {
1350                 TnyHeader *header = tny_msg_get_header (msg);
1351
1352                 if (header) {
1353                         ModestMailOperationState *state;
1354                         state = modest_mail_operation_clone_state (msg_info->mail_op);
1355                         msg_info->sum_total_bytes += tny_header_get_message_size (header);
1356                         state->bytes_done = msg_info->sum_total_bytes;
1357                         state->bytes_total = msg_info->total_bytes;
1358
1359                         /* Notify the status change. Only notify about changes
1360                            referred to bytes */
1361                         g_signal_emit (G_OBJECT (msg_info->mail_op), 
1362                                        signals[PROGRESS_CHANGED_SIGNAL], 
1363                                        0, state, NULL);
1364
1365                         g_object_unref (header);
1366                         g_slice_free (ModestMailOperationState, state);
1367                 }
1368         }
1369
1370         if (priv->done == priv->total) {
1371                 TnyList *new_headers;
1372                 UpdateAccountInfo *info;
1373
1374                 /* After getting all the messages send the ones in the
1375                    outboxes */
1376                 info = (UpdateAccountInfo *) msg_info->user_data;
1377                 update_account_send_mail (info);
1378
1379                 /* Check if the operation was a success */
1380                 if (!priv->error)
1381                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1382                 
1383                 /* Call the user callback and free */
1384                 new_headers = tny_iterator_get_list (msg_info->more_msgs);
1385                 update_account_notify_user_and_free (info, new_headers);
1386                 g_object_unref (new_headers);
1387
1388                 /* Delete the helper */
1389                 g_object_unref (msg_info->more_msgs);
1390                 g_object_unref (msg_info->mail_op);
1391                 g_slice_free (GetMsgInfo, msg_info);
1392         }
1393 }
1394
1395 static void
1396 update_account_notify_user_and_free (UpdateAccountInfo *info, 
1397                                      TnyList *new_headers)
1398 {
1399         /* Set the account back to not busy */
1400         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), 
1401                                              info->account_name, FALSE);
1402         
1403         /* User callback */
1404         if (info->callback)
1405                 info->callback (info->mail_op, new_headers, info->user_data);
1406         
1407         /* Mail operation end */
1408         modest_mail_operation_notify_end (info->mail_op);
1409
1410         /* Frees */
1411         if (new_headers)
1412                 g_object_unref (new_headers);
1413         destroy_update_account_info (info);
1414 }
1415
1416 static void
1417 inbox_refreshed_cb (TnyFolder *inbox, 
1418                     gboolean canceled, 
1419                     GError *err, 
1420                     gpointer user_data)
1421 {       
1422         UpdateAccountInfo *info;
1423         ModestMailOperationPrivate *priv;
1424         TnyIterator *new_headers_iter;
1425         GPtrArray *new_headers_array = NULL;   
1426         gint max_size, retrieve_limit, i;
1427         ModestAccountMgr *mgr;
1428         ModestAccountRetrieveType retrieve_type;
1429         TnyList *new_headers = NULL;
1430         gboolean headers_only, ignore_limit;
1431
1432         info = (UpdateAccountInfo *) user_data;
1433         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1434         mgr = modest_runtime_get_account_mgr ();
1435
1436         /* Set the last updated as the current time, do it even if the inbox refresh failed */
1437         modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1438
1439         if (canceled || err) {
1440                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1441                 if (err)
1442                         priv->error = g_error_copy (err);
1443                 else
1444                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1445                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1446                                      "canceled");
1447
1448                 tny_folder_remove_observer (inbox, info->inbox_observer);
1449                 g_object_unref (info->inbox_observer);
1450                 info->inbox_observer = NULL;
1451
1452                 /* Notify the user about the error and then exit */
1453                 update_account_notify_user_and_free (info, NULL);
1454                 return;
1455         }
1456
1457         if (!inbox) {
1458                 /* Try to send anyway */
1459                 goto send_mail;
1460         }
1461
1462         /* Get the message max size */
1463         max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1464                                          MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1465         if (max_size == 0)
1466                 max_size = G_MAXINT;
1467         else
1468                 max_size = max_size * KB;
1469
1470         /* Create the new headers array. We need it to sort the
1471            new headers by date */
1472         new_headers_array = g_ptr_array_new ();
1473         if (info->inbox_observer) {
1474                 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1475                 while (!tny_iterator_is_done (new_headers_iter)) {
1476                         TnyHeader *header = NULL;
1477
1478                         header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1479                         /* Apply per-message size limits */
1480                         if (tny_header_get_message_size (header) < max_size)
1481                                 g_ptr_array_add (new_headers_array, g_object_ref (header));
1482                         
1483                         g_object_unref (header);
1484                         tny_iterator_next (new_headers_iter);
1485                 }
1486                 g_object_unref (new_headers_iter);
1487
1488                 tny_folder_remove_observer (inbox, info->inbox_observer);
1489                 g_object_unref (info->inbox_observer);
1490                 info->inbox_observer = NULL;
1491         }
1492
1493         if (new_headers_array->len == 0) {
1494                 g_ptr_array_free (new_headers_array, FALSE);
1495                 goto send_mail;
1496         }
1497
1498         /* Get per-account message amount retrieval limit */
1499         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1500         if (retrieve_limit == 0)
1501                 retrieve_limit = G_MAXINT;
1502         
1503         /* Get per-account retrieval type */
1504         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1505         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1506
1507         /* Order by date */
1508         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1509         
1510         /* Ask the users if they want to retrieve all the messages
1511            even though the limit was exceeded */
1512         ignore_limit = FALSE;
1513         if (new_headers_array->len > retrieve_limit) {
1514                 /* Ask the user if a callback has been specified and
1515                    if the mail operation has a source (this means that
1516                    was invoked by the user and not automatically by a
1517                    D-Bus method) */
1518                 if (info->retrieve_all_cb && priv->source)
1519                         ignore_limit = info->retrieve_all_cb (priv->source,
1520                                                               new_headers_array->len,
1521                                                               retrieve_limit);
1522         }
1523
1524         /* Copy the headers to a list and free the array */
1525         new_headers = tny_simple_list_new ();
1526         for (i=0; i < new_headers_array->len; i++) {
1527                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1528                 tny_list_append (new_headers, G_OBJECT (header));
1529         }
1530         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1531         g_ptr_array_free (new_headers_array, FALSE);
1532         
1533         if (!headers_only && (tny_list_get_length (new_headers) > 0)) {
1534                 gint msg_num = 0;
1535                 TnyIterator *iter;
1536                 GetMsgInfo *msg_info;
1537
1538                 priv->done = 0;
1539                 if (ignore_limit)
1540                         priv->total = tny_list_get_length (new_headers);
1541                 else
1542                         priv->total = MIN (tny_list_get_length (new_headers), retrieve_limit);
1543
1544                 iter = tny_list_create_iterator (new_headers);
1545
1546                 /* Create the message info */
1547                 msg_info = g_slice_new0 (GetMsgInfo);
1548                 msg_info->mail_op = g_object_ref (info->mail_op);
1549                 msg_info->total_bytes = compute_message_list_size (new_headers, priv->total);
1550                 msg_info->more_msgs = g_object_ref (iter);
1551                 msg_info->user_data = info;
1552
1553                 while ((msg_num < priv->total ) && !tny_iterator_is_done (iter)) {              
1554                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1555                         TnyFolder *folder = tny_header_get_folder (header);
1556
1557                         /* Get message in an async way */
1558                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb, 
1559                                                   NULL, msg_info);
1560
1561                         g_object_unref (folder);
1562                         
1563                         msg_num++;
1564                         tny_iterator_next (iter);
1565                 }
1566                 g_object_unref (iter);
1567
1568                 /* The mail operation will finish when the last
1569                    message is retrieved */
1570                 return;
1571         }
1572  send_mail:
1573         /* If we don't have to retrieve the new messages then
1574            simply send mail */
1575         update_account_send_mail (info);
1576         
1577         /* Check if the operation was a success */
1578         if (!priv->error)
1579                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1580         
1581         /* Call the user callback and free */
1582         update_account_notify_user_and_free (info, new_headers);
1583 }
1584
1585 static void
1586 inbox_refresh_status_update (GObject *obj,
1587                              TnyStatus *status,
1588                              gpointer user_data)
1589 {
1590         UpdateAccountInfo *info = NULL;
1591         ModestMailOperation *self = NULL;
1592         ModestMailOperationPrivate *priv = NULL;
1593         ModestMailOperationState *state;
1594
1595         g_return_if_fail (user_data != NULL);
1596         g_return_if_fail (status != NULL);
1597
1598         /* Show only the status information we want */
1599         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
1600                 return;
1601
1602         info = (UpdateAccountInfo *) user_data;
1603         self = info->mail_op;
1604         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1605
1606         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1607
1608         priv->done = status->position;
1609         priv->total = status->of_total;
1610
1611         state = modest_mail_operation_clone_state (self);
1612
1613         /* This is not a GDK lock because we are a Tinymail callback and
1614          * Tinymail already acquires the Gdk lock */
1615         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1616
1617         g_slice_free (ModestMailOperationState, state);
1618 }
1619
1620 static void 
1621 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1622                           gboolean canceled,
1623                           TnyList *list, 
1624                           GError *err, 
1625                           gpointer user_data)
1626 {
1627         UpdateAccountInfo *info;
1628         ModestMailOperationPrivate *priv;
1629     
1630         info = (UpdateAccountInfo *) user_data;
1631         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1632
1633         if (err || canceled) {
1634                 /* If the error was previosly set by another callback
1635                    don't set it again */
1636                 if (!priv->error) {
1637                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1638                         if (err)
1639                                 priv->error = g_error_copy (err);
1640                         else
1641                                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1642                                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1643                                              "canceled");
1644                 }
1645         } else { 
1646                 /* We're not getting INBOX children if we don't want to poke all */
1647                 TnyIterator *iter = tny_list_create_iterator (list);
1648                 while (!tny_iterator_is_done (iter)) {
1649                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1650
1651                         /* Add to the list of all folders */
1652                         tny_list_append (info->folders, (GObject *) folder);
1653                         
1654                         if (info->poke_all) {
1655                                 TnyList *folders = tny_simple_list_new ();
1656                                 /* Add pending call */
1657                                 info->pending_calls++;
1658                                 
1659                                 tny_folder_store_get_folders_async (folder, folders, NULL,
1660                                                                     recurse_folders_async_cb, 
1661                                                                     NULL, info);
1662                                 g_object_unref (folders);
1663                         }
1664                         
1665                         g_object_unref (G_OBJECT (folder));
1666                         
1667                         tny_iterator_next (iter);           
1668                 }
1669                 g_object_unref (G_OBJECT (iter));
1670         }
1671
1672         /* Remove my own pending call */
1673         info->pending_calls--;
1674
1675         /* This means that we have all the folders */
1676         if (info->pending_calls == 0) {
1677                 TnyIterator *iter_all_folders;
1678                 TnyFolder *inbox = NULL;
1679
1680                 /* If there was any error do not continue */
1681                 if (priv->error) {
1682                         update_account_notify_user_and_free (info, NULL);
1683                         return;
1684                 }
1685
1686                 iter_all_folders = tny_list_create_iterator (info->folders);
1687
1688                 /* Do a poke status over all folders */
1689                 while (!tny_iterator_is_done (iter_all_folders) &&
1690                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1691                         TnyFolder *folder = NULL;
1692
1693                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1694
1695                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1696                                 /* Get a reference to the INBOX */
1697                                 inbox = g_object_ref (folder);
1698                         } else {
1699                                 /* Issue a poke status over the folder */
1700                                 if (info->poke_all)
1701                                         tny_folder_poke_status (folder);
1702                         }
1703
1704                         /* Free and go to next */
1705                         g_object_unref (folder);
1706                         tny_iterator_next (iter_all_folders);
1707                 }
1708                 g_object_unref (iter_all_folders);
1709
1710                 /* Refresh the INBOX */
1711                 if (inbox) {
1712                         /* Refresh the folder. Our observer receives
1713                          * the new emails during folder refreshes, so
1714                          * we can use observer->new_headers
1715                          */
1716                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1717                         tny_folder_add_observer (inbox, info->inbox_observer);
1718
1719                         /* Refresh the INBOX */
1720                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, inbox_refresh_status_update, info);
1721                         g_object_unref (inbox);
1722                 } else {
1723                         /* We could not perform the inbox refresh but
1724                            we'll try to send mails anyway */
1725                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1726                 }
1727         }
1728 }
1729
1730 void
1731 modest_mail_operation_update_account (ModestMailOperation *self,
1732                                       const gchar *account_name,
1733                                       gboolean poke_all,
1734                                       gboolean interactive,
1735                                       RetrieveAllCallback retrieve_all_cb,
1736                                       UpdateAccountCallback callback,
1737                                       gpointer user_data)
1738 {
1739         UpdateAccountInfo *info = NULL;
1740         ModestMailOperationPrivate *priv = NULL;
1741         ModestTnyAccountStore *account_store = NULL;
1742         TnyList *folders;
1743         ModestMailOperationState *state;
1744
1745         /* Init mail operation */
1746         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1747         priv->total = 0;
1748         priv->done  = 0;
1749         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1750         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
1751
1752         /* Get the store account */
1753         account_store = modest_runtime_get_account_store ();
1754         priv->account =
1755                 modest_tny_account_store_get_server_account (account_store,
1756                                                              account_name,
1757                                                              TNY_ACCOUNT_TYPE_STORE);
1758
1759         /* The above function could return NULL */
1760         if (!priv->account) {
1761                 /* Check if the operation was a success */
1762                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1763                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1764                              "no account");
1765                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1766
1767                 /* Call the user callback */
1768                 if (callback)
1769                         callback (self, NULL, user_data);
1770
1771                 /* Notify about operation end */
1772                 modest_mail_operation_notify_end (self);
1773
1774                 return;
1775         }
1776         
1777         /* We have once seen priv->account getting finalized during this code,
1778          * therefore adding a reference (bug #82296) */
1779         
1780         g_object_ref (priv->account);
1781
1782         /* Create the helper object */
1783         info = g_slice_new0 (UpdateAccountInfo);
1784         info->pending_calls = 1;
1785         info->folders = tny_simple_list_new ();
1786         info->mail_op = g_object_ref (self);
1787         info->poke_all = poke_all;
1788         info->interactive = interactive;
1789         info->account_name = g_strdup (account_name);
1790         info->callback = callback;
1791         info->user_data = user_data;
1792         info->retrieve_all_cb = retrieve_all_cb;
1793
1794         /* Set account busy */
1795         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1796         modest_mail_operation_notify_start (self);
1797
1798         /* notify about the start of the operation */ 
1799         state = modest_mail_operation_clone_state (self);
1800         state->done = 0;
1801         state->total = 0;
1802
1803         /* Start notifying progress */
1804         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1805         g_slice_free (ModestMailOperationState, state);
1806         
1807         /* Get all folders and continue in the callback */ 
1808         folders = tny_simple_list_new ();
1809         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
1810                                             folders, NULL,
1811                                             recurse_folders_async_cb, 
1812                                             NULL, info);
1813         g_object_unref (folders);
1814         
1815         g_object_unref (priv->account);
1816         
1817 }
1818
1819 /*
1820  * Used to notify the queue from the main
1821  * loop. We call it inside an idle call to achieve that
1822  */
1823 static gboolean
1824 idle_notify_queue (gpointer data)
1825 {
1826         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1827
1828         gdk_threads_enter ();
1829         modest_mail_operation_notify_end (mail_op);
1830         gdk_threads_leave ();
1831         g_object_unref (mail_op);
1832
1833         return FALSE;
1834 }
1835
1836 static int
1837 compare_headers_by_date (gconstpointer a,
1838                          gconstpointer b)
1839 {
1840         TnyHeader **header1, **header2;
1841         time_t sent1, sent2;
1842
1843         header1 = (TnyHeader **) a;
1844         header2 = (TnyHeader **) b;
1845
1846         sent1 = tny_header_get_date_sent (*header1);
1847         sent2 = tny_header_get_date_sent (*header2);
1848
1849         /* We want the most recent ones (greater time_t) at the
1850            beginning */
1851         if (sent1 < sent2)
1852                 return 1;
1853         else
1854                 return -1;
1855 }
1856
1857
1858 /* ******************************************************************* */
1859 /* ************************** STORE  ACTIONS ************************* */
1860 /* ******************************************************************* */
1861
1862 typedef struct {
1863         ModestMailOperation *mail_op;
1864         CreateFolderUserCallback callback;
1865         gpointer user_data;
1866 } CreateFolderInfo;
1867
1868
1869 static void
1870 create_folder_cb (TnyFolderStore *parent_folder, 
1871                   gboolean canceled, 
1872                   TnyFolder *new_folder, 
1873                   GError *err, 
1874                   gpointer user_data)
1875 {
1876         ModestMailOperationPrivate *priv;
1877         CreateFolderInfo *info;
1878
1879         info = (CreateFolderInfo *) user_data;
1880         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1881
1882         if (canceled || err) {
1883                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1884                 if (err)
1885                         priv->error = g_error_copy (err);
1886                 else
1887                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1888                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1889                                      "canceled");               
1890         } else {
1891                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1892         }
1893
1894         /* The user will unref the new_folder */
1895         if (info->callback)
1896                 info->callback (info->mail_op, parent_folder, 
1897                                 new_folder, info->user_data);
1898         
1899         /* Notify about operation end */
1900         modest_mail_operation_notify_end (info->mail_op);
1901
1902         /* Frees */
1903         g_object_unref (info->mail_op);
1904         g_slice_free (CreateFolderInfo, info);
1905 }
1906
1907 void
1908 modest_mail_operation_create_folder (ModestMailOperation *self,
1909                                      TnyFolderStore *parent,
1910                                      const gchar *name,
1911                                      CreateFolderUserCallback callback,
1912                                      gpointer user_data)
1913 {
1914         ModestMailOperationPrivate *priv;
1915
1916         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1917         g_return_if_fail (name);
1918         
1919         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1920         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1921         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1922                 g_object_ref (parent) : 
1923                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1924
1925         /* Check for already existing folder */
1926         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1927                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1928                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1929                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1930                              _CS("ckdg_ib_folder_already_exists"));
1931         }
1932
1933         /* Check parent */
1934         if (TNY_IS_FOLDER (parent)) {
1935                 /* Check folder rules */
1936                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1937                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1938                         /* Set status failed and set an error */
1939                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1940                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1941                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1942                                      _("mail_in_ui_folder_create_error"));
1943                 }
1944         }
1945
1946         if (!strcmp (name, " ") || strchr (name, '/')) {
1947                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1948                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1949                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1950                              _("mail_in_ui_folder_create_error"));
1951         }
1952
1953         if (!priv->error) {
1954                 CreateFolderInfo *info;
1955
1956                 info = g_slice_new0 (CreateFolderInfo);
1957                 info->mail_op = g_object_ref (self);
1958                 info->callback = callback;
1959                 info->user_data = user_data;
1960
1961                 modest_mail_operation_notify_start (self);
1962
1963                 /* Create the folder */
1964                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
1965                                                       NULL, info);
1966         } else {
1967                 /* Call the user callback anyway */
1968                 if (callback)
1969                         callback (self, parent, NULL, user_data);
1970                 /* Notify about operation end */
1971                 modest_mail_operation_notify_end (self);
1972         }
1973 }
1974
1975 void
1976 modest_mail_operation_remove_folder (ModestMailOperation *self,
1977                                      TnyFolder           *folder,
1978                                      gboolean             remove_to_trash)
1979 {
1980         ModestMailOperationPrivate *priv;
1981         ModestTnyFolderRules rules;
1982
1983         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1984         g_return_if_fail (TNY_IS_FOLDER (folder));
1985         
1986         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1987         
1988         /* Check folder rules */
1989         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1990         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1991                 /* Set status failed and set an error */
1992                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1993                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1994                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1995                              _("mail_in_ui_folder_delete_error"));
1996                 goto end;
1997         }
1998
1999         /* Get the account */
2000         priv->account = modest_tny_folder_get_account (folder);
2001         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2002
2003         /* Delete folder or move to trash */
2004         if (remove_to_trash) {
2005                 TnyFolder *trash_folder = NULL;
2006                 trash_folder = modest_tny_account_get_special_folder (priv->account,
2007                                                                       TNY_FOLDER_TYPE_TRASH);
2008                 /* TODO: error_handling */
2009                 if (trash_folder) {
2010                         modest_mail_operation_notify_start (self);
2011                         modest_mail_operation_xfer_folder (self, folder,
2012                                                     TNY_FOLDER_STORE (trash_folder), 
2013                                                     TRUE, NULL, NULL);
2014                         g_object_unref (trash_folder);
2015                 } else {
2016                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
2017                 }
2018         } else {
2019                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
2020                 if (parent) {
2021                         modest_mail_operation_notify_start (self);
2022                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
2023                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
2024                         
2025                         if (!priv->error)
2026                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2027
2028                         g_object_unref (parent);
2029                 } else
2030                         g_warning ("%s: could not get parent folder", __FUNCTION__);
2031         }
2032
2033  end:
2034         /* Notify about operation end */
2035         modest_mail_operation_notify_end (self);
2036 }
2037
2038 static void
2039 transfer_folder_status_cb (GObject *obj,
2040                            TnyStatus *status,
2041                            gpointer user_data)
2042 {
2043         ModestMailOperation *self;
2044         ModestMailOperationPrivate *priv;
2045         ModestMailOperationState *state;
2046         XFerFolderAsyncHelper *helper;
2047
2048         g_return_if_fail (status != NULL);
2049
2050         /* Show only the status information we want */
2051         if (status->code != TNY_FOLDER_STATUS_CODE_COPY_FOLDER)
2052                 return;
2053
2054         helper = (XFerFolderAsyncHelper *) user_data;
2055         g_return_if_fail (helper != NULL);
2056
2057         self = helper->mail_op;
2058         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2059
2060         priv->done = status->position;
2061         priv->total = status->of_total;
2062
2063         state = modest_mail_operation_clone_state (self);
2064
2065         /* This is not a GDK lock because we are a Tinymail callback
2066          * which is already GDK locked by Tinymail */
2067
2068         /* no gdk_threads_enter (), CHECKED */
2069
2070         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
2071
2072         /* no gdk_threads_leave (), CHECKED */
2073
2074         g_slice_free (ModestMailOperationState, state);
2075 }
2076
2077
2078 static void
2079 transfer_folder_cb (TnyFolder *folder, 
2080                     gboolean cancelled, 
2081                     TnyFolderStore *into, 
2082                     TnyFolder *new_folder, 
2083                     GError *err, 
2084                     gpointer user_data)
2085 {
2086         XFerFolderAsyncHelper *helper;
2087         ModestMailOperation *self = NULL;
2088         ModestMailOperationPrivate *priv = NULL;
2089
2090         helper = (XFerFolderAsyncHelper *) user_data;
2091         g_return_if_fail (helper != NULL);       
2092
2093         self = helper->mail_op;
2094         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2095
2096         if (err) {
2097                 priv->error = g_error_copy (err);
2098                 priv->done = 0;
2099                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2100         } else if (cancelled) {
2101                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2102                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2103                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2104                              _("Transference of %s was cancelled."),
2105                              tny_folder_get_name (folder));
2106         } else {
2107                 priv->done = 1;
2108                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2109         }
2110                 
2111         /* Notify about operation end */
2112         modest_mail_operation_notify_end (self);
2113
2114         /* If user defined callback function was defined, call it */
2115         if (helper->user_callback) {
2116
2117                 /* This is not a GDK lock because we are a Tinymail callback
2118                  * which is already GDK locked by Tinymail */
2119
2120                 /* no gdk_threads_enter (), CHECKED */
2121                 helper->user_callback (self, new_folder, helper->user_data);
2122                 /* no gdk_threads_leave () , CHECKED */
2123         }
2124
2125         /* Free */
2126         g_object_unref (helper->mail_op);
2127         g_slice_free   (XFerFolderAsyncHelper, helper);
2128 }
2129
2130 /**
2131  *
2132  * This function checks if the new name is a valid name for our local
2133  * folders account. The new name could not be the same than then name
2134  * of any of the mandatory local folders
2135  *
2136  * We can not rely on tinymail because tinymail does not check the
2137  * name of the virtual folders that the account could have in the case
2138  * that we're doing a rename (because it directly calls Camel which
2139  * knows nothing about our virtual folders). 
2140  *
2141  * In the case of an actual copy/move (i.e. move/copy a folder between
2142  * accounts) tinymail uses the tny_folder_store_create_account which
2143  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2144  * checks the new name of the folder, so this call in that case
2145  * wouldn't be needed. *But* NOTE that if tinymail changes its
2146  * implementation (if folder transfers within the same account is no
2147  * longer implemented as a rename) this call will allow Modest to work
2148  * perfectly
2149  *
2150  * If the new name is not valid, this function will set the status to
2151  * failed and will set also an error in the mail operation
2152  */
2153 static gboolean
2154 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2155                                  TnyFolderStore *into,
2156                                  const gchar *new_name)
2157 {
2158         if (TNY_IS_ACCOUNT (into) && 
2159             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2160             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2161                                                                  new_name)) {
2162                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2163                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2164                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2165                              _CS("ckdg_ib_folder_already_exists"));
2166                 return FALSE;
2167         } else
2168                 return TRUE;
2169 }
2170
2171 void
2172 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2173                                    TnyFolder *folder,
2174                                    TnyFolderStore *parent,
2175                                    gboolean delete_original,
2176                                    XferFolderAsyncUserCallback user_callback,
2177                                    gpointer user_data)
2178 {
2179         ModestMailOperationPrivate *priv = NULL;
2180         ModestTnyFolderRules parent_rules = 0, rules; 
2181         XFerFolderAsyncHelper *helper = NULL;
2182         const gchar *folder_name = NULL;
2183         const gchar *error_msg;
2184
2185         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2186         g_return_if_fail (TNY_IS_FOLDER (folder));
2187         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2188
2189         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2190         folder_name = tny_folder_get_name (folder);
2191
2192         /* Set the error msg */
2193         error_msg = _("mail_in_ui_folder_move_target_error");
2194
2195         /* Get account and set it into mail_operation */
2196         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2197         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2198         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2199
2200         /* Get folder rules */
2201         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2202         if (TNY_IS_FOLDER (parent))
2203                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2204         
2205         /* Apply operation constraints */
2206         if ((gpointer) parent == (gpointer) folder ||
2207             (!TNY_IS_FOLDER_STORE (parent)) || 
2208             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2209                 /* Folder rules */
2210                 goto error;
2211         } else if (TNY_IS_FOLDER (parent) && 
2212                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2213                 /* Folder rules */
2214                 goto error;
2215
2216         } else if (TNY_IS_FOLDER (parent) &&
2217                    TNY_IS_FOLDER_STORE (folder) &&
2218                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2219                                                   TNY_FOLDER_STORE (folder))) {
2220                 /* Do not move a parent into a child */
2221                 goto error;
2222         } else if (TNY_IS_FOLDER_STORE (parent) &&
2223                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2224                 /* Check that the new folder name is not used by any
2225                    parent subfolder */
2226                 goto error;     
2227         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2228                 /* Check that the new folder name is not used by any
2229                    special local folder */
2230                 goto error;
2231         } else {
2232                 /* Create the helper */
2233                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2234                 helper->mail_op = g_object_ref (self);
2235                 helper->user_callback = user_callback;
2236                 helper->user_data = user_data;
2237                 
2238                 /* Move/Copy folder */
2239                 modest_mail_operation_notify_start (self);
2240                 tny_folder_copy_async (folder,
2241                                        parent,
2242                                        tny_folder_get_name (folder),
2243                                        delete_original,
2244                                        transfer_folder_cb,
2245                                        transfer_folder_status_cb,
2246                                        helper);
2247                 return;
2248         }
2249
2250  error:
2251         /* Set status failed and set an error */
2252         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2253         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2254                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2255                      error_msg);
2256
2257         /* Call the user callback if exists */
2258         if (user_callback)
2259                 user_callback (self, NULL, user_data);
2260
2261         /* Notify the queue */
2262         modest_mail_operation_notify_end (self);
2263 }
2264
2265 void
2266 modest_mail_operation_rename_folder (ModestMailOperation *self,
2267                                      TnyFolder *folder,
2268                                      const gchar *name,
2269                                      XferFolderAsyncUserCallback user_callback,
2270                                      gpointer user_data)
2271 {
2272         ModestMailOperationPrivate *priv;
2273         ModestTnyFolderRules rules;
2274         XFerFolderAsyncHelper *helper;
2275
2276         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2277         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2278         g_return_if_fail (name);
2279         
2280         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2281
2282         /* Get account and set it into mail_operation */
2283         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2284         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2285
2286         /* Check folder rules */
2287         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2288         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2289                 goto error;
2290         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2291                 goto error;
2292         } else {
2293                 TnyFolderStore *into;
2294
2295                 into = tny_folder_get_folder_store (folder);    
2296
2297                 /* Check that the new folder name is not used by any
2298                    special local folder */
2299                 if (new_name_valid_if_local_account (priv, into, name)) {
2300                         /* Create the helper */
2301                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2302                         helper->mail_op = g_object_ref(self);
2303                         helper->user_callback = user_callback;
2304                         helper->user_data = user_data;
2305                 
2306                         /* Rename. Camel handles folder subscription/unsubscription */
2307                         modest_mail_operation_notify_start (self);
2308                         tny_folder_copy_async (folder, into, name, TRUE,
2309                                                transfer_folder_cb,
2310                                                transfer_folder_status_cb,
2311                                                helper);
2312                         g_object_unref (into);
2313                 } else {
2314                         g_object_unref (into);
2315                         goto error;
2316                 }
2317
2318                 return;
2319         }
2320  error:
2321         /* Set status failed and set an error */
2322         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2323         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2324                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2325                      _("FIXME: unable to rename"));
2326         
2327         if (user_callback)
2328                 user_callback (self, NULL, user_data);
2329
2330         /* Notify about operation end */
2331         modest_mail_operation_notify_end (self);
2332 }
2333
2334 /* ******************************************************************* */
2335 /* **************************  MSG  ACTIONS  ************************* */
2336 /* ******************************************************************* */
2337
2338 void 
2339 modest_mail_operation_get_msg (ModestMailOperation *self,
2340                                TnyHeader *header,
2341                                gboolean progress_feedback,
2342                                GetMsgAsyncUserCallback user_callback,
2343                                gpointer user_data)
2344 {
2345         GetMsgInfo *helper = NULL;
2346         TnyFolder *folder;
2347         ModestMailOperationPrivate *priv;
2348         
2349         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2350         g_return_if_fail (TNY_IS_HEADER (header));
2351         
2352         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2353         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2354         priv->total = 1;
2355         priv->done = 0;
2356
2357         /* Check memory low */
2358         if (_check_memory_low (self)) {
2359                 if (user_callback)
2360                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2361                 modest_mail_operation_notify_end (self);
2362                 return;
2363         }
2364
2365         /* Get account and set it into mail_operation */
2366         folder = tny_header_get_folder (header);
2367         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2368         
2369         /* Check for cached messages */
2370         if (progress_feedback) {
2371                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2372                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2373                 else 
2374                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2375         } else {
2376                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2377         }
2378         
2379         /* Create the helper */
2380         helper = g_slice_new0 (GetMsgInfo);
2381         helper->header = g_object_ref (header);
2382         helper->mail_op = g_object_ref (self);
2383         helper->user_callback = user_callback;
2384         helper->user_data = user_data;
2385         helper->destroy_notify = NULL;
2386         helper->last_total_bytes = 0;
2387         helper->sum_total_bytes = 0;
2388         helper->total_bytes = tny_header_get_message_size (header);
2389         helper->more_msgs = NULL;
2390
2391         modest_mail_operation_notify_start (self);
2392         
2393         /* notify about the start of the operation */ 
2394         ModestMailOperationState *state;
2395         state = modest_mail_operation_clone_state (self);
2396         state->done = 0;
2397         state->total = 0;
2398         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2399                                 0, state, NULL);
2400         g_slice_free (ModestMailOperationState, state);
2401         
2402         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2403
2404         g_object_unref (G_OBJECT (folder));
2405 }
2406
2407 static void     
2408 get_msg_status_cb (GObject *obj,
2409                    TnyStatus *status,  
2410                    gpointer user_data)
2411 {
2412         GetMsgInfo *helper = NULL;
2413
2414         g_return_if_fail (status != NULL);
2415
2416         /* Show only the status information we want */
2417         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2418                 return;
2419
2420         helper = (GetMsgInfo *) user_data;
2421         g_return_if_fail (helper != NULL);       
2422
2423         /* Notify progress */
2424         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2425                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2426 }
2427
2428 static void
2429 get_msg_async_cb (TnyFolder *folder, 
2430                   gboolean canceled, 
2431                   TnyMsg *msg, 
2432                   GError *err, 
2433                   gpointer user_data)
2434 {
2435         GetMsgInfo *info = NULL;
2436         ModestMailOperationPrivate *priv = NULL;
2437         gboolean finished;
2438
2439         info = (GetMsgInfo *) user_data;
2440
2441         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2442         priv->done++;
2443
2444         if (info->more_msgs) {
2445                 tny_iterator_next (info->more_msgs);
2446                 finished = (tny_iterator_is_done (info->more_msgs));
2447         } else {
2448                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2449         }
2450
2451         /* If canceled by the user, ignore the error given by Tinymail */
2452         if (canceled) {
2453                 canceled = TRUE;
2454                 finished = TRUE;
2455                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2456         } else if (err) {
2457                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2458                 if (err) {
2459                         priv->error = g_error_copy ((const GError *) err);
2460                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2461                 }
2462                 if (!priv->error) {
2463                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2464                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2465                                      err->message);
2466                 }
2467         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2468                 /* Set the success status before calling the user callback */
2469                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2470         }
2471
2472
2473         /* Call the user callback */
2474         if (info->user_callback)
2475                 info->user_callback (info->mail_op, info->header, canceled, 
2476                                      msg, err, info->user_data);
2477
2478         /* Notify about operation end if this is the last callback */
2479         if (finished) {
2480                 /* Free user data */
2481                 if (info->destroy_notify)
2482                         info->destroy_notify (info->user_data);
2483
2484                 /* Notify about operation end */
2485                 modest_mail_operation_notify_end (info->mail_op);
2486
2487                 /* Clean */
2488                 if (info->more_msgs)
2489                         g_object_unref (info->more_msgs);
2490                 g_object_unref (info->header);
2491                 g_object_unref (info->mail_op);
2492                 g_slice_free (GetMsgInfo, info);
2493         } else if (info->more_msgs) {
2494                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
2495                 TnyFolder *folder = tny_header_get_folder (header);
2496
2497                 g_object_unref (info->header);
2498                 info->header = g_object_ref (header);
2499
2500                 /* Retrieve the next message */
2501                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
2502
2503                 g_object_unref (header);
2504                 g_object_unref (folder);
2505         } else {
2506                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
2507         }
2508 }
2509
2510 void 
2511 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2512                                      TnyList *header_list, 
2513                                      GetMsgAsyncUserCallback user_callback,
2514                                      gpointer user_data,
2515                                      GDestroyNotify notify)
2516 {
2517         ModestMailOperationPrivate *priv = NULL;
2518         gint msg_list_size;
2519         TnyIterator *iter = NULL;
2520         gboolean has_uncached_messages;
2521         
2522         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2523
2524         /* Init mail operation */
2525         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2526         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2527         priv->done = 0;
2528         priv->total = tny_list_get_length(header_list);
2529
2530         /* Check memory low */
2531         if (_check_memory_low (self)) {
2532                 if (user_callback) {
2533                         TnyHeader *header = NULL;
2534                         TnyIterator *iter;
2535
2536                         if (tny_list_get_length (header_list) > 0) {
2537                                 iter = tny_list_create_iterator (header_list);
2538                                 header = (TnyHeader *) tny_iterator_get_current (iter);
2539                                 g_object_unref (iter);
2540                         }
2541                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2542                         if (header)
2543                                 g_object_unref (header);
2544                 }
2545                 if (notify)
2546                         notify (user_data);
2547                 /* Notify about operation end */
2548                 modest_mail_operation_notify_end (self);
2549                 return;
2550         }
2551
2552         /* Check uncached messages */
2553         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
2554              !has_uncached_messages && !tny_iterator_is_done (iter); 
2555              tny_iterator_next (iter)) {
2556                 TnyHeader *header;
2557
2558                 header = (TnyHeader *) tny_iterator_get_current (iter);
2559                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
2560                         has_uncached_messages = TRUE;
2561                 g_object_unref (header);
2562         }       
2563         g_object_unref (iter);
2564         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
2565
2566         /* Get account and set it into mail_operation */
2567         if (tny_list_get_length (header_list) >= 1) {
2568                 TnyIterator *iterator = tny_list_create_iterator (header_list);
2569                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
2570                 if (header) {
2571                         TnyFolder *folder = tny_header_get_folder (header);
2572                         if (folder) {           
2573                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2574                                 g_object_unref (folder);
2575                         }
2576                         g_object_unref (header);
2577                 }
2578                 g_object_unref (iterator);
2579         }
2580
2581         msg_list_size = compute_message_list_size (header_list, 0);
2582
2583         modest_mail_operation_notify_start (self);
2584         iter = tny_list_create_iterator (header_list);
2585         if (!tny_iterator_is_done (iter)) {
2586                 /* notify about the start of the operation */
2587                 ModestMailOperationState *state;
2588                 state = modest_mail_operation_clone_state (self);
2589                 state->done = 0;
2590                 state->total = 0;
2591                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2592                                0, state, NULL);
2593
2594                 GetMsgInfo *msg_info = NULL;
2595                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2596                 TnyFolder *folder = tny_header_get_folder (header);
2597
2598                 /* Create the message info */
2599                 msg_info = g_slice_new0 (GetMsgInfo);
2600                 msg_info->mail_op = g_object_ref (self);
2601                 msg_info->header = g_object_ref (header);
2602                 msg_info->more_msgs = g_object_ref (iter);
2603                 msg_info->user_callback = user_callback;
2604                 msg_info->user_data = user_data;
2605                 msg_info->destroy_notify = notify;
2606                 msg_info->last_total_bytes = 0;
2607                 msg_info->sum_total_bytes = 0;
2608                 msg_info->total_bytes = msg_list_size;
2609
2610                 /* The callback will call it per each header */
2611                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2612
2613                 /* Free and go on */
2614                 g_object_unref (header);
2615                 g_object_unref (folder);
2616                 g_slice_free (ModestMailOperationState, state);
2617         }
2618         g_object_unref (iter);
2619 }
2620
2621
2622 static void
2623 remove_msgs_async_cb (TnyFolder *folder, 
2624                       gboolean canceled, 
2625                       GError *err, 
2626                       gpointer user_data)
2627 {
2628         gboolean expunge, leave_on_server;
2629         const gchar *account_name;
2630         const gchar *proto;
2631         TnyAccount *account;
2632         ModestTransportStoreProtocol account_proto = MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
2633         ModestMailOperation *self;
2634         ModestMailOperationPrivate *priv;
2635
2636         self = (ModestMailOperation *) user_data;
2637         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2638
2639         if (canceled || err) {
2640                 /* If canceled by the user, ignore the error given by Tinymail */
2641                 if (canceled) {
2642                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2643                 } else if (err) {
2644                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2645                         priv->error = g_error_copy ((const GError *) err);
2646                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2647                 }
2648                 /* Exit */
2649                 modest_mail_operation_notify_end (self);
2650                 g_object_unref (self);
2651                 return;
2652         }
2653
2654         account = tny_folder_get_account (folder);
2655         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2656         leave_on_server =
2657                 modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2658                                                         account_name);  
2659         proto = tny_account_get_proto (account);
2660         g_object_unref (account);
2661
2662         if (proto)
2663                 account_proto = modest_protocol_info_get_transport_store_protocol (proto);
2664         
2665         if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2666                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2667                 expunge = TRUE;
2668         else
2669                 expunge = FALSE;
2670         
2671         /* Sync folder */
2672         tny_folder_sync_async(folder, expunge, sync_folder_finish_callback, 
2673                               NULL, self);
2674 }
2675
2676 void 
2677 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2678                                    TnyList *headers,
2679                                    gboolean remove_to_trash /*ignored*/)
2680 {
2681         TnyFolder *folder = NULL;
2682         ModestMailOperationPrivate *priv;
2683         TnyIterator *iter = NULL;
2684         TnyHeader *header = NULL;
2685         TnyList *remove_headers = NULL;
2686         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2687
2688         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2689         g_return_if_fail (TNY_IS_LIST (headers));
2690
2691         if (remove_to_trash)
2692                 g_warning ("remove to trash is not implemented");
2693
2694         if (tny_list_get_length(headers) == 0) {
2695                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
2696                 goto cleanup; /* nothing to do */
2697         }
2698         
2699         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2700         remove_headers = g_object_ref(headers);
2701
2702         /* Get folder from first header and sync it */
2703         iter = tny_list_create_iterator (headers);      
2704         header = TNY_HEADER (tny_iterator_get_current (iter));
2705
2706         folder = tny_header_get_folder (header);        
2707         if (!TNY_IS_FOLDER(folder)) {
2708                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
2709                 goto cleanup;
2710         }
2711
2712         /* Don't remove messages that are being sent */
2713         if (modest_tny_folder_is_local_folder (folder)) {
2714                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2715         }
2716         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2717                 TnyTransportAccount *traccount = NULL;
2718                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2719                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2720                 if (traccount) {
2721                         ModestTnySendQueueStatus status;
2722                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount, TRUE);
2723
2724                         if (TNY_IS_SEND_QUEUE (send_queue)) {
2725                                 TnyIterator *iter = tny_list_create_iterator(headers);
2726                                 g_object_unref(remove_headers);
2727                                 remove_headers = TNY_LIST(tny_simple_list_new());
2728                                 while (!tny_iterator_is_done(iter)) {
2729                                         char *msg_id;
2730                                         TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2731                                         msg_id = modest_tny_send_queue_get_msg_id (hdr);
2732                                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2733                                         if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2734                                                 tny_list_append(remove_headers, G_OBJECT(hdr));
2735                                         }
2736                                         g_object_unref(hdr);
2737                                         g_free(msg_id);
2738                                         tny_iterator_next(iter);
2739                                 }
2740                                 g_object_unref(iter);
2741                         }
2742                         g_object_unref(traccount);
2743                 }
2744         }
2745
2746         /* Get account and set it into mail_operation */
2747         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2748         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2749         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2750
2751         /* remove message from folder */
2752         modest_mail_operation_notify_start (self);
2753         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
2754                                       NULL, g_object_ref (self));
2755
2756 cleanup:
2757         if (remove_headers)
2758                 g_object_unref (remove_headers);
2759         if (header)
2760                 g_object_unref (header);
2761         if (iter)
2762                 g_object_unref (iter);
2763         if (folder)
2764                 g_object_unref (folder);
2765 }
2766
2767 static void
2768 notify_progress_of_multiple_messages (ModestMailOperation *self,
2769                                       TnyStatus *status,
2770                                       gint *last_total_bytes,
2771                                       gint *sum_total_bytes,
2772                                       gint total_bytes, 
2773                                       gboolean increment_done)
2774 {
2775         ModestMailOperationPrivate *priv;
2776         ModestMailOperationState *state;
2777         gboolean is_num_bytes = FALSE;
2778
2779         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2780
2781         /* We know that tinymail sends us information about
2782          *  transferred bytes with this particular message
2783          *  
2784          *  (FIXME: this is very ugly, and no I (djcb) didn't write this code,
2785          *  I just added the 'if' so we don't get runtime warning)
2786          */
2787         if (status->message)
2788                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2789
2790         state = modest_mail_operation_clone_state (self);
2791         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2792                 /* We know that we're in a different message when the
2793                    total number of bytes to transfer is different. Of
2794                    course it could fail if we're transferring messages
2795                    of the same size, but this is a workarround */
2796                 if (status->of_total != *last_total_bytes) {
2797                         /* We need to increment the done when there is
2798                            no information about each individual
2799                            message, we need to do this in message
2800                            transfers, and we don't do it for getting
2801                            messages */
2802                         if (increment_done)
2803                                 priv->done++;
2804                         *sum_total_bytes += *last_total_bytes;
2805                         *last_total_bytes = status->of_total;
2806                 }
2807                 state->bytes_done += status->position + *sum_total_bytes;
2808                 state->bytes_total = total_bytes;
2809
2810                 /* Notify the status change. Only notify about changes
2811                    referred to bytes */
2812                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2813                                0, state, NULL);
2814         }
2815
2816         g_slice_free (ModestMailOperationState, state);
2817 }
2818
2819 static void
2820 transfer_msgs_status_cb (GObject *obj,
2821                          TnyStatus *status,  
2822                          gpointer user_data)
2823 {
2824         XFerMsgsAsyncHelper *helper;
2825
2826         g_return_if_fail (status != NULL);
2827
2828         /* Show only the status information we want */
2829         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
2830                 return;
2831
2832         helper = (XFerMsgsAsyncHelper *) user_data;
2833         g_return_if_fail (helper != NULL);       
2834
2835         /* Notify progress */
2836         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2837                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2838 }
2839
2840 static void
2841 transfer_msgs_sync_folder_cb (TnyFolder *self, 
2842                               gboolean cancelled, 
2843                               GError *err, 
2844                               gpointer user_data)
2845 {
2846         XFerMsgsAsyncHelper *helper;
2847         /* We don't care here about the results of the
2848            synchronization */
2849         helper = (XFerMsgsAsyncHelper *) user_data;
2850
2851         /* Notify about operation end */
2852         modest_mail_operation_notify_end (helper->mail_op);
2853
2854         /* If user defined callback function was defined, call it */
2855         if (helper->user_callback)
2856                 helper->user_callback (helper->mail_op, helper->user_data);
2857         
2858         /* Free */
2859         if (helper->more_msgs)
2860                 g_object_unref (helper->more_msgs);
2861         if (helper->headers)
2862                 g_object_unref (helper->headers);
2863         if (helper->dest_folder)
2864                 g_object_unref (helper->dest_folder);
2865         if (helper->mail_op)
2866                 g_object_unref (helper->mail_op);
2867         g_slice_free (XFerMsgsAsyncHelper, helper);
2868 }
2869
2870 static void
2871 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2872 {
2873         XFerMsgsAsyncHelper *helper;
2874         ModestMailOperation *self;
2875         ModestMailOperationPrivate *priv;
2876         gboolean finished = TRUE;
2877
2878         helper = (XFerMsgsAsyncHelper *) user_data;
2879         self = helper->mail_op;
2880
2881         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2882
2883         if (cancelled) {
2884                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2885         } else if (err) {
2886                 priv->error = g_error_copy (err);
2887                 priv->done = 0;
2888                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2889         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2890                 if (helper->more_msgs) {
2891                         /* We'll transfer the next message in the list */
2892                         tny_iterator_next (helper->more_msgs);
2893                         if (!tny_iterator_is_done (helper->more_msgs)) {
2894                                 GObject *next_header;
2895                                 g_object_unref (helper->headers);
2896                                 helper->headers = tny_simple_list_new ();
2897                                 next_header = tny_iterator_get_current (helper->more_msgs);
2898                                 tny_list_append (helper->headers, next_header);
2899                                 g_object_unref (next_header);
2900                                 finished = FALSE;
2901                         }
2902                 }
2903                 if (finished) {
2904                         priv->done = 1;
2905                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2906                 }
2907         }
2908
2909         if (finished) {
2910                 /* Synchronize the source folder contents. This should
2911                    be done by tinymail but the camel_folder_sync it's
2912                    actually disabled in transfer_msgs_thread_clean
2913                    because it's supposed to cause hangs */
2914                 tny_folder_sync_async (folder, helper->delete, 
2915                                        transfer_msgs_sync_folder_cb, 
2916                                        NULL, helper);
2917         } else {
2918                 /* Transfer more messages */
2919                 tny_folder_transfer_msgs_async (folder,
2920                                                 helper->headers,
2921                                                 helper->dest_folder,
2922                                                 helper->delete,
2923                                                 transfer_msgs_cb,
2924                                                 transfer_msgs_status_cb,
2925                                                 helper);
2926         }
2927 }
2928
2929 /* Computes the size of the messages the headers in the list belongs
2930    to. If num_elements is different from 0 then it only takes into
2931    account the first num_elements for the calculation */
2932 static guint
2933 compute_message_list_size (TnyList *headers, 
2934                            guint num_elements)
2935 {
2936         TnyIterator *iter;
2937         guint size = 0, element = 0;
2938
2939         /* If num_elements is not valid then take all into account */
2940         if ((num_elements <= 0) || (num_elements > tny_list_get_length (headers)))
2941                 num_elements = tny_list_get_length (headers);
2942
2943         iter = tny_list_create_iterator (headers);
2944         while (!tny_iterator_is_done (iter) && element < num_elements) {
2945                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2946                 size += tny_header_get_message_size (header);
2947                 g_object_unref (header);
2948                 tny_iterator_next (iter);
2949                 element++;
2950         }
2951         g_object_unref (iter);
2952
2953         return size;
2954 }
2955
2956 void
2957 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2958                                  TnyList *headers, 
2959                                  TnyFolder *folder, 
2960                                  gboolean delete_original,
2961                                  XferMsgsAsyncUserCallback user_callback,
2962                                  gpointer user_data)
2963 {
2964         ModestMailOperationPrivate *priv = NULL;
2965         TnyIterator *iter = NULL;
2966         TnyFolder *src_folder = NULL;
2967         XFerMsgsAsyncHelper *helper = NULL;
2968         TnyHeader *header = NULL;
2969         ModestTnyFolderRules rules = 0;
2970         TnyAccount *dst_account = NULL;
2971         gboolean leave_on_server;
2972         ModestMailOperationState *state;
2973
2974         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2975         g_return_if_fail (headers && TNY_IS_LIST (headers));
2976         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2977
2978         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2979         priv->total = tny_list_get_length (headers);
2980         priv->done = 0;
2981         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2982         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2983
2984         /* Apply folder rules */
2985         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2986         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2987                 /* Set status failed and set an error */
2988                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2989                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2990                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2991                              _CS("ckct_ib_unable_to_paste_here"));
2992                 /* Notify the queue */
2993                 modest_mail_operation_notify_end (self);
2994                 return;
2995         }
2996                 
2997         /* Get source folder */
2998         iter = tny_list_create_iterator (headers);
2999         header = TNY_HEADER (tny_iterator_get_current (iter));
3000         if (header) {
3001                 src_folder = tny_header_get_folder (header);
3002                 g_object_unref (header);
3003         }
3004         g_object_unref (iter);
3005
3006         if (src_folder == NULL) {
3007                 /* Notify the queue */
3008                 modest_mail_operation_notify_end (self);
3009
3010                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
3011                 return;
3012         }
3013
3014         
3015         /* Check folder source and destination */
3016         if (src_folder == folder) {
3017                 /* Set status failed and set an error */
3018                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3019                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3020                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
3021                              _("mail_in_ui_folder_copy_target_error"));
3022                 
3023                 /* Notify the queue */
3024                 modest_mail_operation_notify_end (self);
3025                 
3026                 /* Free */
3027                 g_object_unref (src_folder);            
3028                 return;
3029         }
3030
3031         /* Create the helper */
3032         helper = g_slice_new0 (XFerMsgsAsyncHelper);
3033         helper->mail_op = g_object_ref(self);
3034         helper->dest_folder = g_object_ref(folder);
3035         helper->user_callback = user_callback;
3036         helper->user_data = user_data;
3037         helper->last_total_bytes = 0;
3038         helper->sum_total_bytes = 0;
3039         helper->total_bytes = compute_message_list_size (headers, 0);
3040
3041         /* Get account and set it into mail_operation */
3042         priv->account = modest_tny_folder_get_account (src_folder);
3043         dst_account = modest_tny_folder_get_account (folder);
3044
3045         if (priv->account == dst_account) {
3046                 /* Transfer all messages at once using the fast
3047                  * method. Note that depending on the server this
3048                  * might not be that fast, and might not be
3049                  * user-cancellable either */
3050                 helper->headers = g_object_ref (headers);
3051                 helper->more_msgs = NULL;
3052         } else {
3053                 /* Transfer messages one by one so the user can cancel
3054                  * the operation */
3055                 GObject *hdr;
3056                 helper->headers = tny_simple_list_new ();
3057                 helper->more_msgs = tny_list_create_iterator (headers);
3058                 hdr = tny_iterator_get_current (helper->more_msgs);
3059                 tny_list_append (helper->headers, hdr);
3060                 g_object_unref (hdr);
3061         }
3062
3063         /* If leave_on_server is set to TRUE then don't use
3064            delete_original, we always pass FALSE. This is because
3065            otherwise tinymail will try to sync the source folder and
3066            this could cause an error if we're offline while
3067            transferring an already downloaded message from a POP
3068            account */
3069         if (modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (priv->account)) == 
3070             MODEST_PROTOCOL_STORE_POP) {
3071                 const gchar *account_name;
3072
3073                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
3074                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3075                                                                           account_name);
3076         } else {
3077                 leave_on_server = FALSE;
3078         }
3079
3080         /* Do not delete messages if leave on server is TRUE */
3081         helper->delete = (leave_on_server) ? FALSE : delete_original;
3082
3083         modest_mail_operation_notify_start (self);
3084
3085         /* Start notifying progress */
3086         state = modest_mail_operation_clone_state (self);
3087         state->done = 0;
3088         state->total = 0;
3089         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3090         g_slice_free (ModestMailOperationState, state);
3091
3092         tny_folder_transfer_msgs_async (src_folder, 
3093                                         helper->headers, 
3094                                         folder, 
3095                                         helper->delete, 
3096                                         transfer_msgs_cb, 
3097                                         transfer_msgs_status_cb,
3098                                         helper);
3099         g_object_unref (src_folder);
3100         g_object_unref (dst_account);
3101 }
3102
3103
3104 static void
3105 on_refresh_folder (TnyFolder   *folder, 
3106                    gboolean     cancelled, 
3107                    GError     *error,
3108                    gpointer     user_data)
3109 {
3110         RefreshAsyncHelper *helper = NULL;
3111         ModestMailOperation *self = NULL;
3112         ModestMailOperationPrivate *priv = NULL;
3113
3114         helper = (RefreshAsyncHelper *) user_data;
3115         self = helper->mail_op;
3116         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3117
3118         g_return_if_fail(priv!=NULL);
3119
3120         if (error) {
3121                 priv->error = g_error_copy (error);
3122                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3123                 goto out;
3124         }
3125
3126         if (cancelled) {
3127                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3128                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3129                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3130                              _("Error trying to refresh the contents of %s"),
3131                              tny_folder_get_name (folder));
3132                 goto out;
3133         }
3134
3135         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3136  out:
3137
3138         /* Call user defined callback, if it exists */
3139         if (helper->user_callback) {
3140
3141                 /* This is not a GDK lock because we are a Tinymail callback and
3142                  * Tinymail already acquires the Gdk lock */
3143                 helper->user_callback (self, folder, helper->user_data);
3144         }
3145
3146         /* Free */
3147         g_slice_free (RefreshAsyncHelper, helper);
3148
3149         /* Notify about operation end */
3150         modest_mail_operation_notify_end (self);
3151         g_object_unref(self);
3152 }
3153
3154 static void
3155 on_refresh_folder_status_update (GObject *obj,
3156                                  TnyStatus *status,
3157                                  gpointer user_data)
3158 {
3159         RefreshAsyncHelper *helper = NULL;
3160         ModestMailOperation *self = NULL;
3161         ModestMailOperationPrivate *priv = NULL;
3162         ModestMailOperationState *state;
3163
3164         g_return_if_fail (user_data != NULL);
3165         g_return_if_fail (status != NULL);
3166
3167         /* Show only the status information we want */
3168         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
3169                 return;
3170
3171         helper = (RefreshAsyncHelper *) user_data;
3172         self = helper->mail_op;
3173         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3174
3175         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3176
3177         priv->done = status->position;
3178         priv->total = status->of_total;
3179
3180         state = modest_mail_operation_clone_state (self);
3181
3182         /* This is not a GDK lock because we are a Tinymail callback and
3183          * Tinymail already acquires the Gdk lock */
3184         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3185
3186         g_slice_free (ModestMailOperationState, state);
3187 }
3188
3189 void 
3190 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3191                                        TnyFolder *folder,
3192                                        RefreshAsyncUserCallback user_callback,
3193                                        gpointer user_data)
3194 {
3195         ModestMailOperationPrivate *priv = NULL;
3196         RefreshAsyncHelper *helper = NULL;
3197
3198         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3199
3200         /* Check memory low */
3201         if (_check_memory_low (self)) {
3202                 if (user_callback)
3203                         user_callback (self, folder, user_data);
3204                 /* Notify about operation end */
3205                 modest_mail_operation_notify_end (self);
3206                 return;
3207         }
3208
3209         /* Get account and set it into mail_operation */
3210         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3211         priv->account = modest_tny_folder_get_account  (folder);
3212         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3213
3214         /* Create the helper */
3215         helper = g_slice_new0 (RefreshAsyncHelper);
3216         helper->mail_op = g_object_ref(self);
3217         helper->user_callback = user_callback;
3218         helper->user_data = user_data;
3219
3220         modest_mail_operation_notify_start (self);
3221         
3222         /* notify that the operation was started */
3223         ModestMailOperationState *state;
3224         state = modest_mail_operation_clone_state (self);
3225         state->done = 0;
3226         state->total = 0;
3227         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3228                         0, state, NULL);
3229         g_slice_free (ModestMailOperationState, state);
3230         
3231         tny_folder_refresh_async (folder,
3232                                   on_refresh_folder,
3233                                   on_refresh_folder_status_update,
3234                                   helper);
3235 }
3236
3237 static void
3238 run_queue_stop (ModestTnySendQueue *queue,
3239                 ModestMailOperation *self)
3240 {
3241         ModestMailOperationPrivate *priv;
3242
3243         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3244         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3245         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3246
3247         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3248
3249         modest_mail_operation_notify_end (self);
3250         g_signal_handlers_disconnect_by_func (queue, run_queue_stop, self);
3251         g_object_unref (self);
3252 }
3253 void
3254 modest_mail_operation_run_queue (ModestMailOperation *self,
3255                                  ModestTnySendQueue *queue)
3256 {
3257         ModestMailOperationPrivate *priv;
3258
3259         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3260         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3261         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3262
3263         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3264         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3265         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3266
3267         modest_mail_operation_notify_start (self);
3268         g_object_ref (self);
3269         g_signal_connect ((gpointer) queue, "queue-stop", G_CALLBACK (run_queue_stop), (gpointer) self);
3270 }
3271
3272 static void
3273 sync_folder_finish_callback (TnyFolder *self, 
3274                              gboolean cancelled, 
3275                              GError *err, 
3276                              gpointer user_data)
3277
3278 {
3279         ModestMailOperation *mail_op;
3280         ModestMailOperationPrivate *priv;
3281
3282         mail_op = (ModestMailOperation *) user_data;
3283         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3284
3285         /* If canceled by the user, ignore the error given by Tinymail */
3286         if (cancelled) {
3287                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3288         } else if (err) {
3289                 /* If the operation was a sync then the status is
3290                    failed, but if it's part of another operation then
3291                    just set it as finished with errors */
3292                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
3293                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3294                 else
3295                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3296                 priv->error = g_error_copy ((const GError *) err);
3297                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3298         } else {
3299                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3300         }
3301
3302         modest_mail_operation_notify_end (mail_op);
3303         g_object_unref (mail_op);
3304 }
3305
3306 void
3307 modest_mail_operation_sync_folder (ModestMailOperation *self,
3308                                    TnyFolder *folder, gboolean expunge)
3309 {
3310         ModestMailOperationPrivate *priv;
3311
3312         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3313         g_return_if_fail (TNY_IS_FOLDER (folder));
3314         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3315
3316         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3317         priv->account = modest_tny_folder_get_account (folder);
3318         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3319
3320         modest_mail_operation_notify_start (self);
3321         g_object_ref (self);
3322         tny_folder_sync_async (folder, expunge, 
3323                                (TnyFolderCallback) sync_folder_finish_callback, 
3324                                NULL, self);
3325 }
3326
3327 static void
3328 modest_mail_operation_notify_start (ModestMailOperation *self)
3329 {
3330         ModestMailOperationPrivate *priv = NULL;
3331
3332         g_return_if_fail (self);
3333
3334         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3335
3336         /* Ensure that all the fields are filled correctly */
3337         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3338
3339         /* Notify the observers about the mail operation. We do not
3340            wrapp this emission because we assume that this function is
3341            always called from within the main lock */
3342         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3343 }
3344
3345 /**
3346  *
3347  * It's used by the mail operation queue to notify the observers
3348  * attached to that signal that the operation finished. We need to use
3349  * that because tinymail does not give us the progress of a given
3350  * operation when it finishes (it directly calls the operation
3351  * callback).
3352  */
3353 static void
3354 modest_mail_operation_notify_end (ModestMailOperation *self)
3355 {
3356         ModestMailOperationPrivate *priv = NULL;
3357
3358         g_return_if_fail (self);
3359
3360         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3361
3362         /* Notify the observers about the mail operation end. We do
3363            not wrapp this emission because we assume that this
3364            function is always called from within the main lock */
3365         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3366
3367         /* Remove the error user data */
3368         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3369                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3370 }
3371
3372 TnyAccount *
3373 modest_mail_operation_get_account (ModestMailOperation *self)
3374 {
3375         ModestMailOperationPrivate *priv = NULL;
3376
3377         g_return_val_if_fail (self, NULL);
3378
3379         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3380
3381         return (priv->account) ? g_object_ref (priv->account) : NULL;
3382 }
3383
3384 void
3385 modest_mail_operation_noop (ModestMailOperation *self)
3386 {
3387         ModestMailOperationPrivate *priv = NULL;
3388
3389         g_return_if_fail (self);
3390
3391         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3392         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3393         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3394         priv->done = 0;
3395         priv->total = 0;
3396
3397         /* This mail operation does nothing actually */
3398         modest_mail_operation_notify_start (self);
3399         modest_mail_operation_notify_end (self);
3400 }
3401
3402
3403 gchar*
3404 modest_mail_operation_to_string (ModestMailOperation *self)
3405 {
3406         const gchar *type, *status, *account_id;
3407         ModestMailOperationPrivate *priv = NULL;
3408         
3409         g_return_val_if_fail (self, NULL);
3410
3411         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3412
3413         /* new operations don't have anything interesting */
3414         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3415                 return g_strdup_printf ("%p <new operation>", self);
3416         
3417         switch (priv->op_type) {
3418         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3419         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3420         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3421         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3422         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3423         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
3424         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
3425         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3426         default: type = "UNEXPECTED"; break;
3427         }
3428
3429         switch (priv->status) {
3430         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3431         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3432         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3433         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3434         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3435         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3436         default:                                                status= "UNEXPECTED"; break;
3437         } 
3438
3439         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3440
3441         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3442                                 priv->done, priv->total,
3443                                 priv->error && priv->error->message ? priv->error->message : "");
3444 }
3445
3446 /* 
3447  * Once the mail operations were objects this will be no longer
3448  * needed. I don't like it, but we need it for the moment
3449  */
3450 static gboolean
3451 _check_memory_low (ModestMailOperation *mail_op)
3452 {
3453         if (modest_platform_check_memory_low (NULL, FALSE)) {
3454                 ModestMailOperationPrivate *priv;
3455
3456                 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3457                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3458                 g_set_error (&(priv->error),
3459                              MODEST_MAIL_OPERATION_ERROR,
3460                              MODEST_MAIL_OPERATION_ERROR_LOW_MEMORY,
3461                              "Not enough memory to complete the operation");
3462                 return TRUE;
3463         } else {
3464                 return FALSE;
3465         }
3466 }