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