* Set the proper status of the mail operation
[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                         g_object_unref (into);
2113                 } else {
2114                         g_object_unref (into);
2115                         goto error;
2116                 }
2117
2118                 return;
2119         }
2120  error:
2121         /* Set status failed and set an error */
2122         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2123         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2124                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2125                      _("FIXME: unable to rename"));
2126         
2127         if (user_callback)
2128                 user_callback (self, NULL, user_data);
2129
2130         /* Notify about operation end */
2131         modest_mail_operation_notify_end (self);
2132 }
2133
2134 /* ******************************************************************* */
2135 /* **************************  MSG  ACTIONS  ************************* */
2136 /* ******************************************************************* */
2137
2138 void 
2139 modest_mail_operation_get_msg (ModestMailOperation *self,
2140                                TnyHeader *header,
2141                                GetMsgAsyncUserCallback user_callback,
2142                                gpointer user_data)
2143 {
2144         GetMsgInfo *helper = NULL;
2145         TnyFolder *folder;
2146         ModestMailOperationPrivate *priv;
2147         
2148         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2149         g_return_if_fail (TNY_IS_HEADER (header));
2150         
2151         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2152         folder = tny_header_get_folder (header);
2153
2154         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2155         priv->total = 1;
2156         priv->done = 0;
2157
2158         /* Get account and set it into mail_operation */
2159         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2160         
2161         /* Check for cached messages */
2162         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2163                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2164         else 
2165                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2166         
2167         /* Create the helper */
2168         helper = g_slice_new0 (GetMsgInfo);
2169         helper->header = g_object_ref (header);
2170         helper->mail_op = g_object_ref (self);
2171         helper->user_callback = user_callback;
2172         helper->user_data = user_data;
2173         helper->destroy_notify = NULL;
2174         helper->last_total_bytes = 0;
2175         helper->sum_total_bytes = 0;
2176         helper->total_bytes = tny_header_get_message_size (header);
2177
2178         modest_mail_operation_notify_start (self);
2179         
2180         /* notify about the start of the operation */ 
2181         ModestMailOperationState *state;
2182         state = modest_mail_operation_clone_state (self);
2183         state->done = 0;
2184         state->total = 0;
2185         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2186                                 0, state, NULL);
2187         
2188         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2189
2190         g_object_unref (G_OBJECT (folder));
2191 }
2192
2193 static void     
2194 get_msg_status_cb (GObject *obj,
2195                    TnyStatus *status,  
2196                    gpointer user_data)
2197 {
2198         GetMsgInfo *helper = NULL;
2199
2200         g_return_if_fail (status != NULL);
2201
2202         /* Show only the status information we want */
2203         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2204                 return;
2205
2206         helper = (GetMsgInfo *) user_data;
2207         g_return_if_fail (helper != NULL);       
2208
2209         /* Notify progress */
2210         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2211                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2212 }
2213
2214 static void
2215 get_msg_async_cb (TnyFolder *folder, 
2216                   gboolean canceled, 
2217                   TnyMsg *msg, 
2218                   GError *err, 
2219                   gpointer user_data)
2220 {
2221         GetMsgInfo *info = NULL;
2222         ModestMailOperationPrivate *priv = NULL;
2223         gboolean finished;
2224
2225         info = (GetMsgInfo *) user_data;
2226
2227         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2228         priv->done++;
2229
2230         if (info->more_msgs) {
2231                 tny_iterator_next (info->more_msgs);
2232                 finished = (tny_iterator_is_done (info->more_msgs));
2233         } else {
2234                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2235         }
2236
2237         /* If canceled by the user, ignore the error given by Tinymail */
2238         if (canceled) {
2239                 canceled = TRUE;
2240                 finished = TRUE;
2241                 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED;
2242         } else if (err) {
2243                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2244                 if (err) {
2245                         priv->error = g_error_copy ((const GError *) err);
2246                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2247                 }
2248                 if (!priv->error) {
2249                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2250                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2251                                      err->message);
2252                 }
2253         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2254                 /* Set the success status before calling the user callback */
2255                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2256         }
2257
2258
2259         /* Call the user callback */
2260         if (info->user_callback)
2261                 info->user_callback (info->mail_op, info->header, canceled, 
2262                                      msg, err, info->user_data);
2263
2264         /* Notify about operation end if this is the last callback */
2265         if (finished) {
2266                 /* Free user data */
2267                 if (info->destroy_notify)
2268                         info->destroy_notify (info->user_data);
2269
2270                 /* Notify about operation end */
2271                 modest_mail_operation_notify_end (info->mail_op);
2272
2273                 /* Clean */
2274                 if (info->more_msgs)
2275                         g_object_unref (info->more_msgs);
2276                 g_object_unref (info->header);
2277                 g_object_unref (info->mail_op);
2278                 g_slice_free (GetMsgInfo, info);
2279         } else if (info->more_msgs) {
2280                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
2281                 TnyFolder *folder = tny_header_get_folder (header);
2282
2283                 g_object_unref (info->header);
2284                 info->header = g_object_ref (header);
2285
2286                 /* Retrieve the next message */
2287                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
2288
2289                 g_object_unref (header);
2290                 g_object_unref (folder);
2291         } else {
2292                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
2293         }
2294 }
2295
2296 void 
2297 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2298                                      TnyList *header_list, 
2299                                      GetMsgAsyncUserCallback user_callback,
2300                                      gpointer user_data,
2301                                      GDestroyNotify notify)
2302 {
2303         ModestMailOperationPrivate *priv = NULL;
2304         gint msg_list_size;
2305         TnyIterator *iter = NULL;
2306         gboolean has_uncached_messages;
2307         
2308         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2309
2310         /* Init mail operation */
2311         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2312         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2313         priv->done = 0;
2314         priv->total = tny_list_get_length(header_list);
2315
2316         /* Check uncached messages */
2317         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
2318              !has_uncached_messages && !tny_iterator_is_done (iter); 
2319              tny_iterator_next (iter)) {
2320                 TnyHeader *header;
2321
2322                 header = (TnyHeader *) tny_iterator_get_current (iter);
2323                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
2324                         has_uncached_messages = TRUE;
2325                 g_object_unref (header);
2326         }       
2327         g_object_unref (iter);
2328         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
2329
2330         /* Get account and set it into mail_operation */
2331         if (tny_list_get_length (header_list) >= 1) {
2332                 TnyIterator *iterator = tny_list_create_iterator (header_list);
2333                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
2334                 if (header) {
2335                         TnyFolder *folder = tny_header_get_folder (header);
2336                         if (folder) {           
2337                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2338                                 g_object_unref (folder);
2339                         }
2340                         g_object_unref (header);
2341                 }
2342                 g_object_unref (iterator);
2343         }
2344
2345         msg_list_size = compute_message_list_size (header_list);
2346
2347         modest_mail_operation_notify_start (self);
2348         iter = tny_list_create_iterator (header_list);
2349         if (!tny_iterator_is_done (iter)) {
2350                 /* notify about the start of the operation */
2351                 ModestMailOperationState *state;
2352                 state = modest_mail_operation_clone_state (self);
2353                 state->done = 0;
2354                 state->total = 0;
2355                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2356                                0, state, NULL);
2357
2358                 GetMsgInfo *msg_info = NULL;
2359                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2360                 TnyFolder *folder = tny_header_get_folder (header);
2361
2362                 /* Create the message info */
2363                 msg_info = g_slice_new0 (GetMsgInfo);
2364                 msg_info->mail_op = g_object_ref (self);
2365                 msg_info->header = g_object_ref (header);
2366                 msg_info->more_msgs = g_object_ref (iter);
2367                 msg_info->user_callback = user_callback;
2368                 msg_info->user_data = user_data;
2369                 msg_info->destroy_notify = notify;
2370                 msg_info->last_total_bytes = 0;
2371                 msg_info->sum_total_bytes = 0;
2372                 msg_info->total_bytes = msg_list_size;
2373
2374                 /* The callback will call it per each header */
2375                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2376
2377                 /* Free and go on */
2378                 g_object_unref (header);
2379                 g_object_unref (folder);
2380                 g_slice_free (ModestMailOperationState, state);
2381         }
2382         g_object_unref (iter);
2383 }
2384
2385
2386 static void
2387 remove_msgs_async_cb (TnyFolder *folder, 
2388                       gboolean canceled, 
2389                       GError *err, 
2390                       gpointer user_data)
2391 {
2392         gboolean expunge, leave_on_server;
2393         const gchar *account_name;
2394         const gchar *proto;
2395         TnyAccount *account;
2396         ModestTransportStoreProtocol account_proto = MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
2397         ModestMailOperation *self;
2398         ModestMailOperationPrivate *priv;
2399
2400         self = (ModestMailOperation *) user_data;
2401         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2402
2403         if (canceled || err) {
2404                 /* If canceled by the user, ignore the error given by Tinymail */
2405                 if (canceled) {
2406                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2407                 } else if (err) {
2408                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2409                         priv->error = g_error_copy ((const GError *) err);
2410                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2411                 }
2412                 /* Exit */
2413                 modest_mail_operation_notify_end (self);
2414                 g_object_unref (self);
2415                 return;
2416         }
2417
2418         account = tny_folder_get_account (folder);
2419         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2420         leave_on_server =
2421                 modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2422                                                         account_name);  
2423         proto = tny_account_get_proto (account);
2424         g_object_unref (account);
2425
2426         if (proto)
2427                 account_proto = modest_protocol_info_get_transport_store_protocol (proto);
2428         
2429         if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2430                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2431                 expunge = TRUE;
2432         else
2433                 expunge = FALSE;
2434         
2435         /* Sync folder */
2436         tny_folder_sync_async(folder, expunge, sync_folder_finish_callback, 
2437                               NULL, self);
2438 }
2439
2440 void 
2441 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2442                                    TnyList *headers,
2443                                    gboolean remove_to_trash /*ignored*/)
2444 {
2445         TnyFolder *folder = NULL;
2446         ModestMailOperationPrivate *priv;
2447         TnyIterator *iter = NULL;
2448         TnyHeader *header = NULL;
2449         TnyList *remove_headers = NULL;
2450         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2451
2452         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2453         g_return_if_fail (TNY_IS_LIST (headers));
2454
2455         if (remove_to_trash)
2456                 g_warning ("remove to trash is not implemented");
2457
2458         if (tny_list_get_length(headers) == 0) {
2459                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
2460                 goto cleanup; /* nothing to do */
2461         }
2462         
2463         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2464         remove_headers = g_object_ref(headers);
2465
2466         /* Get folder from first header and sync it */
2467         iter = tny_list_create_iterator (headers);      
2468         header = TNY_HEADER (tny_iterator_get_current (iter));
2469
2470         folder = tny_header_get_folder (header);        
2471         if (!TNY_IS_FOLDER(folder)) {
2472                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
2473                 goto cleanup;
2474         }
2475
2476         /* Don't remove messages that are being sent */
2477         if (modest_tny_folder_is_local_folder (folder)) {
2478                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2479         }
2480         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2481                 TnyTransportAccount *traccount = NULL;
2482                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2483                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2484                 if (traccount) {
2485                         ModestTnySendQueueStatus status;
2486                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2487                         TnyIterator *iter = tny_list_create_iterator(headers);
2488                         g_object_unref(remove_headers);
2489                         remove_headers = TNY_LIST(tny_simple_list_new());
2490                         while (!tny_iterator_is_done(iter)) {
2491                                 char *msg_id;
2492                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2493                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2494                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2495                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2496                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2497                                 }
2498                                 g_object_unref(hdr);
2499                                 g_free(msg_id);
2500                                 tny_iterator_next(iter);
2501                         }
2502                         g_object_unref(iter);
2503                         g_object_unref(traccount);
2504                 }
2505         }
2506
2507         /* Get account and set it into mail_operation */
2508         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2509         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2510         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2511
2512         /* remove message from folder */
2513         modest_mail_operation_notify_start (self);
2514         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
2515                                       NULL, g_object_ref (self));
2516
2517 cleanup:
2518         if (remove_headers)
2519                 g_object_unref (remove_headers);
2520         if (header)
2521                 g_object_unref (header);
2522         if (iter)
2523                 g_object_unref (iter);
2524         if (folder)
2525                 g_object_unref (folder);
2526 }
2527
2528 static void
2529 notify_progress_of_multiple_messages (ModestMailOperation *self,
2530                                       TnyStatus *status,
2531                                       gint *last_total_bytes,
2532                                       gint *sum_total_bytes,
2533                                       gint total_bytes, 
2534                                       gboolean increment_done)
2535 {
2536         ModestMailOperationPrivate *priv;
2537         ModestMailOperationState *state;
2538         gboolean is_num_bytes = FALSE;
2539
2540         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2541
2542         /* We know that tinymail sends us information about
2543          *  transferred bytes with this particular message
2544          *  
2545          *  (FIXME: this is very ugly, and no I (djcb) didn't write this code,
2546          *  I just added the 'if' so we don't get runtime warning)
2547          */
2548         if (status->message)
2549                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2550
2551         state = modest_mail_operation_clone_state (self);
2552         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2553                 /* We know that we're in a different message when the
2554                    total number of bytes to transfer is different. Of
2555                    course it could fail if we're transferring messages
2556                    of the same size, but this is a workarround */
2557                 if (status->of_total != *last_total_bytes) {
2558                         /* We need to increment the done when there is
2559                            no information about each individual
2560                            message, we need to do this in message
2561                            transfers, and we don't do it for getting
2562                            messages */
2563                         if (increment_done)
2564                                 priv->done++;
2565                         *sum_total_bytes += *last_total_bytes;
2566                         *last_total_bytes = status->of_total;
2567                 }
2568                 state->bytes_done += status->position + *sum_total_bytes;
2569                 state->bytes_total = total_bytes;
2570
2571                 /* Notify the status change. Only notify about changes
2572                    referred to bytes */
2573                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2574                                0, state, NULL);
2575         }
2576
2577         g_slice_free (ModestMailOperationState, state);
2578 }
2579
2580 static void
2581 transfer_msgs_status_cb (GObject *obj,
2582                          TnyStatus *status,  
2583                          gpointer user_data)
2584 {
2585         XFerMsgsAsyncHelper *helper;
2586
2587         g_return_if_fail (status != NULL);
2588
2589         /* Show only the status information we want */
2590         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
2591                 return;
2592
2593         helper = (XFerMsgsAsyncHelper *) user_data;
2594         g_return_if_fail (helper != NULL);       
2595
2596         /* Notify progress */
2597         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2598                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2599 }
2600
2601
2602 static void
2603 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2604 {
2605         XFerMsgsAsyncHelper *helper;
2606         ModestMailOperation *self;
2607         ModestMailOperationPrivate *priv;
2608         gboolean finished = TRUE;
2609
2610         helper = (XFerMsgsAsyncHelper *) user_data;
2611         self = helper->mail_op;
2612
2613         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2614
2615         if (cancelled) {
2616                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2617         } else if (err) {
2618                 priv->error = g_error_copy (err);
2619                 priv->done = 0;
2620                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2621         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2622                 if (helper->more_msgs) {
2623                         /* We'll transfer the next message in the list */
2624                         tny_iterator_next (helper->more_msgs);
2625                         if (!tny_iterator_is_done (helper->more_msgs)) {
2626                                 GObject *next_header;
2627                                 g_object_unref (helper->headers);
2628                                 helper->headers = tny_simple_list_new ();
2629                                 next_header = tny_iterator_get_current (helper->more_msgs);
2630                                 tny_list_append (helper->headers, next_header);
2631                                 g_object_unref (next_header);
2632                                 finished = FALSE;
2633                         }
2634                 }
2635                 if (finished) {
2636                         priv->done = 1;
2637                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2638                 }
2639         }
2640
2641         if (finished) {
2642
2643                 /* Update folder counts */
2644                 tny_folder_poke_status (folder);
2645                 tny_folder_poke_status (helper->dest_folder);
2646
2647                 /* Notify about operation end */
2648                 modest_mail_operation_notify_end (self);
2649
2650                 /* If user defined callback function was defined, call it */
2651                 if (helper->user_callback) {
2652                         /* This is not a GDK lock because we are a Tinymail callback and
2653                          * Tinymail already acquires the Gdk lock */
2654
2655                         /* no gdk_threads_enter (), CHECKED */
2656                         helper->user_callback (self, helper->user_data);
2657                         /* no gdk_threads_leave (), CHECKED */
2658                 }
2659
2660                 /* Free */
2661                 if (helper->more_msgs)
2662                         g_object_unref (helper->more_msgs);
2663                 if (helper->headers)
2664                         g_object_unref (helper->headers);
2665                 if (helper->dest_folder)
2666                         g_object_unref (helper->dest_folder);
2667                 if (helper->mail_op)
2668                         g_object_unref (helper->mail_op);
2669                 g_slice_free (XFerMsgsAsyncHelper, helper);
2670         } else {
2671                 /* Transfer more messages */
2672                 tny_folder_transfer_msgs_async (folder,
2673                                                 helper->headers,
2674                                                 helper->dest_folder,
2675                                                 helper->delete,
2676                                                 transfer_msgs_cb,
2677                                                 transfer_msgs_status_cb,
2678                                                 helper);
2679         }
2680 }
2681
2682 static guint
2683 compute_message_list_size (TnyList *headers)
2684 {
2685         TnyIterator *iter;
2686         guint size = 0;
2687
2688         iter = tny_list_create_iterator (headers);
2689         while (!tny_iterator_is_done (iter)) {
2690                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2691                 size += tny_header_get_message_size (header);
2692                 g_object_unref (header);
2693                 tny_iterator_next (iter);
2694         }
2695         g_object_unref (iter);
2696
2697         return size;
2698 }
2699
2700 static guint
2701 compute_message_array_size (GPtrArray *headers)
2702 {
2703         guint size = 0;
2704         gint i;
2705
2706         for (i = 0; i < headers->len; i++) {
2707                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2708                 size += tny_header_get_message_size (header);
2709         }
2710
2711         return size;
2712 }
2713
2714
2715 void
2716 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2717                                  TnyList *headers, 
2718                                  TnyFolder *folder, 
2719                                  gboolean delete_original,
2720                                  XferMsgsAsyncUserCallback user_callback,
2721                                  gpointer user_data)
2722 {
2723         ModestMailOperationPrivate *priv = NULL;
2724         TnyIterator *iter = NULL;
2725         TnyFolder *src_folder = NULL;
2726         XFerMsgsAsyncHelper *helper = NULL;
2727         TnyHeader *header = NULL;
2728         ModestTnyFolderRules rules = 0;
2729         TnyAccount *dst_account = NULL;
2730         gboolean leave_on_server;
2731
2732         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2733         g_return_if_fail (headers && TNY_IS_LIST (headers));
2734         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2735
2736         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2737         priv->total = tny_list_get_length (headers);
2738         priv->done = 0;
2739         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2740         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2741
2742         /* Apply folder rules */
2743         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2744         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2745                 /* Set status failed and set an error */
2746                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2747                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2748                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2749                              _CS("ckct_ib_unable_to_paste_here"));
2750                 /* Notify the queue */
2751                 modest_mail_operation_notify_end (self);
2752                 return;
2753         }
2754                 
2755         /* Get source folder */
2756         iter = tny_list_create_iterator (headers);
2757         header = TNY_HEADER (tny_iterator_get_current (iter));
2758         if (header) {
2759                 src_folder = tny_header_get_folder (header);
2760                 g_object_unref (header);
2761         }
2762         g_object_unref (iter);
2763
2764         if (src_folder == NULL) {
2765                 /* Notify the queue */
2766                 modest_mail_operation_notify_end (self);
2767
2768                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2769                 return;
2770         }
2771
2772         
2773         /* Check folder source and destination */
2774         if (src_folder == folder) {
2775                 /* Set status failed and set an error */
2776                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2777                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2778                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2779                              _("mail_in_ui_folder_copy_target_error"));
2780                 
2781                 /* Notify the queue */
2782                 modest_mail_operation_notify_end (self);
2783                 
2784                 /* Free */
2785                 g_object_unref (src_folder);            
2786                 return;
2787         }
2788
2789         /* Create the helper */
2790         helper = g_slice_new0 (XFerMsgsAsyncHelper);
2791         helper->mail_op = g_object_ref(self);
2792         helper->dest_folder = g_object_ref(folder);
2793         helper->user_callback = user_callback;
2794         helper->user_data = user_data;
2795         helper->delete = delete_original;
2796         helper->last_total_bytes = 0;
2797         helper->sum_total_bytes = 0;
2798         helper->total_bytes = compute_message_list_size (headers);
2799
2800         /* Get account and set it into mail_operation */
2801         priv->account = modest_tny_folder_get_account (src_folder);
2802         dst_account = modest_tny_folder_get_account (folder);
2803
2804         if (priv->account == dst_account) {
2805                 /* Transfer all messages at once using the fast
2806                  * method. Note that depending on the server this
2807                  * might not be that fast, and might not be
2808                  * user-cancellable either */
2809                 helper->headers = g_object_ref (headers);
2810                 helper->more_msgs = NULL;
2811         } else {
2812                 /* Transfer messages one by one so the user can cancel
2813                  * the operation */
2814                 GObject *hdr;
2815                 helper->headers = tny_simple_list_new ();
2816                 helper->more_msgs = tny_list_create_iterator (headers);
2817                 hdr = tny_iterator_get_current (helper->more_msgs);
2818                 tny_list_append (helper->headers, hdr);
2819                 g_object_unref (hdr);
2820         }
2821
2822         /* If leave_on_server is set to TRUE then don't use
2823            delete_original, we always pass FALSE. This is because
2824            otherwise tinymail will try to sync the source folder and
2825            this could cause an error if we're offline while
2826            transferring an already downloaded message from a POP
2827            account */
2828         if (modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (priv->account)) == 
2829             MODEST_PROTOCOL_STORE_POP) {
2830                 const gchar *account_name;
2831
2832                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
2833                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2834                                                                           account_name);
2835         } else {
2836                 leave_on_server = FALSE;
2837         }
2838
2839         modest_mail_operation_notify_start (self);
2840         tny_folder_transfer_msgs_async (src_folder, 
2841                                         helper->headers, 
2842                                         folder, 
2843                                         (leave_on_server) ? FALSE : delete_original, 
2844                                         transfer_msgs_cb, 
2845                                         transfer_msgs_status_cb,
2846                                         helper);
2847         g_object_unref (src_folder);
2848         g_object_unref (dst_account);
2849 }
2850
2851
2852 static void
2853 on_refresh_folder (TnyFolder   *folder, 
2854                    gboolean     cancelled, 
2855                    GError     *error,
2856                    gpointer     user_data)
2857 {
2858         RefreshAsyncHelper *helper = NULL;
2859         ModestMailOperation *self = NULL;
2860         ModestMailOperationPrivate *priv = NULL;
2861
2862         helper = (RefreshAsyncHelper *) user_data;
2863         self = helper->mail_op;
2864         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2865
2866         g_return_if_fail(priv!=NULL);
2867
2868         if (error) {
2869                 priv->error = g_error_copy (error);
2870                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2871                 goto out;
2872         }
2873
2874         if (cancelled) {
2875                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2876                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2877                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2878                              _("Error trying to refresh the contents of %s"),
2879                              tny_folder_get_name (folder));
2880                 goto out;
2881         }
2882
2883         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2884  out:
2885
2886         /* Call user defined callback, if it exists */
2887         if (helper->user_callback) {
2888
2889                 /* This is not a GDK lock because we are a Tinymail callback and
2890                  * Tinymail already acquires the Gdk lock */
2891                 helper->user_callback (self, folder, helper->user_data);
2892         }
2893
2894         /* Free */
2895         g_slice_free (RefreshAsyncHelper, helper);
2896
2897         /* Notify about operation end */
2898         modest_mail_operation_notify_end (self);
2899         g_object_unref(self);
2900 }
2901
2902 static void
2903 on_refresh_folder_status_update (GObject *obj,
2904                                  TnyStatus *status,
2905                                  gpointer user_data)
2906 {
2907         RefreshAsyncHelper *helper = NULL;
2908         ModestMailOperation *self = NULL;
2909         ModestMailOperationPrivate *priv = NULL;
2910         ModestMailOperationState *state;
2911
2912         g_return_if_fail (user_data != NULL);
2913         g_return_if_fail (status != NULL);
2914
2915         /* Show only the status information we want */
2916         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
2917                 return;
2918
2919         helper = (RefreshAsyncHelper *) user_data;
2920         self = helper->mail_op;
2921         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2922
2923         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2924
2925         priv->done = status->position;
2926         priv->total = status->of_total;
2927
2928         state = modest_mail_operation_clone_state (self);
2929
2930         /* This is not a GDK lock because we are a Tinymail callback and
2931          * Tinymail already acquires the Gdk lock */
2932         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2933
2934         g_slice_free (ModestMailOperationState, state);
2935 }
2936
2937 void 
2938 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2939                                        TnyFolder *folder,
2940                                        RefreshAsyncUserCallback user_callback,
2941                                        gpointer user_data)
2942 {
2943         ModestMailOperationPrivate *priv = NULL;
2944         RefreshAsyncHelper *helper = NULL;
2945
2946         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2947
2948         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2949
2950         /* Get account and set it into mail_operation */
2951         priv->account = modest_tny_folder_get_account  (folder);
2952         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2953
2954         /* Create the helper */
2955         helper = g_slice_new0 (RefreshAsyncHelper);
2956         helper->mail_op = g_object_ref(self);
2957         helper->user_callback = user_callback;
2958         helper->user_data = user_data;
2959
2960         modest_mail_operation_notify_start (self);
2961         
2962         /* notify that the operation was started */
2963         ModestMailOperationState *state;
2964         state = modest_mail_operation_clone_state (self);
2965         state->done = 0;
2966         state->total = 0;
2967         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2968                         0, state, NULL);
2969
2970         /* FIXME: we're leaking the state here, or? valgrind thinks so */
2971         
2972         tny_folder_refresh_async (folder,
2973                                   on_refresh_folder,
2974                                   on_refresh_folder_status_update,
2975                                   helper);
2976 }
2977
2978 static void
2979 run_queue_stop (ModestTnySendQueue *queue,
2980                 ModestMailOperation *self)
2981 {
2982         ModestMailOperationPrivate *priv;
2983
2984         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2985         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
2986         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2987
2988         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2989
2990         modest_mail_operation_notify_end (self);
2991         g_signal_handlers_disconnect_by_func (queue, run_queue_stop, self);
2992         g_object_unref (self);
2993 }
2994 void
2995 modest_mail_operation_run_queue (ModestMailOperation *self,
2996                                  ModestTnySendQueue *queue)
2997 {
2998         ModestMailOperationPrivate *priv;
2999
3000         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3001         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3002         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3003
3004         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3005         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3006         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3007
3008         modest_mail_operation_notify_start (self);
3009         g_object_ref (self);
3010         g_signal_connect ((gpointer) queue, "queue-stop", G_CALLBACK (run_queue_stop), (gpointer) self);
3011 }
3012
3013 static void
3014 sync_folder_finish_callback (TnyFolder *self, 
3015                              gboolean cancelled, 
3016                              GError *err, 
3017                              gpointer user_data)
3018
3019 {
3020         ModestMailOperation *mail_op;
3021         ModestMailOperationPrivate *priv;
3022
3023         mail_op = (ModestMailOperation *) user_data;
3024         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3025
3026         /* If canceled by the user, ignore the error given by Tinymail */
3027         if (cancelled) {
3028                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3029         } else if (err) {
3030                 /* If the operation was a sync then the status is
3031                    failed, but if it's part of another operation then
3032                    just set it as finished with errors */
3033                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
3034                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3035                 else
3036                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3037                 priv->error = g_error_copy ((const GError *) err);
3038                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3039         } else {
3040                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3041         }
3042
3043         modest_mail_operation_notify_end (mail_op);
3044         g_object_unref (mail_op);
3045 }
3046
3047 void
3048 modest_mail_operation_sync_folder (ModestMailOperation *self,
3049                                    TnyFolder *folder, gboolean expunge)
3050 {
3051         ModestMailOperationPrivate *priv;
3052
3053         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3054         g_return_if_fail (TNY_IS_FOLDER (folder));
3055         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3056
3057         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3058         priv->account = TNY_ACCOUNT (tny_folder_get_account (folder));
3059         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3060
3061         modest_mail_operation_notify_start (self);
3062         g_object_ref (self);
3063         tny_folder_sync_async (folder, expunge, 
3064                                (TnyFolderCallback) sync_folder_finish_callback, 
3065                                NULL, self);
3066 }
3067
3068 static void
3069 modest_mail_operation_notify_start (ModestMailOperation *self)
3070 {
3071         ModestMailOperationPrivate *priv = NULL;
3072
3073         g_return_if_fail (self);
3074
3075         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3076
3077         /* Ensure that all the fields are filled correctly */
3078         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3079
3080         /* Notify the observers about the mail operation. We do not
3081            wrapp this emission because we assume that this function is
3082            always called from within the main lock */
3083         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3084 }
3085
3086 /**
3087  *
3088  * It's used by the mail operation queue to notify the observers
3089  * attached to that signal that the operation finished. We need to use
3090  * that because tinymail does not give us the progress of a given
3091  * operation when it finishes (it directly calls the operation
3092  * callback).
3093  */
3094 static void
3095 modest_mail_operation_notify_end (ModestMailOperation *self)
3096 {
3097         ModestMailOperationPrivate *priv = NULL;
3098
3099         g_return_if_fail (self);
3100
3101         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3102
3103         /* Notify the observers about the mail operation end. We do
3104            not wrapp this emission because we assume that this
3105            function is always called from within the main lock */
3106         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3107
3108         /* Remove the error user data */
3109         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3110                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3111 }
3112
3113 TnyAccount *
3114 modest_mail_operation_get_account (ModestMailOperation *self)
3115 {
3116         ModestMailOperationPrivate *priv = NULL;
3117
3118         g_return_val_if_fail (self, NULL);
3119
3120         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3121
3122         return (priv->account) ? g_object_ref (priv->account) : NULL;
3123 }
3124
3125 void
3126 modest_mail_operation_noop (ModestMailOperation *self)
3127 {
3128         ModestMailOperationPrivate *priv = NULL;
3129
3130         g_return_if_fail (self);
3131
3132         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3133         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3134         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3135         priv->done = 0;
3136         priv->total = 0;
3137
3138         /* This mail operation does nothing actually */
3139         modest_mail_operation_notify_start (self);
3140         modest_mail_operation_notify_end (self);
3141 }
3142
3143
3144 gchar*
3145 modest_mail_operation_to_string (ModestMailOperation *self)
3146 {
3147         const gchar *type, *status, *account_id;
3148         ModestMailOperationPrivate *priv = NULL;
3149         
3150         g_return_val_if_fail (self, NULL);
3151
3152         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3153
3154         /* new operations don't have anything interesting */
3155         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3156                 return g_strdup_printf ("%p <new operation>", self);
3157         
3158         switch (priv->op_type) {
3159         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3160         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3161         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3162         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3163         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3164         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
3165         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
3166         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3167         default: type = "UNEXPECTED"; break;
3168         }
3169
3170         switch (priv->status) {
3171         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3172         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3173         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3174         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3175         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3176         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3177         default:                                                status= "UNEXPECTED"; break;
3178         } 
3179
3180         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3181
3182         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3183                                 priv->done, priv->total,
3184                                 priv->error && priv->error->message ? priv->error->message : "");
3185 }