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