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