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