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