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