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