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