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