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