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