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