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