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