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