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