* Partially fixes NB#63588, it works but we still need to find a proper solution...
[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 *rr, 
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         /* This is a GDK lock because we are an idle callback and
579          * info->callback can contain Gtk+ code */
580
581         gdk_threads_enter (); /* CHECKED */
582         info->callback (info->mail_op, info->msg, info->userdata);
583         gdk_threads_leave (); /* CHECKED */
584
585         g_object_unref (info->mail_op);
586         if (info->msg)
587                 g_object_unref (info->msg);
588         g_slice_free (CreateMsgIdleInfo, info);
589
590         return FALSE;
591 }
592
593 static gpointer 
594 create_msg_thread (gpointer thread_data)
595 {
596         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
597         TnyMsg *new_msg = NULL;
598         ModestMailOperationPrivate *priv;
599
600         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
601         if (info->html_body == NULL) {
602                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
603                                               info->bcc, info->subject, info->plain_body, 
604                                               info->attachments_list); /* FIXME: attachments */
605         } else {
606                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
607                                                          info->bcc, info->subject, info->html_body,
608                                                          info->plain_body, info->attachments_list);
609         }
610
611         if (new_msg) {
612                 TnyHeader *header;
613                 TnyHeaderFlags flags = 0;
614
615                 /* Set priority flags in message */
616                 header = tny_msg_get_header (new_msg);
617                 if (info->priority_flags != 0)
618                         flags |= info->priority_flags;
619
620                 /* Set attachment flags in message */
621                 if (info->attachments_list != NULL)
622                         flags |= TNY_HEADER_FLAG_ATTACHMENTS;
623
624                 tny_header_set_flags (header, flags);
625                 g_object_unref (G_OBJECT(header));
626         } else {
627                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
628                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
629                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
630                              "modest: failed to create a new msg\n");
631         }
632
633
634         g_free (info->to);
635         g_free (info->from);
636         g_free (info->cc);
637         g_free (info->bcc);
638         g_free (info->plain_body);
639         g_free (info->html_body);
640         g_free (info->subject);
641         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
642         g_list_free (info->attachments_list);
643
644         if (info->callback) {
645                 CreateMsgIdleInfo *idle_info;
646                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
647                 idle_info->mail_op = info->mail_op;
648                 g_object_ref (info->mail_op);
649                 idle_info->msg = new_msg;
650                 if (new_msg)
651                         g_object_ref (new_msg);
652                 idle_info->callback = info->callback;
653                 idle_info->userdata = info->userdata;
654                 g_idle_add (idle_create_msg_cb, idle_info);
655         } else {
656                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
657         }
658
659         g_object_unref (info->mail_op);
660         g_slice_free (CreateMsgInfo, info);
661         return NULL;
662 }
663
664 void
665 modest_mail_operation_create_msg (ModestMailOperation *self,
666                                   const gchar *from, const gchar *to,
667                                   const gchar *cc, const gchar *bcc,
668                                   const gchar *subject, const gchar *plain_body,
669                                   const gchar *html_body,
670                                   const GList *attachments_list,
671                                   TnyHeaderFlags priority_flags,
672                                   ModestMailOperationCreateMsgCallback callback,
673                                   gpointer userdata)
674 {
675         CreateMsgInfo *info = NULL;
676
677         info = g_slice_new0 (CreateMsgInfo);
678         info->mail_op = self;
679         g_object_ref (self);
680
681         info->from = g_strdup (from);
682         info->to = g_strdup (to);
683         info->cc = g_strdup (cc);
684         info->bcc  = g_strdup (bcc);
685         info->subject = g_strdup (subject);
686         info->plain_body = g_strdup (plain_body);
687         info->html_body = g_strdup (html_body);
688         info->attachments_list = g_list_copy ((GList *) attachments_list);
689         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
690         info->priority_flags = priority_flags;
691
692         info->callback = callback;
693         info->userdata = userdata;
694
695         g_thread_create (create_msg_thread, info, FALSE, NULL);
696 }
697
698 typedef struct
699 {
700         TnyTransportAccount *transport_account;
701         TnyMsg *draft_msg;
702 } SendNewMailInfo;
703
704 static void
705 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
706                                         TnyMsg *msg,
707                                         gpointer userdata)
708 {
709         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
710         TnyFolder *draft_folder = NULL;
711         TnyFolder *outbox_folder = NULL;
712         TnyHeader *header;
713
714         if (!msg) {
715                 goto end;
716         }
717
718         /* Call mail operation */
719         modest_mail_operation_send_mail (self, info->transport_account, msg);
720
721         /* Remove old mail from its source folder */
722         draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
723         outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
724         if (info->draft_msg != NULL) {
725                 TnyFolder *folder = NULL;
726                 TnyFolder *src_folder = NULL;
727                 TnyFolderType folder_type;              
728                 folder = tny_msg_get_folder (info->draft_msg);          
729                 if (folder == NULL) goto end;
730                 folder_type = modest_tny_folder_guess_folder_type (folder);
731                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
732                         src_folder = outbox_folder;
733                 else 
734                         src_folder = draft_folder;
735
736                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
737                  * because this function requires it to have a UID. */          
738                 header = tny_msg_get_header (info->draft_msg);
739                 tny_folder_remove_msg (src_folder, header, NULL);
740                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
741                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
742                 g_object_unref (header);
743                 g_object_unref (folder);
744         }
745
746 end:
747         if (info->draft_msg)
748                 g_object_unref (info->draft_msg);
749         if (draft_folder)
750                 g_object_unref (draft_folder);
751         if (outbox_folder)
752                 g_object_unref (outbox_folder);
753         if (info->transport_account)
754                 g_object_unref (info->transport_account);
755         g_slice_free (SendNewMailInfo, info);
756         modest_mail_operation_notify_end (self);
757 }
758
759 void
760 modest_mail_operation_send_new_mail (ModestMailOperation *self,
761                                      TnyTransportAccount *transport_account,
762                                      TnyMsg *draft_msg,
763                                      const gchar *from,  const gchar *to,
764                                      const gchar *cc,  const gchar *bcc,
765                                      const gchar *subject, const gchar *plain_body,
766                                      const gchar *html_body,
767                                      const GList *attachments_list,
768                                      TnyHeaderFlags priority_flags)
769 {
770         ModestMailOperationPrivate *priv = NULL;
771         SendNewMailInfo *info;
772
773         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
774         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
775
776         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
777
778         /* Check parametters */
779         if (to == NULL) {
780                 /* Set status failed and set an error */
781                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
782                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
783                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
784                              _("Error trying to send a mail. You need to set at least one recipient"));
785                 return;
786         }
787         info = g_slice_new0 (SendNewMailInfo);
788         info->transport_account = transport_account;
789         if (transport_account)
790                 g_object_ref (transport_account);
791         info->draft_msg = draft_msg;
792         if (draft_msg)
793                 g_object_ref (draft_msg);
794         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
795                                           attachments_list, priority_flags,
796                                           modest_mail_operation_send_new_mail_cb, info);
797
798 }
799
800 typedef struct
801 {
802         TnyTransportAccount *transport_account;
803         TnyMsg *draft_msg;
804         ModestMsgEditWindow *edit_window;
805 } SaveToDraftsInfo;
806
807 static void
808 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
809                                          TnyMsg *msg,
810                                          gpointer userdata)
811 {
812         TnyFolder *src_folder = NULL;
813         TnyFolder *folder = NULL;
814         TnyHeader *header = NULL;
815         ModestMailOperationPrivate *priv = NULL;
816         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
817
818         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
819         if (!msg) {
820                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
821                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
822                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
823                              "modest: failed to create a new msg\n");
824                 goto end;
825         }
826
827         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
828         if (!folder) {
829                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
830                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
831                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
832                              "modest: failed to create a new msg\n");
833                 goto end;
834         }
835
836         if (!priv->error)
837                 tny_folder_add_msg (folder, msg, &(priv->error));
838
839         if ((!priv->error) && (info->draft_msg != NULL)) {
840                 header = tny_msg_get_header (info->draft_msg);
841                 src_folder = tny_header_get_folder (header); 
842                 /* Remove the old draft expunging it */
843                 tny_folder_remove_msg (src_folder, header, NULL);
844                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
845                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
846                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);  /* expunge */
847                 g_object_unref (header);
848         }
849         
850         if (!priv->error)
851                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
852         else
853                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
854
855         if (info->edit_window)
856                 modest_msg_edit_window_set_draft (info->edit_window, msg);
857
858
859 end:
860         if (folder)
861                 g_object_unref (G_OBJECT(folder));
862         if (src_folder)
863                 g_object_unref (G_OBJECT(src_folder));
864         if (info->edit_window)
865                 g_object_unref (G_OBJECT(info->edit_window));
866         if (info->draft_msg)
867                 g_object_unref (G_OBJECT (info->draft_msg));
868         if (info->transport_account)
869                 g_object_unref (G_OBJECT(info->transport_account));
870         g_slice_free (SaveToDraftsInfo, info);
871
872         modest_mail_operation_notify_end (self);
873 }
874
875 void
876 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
877                                       TnyTransportAccount *transport_account,
878                                       TnyMsg *draft_msg,
879                                       ModestMsgEditWindow *edit_window,
880                                       const gchar *from,  const gchar *to,
881                                       const gchar *cc,  const gchar *bcc,
882                                       const gchar *subject, const gchar *plain_body,
883                                       const gchar *html_body,
884                                       const GList *attachments_list,
885                                       TnyHeaderFlags priority_flags)
886 {
887         ModestMailOperationPrivate *priv = NULL;
888         SaveToDraftsInfo *info = NULL;
889
890         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
891         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
892
893         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
894
895         /* Get account and set it into mail_operation */
896         priv->account = g_object_ref (transport_account);
897
898         info = g_slice_new0 (SaveToDraftsInfo);
899         info->transport_account = g_object_ref (transport_account);
900         info->draft_msg = draft_msg;
901         if (draft_msg)
902                 g_object_ref (draft_msg);
903         info->edit_window = edit_window;
904         if (edit_window)
905                 g_object_ref (edit_window);
906
907         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
908                                           attachments_list, priority_flags,
909                                           modest_mail_operation_save_to_drafts_cb, info);
910
911 }
912
913 typedef struct 
914 {
915         ModestMailOperation *mail_op;
916         TnyStoreAccount *account;
917         TnyTransportAccount *transport_account;
918         gint max_size;
919         gint retrieve_limit;
920         gchar *retrieve_type;
921         gchar *account_name;
922         UpdateAccountCallback callback;
923         gpointer user_data;
924         gint new_headers;
925 } UpdateAccountInfo;
926
927 typedef struct
928 {
929         ModestMailOperation *mail_op;
930         TnyMimePart *mime_part;
931         gssize size;
932         GetMimePartSizeCallback callback;
933         gpointer userdata;
934 } GetMimePartSizeInfo;
935
936 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
937 /* We use this folder observer to track the headers that have been
938  * added to a folder */
939 typedef struct {
940         GObject parent;
941         TnyList *new_headers;
942 } InternalFolderObserver;
943
944 typedef struct {
945         GObjectClass parent;
946 } InternalFolderObserverClass;
947
948 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
949
950 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
951                          internal_folder_observer,
952                          G_TYPE_OBJECT,
953                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
954
955
956 static void
957 foreach_add_item (gpointer header, gpointer user_data)
958 {
959         tny_list_prepend (TNY_LIST (user_data), 
960                           g_object_ref (G_OBJECT (header)));
961 }
962
963 /* This is the method that looks for new messages in a folder */
964 static void
965 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
966 {
967         InternalFolderObserver *derived = (InternalFolderObserver *)self;
968         
969         TnyFolderChangeChanged changed;
970
971         changed = tny_folder_change_get_changed (change);
972
973         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
974                 TnyList *list;
975
976                 /* Get added headers */
977                 list = tny_simple_list_new ();
978                 tny_folder_change_get_added_headers (change, list);
979
980                 /* Add them to the folder observer */
981                 tny_list_foreach (list, foreach_add_item, 
982                                   derived->new_headers);
983
984                 g_object_unref (G_OBJECT (list));
985         }
986 }
987
988 static void
989 internal_folder_observer_init (InternalFolderObserver *self) 
990 {
991         self->new_headers = tny_simple_list_new ();
992 }
993 static void
994 internal_folder_observer_finalize (GObject *object) 
995 {
996         InternalFolderObserver *self;
997
998         self = (InternalFolderObserver *) object;
999         g_object_unref (self->new_headers);
1000
1001         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1002 }
1003 static void
1004 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1005 {
1006         iface->update_func = internal_folder_observer_update;
1007 }
1008 static void
1009 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1010 {
1011         GObjectClass *object_class;
1012
1013         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1014         object_class = (GObjectClass*) klass;
1015         object_class->finalize = internal_folder_observer_finalize;
1016 }
1017
1018 /*****************/
1019
1020 static void
1021 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1022 {
1023         TnyIterator *iter;
1024         TnyList *folders = tny_simple_list_new ();
1025
1026         tny_folder_store_get_folders (store, folders, query, NULL);
1027         iter = tny_list_create_iterator (folders);
1028
1029         while (!tny_iterator_is_done (iter)) {
1030
1031                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1032                 if (folder) {
1033                         tny_list_prepend (all_folders, G_OBJECT (folder));
1034                         recurse_folders (folder, query, all_folders);    
1035                         g_object_unref (G_OBJECT (folder));
1036                 }
1037
1038                 tny_iterator_next (iter);
1039         }
1040          g_object_unref (G_OBJECT (iter));
1041          g_object_unref (G_OBJECT (folders));
1042 }
1043
1044 /* 
1045  * Issues the "progress-changed" signal. The timer won't be removed,
1046  * so you must call g_source_remove to stop the signal emission
1047  */
1048 static gboolean
1049 idle_notify_progress (gpointer data)
1050 {
1051         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1052         ModestMailOperationState *state;
1053
1054         state = modest_mail_operation_clone_state (mail_op);
1055
1056         /* This is a GDK lock because we are an idle callback and
1057          * the handlers of this signal can contain Gtk+ code */
1058
1059         gdk_threads_enter (); /* CHECKED */
1060         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1061         gdk_threads_leave (); /* CHECKED */
1062
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         /* This is a GDK lock because we are an idle callback and
1081          * the handlers of this signal can contain Gtk+ code */
1082
1083         gdk_threads_enter (); /* CHECKED */
1084         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1085         gdk_threads_leave (); /* CHECKED */
1086
1087         /* Free the state and the reference to the mail operation */
1088         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1089         g_object_unref (pair->first);
1090
1091         return FALSE;
1092 }
1093
1094 /* 
1095  * Used to notify the queue from the main
1096  * loop. We call it inside an idle call to achieve that
1097  */
1098 static gboolean
1099 idle_notify_queue (gpointer data)
1100 {
1101         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1102
1103         /* Do not need to block, the notify end will do it for us */    
1104         modest_mail_operation_notify_end (mail_op);
1105         g_object_unref (mail_op);
1106
1107         return FALSE;
1108 }
1109
1110 static int
1111 compare_headers_by_date (gconstpointer a, 
1112                          gconstpointer b)
1113 {
1114         TnyHeader **header1, **header2;
1115         time_t sent1, sent2;
1116
1117         header1 = (TnyHeader **) a;
1118         header2 = (TnyHeader **) b;
1119
1120         sent1 = tny_header_get_date_sent (*header1);
1121         sent2 = tny_header_get_date_sent (*header2);
1122
1123         /* We want the most recent ones (greater time_t) at the
1124            beginning */
1125         if (sent1 < sent2)
1126                 return 1;
1127         else
1128                 return -1;
1129 }
1130
1131 static gboolean 
1132 set_last_updated_idle (gpointer data)
1133 {
1134
1135         /* This is a GDK lock because we are an idle callback and
1136          * modest_account_mgr_set_int can contain Gtk+ code */
1137
1138         gdk_threads_enter (); /* CHECKED - please recheck */
1139
1140         /* It does not matter if the time is not exactly the same than
1141            the time when this idle was called, it's just an
1142            approximation and it won't be very different */
1143
1144         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
1145                                     (gchar *) data, 
1146                                     MODEST_ACCOUNT_LAST_UPDATED, 
1147                                     time(NULL), 
1148                                     TRUE);
1149
1150         gdk_threads_leave (); /* CHECKED - please recheck */
1151
1152         return FALSE;
1153 }
1154
1155 static gboolean
1156 idle_update_account_cb (gpointer data)
1157 {
1158         UpdateAccountInfo *idle_info;
1159
1160         idle_info = (UpdateAccountInfo *) data;
1161
1162         /* This is a GDK lock because we are an idle callback and
1163          * idle_info->callback can contain Gtk+ code */
1164
1165         gdk_threads_enter (); /* CHECKED */
1166         idle_info->callback (idle_info->mail_op,
1167                              idle_info->new_headers,
1168                              idle_info->user_data);
1169         gdk_threads_leave (); /* CHECKED */
1170
1171         /* Frees */
1172         g_object_unref (idle_info->mail_op);
1173         g_free (idle_info);
1174
1175         return FALSE;
1176 }
1177
1178
1179 static gpointer
1180 update_account_thread (gpointer thr_user_data)
1181 {
1182         static gboolean first_time = TRUE;
1183         UpdateAccountInfo *info = NULL;
1184         TnyList *all_folders = NULL;
1185         GPtrArray *new_headers = NULL;
1186         TnyIterator *iter = NULL;
1187         TnyFolderStoreQuery *query = NULL;
1188         ModestMailOperationPrivate *priv = NULL;
1189         ModestTnySendQueue *send_queue = NULL;
1190         gint num_new_headers = 0;
1191
1192         info = (UpdateAccountInfo *) thr_user_data;
1193         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1194
1195         /* Get account and set it into mail_operation */
1196         priv->account = g_object_ref (info->account);
1197
1198         /*
1199          * Previousl, we did this for POP3, to do a logout-login upon send/receive, 
1200          * because many POP-servers (like Gmail) do not
1201          * show any updates unless we do that.
1202          * But that didn't work with gmail anyway, 
1203          * and tinymail now takes care of this itself by disconnecting 
1204          * automatically after using the connection.
1205          */
1206         /*
1207         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account)) 
1208                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1209         */
1210
1211         /* Get all the folders. We can do it synchronously because
1212            we're already running in a different thread than the UI */
1213         all_folders = tny_simple_list_new ();
1214         query = tny_folder_store_query_new ();
1215         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1216         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1217                                       all_folders,
1218                                       query,
1219                                       &(priv->error));
1220         if (priv->error) {
1221                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1222                 goto out;
1223         }
1224
1225         iter = tny_list_create_iterator (all_folders);
1226         while (!tny_iterator_is_done (iter)) {
1227                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1228                 if (folder) {
1229                         recurse_folders (folder, query, all_folders);
1230                         g_object_unref (folder);
1231                 }
1232                 tny_iterator_next (iter);
1233         }
1234         g_object_unref (G_OBJECT (iter));
1235
1236         /* Update status and notify. We need to call the notification
1237            with a source function in order to call it from the main
1238            loop. We need that in order not to get into trouble with
1239            Gtk+. We use a timeout in order to provide more status
1240            information, because the sync tinymail call does not
1241            provide it for the moment */
1242         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1243
1244         /* Refresh folders */
1245         num_new_headers = 0;
1246         new_headers = g_ptr_array_new ();
1247         iter = tny_list_create_iterator (all_folders);
1248
1249         while (!tny_iterator_is_done (iter) && !priv->error && 
1250                priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1251
1252                 InternalFolderObserver *observer;
1253                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1254
1255                 /* Refresh the folder */
1256                 /* Our observer receives notification of new emails during folder refreshes,
1257                  * so we can use observer->new_headers.
1258                  */
1259                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1260                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1261                 
1262                 /* This gets the status information (headers) from the server.
1263                  * We use the blocking version, because we are already in a separate 
1264                  * thread.
1265                  */
1266
1267                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
1268                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1269                         TnyIterator *iter;
1270
1271                         /* If the retrieve type is full messages, refresh and get the messages */
1272                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1273
1274                         iter = tny_list_create_iterator (observer->new_headers);
1275                         while (!tny_iterator_is_done (iter)) {
1276                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1277                                  
1278                                 /* Apply per-message size limits */
1279                                 if (tny_header_get_message_size (header) < info->max_size)
1280                                         g_ptr_array_add (new_headers, g_object_ref (header));
1281
1282                                 g_object_unref (header);
1283                                 tny_iterator_next (iter);
1284                         }
1285                         g_object_unref (iter);
1286                 } else {
1287                         /* We do not need to do it the first time
1288                            because it's automatically done by the tree
1289                            model */
1290                         if (G_UNLIKELY (!first_time))
1291                                 tny_folder_poke_status (TNY_FOLDER (folder));
1292                 }
1293                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1294                 g_object_unref (observer);
1295                 observer = NULL;                        
1296
1297                 if (folder)
1298                         g_object_unref (G_OBJECT (folder));
1299
1300                 if (priv->error)
1301                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1302
1303                 tny_iterator_next (iter);
1304         }
1305
1306         g_object_unref (G_OBJECT (iter));
1307         g_source_remove (timeout);
1308
1309         if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED && 
1310             priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1311             new_headers->len > 0) {
1312                 gint msg_num = 0;
1313
1314                 /* Order by date */
1315                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1316
1317                 /* Apply message count limit */
1318                 /* If the number of messages exceeds the maximum, ask the
1319                  * user to download them all,
1320                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1321                  */
1322                 if (new_headers->len > info->retrieve_limit) {
1323                         /* TODO: Ask the user, instead of just
1324                          * failing, showing
1325                          * mail_nc_msg_count_limit_exceeded, with 'Get
1326                          * all' and 'Newest only' buttons. */
1327                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1328                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1329                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1330                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1331                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1332                         goto out;
1333                 }
1334                 
1335                 priv->done = 0;
1336                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1337                 while (msg_num < priv->total) {
1338
1339                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1340                         TnyFolder *folder = tny_header_get_folder (header);
1341                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1342                         ModestMailOperationState *state;
1343                         ModestPair* pair;
1344
1345                         priv->done++;
1346                         /* We can not just use the mail operation because the
1347                            values of done and total could change before the
1348                            idle is called */
1349                         state = modest_mail_operation_clone_state (info->mail_op);
1350                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1351                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1352                                          pair, (GDestroyNotify) modest_pair_free);
1353
1354                         g_object_unref (msg);
1355                         g_object_unref (folder);
1356
1357                         msg_num++;
1358                 }
1359         }
1360
1361         /* Get the number of new headers and free them */
1362         num_new_headers = new_headers->len;
1363         g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1364         g_ptr_array_free (new_headers, FALSE);
1365         
1366         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1367                 goto out;
1368
1369         /* Perform send (if operation was not cancelled) */
1370         priv->done = 0;
1371         priv->total = 0;
1372         if (priv->account != NULL) 
1373                 g_object_unref (priv->account);
1374         priv->account = g_object_ref (info->transport_account);
1375         
1376         send_queue = modest_runtime_get_send_queue (info->transport_account);
1377         if (send_queue) {
1378                 modest_tny_send_queue_try_to_send (send_queue);
1379         } else {
1380                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1381                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1382                              "cannot create a send queue for %s\n", 
1383                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1384                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1385         }
1386         
1387         /* Check if the operation was a success */
1388         if (!priv->error) {
1389                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1390
1391                 /* Update the last updated key */
1392                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1393                                  set_last_updated_idle, 
1394                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1395                                  (GDestroyNotify) g_free);
1396         }
1397
1398  out:
1399
1400         if (info->callback) {
1401                 UpdateAccountInfo *idle_info;
1402
1403                 /* This thread is not in the main lock */
1404                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1405                 idle_info->mail_op = g_object_ref (info->mail_op);
1406                 idle_info->new_headers = num_new_headers;
1407                 idle_info->callback = info->callback;
1408                 idle_info->user_data = info->user_data;
1409                 g_idle_add (idle_update_account_cb, idle_info);
1410         }
1411
1412         /* Notify about operation end. Note that the info could be
1413            freed before this idle happens, but the mail operation will
1414            be still alive */
1415         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1416
1417         /* Frees */
1418         g_object_unref (query);
1419         g_object_unref (all_folders);
1420         g_object_unref (info->account);
1421         g_object_unref (info->transport_account);
1422         g_free (info->retrieve_type);
1423         g_slice_free (UpdateAccountInfo, info);
1424
1425         first_time = FALSE;
1426
1427         return NULL;
1428 }
1429
1430 gboolean
1431 modest_mail_operation_update_account (ModestMailOperation *self,
1432                                       const gchar *account_name,
1433                                       UpdateAccountCallback callback,
1434                                       gpointer user_data)
1435 {
1436         GThread *thread = NULL;
1437         UpdateAccountInfo *info = NULL;
1438         ModestMailOperationPrivate *priv = NULL;
1439         ModestAccountMgr *mgr = NULL;
1440         TnyStoreAccount *store_account = NULL;
1441         TnyTransportAccount *transport_account = NULL;
1442
1443         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1444         g_return_val_if_fail (account_name, FALSE);
1445
1446         /* Init mail operation. Set total and done to 0, and do not
1447            update them, this way the progress objects will know that
1448            we have no clue about the number of the objects */
1449         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1450         priv->total = 0;
1451         priv->done  = 0;
1452         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1453
1454         /* Get the Modest account */
1455         store_account = (TnyStoreAccount *)
1456                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1457                                                                      account_name,
1458                                                                      TNY_ACCOUNT_TYPE_STORE);
1459                                                                      
1460         /* Make sure that we have a connection, and request one 
1461          * if necessary:
1462          * TODO: Is there some way to trigger this for every attempt to 
1463          * use the network? */
1464         if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1465                 goto error;
1466
1467         if (!store_account) {
1468                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1469                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1470                              "cannot get tny store account for %s\n", account_name);
1471                 goto error;
1472         }
1473
1474         
1475         /* Get the transport account, we can not do it in the thread
1476            due to some problems with dbus */
1477         transport_account = (TnyTransportAccount *)
1478                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1479                                                                                     account_name);
1480         if (!transport_account) {
1481                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1482                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1483                              "cannot get tny transport account for %s\n", account_name);
1484                 goto error;
1485         }
1486
1487         /* Create the helper object */
1488         info = g_slice_new (UpdateAccountInfo);
1489         info->mail_op = self;
1490         info->account = store_account;
1491         info->transport_account = transport_account;
1492         info->callback = callback;
1493         info->user_data = user_data;
1494
1495         /* Get the message size limit */
1496         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1497                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1498         if (info->max_size == 0)
1499                 info->max_size = G_MAXINT;
1500         else
1501                 info->max_size = info->max_size * KB;
1502
1503         /* Get per-account retrieval type */
1504         mgr = modest_runtime_get_account_mgr ();
1505         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1506                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1507
1508         /* Get per-account message amount retrieval limit */
1509         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1510                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1511         if (info->retrieve_limit == 0)
1512                 info->retrieve_limit = G_MAXINT;
1513                 
1514         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1515
1516         /* Set account busy */
1517         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1518         priv->account_name = g_strdup(account_name);
1519         
1520         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1521
1522         return TRUE;
1523
1524  error:
1525         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1526         if (callback) 
1527                 callback (self, 0, user_data);
1528         modest_mail_operation_notify_end (self);
1529         return FALSE;
1530 }
1531
1532 /* ******************************************************************* */
1533 /* ************************** STORE  ACTIONS ************************* */
1534 /* ******************************************************************* */
1535
1536
1537 TnyFolder *
1538 modest_mail_operation_create_folder (ModestMailOperation *self,
1539                                      TnyFolderStore *parent,
1540                                      const gchar *name)
1541 {
1542         ModestMailOperationPrivate *priv;
1543         TnyFolder *new_folder = NULL;
1544
1545         TnyList *list = tny_simple_list_new ();
1546         TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1547
1548         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1549         g_return_val_if_fail (name, NULL);
1550         
1551         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1552
1553         /* Check for already existing folder */
1554         tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1555         tny_folder_store_get_folders (parent, list, query, NULL);
1556         g_object_unref (G_OBJECT (query));
1557
1558         if (tny_list_get_length (list) > 0) {
1559                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1560                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1561                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1562                              _CS("ckdg_ib_folder_already_exists"));
1563         }
1564
1565         g_object_unref (G_OBJECT (list));
1566
1567         /* Check parent */
1568         if (TNY_IS_FOLDER (parent)) {
1569                 /* Check folder rules */
1570                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1571                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1572                         /* Set status failed and set an error */
1573                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1574                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1575                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1576                                      _("mail_in_ui_folder_create_error"));
1577                 }
1578         }
1579
1580         if (!strcmp (name, " ") || strchr (name, '/')) {
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         if (!priv->error) {
1588                 /* Create the folder */
1589                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1590                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1591                 if (!priv->error)
1592                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1593         }
1594
1595         /* Notify about operation end */
1596         modest_mail_operation_notify_end (self);
1597
1598         return new_folder;
1599 }
1600
1601 void
1602 modest_mail_operation_remove_folder (ModestMailOperation *self,
1603                                      TnyFolder           *folder,
1604                                      gboolean             remove_to_trash)
1605 {
1606         TnyAccount *account;
1607         ModestMailOperationPrivate *priv;
1608         ModestTnyFolderRules rules;
1609
1610         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1611         g_return_if_fail (TNY_IS_FOLDER (folder));
1612         
1613         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1614         
1615         /* Check folder rules */
1616         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1617         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1618                 /* Set status failed and set an error */
1619                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1620                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1621                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1622                              _("mail_in_ui_folder_delete_error"));
1623                 goto end;
1624         }
1625
1626         /* Get the account */
1627         account = modest_tny_folder_get_account (folder);
1628         priv->account = g_object_ref(account);
1629
1630         /* Delete folder or move to trash */
1631         if (remove_to_trash) {
1632                 TnyFolder *trash_folder = NULL;
1633                 trash_folder = modest_tny_account_get_special_folder (account,
1634                                                                       TNY_FOLDER_TYPE_TRASH);
1635                 /* TODO: error_handling */
1636                 if (trash_folder) {
1637                         modest_mail_operation_xfer_folder (self, folder,
1638                                                     TNY_FOLDER_STORE (trash_folder), 
1639                                                     TRUE, NULL, NULL);
1640                         g_object_unref (trash_folder);
1641                 }
1642         } else {
1643                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1644
1645                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1646                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1647
1648                 if (!priv->error)
1649                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1650
1651                 if (parent)
1652                         g_object_unref (G_OBJECT (parent));
1653         }
1654         g_object_unref (G_OBJECT (account));
1655
1656  end:
1657         /* Notify about operation end */
1658         modest_mail_operation_notify_end (self);
1659 }
1660
1661 static void
1662 transfer_folder_status_cb (GObject *obj,
1663                            TnyStatus *status,
1664                            gpointer user_data)
1665 {
1666         ModestMailOperation *self;
1667         ModestMailOperationPrivate *priv;
1668         ModestMailOperationState *state;
1669         XFerMsgAsyncHelper *helper;
1670
1671         g_return_if_fail (status != NULL);
1672         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1673
1674         helper = (XFerMsgAsyncHelper *) user_data;
1675         g_return_if_fail (helper != NULL);
1676
1677         self = helper->mail_op;
1678         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1679
1680         priv->done = status->position;
1681         priv->total = status->of_total;
1682
1683         state = modest_mail_operation_clone_state (self);
1684
1685         /* This is not a GDK lock because we are a Tinymail callback
1686          * which is already GDK locked by Tinymail */
1687
1688         /* no gdk_threads_enter (), CHECKED */
1689
1690         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1691
1692         /* no gdk_threads_leave (), CHECKED */
1693
1694         g_slice_free (ModestMailOperationState, state);
1695 }
1696
1697
1698 static void
1699 transfer_folder_cb (TnyFolder *folder, gboolean cancelled, 
1700                     TnyFolderStore *into, 
1701                     TnyFolder *new_folder, 
1702                     GError *err, 
1703                     gpointer user_data)
1704 {
1705         XFerMsgAsyncHelper *helper;
1706         ModestMailOperation *self = NULL;
1707         ModestMailOperationPrivate *priv = NULL;
1708
1709         helper = (XFerMsgAsyncHelper *) user_data;
1710         g_return_if_fail (helper != NULL);       
1711
1712         self = helper->mail_op;
1713         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1714
1715         if (err) {
1716                 priv->error = g_error_copy (err);
1717                 priv->done = 0;
1718                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1719         } else if (cancelled) {
1720                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1721                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1722                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1723                              _("Transference of %s was cancelled."),
1724                              tny_folder_get_name (folder));
1725         } else {
1726                 priv->done = 1;
1727                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1728         }
1729                 
1730         /* Notify about operation end */
1731         modest_mail_operation_notify_end (self);
1732
1733         /* If user defined callback function was defined, call it */
1734         if (helper->user_callback) {
1735
1736                 /* This is not a GDK lock because we are a Tinymail callback
1737                  * which is already GDK locked by Tinymail */
1738
1739                 /* no gdk_threads_enter (), CHECKED */
1740                 helper->user_callback (priv->source, helper->user_data);
1741                 /* no gdk_threads_leave () , CHECKED */
1742         }
1743
1744         /* Free */
1745         g_object_unref (helper->mail_op);
1746         g_slice_free   (XFerMsgAsyncHelper, helper);
1747 }
1748
1749 /**
1750  *
1751  * This function checks if the new name is a valid name for our local
1752  * folders account. The new name could not be the same than then name
1753  * of any of the mandatory local folders
1754  *
1755  * We can not rely on tinymail because tinymail does not check the
1756  * name of the virtual folders that the account could have in the case
1757  * that we're doing a rename (because it directly calls Camel which
1758  * knows nothing about our virtual folders). 
1759  *
1760  * In the case of an actual copy/move (i.e. move/copy a folder between
1761  * accounts) tinymail uses the tny_folder_store_create_account which
1762  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1763  * checks the new name of the folder, so this call in that case
1764  * wouldn't be needed. *But* NOTE that if tinymail changes its
1765  * implementation (if folder transfers within the same account is no
1766  * longer implemented as a rename) this call will allow Modest to work
1767  * perfectly
1768  *
1769  * If the new name is not valid, this function will set the status to
1770  * failed and will set also an error in the mail operation
1771  */
1772 static gboolean
1773 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1774                                  TnyFolderStore *into,
1775                                  const gchar *new_name)
1776 {
1777         if (TNY_IS_ACCOUNT (into) && 
1778             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1779             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1780                                                                  new_name)) {
1781                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1782                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1783                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1784                              _("ckdg_ib_folder_already_exists"));
1785                 return FALSE;
1786         } else
1787                 return TRUE;
1788 }
1789
1790 /**
1791  * This function checks if @ancestor is an acestor of @folder and
1792  * returns TRUE in that case
1793  */
1794 static gboolean
1795 folder_is_ancestor (TnyFolder *folder,
1796                     TnyFolderStore *ancestor)
1797 {
1798         TnyFolder *tmp = NULL;
1799         gboolean found = FALSE;
1800
1801         tmp = folder;
1802         while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1803                 TnyFolderStore *folder_store;
1804
1805                 folder_store = tny_folder_get_folder_store (tmp);
1806                 if (ancestor == folder_store)
1807                         found = TRUE;
1808                 else
1809                         tmp = g_object_ref (folder_store);
1810                 g_object_unref (folder_store);
1811         }
1812         return found;
1813 }
1814
1815 void
1816 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1817                                    TnyFolder *folder,
1818                                    TnyFolderStore *parent,
1819                                    gboolean delete_original,
1820                                    XferMsgsAsynUserCallback user_callback,
1821                                    gpointer user_data)
1822 {
1823         ModestMailOperationPrivate *priv = NULL;
1824         ModestTnyFolderRules parent_rules = 0, rules; 
1825         XFerMsgAsyncHelper *helper = NULL;
1826         const gchar *folder_name = NULL;
1827
1828         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1829         g_return_if_fail (TNY_IS_FOLDER (folder));
1830         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1831
1832         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1833         folder_name = tny_folder_get_name (folder);
1834
1835         /* Get account and set it into mail_operation */
1836         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1837         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1838
1839         /* Get folder rules */
1840         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1841         if (TNY_IS_FOLDER (parent))
1842                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1843         
1844         /* The moveable restriction is applied also to copy operation */
1845         if ((gpointer) parent == (gpointer) folder ||
1846             (!TNY_IS_FOLDER_STORE (parent)) || 
1847             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1848
1849                 /* Set status failed and set an error */
1850                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1851                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1852                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1853                              _("mail_in_ui_folder_move_target_error"));
1854
1855                 /* Notify the queue */
1856                 modest_mail_operation_notify_end (self);
1857
1858         } else if (TNY_IS_FOLDER (parent) && 
1859                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1860
1861                 /* Set status failed and set an error */
1862                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1863                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1864                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1865                              _("FIXME: parent folder does not accept new folders"));
1866
1867                 /* Notify the queue */
1868                 modest_mail_operation_notify_end (self);
1869
1870         } else if (TNY_IS_FOLDER (parent) &&
1871                    TNY_IS_FOLDER_STORE (folder) &&
1872                    folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1873                 /* Set status failed and set an error */
1874                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1875                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1876                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1877                              _("mail_in_ui_folder_copy_target_error"));
1878
1879                 /* Notify the queue */
1880                 modest_mail_operation_notify_end (self);
1881
1882         } else if (TNY_IS_FOLDER_STORE (parent) &&
1883                    modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1884                 /* Check that the new folder name is not used by any
1885                     parent subfolder */
1886
1887                 /* Set status failed and set an error */
1888                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1889                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1890                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1891                              _("mail_in_ui_folder_move_target_error"));
1892
1893                 /* Notify the queue */
1894                 modest_mail_operation_notify_end (self);
1895                 
1896         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1897                 /* Check that the new folder name is not used by any
1898                    special local folder */
1899
1900                 /* Set status failed and set an error */
1901                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1902                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1903                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1904                              _("mail_in_ui_folder_move_target_error"));
1905
1906                 /* Notify the queue */
1907                 modest_mail_operation_notify_end (self);
1908         } else {
1909                 /* Create the helper */
1910                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1911                 helper->mail_op = g_object_ref(self);
1912                 helper->dest_folder = NULL;
1913                 helper->headers = NULL;
1914                 helper->user_callback = user_callback;
1915                 helper->user_data = user_data;
1916                 
1917                 /* Move/Copy folder */          
1918                 tny_folder_copy_async (folder,
1919                                        parent,
1920                                        tny_folder_get_name (folder),
1921                                                delete_original,
1922                                        transfer_folder_cb,
1923                                        transfer_folder_status_cb,
1924                                        helper);
1925         } 
1926         
1927 }
1928
1929 void
1930 modest_mail_operation_rename_folder (ModestMailOperation *self,
1931                                      TnyFolder *folder,
1932                                      const gchar *name)
1933 {
1934         ModestMailOperationPrivate *priv;
1935         ModestTnyFolderRules rules;
1936         XFerMsgAsyncHelper *helper;
1937
1938         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1939         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1940         g_return_if_fail (name);
1941         
1942         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1943
1944         /* Get account and set it into mail_operation */
1945         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1946
1947         /* Check folder rules */
1948         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1949         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1950                 /* Set status failed and set an error */
1951                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1952                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1953                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1954                              _("FIXME: unable to rename"));
1955
1956                 /* Notify about operation end */
1957                 modest_mail_operation_notify_end (self);
1958         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1959                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1960                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1961                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1962                              _("FIXME: unable to rename"));
1963                 /* Notify about operation end */
1964                 modest_mail_operation_notify_end (self);
1965         } else {
1966                 TnyFolderStore *into;
1967
1968                 into = tny_folder_get_folder_store (folder);    
1969
1970                 /* Check that the new folder name is not used by any
1971                    special local folder */
1972                 if (new_name_valid_if_local_account (priv, into, name)) {
1973                         /* Create the helper */
1974                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1975                         helper->mail_op = g_object_ref(self);
1976                         helper->dest_folder = NULL;
1977                         helper->headers = NULL;
1978                         helper->user_callback = NULL;
1979                         helper->user_data = NULL;
1980                 
1981                         /* Rename. Camel handles folder subscription/unsubscription */
1982                         tny_folder_copy_async (folder, into, name, TRUE,
1983                                                transfer_folder_cb,
1984                                                transfer_folder_status_cb,
1985                                                helper);
1986                 } else {
1987                         modest_mail_operation_notify_end (self);
1988                 }
1989                 g_object_unref (into);
1990         }
1991 }
1992
1993 /* ******************************************************************* */
1994 /* **************************  MSG  ACTIONS  ************************* */
1995 /* ******************************************************************* */
1996
1997 void modest_mail_operation_get_msg (ModestMailOperation *self,
1998                                     TnyHeader *header,
1999                                     GetMsgAsyncUserCallback user_callback,
2000                                     gpointer user_data)
2001 {
2002         GetMsgAsyncHelper *helper = NULL;
2003         TnyFolder *folder;
2004         ModestMailOperationPrivate *priv;
2005         
2006         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2007         g_return_if_fail (TNY_IS_HEADER (header));
2008         
2009         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2010         folder = tny_header_get_folder (header);
2011
2012         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2013
2014         /* Get message from folder */
2015         if (folder) {
2016                 /* Get account and set it into mail_operation */
2017                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2018                 
2019                 /* Check for cached messages */
2020                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2021                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2022                 else 
2023                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2024
2025                 helper = g_slice_new0 (GetMsgAsyncHelper);
2026                 helper->mail_op = self;
2027                 helper->user_callback = user_callback;
2028                 helper->user_data = user_data;
2029                 helper->header = g_object_ref (header);
2030
2031                 // The callback's reference so that the mail op is not
2032                 // finalized until the async operation is completed even if
2033                 // the user canceled the request meanwhile.
2034                 g_object_ref (G_OBJECT (helper->mail_op));
2035
2036                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2037
2038                 g_object_unref (G_OBJECT (folder));
2039         } else {
2040                 /* Set status failed and set an error */
2041                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2042                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2043                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2044                              _("Error trying to get a message. No folder found for header"));
2045
2046                 /* Notify the queue */
2047                 modest_mail_operation_notify_end (self);
2048         }
2049 }
2050
2051 static void
2052 get_msg_cb (TnyFolder *folder, 
2053             gboolean cancelled, 
2054             TnyMsg *msg, 
2055             GError *error, 
2056             gpointer user_data)
2057 {
2058         GetMsgAsyncHelper *helper = NULL;
2059         ModestMailOperation *self = NULL;
2060         ModestMailOperationPrivate *priv = NULL;
2061
2062         helper = (GetMsgAsyncHelper *) user_data;
2063         g_return_if_fail (helper != NULL);       
2064         self = helper->mail_op;
2065         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2066         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2067
2068         /* Check errors and cancel */
2069         if (error) {
2070                 priv->error = g_error_copy (error);
2071                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2072         } else if (cancelled) {
2073                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2074                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2075                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2076                              _("Error trying to refresh the contents of %s"),
2077                              tny_folder_get_name (folder));
2078         } else {
2079                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2080         }
2081
2082         /* If user defined callback function was defined, call it even
2083            if the operation failed*/
2084         if (helper->user_callback) {
2085                 /* This is not a GDK lock because we are a Tinymail callback
2086                  * which is already GDK locked by Tinymail */
2087
2088                 /* no gdk_threads_enter (), CHECKED */
2089                 helper->user_callback (self, helper->header, msg, helper->user_data);
2090                 /* no gdk_threads_leave (), CHECKED */
2091         }
2092
2093         /* Notify about operation end */
2094         modest_mail_operation_notify_end (self);
2095         /* Free */
2096         g_object_unref (helper->mail_op);
2097         g_object_unref (helper->header);
2098         g_slice_free (GetMsgAsyncHelper, helper);
2099                 
2100 }
2101
2102 static void     
2103 get_msg_status_cb (GObject *obj,
2104                    TnyStatus *status,  
2105                    gpointer user_data)
2106 {
2107         GetMsgAsyncHelper *helper = NULL;
2108         ModestMailOperation *self;
2109         ModestMailOperationPrivate *priv;
2110         ModestMailOperationState *state;
2111
2112         g_return_if_fail (status != NULL);
2113         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2114
2115         helper = (GetMsgAsyncHelper *) user_data;
2116         g_return_if_fail (helper != NULL);       
2117
2118         self = helper->mail_op;
2119         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2120
2121         priv->done = 1;
2122         priv->total = 1;
2123
2124         state = modest_mail_operation_clone_state (self);
2125         state->bytes_done = status->position;
2126         state->bytes_total = status->of_total;
2127
2128         /* This is not a GDK lock because we are a Tinymail callback
2129          * which is already GDK locked by Tinymail */
2130
2131         /* no gdk_threads_enter (), CHECKED */
2132         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2133         /* no gdk_threads_leave (), CHECKED */
2134
2135         g_slice_free (ModestMailOperationState, state);
2136 }
2137
2138 /****************************************************/
2139 typedef struct {
2140         ModestMailOperation *mail_op;
2141         TnyList *headers;
2142         GetMsgAsyncUserCallback user_callback;
2143         gpointer user_data;
2144         GDestroyNotify notify;
2145 } GetFullMsgsInfo;
2146
2147 typedef struct {
2148         GetMsgAsyncUserCallback user_callback;
2149         TnyHeader *header;
2150         TnyMsg *msg;
2151         gpointer user_data;
2152         ModestMailOperation *mail_op;
2153 } NotifyGetMsgsInfo;
2154
2155
2156 /* 
2157  * Used by get_msgs_full_thread to call the user_callback for each
2158  * message that has been read
2159  */
2160 static gboolean
2161 notify_get_msgs_full (gpointer data)
2162 {
2163         NotifyGetMsgsInfo *info;
2164
2165         info = (NotifyGetMsgsInfo *) data;      
2166
2167         /* This is a GDK lock because we are an idle callback and
2168          * because info->user_callback can contain Gtk+ code */
2169
2170         gdk_threads_enter (); /* CHECKED */
2171         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2172         gdk_threads_leave (); /* CHECKED */
2173
2174         g_slice_free (NotifyGetMsgsInfo, info);
2175
2176         return FALSE;
2177 }
2178
2179 /* 
2180  * Used by get_msgs_full_thread to free al the thread resources and to
2181  * call the destroy function for the passed user_data
2182  */
2183 static gboolean
2184 get_msgs_full_destroyer (gpointer data)
2185 {
2186         GetFullMsgsInfo *info;
2187
2188         info = (GetFullMsgsInfo *) data;
2189
2190         if (info->notify) {
2191
2192                 /* This is a GDK lock because we are an idle callback and
2193                  * because info->notify can contain Gtk+ code */
2194
2195                 gdk_threads_enter (); /* CHECKED */
2196                 info->notify (info->user_data);
2197                 gdk_threads_leave (); /* CHECKED */
2198         }
2199
2200         /* free */
2201         g_object_unref (info->headers);
2202         g_slice_free (GetFullMsgsInfo, info);
2203
2204         return FALSE;
2205 }
2206
2207 static gpointer
2208 get_msgs_full_thread (gpointer thr_user_data)
2209 {
2210         GetFullMsgsInfo *info;
2211         ModestMailOperationPrivate *priv = NULL;
2212         TnyIterator *iter = NULL;
2213         
2214         info = (GetFullMsgsInfo *) thr_user_data;       
2215         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2216
2217         iter = tny_list_create_iterator (info->headers);
2218         while (!tny_iterator_is_done (iter)) { 
2219                 TnyHeader *header;
2220                 TnyFolder *folder;
2221                 
2222                 header = TNY_HEADER (tny_iterator_get_current (iter));
2223                 folder = tny_header_get_folder (header);
2224                                 
2225                 /* Check for cached messages */
2226                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2227                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2228                 else 
2229                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2230
2231                 /* Get message from folder */
2232                 if (folder) {
2233                         TnyMsg *msg;
2234                         /* The callback will call it per each header */
2235                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2236
2237                         if (msg) {
2238                                 ModestMailOperationState *state;
2239                                 ModestPair *pair;
2240
2241                                 priv->done++;
2242
2243                                 /* notify progress */
2244                                 state = modest_mail_operation_clone_state (info->mail_op);
2245                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2246                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2247                                                  pair, (GDestroyNotify) modest_pair_free);
2248
2249                                 /* The callback is the responsible for
2250                                    freeing the message */
2251                                 if (info->user_callback) {
2252                                         NotifyGetMsgsInfo *info_notify;
2253                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2254                                         info_notify->user_callback = info->user_callback;
2255                                         info_notify->mail_op = info->mail_op;
2256                                         info_notify->header = g_object_ref (header);
2257                                         info_notify->msg = g_object_ref (msg);
2258                                         info_notify->user_data = info->user_data;
2259                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2260                                                          notify_get_msgs_full, 
2261                                                          info_notify, NULL);
2262                                 }
2263                                 g_object_unref (msg);
2264                         } 
2265                 } else {
2266                         /* Set status failed and set an error */
2267                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2268                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2269                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2270                                      "Error trying to get a message. No folder found for header");
2271                 }
2272
2273                 if (header)
2274                         g_object_unref (header);
2275                 
2276                 tny_iterator_next (iter);
2277         }
2278
2279         /* Set operation status */
2280         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2281                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2282
2283         /* Notify about operation end */
2284         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2285
2286         /* Free thread resources. Will be called after all previous idles */
2287         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2288
2289         return NULL;
2290 }
2291
2292 void 
2293 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2294                                      TnyList *header_list, 
2295                                      GetMsgAsyncUserCallback user_callback,
2296                                      gpointer user_data,
2297                                      GDestroyNotify notify)
2298 {
2299         TnyHeader *header = NULL;
2300         TnyFolder *folder = NULL;
2301         GThread *thread;
2302         ModestMailOperationPrivate *priv = NULL;
2303         GetFullMsgsInfo *info = NULL;
2304         gboolean size_ok = TRUE;
2305         gint max_size;
2306         TnyIterator *iter = NULL;
2307         
2308         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2309         
2310         /* Init mail operation */
2311         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2312         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2313         priv->done = 0;
2314         priv->total = tny_list_get_length(header_list);
2315
2316         /* Get account and set it into mail_operation */
2317         if (tny_list_get_length (header_list) >= 1) {
2318                 iter = tny_list_create_iterator (header_list);
2319                 header = TNY_HEADER (tny_iterator_get_current (iter));
2320                 if (header) {
2321                         folder = tny_header_get_folder (header);
2322                         if (folder) {           
2323                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2324
2325                                 g_object_unref (folder);
2326                         }
2327
2328                         g_object_unref (header);
2329                 }
2330
2331                 if (tny_list_get_length (header_list) == 1) {
2332                         g_object_unref (iter);
2333                         iter = NULL;
2334                 }
2335         }
2336
2337         /* Get msg size limit */
2338         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2339                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2340                                          &(priv->error));
2341         if (priv->error) {
2342                 g_clear_error (&(priv->error));
2343                 max_size = G_MAXINT;
2344         } else {
2345                 max_size = max_size * KB;
2346         }
2347
2348         /* Check message size limits. If there is only one message
2349            always retrieve it */
2350         if (iter != NULL) {
2351                 while (!tny_iterator_is_done (iter) && size_ok) {
2352                         header = TNY_HEADER (tny_iterator_get_current (iter));
2353                         if (header) {
2354                                 if (tny_header_get_message_size (header) >= max_size)
2355                                         size_ok = FALSE;
2356                                 g_object_unref (header);
2357                         }
2358
2359                         tny_iterator_next (iter);
2360                 }
2361                 g_object_unref (iter);
2362         }
2363
2364         if (size_ok) {
2365                 /* Create the info */
2366                 info = g_slice_new0 (GetFullMsgsInfo);
2367                 info->mail_op = self;
2368                 info->user_callback = user_callback;
2369                 info->user_data = user_data;
2370                 info->headers = g_object_ref (header_list);
2371                 info->notify = notify;
2372
2373                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2374         } else {
2375                 /* Set status failed and set an error */
2376                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2377                 /* FIXME: the error msg is different for pop */
2378                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2379                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2380                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2381                 /* Remove from queue and free resources */
2382                 modest_mail_operation_notify_end (self);
2383                 if (notify)
2384                         notify (user_data);
2385         }
2386 }
2387
2388
2389 void 
2390 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
2391                                   gboolean remove_to_trash /*ignored*/)
2392 {
2393         TnyFolder *folder;
2394         ModestMailOperationPrivate *priv;
2395
2396         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2397         g_return_if_fail (TNY_IS_HEADER (header));
2398
2399         if (remove_to_trash)
2400                 g_warning ("remove to trash is not implemented");
2401
2402         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2403         folder = tny_header_get_folder (header);
2404
2405         /* Get account and set it into mail_operation */
2406         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2407
2408         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2409
2410         /* remove message from folder */
2411         tny_folder_remove_msg (folder, header, &(priv->error));
2412 /*      if (!priv->error) { */
2413 /*              tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
2414 /*              tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
2415
2416 /*              if (TNY_IS_CAMEL_IMAP_FOLDER (folder)) */
2417 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2418 /* /\*                  tny_folder_sync (folder, FALSE, &(priv->error)); /\\* FALSE --> don't expunge *\\/ *\/ */
2419 /*              else if (TNY_IS_CAMEL_POP_FOLDER (folder)) */
2420 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2421 /* /\*                  tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2422 /*              else */
2423 /*                      /\* local folders *\/ */
2424 /*                      tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2425 /* /\*                  tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2426 /*      } */
2427         
2428         
2429         /* Set status */
2430         if (!priv->error)
2431                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2432         else
2433                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2434
2435         /* Free */
2436         g_object_unref (G_OBJECT (folder));
2437
2438         /* Notify about operation end */
2439         modest_mail_operation_notify_end (self);
2440 }
2441
2442 static void
2443 transfer_msgs_status_cb (GObject *obj,
2444                          TnyStatus *status,  
2445                          gpointer user_data)
2446 {
2447         XFerMsgAsyncHelper *helper = NULL;
2448         ModestMailOperation *self;
2449         ModestMailOperationPrivate *priv;
2450         ModestMailOperationState *state;
2451
2452
2453         g_return_if_fail (status != NULL);
2454         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2455
2456         helper = (XFerMsgAsyncHelper *) user_data;
2457         g_return_if_fail (helper != NULL);       
2458
2459         self = helper->mail_op;
2460         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2461
2462         priv->done = status->position;
2463         priv->total = status->of_total;
2464
2465         state = modest_mail_operation_clone_state (self);
2466
2467         /* This is not a GDK lock because we are a Tinymail callback and
2468          * Tinymail already acquires the Gdk lock */
2469
2470         /* no gdk_threads_enter (), CHECKED */
2471
2472         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2473
2474         /* no gdk_threads_leave (), CHECKED */
2475
2476         g_slice_free (ModestMailOperationState, state);
2477 }
2478
2479
2480 static void
2481 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2482 {
2483         XFerMsgAsyncHelper *helper;
2484         ModestMailOperation *self;
2485         ModestMailOperationPrivate *priv;
2486         TnyIterator *iter = NULL;
2487         TnyHeader *header = NULL;
2488
2489         helper = (XFerMsgAsyncHelper *) user_data;
2490         self = helper->mail_op;
2491
2492         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2493
2494         if (err) {
2495                 priv->error = g_error_copy (err);
2496                 priv->done = 0;
2497                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2498         } else if (cancelled) {
2499                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2500                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2501                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2502                              _("Error trying to refresh the contents of %s"),
2503                              tny_folder_get_name (folder));
2504         } else {
2505                 priv->done = 1;
2506                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2507         }
2508
2509         
2510         /* Mark headers as deleted and seen */
2511         if ((helper->delete) && 
2512             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2513                 iter = tny_list_create_iterator (helper->headers);
2514                 while (!tny_iterator_is_done (iter)) {
2515                         header = TNY_HEADER (tny_iterator_get_current (iter));
2516                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2517                         tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2518                         g_object_unref (header);
2519
2520                         tny_iterator_next (iter);
2521                 }
2522
2523         }
2524                 
2525
2526         /* Notify about operation end */
2527         modest_mail_operation_notify_end (self);
2528
2529         /* If user defined callback function was defined, call it */
2530         if (helper->user_callback) {
2531                 /* This is not a GDK lock because we are a Tinymail callback and
2532                  * Tinymail already acquires the Gdk lock */
2533
2534                 /* no gdk_threads_enter (), CHECKED */
2535                 helper->user_callback (priv->source, helper->user_data);
2536                 /* no gdk_threads_leave (), CHECKED */
2537         }
2538
2539         /* Free */
2540         if (helper->headers)
2541                 g_object_unref (helper->headers);
2542         if (helper->dest_folder)
2543                 g_object_unref (helper->dest_folder);
2544         if (helper->mail_op)
2545                 g_object_unref (helper->mail_op);
2546         if (folder)
2547                 g_object_unref (folder);
2548         if (iter)
2549                 g_object_unref (iter);
2550         g_slice_free   (XFerMsgAsyncHelper, helper);
2551
2552 }
2553
2554 void
2555 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2556                                  TnyList *headers, 
2557                                  TnyFolder *folder, 
2558                                  gboolean delete_original,
2559                                  XferMsgsAsynUserCallback user_callback,
2560                                  gpointer user_data)
2561 {
2562         ModestMailOperationPrivate *priv = NULL;
2563         TnyIterator *iter = NULL;
2564         TnyFolder *src_folder = NULL;
2565         XFerMsgAsyncHelper *helper = NULL;
2566         TnyHeader *header = NULL;
2567         ModestTnyFolderRules rules = 0;
2568         const gchar *id1 = NULL;
2569         const gchar *id2 = NULL;
2570         gboolean same_folder = FALSE;
2571
2572         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2573         g_return_if_fail (TNY_IS_LIST (headers));
2574         g_return_if_fail (TNY_IS_FOLDER (folder));
2575
2576         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2577         priv->total = 1;
2578         priv->done = 0;
2579         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2580
2581         /* Apply folder rules */
2582         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2583         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2584                 /* Set status failed and set an error */
2585                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2586                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2587                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2588                              _CS("ckct_ib_unable_to_paste_here"));
2589                 /* Notify the queue */
2590                 modest_mail_operation_notify_end (self);
2591                 return;
2592         }
2593                 
2594         /* Get source folder */
2595         iter = tny_list_create_iterator (headers);
2596         header = TNY_HEADER (tny_iterator_get_current (iter));
2597         if (header) {
2598                 src_folder = tny_header_get_folder (header);
2599                 g_object_unref (header);
2600         }
2601
2602         g_object_unref (iter);
2603
2604         /* Check folder source and destination */
2605         id1 = tny_folder_get_id (src_folder);
2606         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2607         same_folder = !g_ascii_strcasecmp (id1, id2);
2608         if (same_folder) {
2609                 /* Set status failed and set an error */
2610                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2611                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2612                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2613                              _("mcen_ib_unable_to_copy_samefolder"));
2614                 
2615                 /* Notify the queue */
2616                 modest_mail_operation_notify_end (self);
2617                 
2618                 /* Free */
2619                 g_object_unref (src_folder);            
2620                 return;
2621         }
2622
2623         /* Create the helper */
2624         helper = g_slice_new0 (XFerMsgAsyncHelper);
2625         helper->mail_op = g_object_ref(self);
2626         helper->dest_folder = g_object_ref(folder);
2627         helper->headers = g_object_ref(headers);
2628         helper->user_callback = user_callback;
2629         helper->user_data = user_data;
2630         helper->delete = delete_original;
2631
2632         /* Get account and set it into mail_operation */
2633         priv->account = modest_tny_folder_get_account (src_folder);
2634
2635         /* Transfer messages */
2636         tny_folder_transfer_msgs_async (src_folder, 
2637                                         headers, 
2638                                         folder, 
2639                                         delete_original, 
2640                                         transfer_msgs_cb, 
2641                                         transfer_msgs_status_cb,
2642                                         helper);
2643 }
2644
2645
2646 static void
2647 on_refresh_folder (TnyFolder   *folder, 
2648                    gboolean     cancelled, 
2649                    GError     *error,
2650                    gpointer     user_data)
2651 {
2652         RefreshAsyncHelper *helper = NULL;
2653         ModestMailOperation *self = NULL;
2654         ModestMailOperationPrivate *priv = NULL;
2655
2656         helper = (RefreshAsyncHelper *) user_data;
2657         self = helper->mail_op;
2658         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2659
2660         g_return_if_fail(priv!=NULL);
2661
2662         if (error) {
2663                 priv->error = g_error_copy (error);
2664                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2665                 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2666                        error->message);
2667                 goto out;
2668         }
2669
2670         if (cancelled) {
2671                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2672                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2673                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2674                              _("Error trying to refresh the contents of %s"),
2675                              tny_folder_get_name (folder));
2676                 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2677                 goto out;
2678         }
2679
2680         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2681
2682         /* Call user defined callback, if it exists */
2683         if (helper->user_callback) {
2684
2685                 /* This is not a GDK lock because we are a Tinymail callback and
2686                  * Tinymail already acquires the Gdk lock */
2687
2688                 /* no gdk_threads_enter (), CHECKED */
2689                 helper->user_callback (self, folder, helper->user_data);
2690                 /* no gdk_threads_leave (), CHECKED */
2691         }
2692
2693  out:
2694         /* Free */
2695         g_slice_free   (RefreshAsyncHelper, helper);
2696
2697         /* Notify about operation end */
2698         modest_mail_operation_notify_end (self);
2699         g_object_unref(self);
2700 }
2701
2702 static void
2703 on_refresh_folder_status_update (GObject *obj,
2704                                  TnyStatus *status,
2705                                  gpointer user_data)
2706 {
2707         RefreshAsyncHelper *helper = NULL;
2708         ModestMailOperation *self = NULL;
2709         ModestMailOperationPrivate *priv = NULL;
2710         ModestMailOperationState *state;
2711
2712         g_return_if_fail (user_data != NULL);
2713         g_return_if_fail (status != NULL);
2714         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2715
2716         helper = (RefreshAsyncHelper *) user_data;
2717         self = helper->mail_op;
2718         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2719
2720         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2721
2722         priv->done = status->position;
2723         priv->total = status->of_total;
2724
2725         state = modest_mail_operation_clone_state (self);
2726
2727         /* This is not a GDK lock because we are a Tinymail callback and
2728          * Tinymail already acquires the Gdk lock */
2729
2730         /* no gdk_threads_enter (), CHECKED */
2731
2732         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2733
2734         /* no gdk_threads_leave (), CHECKED */
2735
2736         g_slice_free (ModestMailOperationState, state);
2737 }
2738
2739 void 
2740 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2741                                        TnyFolder *folder,
2742                                        RefreshAsyncUserCallback user_callback,
2743                                        gpointer user_data)
2744 {
2745         ModestMailOperationPrivate *priv = NULL;
2746         RefreshAsyncHelper *helper = NULL;
2747
2748         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2749
2750         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2751
2752         /* Get account and set it into mail_operation */
2753         priv->account = modest_tny_folder_get_account  (folder);
2754
2755         /* Create the helper */
2756         helper = g_slice_new0 (RefreshAsyncHelper);
2757         helper->mail_op = g_object_ref(self);
2758         helper->user_callback = user_callback;
2759         helper->user_data = user_data;
2760
2761         /* Refresh the folder. TODO: tinymail could issue a status
2762            updates before the callback call then this could happen. We
2763            must review the design */
2764         tny_folder_refresh_async (folder,
2765                                   on_refresh_folder,
2766                                   on_refresh_folder_status_update,
2767                                   helper);
2768 }
2769
2770 /**
2771  *
2772  * It's used by the mail operation queue to notify the observers
2773  * attached to that signal that the operation finished. We need to use
2774  * that because tinymail does not give us the progress of a given
2775  * operation when it finishes (it directly calls the operation
2776  * callback).
2777  */
2778 static void
2779 modest_mail_operation_notify_end (ModestMailOperation *self)
2780 {
2781         ModestMailOperationState *state;
2782         ModestMailOperationPrivate *priv = NULL;
2783
2784         g_return_if_fail (self);
2785
2786         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2787
2788         /* Set the account back to not busy */
2789         if (priv->account_name) {
2790                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2791                                                      priv->account_name, FALSE);
2792                 g_free(priv->account_name);
2793                 priv->account_name = NULL;
2794         }
2795         
2796         /* Notify the observers about the mail operation end */
2797         /* We do not wrapp this emission because we assume that this
2798            function is always called from within the main lock */
2799         state = modest_mail_operation_clone_state (self);
2800         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2801         g_slice_free (ModestMailOperationState, state);
2802 }