* Deleted unused code
[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, succeeded;
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         if (!canceled && !err) 
1268                 modest_account_mgr_set_server_account_username_has_succeeded (mgr, tny_account_get_id (priv->account), TRUE);
1269         
1270         if (new_headers_array->len == 0)
1271                 goto send_mail;
1272
1273         /* Get per-account message amount retrieval limit */
1274         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1275         if (retrieve_limit == 0)
1276                 retrieve_limit = G_MAXINT;
1277         
1278         /* Get per-account retrieval type */
1279         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name); 
1280         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1281
1282         /* Order by date */
1283         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1284         
1285         /* Ask the users if they want to retrieve all the messages
1286            even though the limit was exceeded */
1287         ignore_limit = FALSE;
1288         if (new_headers_array->len > retrieve_limit) {
1289                 /* Ask the user if a callback has been specified and
1290                    if the mail operation has a source (this means that
1291                    was invoked by the user and not automatically by a
1292                    D-Bus method) */
1293                 if (info->retrieve_all_cb && priv->source)
1294                         ignore_limit = info->retrieve_all_cb (priv->source,
1295                                                               new_headers_array->len,
1296                                                               retrieve_limit);
1297         }
1298         
1299         if (!headers_only) {
1300                 gint msg_num = 0;
1301                 const gint msg_list_size = compute_message_array_size (new_headers_array);
1302
1303                 priv->done = 0;
1304                 if (ignore_limit)
1305                         priv->total = new_headers_array->len;
1306                 else
1307                         priv->total = MIN (new_headers_array->len, retrieve_limit);
1308                 while (msg_num < priv->total) {         
1309                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1310                         TnyFolder *folder = tny_header_get_folder (header);
1311                         GetMsgInfo *msg_info;
1312
1313                         /* Create the message info */
1314                         msg_info = g_slice_new0 (GetMsgInfo);
1315                         msg_info->mail_op = g_object_ref (info->mail_op);
1316                         msg_info->header = g_object_ref (header);
1317                         msg_info->total_bytes = msg_list_size;
1318
1319                         /* Get message in an async way */
1320                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb, 
1321                                                   get_msg_status_cb, msg_info);
1322
1323                         g_object_unref (folder);
1324                         
1325                         msg_num++;
1326                 }
1327         }
1328
1329         /* Copy the headers to a list and free the array */
1330         new_headers = tny_simple_list_new ();
1331         for (i=0; i < new_headers_array->len; i++) {
1332                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1333                 tny_list_append (new_headers, G_OBJECT (header));
1334         }
1335         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1336         g_ptr_array_free (new_headers_array, FALSE);
1337
1338         if (priv->error)
1339                 succeeded = FALSE;
1340         else
1341                 succeeded = TRUE;
1342         modest_account_mgr_set_server_account_username_has_succeeded (modest_runtime_get_account_mgr (), 
1343                                                                       tny_account_get_name (priv->account), 
1344                                                                       succeeded);
1345  send_mail:
1346         /* Get the transport account */
1347         transport_account = (TnyTransportAccount *)
1348                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1349                                                                                     info->account_name);
1350
1351         if (transport_account) {
1352                 ModestTnySendQueue *send_queue;
1353                 TnyFolder *outbox;
1354                 guint num_messages;
1355
1356                 send_queue = modest_runtime_get_send_queue (transport_account);
1357                 g_object_unref (transport_account);
1358
1359                 /* Get outbox folder */
1360                 outbox = tny_send_queue_get_outbox (TNY_SEND_QUEUE (send_queue));
1361                 if (outbox) { /* this could fail in some cases */
1362                         num_messages = tny_folder_get_all_count (outbox);
1363                         g_object_unref (outbox);
1364                 } else {
1365                         g_warning ("%s: could not get outbox", __FUNCTION__);
1366                         num_messages = 0;
1367                 }
1368                 
1369                 if (num_messages != 0) {
1370                         /* Send mails */
1371                         g_object_unref (priv->account);
1372
1373                         /* Reenable suspended items */
1374                         modest_tny_send_queue_wakeup (MODEST_TNY_SEND_QUEUE (send_queue));
1375
1376                         /* Try to send */
1377                         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (send_queue));
1378                 }
1379         }
1380
1381         /* Check if the operation was a success */
1382         if (!priv->error)
1383                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1384
1385         /* Set the account back to not busy */
1386         modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1387
1388         /* Call the user callback */
1389         if (info->callback)
1390                 info->callback (info->mail_op, new_headers, info->user_data);
1391
1392         /* Notify about operation end */
1393         modest_mail_operation_notify_end (info->mail_op);
1394
1395         /* Frees */
1396         if (new_headers)
1397                 g_object_unref (new_headers);
1398         destroy_update_account_info (info);
1399 }
1400
1401 static void 
1402 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1403                           gboolean canceled,
1404                           TnyList *list, 
1405                           GError *err, 
1406                           gpointer user_data)
1407 {
1408         UpdateAccountInfo *info;
1409         ModestMailOperationPrivate *priv;
1410     
1411         info = (UpdateAccountInfo *) user_data;
1412         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1413
1414         if (err || canceled) {
1415                 /* Try to continue anyway */
1416         } else if (info->poke_all) {
1417                 /* We're not getting INBOX children if we don't want to poke all */
1418                 TnyIterator *iter = tny_list_create_iterator (list);
1419                 while (!tny_iterator_is_done (iter)) {
1420                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1421                         TnyList *folders = tny_simple_list_new ();
1422
1423                         /* Add to the list of all folders */
1424                         tny_list_append (info->folders, (GObject *) folder);
1425                         
1426                         /* Add pending call */
1427                         info->pending_calls++;
1428                         
1429                         tny_folder_store_get_folders_async (folder, folders, NULL,
1430                                                             recurse_folders_async_cb, 
1431                                                             NULL, info);
1432                         
1433                         g_object_unref (G_OBJECT (folder));
1434                         
1435                         tny_iterator_next (iter);           
1436                 }
1437                 g_object_unref (G_OBJECT (iter));
1438                 g_object_unref (G_OBJECT (list));
1439         }
1440
1441         /* Remove my own pending call */
1442         info->pending_calls--;
1443
1444         /* This means that we have all the folders */
1445         if (info->pending_calls == 0) {
1446                 TnyIterator *iter_all_folders;
1447                 TnyFolder *inbox = NULL;
1448
1449                 iter_all_folders = tny_list_create_iterator (info->folders);
1450
1451                 /* Do a poke status over all folders */
1452                 while (!tny_iterator_is_done (iter_all_folders) &&
1453                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1454                         TnyFolder *folder = NULL;
1455
1456                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1457
1458                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1459                                 /* Get a reference to the INBOX */
1460                                 inbox = g_object_ref (folder);
1461                         } else {
1462                                 /* Issue a poke status over the folder */
1463                                 if (info->poke_all)
1464                                         tny_folder_poke_status (folder);
1465                         }
1466
1467                         /* Free and go to next */
1468                         g_object_unref (folder);
1469                         tny_iterator_next (iter_all_folders);
1470                 }
1471                 g_object_unref (iter_all_folders);
1472
1473                 /* Refresh the INBOX */
1474                 if (inbox) {
1475                         /* Refresh the folder. Our observer receives
1476                          * the new emails during folder refreshes, so
1477                          * we can use observer->new_headers
1478                          */
1479                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1480                         tny_folder_add_observer (inbox, info->inbox_observer);
1481
1482                         /* Refresh the INBOX */
1483                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1484                         g_object_unref (inbox);
1485                 } else {
1486                         /* We could not perform the inbox refresh but
1487                            we'll try to send mails anyway */
1488                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1489                 }
1490         }
1491 }
1492
1493 void
1494 modest_mail_operation_update_account (ModestMailOperation *self,
1495                                       const gchar *account_name,
1496                                       gboolean poke_all,
1497                                       RetrieveAllCallback retrieve_all_cb,
1498                                       UpdateAccountCallback callback,
1499                                       gpointer user_data)
1500 {
1501         UpdateAccountInfo *info = NULL;
1502         ModestMailOperationPrivate *priv = NULL;
1503         ModestTnyAccountStore *account_store = NULL;
1504         TnyList *folders;
1505         ModestMailOperationState *state;
1506
1507         /* Init mail operation */
1508         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1509         priv->total = 0;
1510         priv->done  = 0;
1511         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1512         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
1513
1514         /* Get the store account */
1515         account_store = modest_runtime_get_account_store ();
1516         priv->account =
1517                 modest_tny_account_store_get_server_account (account_store,
1518                                                              account_name,
1519                                                              TNY_ACCOUNT_TYPE_STORE);
1520
1521         /* Create the helper object */
1522         info = g_slice_new0 (UpdateAccountInfo);
1523         info->pending_calls = 1;
1524         info->folders = tny_simple_list_new ();
1525         info->mail_op = g_object_ref (self);
1526         info->poke_all = poke_all;
1527         info->account_name = g_strdup (account_name);
1528         info->callback = callback;
1529         info->user_data = user_data;
1530         info->retrieve_all_cb = retrieve_all_cb;
1531
1532         /* Set account busy */
1533         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1534         modest_mail_operation_notify_start (self);
1535
1536         /* notify about the start of the operation */ 
1537         state = modest_mail_operation_clone_state (self);
1538         state->done = 0;
1539         state->total = 0;
1540
1541         /* Start notifying progress */
1542         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1543         g_slice_free (ModestMailOperationState, state);
1544         
1545         /* Get all folders and continue in the callback */ 
1546         folders = tny_simple_list_new ();
1547         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
1548                                             folders, NULL,
1549                                             recurse_folders_async_cb, 
1550                                             NULL, info);
1551 }
1552
1553 /*
1554  * Used to notify the queue from the main
1555  * loop. We call it inside an idle call to achieve that
1556  */
1557 static gboolean
1558 idle_notify_queue (gpointer data)
1559 {
1560         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1561
1562         gdk_threads_enter ();
1563         modest_mail_operation_notify_end (mail_op);
1564         gdk_threads_leave ();
1565         g_object_unref (mail_op);
1566
1567         return FALSE;
1568 }
1569
1570 static int
1571 compare_headers_by_date (gconstpointer a,
1572                          gconstpointer b)
1573 {
1574         TnyHeader **header1, **header2;
1575         time_t sent1, sent2;
1576
1577         header1 = (TnyHeader **) a;
1578         header2 = (TnyHeader **) b;
1579
1580         sent1 = tny_header_get_date_sent (*header1);
1581         sent2 = tny_header_get_date_sent (*header2);
1582
1583         /* We want the most recent ones (greater time_t) at the
1584            beginning */
1585         if (sent1 < sent2)
1586                 return 1;
1587         else
1588                 return -1;
1589 }
1590
1591
1592 /* ******************************************************************* */
1593 /* ************************** STORE  ACTIONS ************************* */
1594 /* ******************************************************************* */
1595
1596 typedef struct {
1597         ModestMailOperation *mail_op;
1598         CreateFolderUserCallback callback;
1599         gpointer user_data;
1600 } CreateFolderInfo;
1601
1602
1603 static void
1604 create_folder_cb (TnyFolderStore *parent_folder, 
1605                   gboolean canceled, 
1606                   TnyFolder *new_folder, 
1607                   GError *err, 
1608                   gpointer user_data)
1609 {
1610         ModestMailOperationPrivate *priv;
1611         CreateFolderInfo *info;
1612
1613         info = (CreateFolderInfo *) user_data;
1614         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1615
1616         if (canceled || err) {
1617                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1618                 if (err)
1619                         priv->error = g_error_copy (err);
1620                 else
1621                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1622                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1623                                      "canceled");               
1624         } else {
1625                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1626         }
1627
1628         /* The user will unref the new_folder */
1629         if (info->callback)
1630                 info->callback (info->mail_op, parent_folder, 
1631                                 new_folder, info->user_data);
1632         
1633         /* Notify about operation end */
1634         modest_mail_operation_notify_end (info->mail_op);
1635
1636         /* Frees */
1637         g_object_unref (info->mail_op);
1638         g_slice_free (CreateFolderInfo, info);
1639 }
1640
1641 void
1642 modest_mail_operation_create_folder (ModestMailOperation *self,
1643                                      TnyFolderStore *parent,
1644                                      const gchar *name,
1645                                      CreateFolderUserCallback callback,
1646                                      gpointer user_data)
1647 {
1648         ModestMailOperationPrivate *priv;
1649
1650         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1651         g_return_if_fail (name);
1652         
1653         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1654         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1655         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1656                 g_object_ref (parent) : 
1657                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1658
1659         /* Check for already existing folder */
1660         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1661                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1662                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1663                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1664                              _CS("ckdg_ib_folder_already_exists"));
1665         }
1666
1667         /* Check parent */
1668         if (TNY_IS_FOLDER (parent)) {
1669                 /* Check folder rules */
1670                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1671                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1672                         /* Set status failed and set an error */
1673                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1674                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1675                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1676                                      _("mail_in_ui_folder_create_error"));
1677                 }
1678         }
1679
1680         if (!strcmp (name, " ") || strchr (name, '/')) {
1681                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1682                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1683                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1684                              _("mail_in_ui_folder_create_error"));
1685         }
1686
1687         if (!priv->error) {
1688                 CreateFolderInfo *info;
1689
1690                 info = g_slice_new0 (CreateFolderInfo);
1691                 info->mail_op = g_object_ref (self);
1692                 info->callback = callback;
1693                 info->user_data = user_data;
1694
1695                 modest_mail_operation_notify_start (self);
1696
1697                 /* Create the folder */
1698                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
1699                                                       NULL, info);
1700         } else {
1701                 /* Call the user callback anyway */
1702                 if (callback)
1703                         callback (self, parent, NULL, user_data);
1704                 /* Notify about operation end */
1705                 modest_mail_operation_notify_end (self);
1706         }
1707 }
1708
1709 void
1710 modest_mail_operation_remove_folder (ModestMailOperation *self,
1711                                      TnyFolder           *folder,
1712                                      gboolean             remove_to_trash)
1713 {
1714         ModestMailOperationPrivate *priv;
1715         ModestTnyFolderRules rules;
1716
1717         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1718         g_return_if_fail (TNY_IS_FOLDER (folder));
1719         
1720         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1721         
1722         /* Check folder rules */
1723         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1724         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1725                 /* Set status failed and set an error */
1726                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1727                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1728                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1729                              _("mail_in_ui_folder_delete_error"));
1730                 goto end;
1731         }
1732
1733         /* Get the account */
1734         priv->account = modest_tny_folder_get_account (folder);
1735         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1736
1737         /* Delete folder or move to trash */
1738         if (remove_to_trash) {
1739                 TnyFolder *trash_folder = NULL;
1740                 trash_folder = modest_tny_account_get_special_folder (priv->account,
1741                                                                       TNY_FOLDER_TYPE_TRASH);
1742                 /* TODO: error_handling */
1743                 if (trash_folder) {
1744                         modest_mail_operation_notify_start (self);
1745                         modest_mail_operation_xfer_folder (self, folder,
1746                                                     TNY_FOLDER_STORE (trash_folder), 
1747                                                     TRUE, NULL, NULL);
1748                         g_object_unref (trash_folder);
1749                 } else {
1750                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
1751                 }
1752         } else {
1753                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1754                 if (parent) {
1755                         modest_mail_operation_notify_start (self);
1756                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
1757                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1758                         
1759                         if (!priv->error)
1760                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1761
1762                         g_object_unref (parent);
1763                 } else
1764                         g_warning ("%s: could not get parent folder", __FUNCTION__);
1765         }
1766
1767  end:
1768         /* Notify about operation end */
1769         modest_mail_operation_notify_end (self);
1770 }
1771
1772 static void
1773 transfer_folder_status_cb (GObject *obj,
1774                            TnyStatus *status,
1775                            gpointer user_data)
1776 {
1777         ModestMailOperation *self;
1778         ModestMailOperationPrivate *priv;
1779         ModestMailOperationState *state;
1780         XFerFolderAsyncHelper *helper;
1781
1782         g_return_if_fail (status != NULL);
1783         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1784
1785         helper = (XFerFolderAsyncHelper *) user_data;
1786         g_return_if_fail (helper != NULL);
1787
1788         self = helper->mail_op;
1789         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1790
1791         priv->done = status->position;
1792         priv->total = status->of_total;
1793
1794         state = modest_mail_operation_clone_state (self);
1795
1796         /* This is not a GDK lock because we are a Tinymail callback
1797          * which is already GDK locked by Tinymail */
1798
1799         /* no gdk_threads_enter (), CHECKED */
1800
1801         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1802
1803         /* no gdk_threads_leave (), CHECKED */
1804
1805         g_slice_free (ModestMailOperationState, state);
1806 }
1807
1808
1809 static void
1810 transfer_folder_cb (TnyFolder *folder, 
1811                     gboolean cancelled, 
1812                     TnyFolderStore *into, 
1813                     TnyFolder *new_folder, 
1814                     GError *err, 
1815                     gpointer user_data)
1816 {
1817         XFerFolderAsyncHelper *helper;
1818         ModestMailOperation *self = NULL;
1819         ModestMailOperationPrivate *priv = NULL;
1820
1821         helper = (XFerFolderAsyncHelper *) user_data;
1822         g_return_if_fail (helper != NULL);       
1823
1824         self = helper->mail_op;
1825         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1826
1827         if (err) {
1828                 priv->error = g_error_copy (err);
1829                 priv->done = 0;
1830                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1831         } else if (cancelled) {
1832                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1833                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1834                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1835                              _("Transference of %s was cancelled."),
1836                              tny_folder_get_name (folder));
1837         } else {
1838                 priv->done = 1;
1839                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1840         }
1841                 
1842         /* Notify about operation end */
1843         modest_mail_operation_notify_end (self);
1844
1845         /* If user defined callback function was defined, call it */
1846         if (helper->user_callback) {
1847
1848                 /* This is not a GDK lock because we are a Tinymail callback
1849                  * which is already GDK locked by Tinymail */
1850
1851                 /* no gdk_threads_enter (), CHECKED */
1852                 helper->user_callback (self, new_folder, helper->user_data);
1853                 /* no gdk_threads_leave () , CHECKED */
1854         }
1855
1856         /* Free */
1857         g_object_unref (helper->mail_op);
1858         g_slice_free   (XFerFolderAsyncHelper, helper);
1859 }
1860
1861 /**
1862  *
1863  * This function checks if the new name is a valid name for our local
1864  * folders account. The new name could not be the same than then name
1865  * of any of the mandatory local folders
1866  *
1867  * We can not rely on tinymail because tinymail does not check the
1868  * name of the virtual folders that the account could have in the case
1869  * that we're doing a rename (because it directly calls Camel which
1870  * knows nothing about our virtual folders). 
1871  *
1872  * In the case of an actual copy/move (i.e. move/copy a folder between
1873  * accounts) tinymail uses the tny_folder_store_create_account which
1874  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1875  * checks the new name of the folder, so this call in that case
1876  * wouldn't be needed. *But* NOTE that if tinymail changes its
1877  * implementation (if folder transfers within the same account is no
1878  * longer implemented as a rename) this call will allow Modest to work
1879  * perfectly
1880  *
1881  * If the new name is not valid, this function will set the status to
1882  * failed and will set also an error in the mail operation
1883  */
1884 static gboolean
1885 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1886                                  TnyFolderStore *into,
1887                                  const gchar *new_name)
1888 {
1889         if (TNY_IS_ACCOUNT (into) && 
1890             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1891             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1892                                                                  new_name)) {
1893                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1894                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1895                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1896                              _CS("ckdg_ib_folder_already_exists"));
1897                 return FALSE;
1898         } else
1899                 return TRUE;
1900 }
1901
1902 void
1903 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1904                                    TnyFolder *folder,
1905                                    TnyFolderStore *parent,
1906                                    gboolean delete_original,
1907                                    XferFolderAsyncUserCallback user_callback,
1908                                    gpointer user_data)
1909 {
1910         ModestMailOperationPrivate *priv = NULL;
1911         ModestTnyFolderRules parent_rules = 0, rules; 
1912         XFerFolderAsyncHelper *helper = NULL;
1913         const gchar *folder_name = NULL;
1914         const gchar *error_msg;
1915
1916         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1917         g_return_if_fail (TNY_IS_FOLDER (folder));
1918         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1919
1920         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1921         folder_name = tny_folder_get_name (folder);
1922
1923         /* Set the error msg */
1924         error_msg = _("mail_in_ui_folder_move_target_error");
1925
1926         /* Get account and set it into mail_operation */
1927         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1928         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1929         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1930
1931         /* Get folder rules */
1932         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1933         if (TNY_IS_FOLDER (parent))
1934                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1935         
1936         /* Apply operation constraints */
1937         if ((gpointer) parent == (gpointer) folder ||
1938             (!TNY_IS_FOLDER_STORE (parent)) || 
1939             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1940                 /* Folder rules */
1941                 goto error;
1942         } else if (TNY_IS_FOLDER (parent) && 
1943                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1944                 /* Folder rules */
1945                 goto error;
1946
1947         } else if (TNY_IS_FOLDER (parent) &&
1948                    TNY_IS_FOLDER_STORE (folder) &&
1949                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
1950                                                   TNY_FOLDER_STORE (folder))) {
1951                 /* Do not move a parent into a child */
1952                 goto error;
1953         } else if (TNY_IS_FOLDER_STORE (parent) &&
1954                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1955                 /* Check that the new folder name is not used by any
1956                    parent subfolder */
1957                 goto error;     
1958         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1959                 /* Check that the new folder name is not used by any
1960                    special local folder */
1961                 goto error;
1962         } else {
1963                 /* Create the helper */
1964                 helper = g_slice_new0 (XFerFolderAsyncHelper);
1965                 helper->mail_op = g_object_ref (self);
1966                 helper->user_callback = user_callback;
1967                 helper->user_data = user_data;
1968                 
1969                 /* Move/Copy folder */
1970                 modest_mail_operation_notify_start (self);
1971                 tny_folder_copy_async (folder,
1972                                        parent,
1973                                        tny_folder_get_name (folder),
1974                                        delete_original,
1975                                        transfer_folder_cb,
1976                                        transfer_folder_status_cb,
1977                                        helper);
1978                 return;
1979         }
1980
1981  error:
1982         /* Set status failed and set an error */
1983         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1984         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1985                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1986                      error_msg);
1987
1988         /* Call the user callback if exists */
1989         if (user_callback)
1990                 user_callback (self, NULL, user_data);
1991
1992         /* Notify the queue */
1993         modest_mail_operation_notify_end (self);
1994 }
1995
1996 void
1997 modest_mail_operation_rename_folder (ModestMailOperation *self,
1998                                      TnyFolder *folder,
1999                                      const gchar *name,
2000                                      XferFolderAsyncUserCallback user_callback,
2001                                      gpointer user_data)
2002 {
2003         ModestMailOperationPrivate *priv;
2004         ModestTnyFolderRules rules;
2005         XFerFolderAsyncHelper *helper;
2006
2007         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2008         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2009         g_return_if_fail (name);
2010         
2011         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2012
2013         /* Get account and set it into mail_operation */
2014         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2015         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2016
2017         /* Check folder rules */
2018         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2019         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2020                 goto error;
2021         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2022                 goto error;
2023         } else {
2024                 TnyFolderStore *into;
2025
2026                 into = tny_folder_get_folder_store (folder);    
2027
2028                 /* Check that the new folder name is not used by any
2029                    special local folder */
2030                 if (new_name_valid_if_local_account (priv, into, name)) {
2031                         /* Create the helper */
2032                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2033                         helper->mail_op = g_object_ref(self);
2034                         helper->user_callback = user_callback;
2035                         helper->user_data = user_data;
2036                 
2037                         /* Rename. Camel handles folder subscription/unsubscription */
2038                         modest_mail_operation_notify_start (self);
2039                         tny_folder_copy_async (folder, into, name, TRUE,
2040                                                transfer_folder_cb,
2041                                                transfer_folder_status_cb,
2042                                                helper);
2043                 } else {
2044                         goto error;
2045                 }
2046                 g_object_unref (into);
2047
2048                 return;
2049         }
2050  error:
2051         /* Set status failed and set an error */
2052         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2053         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2054                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2055                      _("FIXME: unable to rename"));
2056         
2057         if (user_callback)
2058                 user_callback (self, NULL, user_data);
2059
2060         /* Notify about operation end */
2061         modest_mail_operation_notify_end (self);
2062 }
2063
2064 /* ******************************************************************* */
2065 /* **************************  MSG  ACTIONS  ************************* */
2066 /* ******************************************************************* */
2067
2068 void 
2069 modest_mail_operation_get_msg (ModestMailOperation *self,
2070                                TnyHeader *header,
2071                                GetMsgAsyncUserCallback user_callback,
2072                                gpointer user_data)
2073 {
2074         GetMsgInfo *helper = NULL;
2075         TnyFolder *folder;
2076         ModestMailOperationPrivate *priv;
2077         
2078         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2079         g_return_if_fail (TNY_IS_HEADER (header));
2080         
2081         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2082         folder = tny_header_get_folder (header);
2083
2084         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2085         priv->total = 1;
2086         priv->done = 0;
2087
2088         /* Get account and set it into mail_operation */
2089         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2090         
2091         /* Check for cached messages */
2092         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2093                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2094         else 
2095                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2096         
2097         /* Create the helper */
2098         helper = g_slice_new0 (GetMsgInfo);
2099         helper->header = g_object_ref (header);
2100         helper->mail_op = g_object_ref (self);
2101         helper->user_callback = user_callback;
2102         helper->user_data = user_data;
2103         helper->destroy_notify = NULL;
2104         helper->last_total_bytes = 0;
2105         helper->sum_total_bytes = 0;
2106         helper->total_bytes = tny_header_get_message_size (header);
2107
2108         modest_mail_operation_notify_start (self);
2109         
2110         /* notify about the start of the operation */ 
2111         ModestMailOperationState *state;
2112         state = modest_mail_operation_clone_state (self);
2113         state->done = 0;
2114         state->total = 0;
2115         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2116                                 0, state, NULL);
2117         
2118         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2119
2120         g_object_unref (G_OBJECT (folder));
2121 }
2122
2123 static void     
2124 get_msg_status_cb (GObject *obj,
2125                    TnyStatus *status,  
2126                    gpointer user_data)
2127 {
2128         GetMsgInfo *helper = NULL;
2129
2130         g_return_if_fail (status != NULL);
2131         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2132
2133         helper = (GetMsgInfo *) user_data;
2134         g_return_if_fail (helper != NULL);       
2135
2136         /* Notify progress */
2137         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2138                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2139 }
2140
2141 static void
2142 get_msg_async_cb (TnyFolder *folder, 
2143                   gboolean canceled, 
2144                   TnyMsg *msg, 
2145                   GError *err, 
2146                   gpointer user_data)
2147 {
2148         GetMsgInfo *info = NULL;
2149         ModestMailOperationPrivate *priv = NULL;
2150         gboolean finished;
2151
2152         info = (GetMsgInfo *) user_data;
2153
2154         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2155         priv->done++;
2156
2157         if (info->more_msgs) {
2158                 tny_iterator_next (info->more_msgs);
2159                 finished = (tny_iterator_is_done (info->more_msgs));
2160         } else {
2161                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2162         }
2163
2164         /* If canceled by the user, ignore the error given by Tinymail */
2165         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2166                 canceled = TRUE;
2167                 finished = TRUE;
2168         } else if (canceled || err) {
2169                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2170                 if (err) {
2171                         priv->error = g_error_copy ((const GError *) err);
2172                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2173                 }
2174                 if (!priv->error) {
2175                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2176                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2177                                      err->message);
2178                 }
2179         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2180                 /* Set the success status before calling the user callback */
2181                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2182         }
2183
2184
2185         /* Call the user callback */
2186         if (info->user_callback)
2187                 info->user_callback (info->mail_op, info->header, canceled, 
2188                                      msg, err, info->user_data);
2189
2190         /* Notify about operation end if this is the last callback */
2191         if (finished) {
2192                 /* Free user data */
2193                 if (info->destroy_notify)
2194                         info->destroy_notify (info->user_data);
2195
2196                 /* Notify about operation end */
2197                 modest_mail_operation_notify_end (info->mail_op);
2198
2199                 /* Clean */
2200                 if (info->more_msgs)
2201                         g_object_unref (info->more_msgs);
2202                 g_object_unref (info->header);
2203                 g_object_unref (info->mail_op);
2204                 g_slice_free (GetMsgInfo, info);
2205         } else if (info->more_msgs) {
2206                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
2207                 TnyFolder *folder = tny_header_get_folder (header);
2208
2209                 g_object_unref (info->header);
2210                 info->header = g_object_ref (header);
2211
2212                 /* Retrieve the next message */
2213                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
2214
2215                 g_object_unref (header);
2216                 g_object_unref (folder);
2217         } else {
2218                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
2219         }
2220 }
2221
2222 void 
2223 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2224                                      TnyList *header_list, 
2225                                      GetMsgAsyncUserCallback user_callback,
2226                                      gpointer user_data,
2227                                      GDestroyNotify notify)
2228 {
2229         ModestMailOperationPrivate *priv = NULL;
2230         gint msg_list_size;
2231         TnyIterator *iter = NULL;
2232         
2233         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2234
2235         /* Init mail operation */
2236         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2237         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2238         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2239         priv->done = 0;
2240         priv->total = tny_list_get_length(header_list);
2241
2242         /* Get account and set it into mail_operation */
2243         if (tny_list_get_length (header_list) >= 1) {
2244                 TnyIterator *iterator = tny_list_create_iterator (header_list);
2245                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
2246                 if (header) {
2247                         TnyFolder *folder = tny_header_get_folder (header);
2248                         if (folder) {           
2249                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2250                                 g_object_unref (folder);
2251                         }
2252                         g_object_unref (header);
2253                 }
2254                 g_object_unref (iterator);
2255         }
2256
2257         msg_list_size = compute_message_list_size (header_list);
2258
2259         modest_mail_operation_notify_start (self);
2260         iter = tny_list_create_iterator (header_list);
2261         if (!tny_iterator_is_done (iter)) {
2262                 /* notify about the start of the operation */
2263                 ModestMailOperationState *state;
2264                 state = modest_mail_operation_clone_state (self);
2265                 state->done = 0;
2266                 state->total = 0;
2267                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2268                                0, state, NULL);
2269
2270                 GetMsgInfo *msg_info = NULL;
2271                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2272                 TnyFolder *folder = tny_header_get_folder (header);
2273
2274                 /* Create the message info */
2275                 msg_info = g_slice_new0 (GetMsgInfo);
2276                 msg_info->mail_op = g_object_ref (self);
2277                 msg_info->header = g_object_ref (header);
2278                 msg_info->more_msgs = g_object_ref (iter);
2279                 msg_info->user_callback = user_callback;
2280                 msg_info->user_data = user_data;
2281                 msg_info->destroy_notify = notify;
2282                 msg_info->last_total_bytes = 0;
2283                 msg_info->sum_total_bytes = 0;
2284                 msg_info->total_bytes = msg_list_size;
2285
2286                 /* The callback will call it per each header */
2287                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2288
2289                 /* Free and go on */
2290                 g_object_unref (header);
2291                 g_object_unref (folder);
2292                 g_slice_free (ModestMailOperationState, state);
2293         }
2294         g_object_unref (iter);
2295 }
2296
2297
2298 void 
2299 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2300                                   TnyHeader *header,
2301                                   gboolean remove_to_trash /*ignored*/)
2302 {
2303         TnyFolder *folder;
2304         ModestMailOperationPrivate *priv;
2305
2306         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2307         g_return_if_fail (TNY_IS_HEADER (header));
2308
2309         if (remove_to_trash)
2310                 g_warning ("remove to trash is not implemented");
2311
2312         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2313         folder = tny_header_get_folder (header);
2314
2315         /* Get account and set it into mail_operation */
2316         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2317         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2318         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2319
2320         /* remove message from folder */
2321         tny_folder_remove_msg (folder, header, &(priv->error));
2322         if (!priv->error) {
2323                 gboolean expunge, leave_on_server;
2324                 const gchar *account_name;
2325                 TnyAccount *account;
2326                 ModestTransportStoreProtocol account_proto;
2327
2328                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2329                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2330
2331                 modest_mail_operation_notify_start (self);
2332
2333                 /* Get leave on server setting */
2334                 account = tny_folder_get_account (folder);
2335                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2336                 leave_on_server =
2337                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2338                                                                 account_name);
2339
2340                 account_proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
2341
2342                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2343                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2344                         expunge = TRUE;
2345                 else
2346                         expunge = FALSE;
2347
2348                 /* Sync folder */
2349                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2350
2351                 /* Unref */
2352                 g_object_unref (account);
2353         }
2354         
2355         
2356         /* Set status */
2357         if (!priv->error)
2358                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2359         else
2360                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2361
2362         /* Free */
2363         g_object_unref (G_OBJECT (folder));
2364
2365         /* Notify about operation end */
2366         modest_mail_operation_notify_end (self);
2367 }
2368
2369 void 
2370 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2371                                    TnyList *headers,
2372                                   gboolean remove_to_trash /*ignored*/)
2373 {
2374         TnyFolder *folder = NULL;
2375         ModestMailOperationPrivate *priv;
2376         TnyIterator *iter = NULL;
2377         TnyHeader *header = NULL;
2378         TnyList *remove_headers = NULL;
2379         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2380
2381         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2382         g_return_if_fail (TNY_IS_LIST (headers));
2383
2384         if (remove_to_trash)
2385                 g_warning ("remove to trash is not implemented");
2386
2387         if (tny_list_get_length(headers) == 0) {
2388                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
2389                 goto cleanup; /* nothing to do */
2390         }
2391         
2392         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2393         remove_headers = g_object_ref(headers);
2394
2395         /* Get folder from first header and sync it */
2396         iter = tny_list_create_iterator (headers);      
2397         header = TNY_HEADER (tny_iterator_get_current (iter));
2398
2399         folder = tny_header_get_folder (header);        
2400         if (!TNY_IS_FOLDER(folder)) {
2401                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
2402                 goto cleanup;
2403         }
2404
2405         /* Don't remove messages that are being sent */
2406         if (modest_tny_folder_is_local_folder (folder)) {
2407                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2408         }
2409         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2410                 TnyTransportAccount *traccount = NULL;
2411                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2412                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2413                 if (traccount) {
2414                         ModestTnySendQueueStatus status;
2415                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2416                         TnyIterator *iter = tny_list_create_iterator(headers);
2417                         g_object_unref(remove_headers);
2418                         remove_headers = TNY_LIST(tny_simple_list_new());
2419                         while (!tny_iterator_is_done(iter)) {
2420                                 char *msg_id;
2421                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2422                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2423                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2424                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2425                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2426                                 }
2427                                 g_object_unref(hdr);
2428                                 g_free(msg_id);
2429                                 tny_iterator_next(iter);
2430                         }
2431                         g_object_unref(iter);
2432                         g_object_unref(traccount);
2433                 }
2434         }
2435
2436         /* Get account and set it into mail_operation */
2437         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2438         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2439         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2440
2441         /* remove message from folder */
2442         modest_mail_operation_notify_start (self);
2443
2444         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2445         if (!priv->error) {
2446                 gboolean expunge, leave_on_server;
2447                 const gchar *account_name;
2448                 const gchar *proto;
2449                 TnyAccount *account;
2450                 ModestTransportStoreProtocol account_proto = MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
2451                 
2452                 account = tny_folder_get_account (folder);
2453                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2454                 leave_on_server =
2455                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2456                                                                 account_name);
2457                 
2458                 proto = tny_account_get_proto (account);
2459                 if (proto) {
2460                         account_proto = modest_protocol_info_get_transport_store_protocol (proto);
2461                 }
2462                 
2463                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2464                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2465                         expunge = TRUE;
2466                 else
2467                         expunge = FALSE;
2468
2469                 /* Sync folder */
2470                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2471                 
2472                 g_object_unref (account);
2473         }
2474         
2475         
2476         /* Set status */
2477         if (!priv->error)
2478                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2479         else
2480                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2481
2482         /* Free */
2483 cleanup:
2484         if (remove_headers)
2485                 g_object_unref (remove_headers);
2486         if (header)
2487                 g_object_unref (header);
2488         if (iter)
2489                 g_object_unref (iter);
2490         if (folder)
2491                 g_object_unref (folder);
2492
2493         /* Notify about operation end */
2494         modest_mail_operation_notify_end (self);
2495 }
2496
2497 static void
2498 notify_progress_of_multiple_messages (ModestMailOperation *self,
2499                                       TnyStatus *status,
2500                                       gint *last_total_bytes,
2501                                       gint *sum_total_bytes,
2502                                       gint total_bytes, 
2503                                       gboolean increment_done)
2504 {
2505         ModestMailOperationPrivate *priv;
2506         ModestMailOperationState *state;
2507         gboolean is_num_bytes = FALSE;
2508
2509         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2510
2511         /* We know that tinymail sends us information about
2512          *  transferred bytes with this particular message
2513          *  
2514          *  (FIXME: this is very ugly, and no I (djcb) didn't write this code,
2515          *  I just added the 'if' so we don't get runtime warning)
2516          */
2517         if (status->message)
2518                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2519
2520         state = modest_mail_operation_clone_state (self);
2521         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2522                 /* We know that we're in a different message when the
2523                    total number of bytes to transfer is different. Of
2524                    course it could fail if we're transferring messages
2525                    of the same size, but this is a workarround */
2526                 if (status->of_total != *last_total_bytes) {
2527                         /* We need to increment the done when there is
2528                            no information about each individual
2529                            message, we need to do this in message
2530                            transfers, and we don't do it for getting
2531                            messages */
2532                         if (increment_done)
2533                                 priv->done++;
2534                         *sum_total_bytes += *last_total_bytes;
2535                         *last_total_bytes = status->of_total;
2536                 }
2537                 state->bytes_done += status->position + *sum_total_bytes;
2538                 state->bytes_total = total_bytes;
2539
2540                 /* Notify the status change. Only notify about changes
2541                    referred to bytes */
2542                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2543                                0, state, NULL);
2544         }
2545
2546         g_slice_free (ModestMailOperationState, state);
2547 }
2548
2549 static void
2550 transfer_msgs_status_cb (GObject *obj,
2551                          TnyStatus *status,  
2552                          gpointer user_data)
2553 {
2554         XFerMsgsAsyncHelper *helper;
2555
2556         g_return_if_fail (status != NULL);
2557         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2558
2559         helper = (XFerMsgsAsyncHelper *) user_data;
2560         g_return_if_fail (helper != NULL);       
2561
2562         /* Notify progress */
2563         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2564                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2565 }
2566
2567
2568 static void
2569 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2570 {
2571         XFerMsgsAsyncHelper *helper;
2572         ModestMailOperation *self;
2573         ModestMailOperationPrivate *priv;
2574         gboolean finished = TRUE;
2575
2576         helper = (XFerMsgsAsyncHelper *) user_data;
2577         self = helper->mail_op;
2578
2579         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2580
2581         if (err) {
2582                 priv->error = g_error_copy (err);
2583                 priv->done = 0;
2584                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2585         } else if (cancelled) {
2586                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2587                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2588                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2589                              _("Error trying to refresh the contents of %s"),
2590                              tny_folder_get_name (folder));
2591         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2592                 if (helper->more_msgs) {
2593                         /* We'll transfer the next message in the list */
2594                         tny_iterator_next (helper->more_msgs);
2595                         if (!tny_iterator_is_done (helper->more_msgs)) {
2596                                 GObject *next_header;
2597                                 g_object_unref (helper->headers);
2598                                 helper->headers = tny_simple_list_new ();
2599                                 next_header = tny_iterator_get_current (helper->more_msgs);
2600                                 tny_list_append (helper->headers, next_header);
2601                                 g_object_unref (next_header);
2602                                 finished = FALSE;
2603                         }
2604                 }
2605
2606                 if (finished) {
2607                         priv->done = 1;
2608                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2609                 }
2610         }
2611
2612         if (finished) {
2613
2614                 /* Update folder counts */
2615                 tny_folder_poke_status (folder);
2616                 tny_folder_poke_status (helper->dest_folder);
2617
2618                 /* Notify about operation end */
2619                 modest_mail_operation_notify_end (self);
2620
2621                 /* If user defined callback function was defined, call it */
2622                 if (helper->user_callback) {
2623                         /* This is not a GDK lock because we are a Tinymail callback and
2624                          * Tinymail already acquires the Gdk lock */
2625
2626                         /* no gdk_threads_enter (), CHECKED */
2627                         helper->user_callback (self, helper->user_data);
2628                         /* no gdk_threads_leave (), CHECKED */
2629                 }
2630
2631                 /* Free */
2632                 if (helper->more_msgs)
2633                         g_object_unref (helper->more_msgs);
2634                 if (helper->headers)
2635                         g_object_unref (helper->headers);
2636                 if (helper->dest_folder)
2637                         g_object_unref (helper->dest_folder);
2638                 if (helper->mail_op)
2639                         g_object_unref (helper->mail_op);
2640                 g_slice_free (XFerMsgsAsyncHelper, helper);
2641         } else {
2642                 /* Transfer more messages */
2643                 tny_folder_transfer_msgs_async (folder,
2644                                                 helper->headers,
2645                                                 helper->dest_folder,
2646                                                 helper->delete,
2647                                                 transfer_msgs_cb,
2648                                                 transfer_msgs_status_cb,
2649                                                 helper);
2650         }
2651 }
2652
2653 static guint
2654 compute_message_list_size (TnyList *headers)
2655 {
2656         TnyIterator *iter;
2657         guint size = 0;
2658
2659         iter = tny_list_create_iterator (headers);
2660         while (!tny_iterator_is_done (iter)) {
2661                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2662                 size += tny_header_get_message_size (header);
2663                 g_object_unref (header);
2664                 tny_iterator_next (iter);
2665         }
2666         g_object_unref (iter);
2667
2668         return size;
2669 }
2670
2671 static guint
2672 compute_message_array_size (GPtrArray *headers)
2673 {
2674         guint size = 0;
2675         gint i;
2676
2677         for (i = 0; i < headers->len; i++) {
2678                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2679                 size += tny_header_get_message_size (header);
2680         }
2681
2682         return size;
2683 }
2684
2685
2686 void
2687 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2688                                  TnyList *headers, 
2689                                  TnyFolder *folder, 
2690                                  gboolean delete_original,
2691                                  XferMsgsAsyncUserCallback user_callback,
2692                                  gpointer user_data)
2693 {
2694         ModestMailOperationPrivate *priv = NULL;
2695         TnyIterator *iter = NULL;
2696         TnyFolder *src_folder = NULL;
2697         XFerMsgsAsyncHelper *helper = NULL;
2698         TnyHeader *header = NULL;
2699         ModestTnyFolderRules rules = 0;
2700         TnyAccount *dst_account = NULL;
2701         gboolean leave_on_server;
2702
2703         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2704         g_return_if_fail (headers && TNY_IS_LIST (headers));
2705         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2706
2707         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2708         priv->total = tny_list_get_length (headers);
2709         priv->done = 0;
2710         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2711         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2712
2713         /* Apply folder rules */
2714         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2715         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2716                 /* Set status failed and set an error */
2717                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2718                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2719                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2720                              _CS("ckct_ib_unable_to_paste_here"));
2721                 /* Notify the queue */
2722                 modest_mail_operation_notify_end (self);
2723                 return;
2724         }
2725                 
2726         /* Get source folder */
2727         iter = tny_list_create_iterator (headers);
2728         header = TNY_HEADER (tny_iterator_get_current (iter));
2729         if (header) {
2730                 src_folder = tny_header_get_folder (header);
2731                 g_object_unref (header);
2732         }
2733         g_object_unref (iter);
2734
2735         if (src_folder == NULL) {
2736                 /* Notify the queue */
2737                 modest_mail_operation_notify_end (self);
2738
2739                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2740                 return;
2741         }
2742
2743         
2744         /* Check folder source and destination */
2745         if (src_folder == folder) {
2746                 /* Set status failed and set an error */
2747                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2748                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2749                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2750                              _("mail_in_ui_folder_copy_target_error"));
2751                 
2752                 /* Notify the queue */
2753                 modest_mail_operation_notify_end (self);
2754                 
2755                 /* Free */
2756                 g_object_unref (src_folder);            
2757                 return;
2758         }
2759
2760         /* Create the helper */
2761         helper = g_slice_new0 (XFerMsgsAsyncHelper);
2762         helper->mail_op = g_object_ref(self);
2763         helper->dest_folder = g_object_ref(folder);
2764         helper->user_callback = user_callback;
2765         helper->user_data = user_data;
2766         helper->delete = delete_original;
2767         helper->last_total_bytes = 0;
2768         helper->sum_total_bytes = 0;
2769         helper->total_bytes = compute_message_list_size (headers);
2770
2771         /* Get account and set it into mail_operation */
2772         priv->account = modest_tny_folder_get_account (src_folder);
2773         dst_account = modest_tny_folder_get_account (folder);
2774
2775         if (priv->account == dst_account) {
2776                 /* Transfer all messages at once using the fast
2777                  * method. Note that depending on the server this
2778                  * might not be that fast, and might not be
2779                  * user-cancellable either */
2780                 helper->headers = g_object_ref (headers);
2781                 helper->more_msgs = NULL;
2782         } else {
2783                 /* Transfer messages one by one so the user can cancel
2784                  * the operation */
2785                 GObject *hdr;
2786                 helper->headers = tny_simple_list_new ();
2787                 helper->more_msgs = tny_list_create_iterator (headers);
2788                 hdr = tny_iterator_get_current (helper->more_msgs);
2789                 tny_list_append (helper->headers, hdr);
2790                 g_object_unref (hdr);
2791         }
2792
2793         /* If leave_on_server is set to TRUE then don't use
2794            delete_original, we always pass FALSE. This is because
2795            otherwise tinymail will try to sync the source folder and
2796            this could cause an error if we're offline while
2797            transferring an already downloaded message from a POP
2798            account */
2799         if (modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (priv->account)) == 
2800             MODEST_PROTOCOL_STORE_POP) {
2801                 const gchar *account_name;
2802
2803                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
2804                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2805                                                                           account_name);
2806         } else {
2807                 leave_on_server = FALSE;
2808         }
2809
2810         modest_mail_operation_notify_start (self);
2811         tny_folder_transfer_msgs_async (src_folder, 
2812                                         helper->headers, 
2813                                         folder, 
2814                                         (leave_on_server) ? FALSE : delete_original, 
2815                                         transfer_msgs_cb, 
2816                                         transfer_msgs_status_cb,
2817                                         helper);
2818         g_object_unref (src_folder);
2819         g_object_unref (dst_account);
2820 }
2821
2822
2823 static void
2824 on_refresh_folder (TnyFolder   *folder, 
2825                    gboolean     cancelled, 
2826                    GError     *error,
2827                    gpointer     user_data)
2828 {
2829         RefreshAsyncHelper *helper = NULL;
2830         ModestMailOperation *self = NULL;
2831         ModestMailOperationPrivate *priv = NULL;
2832
2833         helper = (RefreshAsyncHelper *) user_data;
2834         self = helper->mail_op;
2835         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2836
2837         g_return_if_fail(priv!=NULL);
2838
2839         if (error) {
2840                 priv->error = g_error_copy (error);
2841                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2842                 goto out;
2843         }
2844
2845         if (cancelled) {
2846                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2847                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2848                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2849                              _("Error trying to refresh the contents of %s"),
2850                              tny_folder_get_name (folder));
2851                 goto out;
2852         }
2853
2854         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2855  out:
2856
2857         /* Call user defined callback, if it exists */
2858         if (helper->user_callback) {
2859
2860                 /* This is not a GDK lock because we are a Tinymail callback and
2861                  * Tinymail already acquires the Gdk lock */
2862                 helper->user_callback (self, folder, helper->user_data);
2863         }
2864
2865         /* Free */
2866         g_slice_free (RefreshAsyncHelper, helper);
2867
2868         /* Notify about operation end */
2869         modest_mail_operation_notify_end (self);
2870         g_object_unref(self);
2871 }
2872
2873 static void
2874 on_refresh_folder_status_update (GObject *obj,
2875                                  TnyStatus *status,
2876                                  gpointer user_data)
2877 {
2878         RefreshAsyncHelper *helper = NULL;
2879         ModestMailOperation *self = NULL;
2880         ModestMailOperationPrivate *priv = NULL;
2881         ModestMailOperationState *state;
2882
2883         g_return_if_fail (user_data != NULL);
2884         g_return_if_fail (status != NULL);
2885         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2886
2887         helper = (RefreshAsyncHelper *) user_data;
2888         self = helper->mail_op;
2889         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2890
2891         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2892
2893         priv->done = status->position;
2894         priv->total = status->of_total;
2895
2896         state = modest_mail_operation_clone_state (self);
2897
2898         /* This is not a GDK lock because we are a Tinymail callback and
2899          * Tinymail already acquires the Gdk lock */
2900         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2901
2902         g_slice_free (ModestMailOperationState, state);
2903 }
2904
2905 void 
2906 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2907                                        TnyFolder *folder,
2908                                        RefreshAsyncUserCallback user_callback,
2909                                        gpointer user_data)
2910 {
2911         ModestMailOperationPrivate *priv = NULL;
2912         RefreshAsyncHelper *helper = NULL;
2913
2914         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2915
2916         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2917
2918         /* Get account and set it into mail_operation */
2919         priv->account = modest_tny_folder_get_account  (folder);
2920         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2921
2922         /* Create the helper */
2923         helper = g_slice_new0 (RefreshAsyncHelper);
2924         helper->mail_op = g_object_ref(self);
2925         helper->user_callback = user_callback;
2926         helper->user_data = user_data;
2927
2928         modest_mail_operation_notify_start (self);
2929         
2930         /* notify that the operation was started */
2931         ModestMailOperationState *state;
2932         state = modest_mail_operation_clone_state (self);
2933         state->done = 0;
2934         state->total = 0;
2935         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2936                         0, state, NULL);
2937
2938         /* FIXME: we're leaking the state here, or? valgrind thinks so */
2939         
2940         tny_folder_refresh_async (folder,
2941                                   on_refresh_folder,
2942                                   on_refresh_folder_status_update,
2943                                   helper);
2944 }
2945
2946 static void
2947 run_queue_stop (ModestTnySendQueue *queue,
2948                 ModestMailOperation *self)
2949 {
2950         ModestMailOperationPrivate *priv;
2951
2952         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2953         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
2954         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2955
2956         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2957
2958         modest_mail_operation_notify_end (self);
2959         g_signal_handlers_disconnect_by_func (queue, run_queue_stop, self);
2960         g_object_unref (self);
2961 }
2962 void
2963 modest_mail_operation_run_queue (ModestMailOperation *self,
2964                                  ModestTnySendQueue *queue)
2965 {
2966         ModestMailOperationPrivate *priv;
2967
2968         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2969         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
2970         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2971
2972         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2973         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
2974         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
2975
2976         modest_mail_operation_notify_start (self);
2977         g_object_ref (self);
2978         g_signal_connect ((gpointer) queue, "queue-stop", G_CALLBACK (run_queue_stop), (gpointer) self);
2979 }
2980
2981 static void
2982 sync_folder_finish_callback (TnyFolder *self, gboolean cancelled, GError *err, ModestMailOperation *mail_op)
2983 {
2984         ModestMailOperationPrivate *priv;
2985
2986         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
2987         if (err != NULL) {
2988                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2989                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2990                              err->message);
2991                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2992         } else {
2993                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2994         }
2995         modest_mail_operation_notify_end (mail_op);
2996         g_object_unref (mail_op);
2997 }
2998
2999 void
3000 modest_mail_operation_sync_folder (ModestMailOperation *self,
3001                                    TnyFolder *folder, gboolean expunge)
3002 {
3003         ModestMailOperationPrivate *priv;
3004
3005         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3006         g_return_if_fail (TNY_IS_FOLDER (folder));
3007         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3008
3009         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3010         priv->account = TNY_ACCOUNT (tny_folder_get_account (folder));
3011         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3012
3013         modest_mail_operation_notify_start (self);
3014         g_object_ref (self);
3015         tny_folder_sync_async (folder, expunge, (TnyFolderCallback) sync_folder_finish_callback, NULL, self);
3016 }
3017
3018 static void
3019 modest_mail_operation_notify_start (ModestMailOperation *self)
3020 {
3021         ModestMailOperationPrivate *priv = NULL;
3022
3023         g_return_if_fail (self);
3024
3025         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3026
3027         /* Ensure that all the fields are filled correctly */
3028         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3029
3030         /* Notify the observers about the mail operation. We do not
3031            wrapp this emission because we assume that this function is
3032            always called from within the main lock */
3033         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3034 }
3035
3036 /**
3037  *
3038  * It's used by the mail operation queue to notify the observers
3039  * attached to that signal that the operation finished. We need to use
3040  * that because tinymail does not give us the progress of a given
3041  * operation when it finishes (it directly calls the operation
3042  * callback).
3043  */
3044 static void
3045 modest_mail_operation_notify_end (ModestMailOperation *self)
3046 {
3047         ModestMailOperationPrivate *priv = NULL;
3048
3049         g_return_if_fail (self);
3050
3051         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3052
3053         /* Notify the observers about the mail operation end. We do
3054            not wrapp this emission because we assume that this
3055            function is always called from within the main lock */
3056         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3057
3058         /* Remove the error user data */
3059         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3060                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3061 }
3062
3063 TnyAccount *
3064 modest_mail_operation_get_account (ModestMailOperation *self)
3065 {
3066         ModestMailOperationPrivate *priv = NULL;
3067
3068         g_return_val_if_fail (self, NULL);
3069
3070         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3071
3072         return (priv->account) ? g_object_ref (priv->account) : NULL;
3073 }
3074
3075 void
3076 modest_mail_operation_noop (ModestMailOperation *self)
3077 {
3078         ModestMailOperationPrivate *priv = NULL;
3079
3080         g_return_if_fail (self);
3081
3082         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3083         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3084         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3085         priv->done = 0;
3086         priv->total = 0;
3087
3088         /* This mail operation does nothing actually */
3089         modest_mail_operation_notify_start (self);
3090         modest_mail_operation_notify_end (self);
3091 }
3092
3093
3094 gchar*
3095 modest_mail_operation_to_string (ModestMailOperation *self)
3096 {
3097         const gchar *type, *status, *account_id;
3098         ModestMailOperationPrivate *priv = NULL;
3099         
3100         g_return_val_if_fail (self, NULL);
3101
3102         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3103
3104         /* new operations don't have anything interesting */
3105         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3106                 return g_strdup_printf ("%p <new operation>", self);
3107         
3108         switch (priv->op_type) {
3109         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3110         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3111         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3112         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3113         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3114         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
3115         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
3116         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3117         default: type = "UNEXPECTED"; break;
3118         }
3119
3120         switch (priv->status) {
3121         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3122         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3123         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3124         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3125         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3126         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3127         default:                                                status= "UNEXPECTED"; break;
3128         } 
3129
3130         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3131
3132         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3133                                 priv->done, priv->total,
3134                                 priv->error && priv->error->message ? priv->error->message : "");
3135 }