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