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