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