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