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