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