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