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