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