* let the folder view honour the modest-folder-rules, for drag&drop of
[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 ((gpointer) parent == (gpointer) folder ||
1774             (!TNY_IS_FOLDER_STORE (parent)) || 
1775             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1776                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1777                 /* Set status failed and set an error */
1778                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1779                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1780                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1781                              _("mail_in_ui_folder_move_target_error"));
1782
1783                 /* Notify the queue */
1784                 modest_mail_operation_notify_end (self);
1785         } else if (TNY_IS_FOLDER (parent) && 
1786                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1787                 /* Set status failed and set an error */
1788                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1789                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1790                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1791                              _("FIXME: parent folder does not accept new folders"));
1792
1793                 /* Notify the queue */
1794                 modest_mail_operation_notify_end (self);
1795         } else {
1796
1797
1798                 /* Check that the new folder name is not used by any
1799                    special local folder */
1800                 if (new_name_valid_if_local_account (priv, parent, 
1801                                                      tny_folder_get_name (folder))) {
1802                         /* Create the helper */
1803                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1804                         helper->mail_op = g_object_ref(self);
1805                         helper->dest_folder = NULL;
1806                         helper->headers = NULL;
1807                         helper->user_callback = user_callback;
1808                         helper->user_data = user_data;
1809                         
1810                         /* Move/Copy folder */          
1811                         tny_folder_copy_async (folder,
1812                                                parent,
1813                                                tny_folder_get_name (folder),
1814                                                delete_original,
1815                                                transfer_folder_cb,
1816                                                transfer_folder_status_cb,
1817                                                helper);
1818                 } else {
1819                         modest_mail_operation_notify_end (self);
1820                 }
1821         }
1822 }
1823
1824 void
1825 modest_mail_operation_rename_folder (ModestMailOperation *self,
1826                                      TnyFolder *folder,
1827                                      const gchar *name)
1828 {
1829         ModestMailOperationPrivate *priv;
1830         ModestTnyFolderRules rules;
1831         XFerMsgAsyncHelper *helper;
1832
1833         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1834         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1835         g_return_if_fail (name);
1836         
1837         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1838
1839         /* Get account and set it into mail_operation */
1840         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1841
1842         /* Check folder rules */
1843         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1844         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1845                 /* Set status failed and set an error */
1846                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1847                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1848                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1849                              _("FIXME: unable to rename"));
1850
1851                 /* Notify about operation end */
1852                 modest_mail_operation_notify_end (self);
1853         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1854                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1855                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1856                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1857                              _("FIXME: unable to rename"));
1858                 /* Notify about operation end */
1859                 modest_mail_operation_notify_end (self);
1860         } else {
1861                 TnyFolderStore *into;
1862
1863                 into = tny_folder_get_folder_store (folder);    
1864
1865                 /* Check that the new folder name is not used by any
1866                    special local folder */
1867                 if (new_name_valid_if_local_account (priv, into, name)) {
1868                         /* Create the helper */
1869                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1870                         helper->mail_op = g_object_ref(self);
1871                         helper->dest_folder = NULL;
1872                         helper->headers = NULL;
1873                         helper->user_callback = NULL;
1874                         helper->user_data = NULL;
1875                 
1876                         /* Rename. Camel handles folder subscription/unsubscription */
1877                         tny_folder_copy_async (folder, into, name, TRUE,
1878                                                transfer_folder_cb,
1879                                                transfer_folder_status_cb,
1880                                                helper);
1881                 } else {
1882                         modest_mail_operation_notify_end (self);
1883                 }
1884                 g_object_unref (into);
1885         }
1886 }
1887
1888 /* ******************************************************************* */
1889 /* **************************  MSG  ACTIONS  ************************* */
1890 /* ******************************************************************* */
1891
1892 void modest_mail_operation_get_msg (ModestMailOperation *self,
1893                                     TnyHeader *header,
1894                                     GetMsgAsyncUserCallback user_callback,
1895                                     gpointer user_data)
1896 {
1897         GetMsgAsyncHelper *helper = NULL;
1898         TnyFolder *folder;
1899         ModestMailOperationPrivate *priv;
1900         
1901         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1902         g_return_if_fail (TNY_IS_HEADER (header));
1903         
1904         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1905         folder = tny_header_get_folder (header);
1906
1907         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1908
1909         /* Get message from folder */
1910         if (folder) {
1911                 /* Get account and set it into mail_operation */
1912                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1913
1914                 helper = g_slice_new0 (GetMsgAsyncHelper);
1915                 helper->mail_op = self;
1916                 helper->user_callback = user_callback;
1917                 helper->user_data = user_data;
1918                 helper->header = g_object_ref (header);
1919
1920                 // The callback's reference so that the mail op is not
1921                 // finalized until the async operation is completed even if
1922                 // the user canceled the request meanwhile.
1923                 g_object_ref (G_OBJECT (helper->mail_op));
1924
1925                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1926
1927                 g_object_unref (G_OBJECT (folder));
1928         } else {
1929                 /* Set status failed and set an error */
1930                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1931                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1932                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1933                              _("Error trying to get a message. No folder found for header"));
1934
1935                 /* Notify the queue */
1936                 modest_mail_operation_notify_end (self);
1937         }
1938 }
1939
1940 static gboolean
1941 idle_get_mime_part_size_cb (gpointer userdata)
1942 {
1943         GetMimePartSizeInfo *idle_info;
1944
1945         idle_info = (GetMimePartSizeInfo *) userdata;
1946
1947         gdk_threads_enter ();
1948         idle_info->callback (idle_info->mail_op,
1949                              idle_info->size,
1950                              idle_info->userdata);
1951         gdk_threads_leave ();
1952
1953         g_object_unref (idle_info->mail_op);
1954         g_slice_free (GetMimePartSizeInfo, idle_info);
1955
1956         return FALSE;
1957 }
1958
1959 static gpointer
1960 get_mime_part_size_thread (gpointer thr_user_data)
1961 {
1962         GetMimePartSizeInfo *info;
1963         gchar read_buffer[GET_SIZE_BUFFER_SIZE];
1964         TnyStream *stream;
1965         gssize readed_size;
1966         gssize total = 0;
1967         ModestMailOperationPrivate *priv;
1968
1969         info = (GetMimePartSizeInfo *) thr_user_data;
1970         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1971
1972         stream = tny_camel_mem_stream_new ();
1973         tny_mime_part_decode_to_stream (info->mime_part, stream);
1974         tny_stream_reset (stream);
1975         if (tny_stream_is_eos (stream)) {
1976                 tny_stream_close (stream);
1977                 stream = tny_mime_part_get_stream (info->mime_part);
1978         }
1979         
1980         while (!tny_stream_is_eos (stream)) {
1981                 readed_size = tny_stream_read (stream, read_buffer, GET_SIZE_BUFFER_SIZE);
1982                 total += readed_size;
1983         }
1984
1985         if (info->callback) {
1986                 GetMimePartSizeInfo *idle_info;
1987
1988                 idle_info = g_slice_new0 (GetMimePartSizeInfo);
1989                 idle_info->mail_op = g_object_ref (info->mail_op);
1990                 idle_info->size = total;
1991                 idle_info->callback = info->callback;
1992                 idle_info->userdata = info->userdata;
1993                 g_idle_add (idle_get_mime_part_size_cb, idle_info);
1994         }
1995
1996         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1997
1998         g_object_unref (info->mail_op);
1999         g_object_unref (stream);
2000         g_object_unref (info->mime_part);
2001         g_slice_free  (GetMimePartSizeInfo, info);
2002
2003         return NULL;
2004 }
2005
2006 void          
2007 modest_mail_operation_get_mime_part_size (ModestMailOperation *self,
2008                                           TnyMimePart *part,
2009                                           GetMimePartSizeCallback user_callback,
2010                                           gpointer user_data,
2011                                           GDestroyNotify notify)
2012 {
2013         GetMimePartSizeInfo *info;
2014         ModestMailOperationPrivate *priv;
2015         GThread *thread;
2016         
2017         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2018         g_return_if_fail (TNY_IS_MIME_PART (part));
2019         
2020         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2021
2022         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2023         info = g_slice_new0 (GetMimePartSizeInfo);
2024         info->mail_op = g_object_ref (self);
2025         info->mime_part = g_object_ref (part);
2026         info->callback = user_callback;
2027         info->userdata = user_data;
2028
2029         thread = g_thread_create (get_mime_part_size_thread, info, FALSE, NULL);
2030
2031 }
2032
2033 static void
2034 get_msg_cb (TnyFolder *folder, 
2035             gboolean cancelled, 
2036             TnyMsg *msg, 
2037             GError **error, 
2038             gpointer user_data)
2039 {
2040         GetMsgAsyncHelper *helper = NULL;
2041         ModestMailOperation *self = NULL;
2042         ModestMailOperationPrivate *priv = NULL;
2043
2044         helper = (GetMsgAsyncHelper *) user_data;
2045         g_return_if_fail (helper != NULL);       
2046         self = helper->mail_op;
2047         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2048         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2049
2050         /* Check errors and cancel */
2051         if (*error) {
2052                 priv->error = g_error_copy (*error);
2053                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2054         } else if (cancelled) {
2055                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2056                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2057                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2058                              _("Error trying to refresh the contents of %s"),
2059                              tny_folder_get_name (folder));
2060         } else {
2061                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2062         }
2063
2064         /* If user defined callback function was defined, call it even
2065            if the operation failed*/
2066         if (helper->user_callback) {
2067                 /* This callback is called into an iddle by tinymail,
2068                    and idles are not in the main lock */
2069                 gdk_threads_enter ();
2070                 helper->user_callback (self, helper->header, msg, helper->user_data);
2071                 gdk_threads_leave ();   
2072         }
2073
2074         /* Notify about operation end */
2075         modest_mail_operation_notify_end (self);
2076         /* Free */
2077         g_object_unref (helper->mail_op);
2078         g_object_unref (helper->header);
2079         g_slice_free (GetMsgAsyncHelper, helper);
2080                 
2081 }
2082
2083 static void     
2084 get_msg_status_cb (GObject *obj,
2085                    TnyStatus *status,  
2086                    gpointer user_data)
2087 {
2088         GetMsgAsyncHelper *helper = NULL;
2089         ModestMailOperation *self;
2090         ModestMailOperationPrivate *priv;
2091         ModestMailOperationState *state;
2092
2093         g_return_if_fail (status != NULL);
2094         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2095
2096         helper = (GetMsgAsyncHelper *) user_data;
2097         g_return_if_fail (helper != NULL);       
2098
2099         self = helper->mail_op;
2100         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2101
2102         priv->done = 1;
2103         priv->total = 1;
2104
2105         state = modest_mail_operation_clone_state (self);
2106         state->bytes_done = status->position;
2107         state->bytes_total = status->of_total;
2108 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2109         gdk_threads_enter ();
2110 #endif
2111         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2112 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2113         gdk_threads_leave ();
2114 #endif
2115         g_slice_free (ModestMailOperationState, state);
2116 }
2117
2118 /****************************************************/
2119 typedef struct {
2120         ModestMailOperation *mail_op;
2121         TnyList *headers;
2122         GetMsgAsyncUserCallback user_callback;
2123         gpointer user_data;
2124         GDestroyNotify notify;
2125 } GetFullMsgsInfo;
2126
2127 typedef struct {
2128         GetMsgAsyncUserCallback user_callback;
2129         TnyHeader *header;
2130         TnyMsg *msg;
2131         gpointer user_data;
2132         ModestMailOperation *mail_op;
2133 } NotifyGetMsgsInfo;
2134
2135
2136 /* 
2137  * Used by get_msgs_full_thread to call the user_callback for each
2138  * message that has been read
2139  */
2140 static gboolean
2141 notify_get_msgs_full (gpointer data)
2142 {
2143         NotifyGetMsgsInfo *info;
2144
2145         info = (NotifyGetMsgsInfo *) data;      
2146
2147         /* Call the user callback. Idles are not in the main lock, so
2148            lock it */
2149         gdk_threads_enter ();
2150         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2151         gdk_threads_leave ();
2152
2153         g_slice_free (NotifyGetMsgsInfo, info);
2154
2155         return FALSE;
2156 }
2157
2158 /* 
2159  * Used by get_msgs_full_thread to free al the thread resources and to
2160  * call the destroy function for the passed user_data
2161  */
2162 static gboolean
2163 get_msgs_full_destroyer (gpointer data)
2164 {
2165         GetFullMsgsInfo *info;
2166
2167         info = (GetFullMsgsInfo *) data;
2168
2169         if (info->notify) {
2170                 gdk_threads_enter ();   
2171                 info->notify (info->user_data);
2172                 gdk_threads_leave ();
2173         }
2174
2175         /* free */
2176         g_object_unref (info->headers);
2177         g_slice_free (GetFullMsgsInfo, info);
2178
2179         return FALSE;
2180 }
2181
2182 static gpointer
2183 get_msgs_full_thread (gpointer thr_user_data)
2184 {
2185         GetFullMsgsInfo *info;
2186         ModestMailOperationPrivate *priv = NULL;
2187         TnyIterator *iter = NULL;
2188         
2189         info = (GetFullMsgsInfo *) thr_user_data;       
2190         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2191
2192         iter = tny_list_create_iterator (info->headers);
2193         while (!tny_iterator_is_done (iter)) { 
2194                 TnyHeader *header;
2195                 TnyFolder *folder;
2196                 
2197                 header = TNY_HEADER (tny_iterator_get_current (iter));
2198                 folder = tny_header_get_folder (header);
2199                                 
2200                 /* Get message from folder */
2201                 if (folder) {
2202                         TnyMsg *msg;
2203                         /* The callback will call it per each header */
2204                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2205
2206                         if (msg) {
2207                                 ModestMailOperationState *state;
2208                                 ModestPair *pair;
2209
2210                                 priv->done++;
2211
2212                                 /* notify progress */
2213                                 state = modest_mail_operation_clone_state (info->mail_op);
2214                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2215                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2216                                                  pair, (GDestroyNotify) modest_pair_free);
2217
2218                                 /* The callback is the responsible for
2219                                    freeing the message */
2220                                 if (info->user_callback) {
2221                                         NotifyGetMsgsInfo *info_notify;
2222                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2223                                         info_notify->user_callback = info->user_callback;
2224                                         info_notify->mail_op = info->mail_op;
2225                                         info_notify->header = g_object_ref (header);
2226                                         info_notify->msg = g_object_ref (msg);
2227                                         info_notify->user_data = info->user_data;
2228                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2229                                                          notify_get_msgs_full, 
2230                                                          info_notify, NULL);
2231                                 }
2232                                 g_object_unref (msg);
2233                         } 
2234                 } else {
2235                         /* Set status failed and set an error */
2236                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2237                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2238                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2239                                      "Error trying to get a message. No folder found for header");
2240                 }
2241
2242                 if (header)
2243                         g_object_unref (header);
2244                 
2245                 tny_iterator_next (iter);
2246         }
2247
2248         /* Set operation status */
2249         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2250                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2251
2252         /* Notify about operation end */
2253         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2254
2255         /* Free thread resources. Will be called after all previous idles */
2256         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2257
2258         return NULL;
2259 }
2260
2261 void 
2262 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2263                                      TnyList *header_list, 
2264                                      GetMsgAsyncUserCallback user_callback,
2265                                      gpointer user_data,
2266                                      GDestroyNotify notify)
2267 {
2268         TnyHeader *header = NULL;
2269         TnyFolder *folder = NULL;
2270         GThread *thread;
2271         ModestMailOperationPrivate *priv = NULL;
2272         GetFullMsgsInfo *info = NULL;
2273         gboolean size_ok = TRUE;
2274         gint max_size;
2275         TnyIterator *iter = NULL;
2276         
2277         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2278         
2279         /* Init mail operation */
2280         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2281         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2282         priv->done = 0;
2283         priv->total = tny_list_get_length(header_list);
2284
2285         /* Get account and set it into mail_operation */
2286         if (tny_list_get_length (header_list) >= 1) {
2287                 iter = tny_list_create_iterator (header_list);
2288                 header = TNY_HEADER (tny_iterator_get_current (iter));
2289                 if (header) {
2290                         folder = tny_header_get_folder (header);
2291                         if (folder) {           
2292                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2293
2294                                 g_object_unref (folder);
2295                         }
2296
2297                         g_object_unref (header);
2298                 }
2299
2300                 if (tny_list_get_length (header_list) == 1) {
2301                         g_object_unref (iter);
2302                         iter = NULL;
2303                 }
2304         }
2305
2306         /* Get msg size limit */
2307         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2308                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2309                                          &(priv->error));
2310         if (priv->error) {
2311                 g_clear_error (&(priv->error));
2312                 max_size = G_MAXINT;
2313         } else {
2314                 max_size = max_size * KB;
2315         }
2316
2317         /* Check message size limits. If there is only one message
2318            always retrieve it */
2319         if (iter != NULL) {
2320                 while (!tny_iterator_is_done (iter) && size_ok) {
2321                         header = TNY_HEADER (tny_iterator_get_current (iter));
2322                         if (header) {
2323                                 if (tny_header_get_message_size (header) >= max_size)
2324                                         size_ok = FALSE;
2325                                 g_object_unref (header);
2326                         }
2327
2328                         tny_iterator_next (iter);
2329                 }
2330                 g_object_unref (iter);
2331         }
2332
2333         if (size_ok) {
2334                 /* Create the info */
2335                 info = g_slice_new0 (GetFullMsgsInfo);
2336                 info->mail_op = self;
2337                 info->user_callback = user_callback;
2338                 info->user_data = user_data;
2339                 info->headers = g_object_ref (header_list);
2340                 info->notify = notify;
2341
2342                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2343         } else {
2344                 /* Set status failed and set an error */
2345                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2346                 /* FIXME: the error msg is different for pop */
2347                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2348                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2349                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2350                 /* Remove from queue and free resources */
2351                 modest_mail_operation_notify_end (self);
2352                 if (notify)
2353                         notify (user_data);
2354         }
2355 }
2356
2357
2358 void 
2359 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
2360                                   gboolean remove_to_trash /*ignored*/)
2361 {
2362         TnyFolder *folder;
2363         ModestMailOperationPrivate *priv;
2364
2365         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2366         g_return_if_fail (TNY_IS_HEADER (header));
2367
2368         if (remove_to_trash)
2369                 g_warning ("remove to trash is not implemented");
2370
2371         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2372         folder = tny_header_get_folder (header);
2373
2374         /* Get account and set it into mail_operation */
2375         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2376
2377         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2378
2379
2380         tny_folder_remove_msg (folder, header, &(priv->error));
2381         if (!priv->error) {
2382                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2383                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2384
2385                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2386                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2387                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2388                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2389                 else
2390                         /* lcoal folders */
2391                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2392         }
2393         
2394         
2395         /* Set status */
2396         if (!priv->error)
2397                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2398         else
2399                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2400
2401         /* Free */
2402         g_object_unref (G_OBJECT (folder));
2403
2404         /* Notify about operation end */
2405         modest_mail_operation_notify_end (self);
2406 }
2407
2408 static void
2409 transfer_msgs_status_cb (GObject *obj,
2410                          TnyStatus *status,  
2411                          gpointer user_data)
2412 {
2413         XFerMsgAsyncHelper *helper = NULL;
2414         ModestMailOperation *self;
2415         ModestMailOperationPrivate *priv;
2416         ModestMailOperationState *state;
2417
2418
2419         g_return_if_fail (status != NULL);
2420         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2421
2422         helper = (XFerMsgAsyncHelper *) user_data;
2423         g_return_if_fail (helper != NULL);       
2424
2425         self = helper->mail_op;
2426         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2427
2428         priv->done = status->position;
2429         priv->total = status->of_total;
2430
2431         state = modest_mail_operation_clone_state (self);
2432 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2433         gdk_threads_enter ();
2434 #endif
2435         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2436 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2437         gdk_threads_leave ();
2438 #endif
2439         g_slice_free (ModestMailOperationState, state);
2440 }
2441
2442
2443 static void
2444 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2445 {
2446         XFerMsgAsyncHelper *helper;
2447         ModestMailOperation *self;
2448         ModestMailOperationPrivate *priv;
2449
2450         helper = (XFerMsgAsyncHelper *) user_data;
2451         self = helper->mail_op;
2452
2453         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2454
2455         if (*err) {
2456                 priv->error = g_error_copy (*err);
2457                 priv->done = 0;
2458                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2459         } else if (cancelled) {
2460                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2461                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2462                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2463                              _("Error trying to refresh the contents of %s"),
2464                              tny_folder_get_name (folder));
2465         } else {
2466                 priv->done = 1;
2467                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2468         }
2469
2470         /* Notify about operation end */
2471         modest_mail_operation_notify_end (self);
2472
2473         /* If user defined callback function was defined, call it */
2474         if (helper->user_callback) {
2475                 gdk_threads_enter ();
2476                 helper->user_callback (priv->source, helper->user_data);
2477                 gdk_threads_leave ();
2478         }
2479
2480         /* Free */
2481         g_object_unref (helper->headers);
2482         g_object_unref (helper->dest_folder);
2483         g_object_unref (helper->mail_op);
2484         g_slice_free   (XFerMsgAsyncHelper, helper);
2485         g_object_unref (folder);
2486
2487 }
2488
2489 void
2490 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2491                                  TnyList *headers, 
2492                                  TnyFolder *folder, 
2493                                  gboolean delete_original,
2494                                  XferMsgsAsynUserCallback user_callback,
2495                                  gpointer user_data)
2496 {
2497         ModestMailOperationPrivate *priv = NULL;
2498         TnyIterator *iter = NULL;
2499         TnyFolder *src_folder = NULL;
2500         XFerMsgAsyncHelper *helper = NULL;
2501         TnyHeader *header = NULL;
2502         ModestTnyFolderRules rules = 0;
2503         const gchar *id1 = NULL;
2504         const gchar *id2 = NULL;
2505         gboolean same_folder = FALSE;
2506
2507         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2508         g_return_if_fail (TNY_IS_LIST (headers));
2509         g_return_if_fail (TNY_IS_FOLDER (folder));
2510
2511         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2512         priv->total = 1;
2513         priv->done = 0;
2514         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2515
2516         /* Apply folder rules */
2517         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2518         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2519                 /* Set status failed and set an error */
2520                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2521                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2522                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2523                              _CS("ckct_ib_unable_to_paste_here"));
2524                 /* Notify the queue */
2525                 modest_mail_operation_notify_end (self);
2526                 return;
2527         }
2528                 
2529         /* Get source folder */
2530         iter = tny_list_create_iterator (headers);
2531         header = TNY_HEADER (tny_iterator_get_current (iter));
2532         if (header) {
2533                 src_folder = tny_header_get_folder (header);
2534                 g_object_unref (header);
2535         }
2536
2537         g_object_unref (iter);
2538
2539         /* Check folder source and destination */
2540         id1 = tny_folder_get_id (src_folder);
2541         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2542         same_folder = !g_ascii_strcasecmp (id1, id2);
2543         if (same_folder) {
2544                 /* Set status failed and set an error */
2545                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2546                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2547                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2548                              _("mcen_ib_unable_to_copy_samefolder"));
2549                 
2550                 /* Notify the queue */
2551                 modest_mail_operation_notify_end (self);
2552                 
2553                 /* Free */
2554                 g_object_unref (src_folder);            
2555                 return;
2556         }
2557
2558         /* Create the helper */
2559         helper = g_slice_new0 (XFerMsgAsyncHelper);
2560         helper->mail_op = g_object_ref(self);
2561         helper->dest_folder = g_object_ref(folder);
2562         helper->headers = g_object_ref(headers);
2563         helper->user_callback = user_callback;
2564         helper->user_data = user_data;
2565
2566         /* Get account and set it into mail_operation */
2567         priv->account = modest_tny_folder_get_account (src_folder);
2568
2569         /* Transfer messages */
2570         tny_folder_transfer_msgs_async (src_folder, 
2571                                         headers, 
2572                                         folder, 
2573                                         delete_original, 
2574                                         transfer_msgs_cb, 
2575                                         transfer_msgs_status_cb,
2576                                         helper);
2577 }
2578
2579
2580 static void
2581 on_refresh_folder (TnyFolder   *folder, 
2582                    gboolean     cancelled, 
2583                    GError     **error,
2584                    gpointer     user_data)
2585 {
2586         RefreshAsyncHelper *helper = NULL;
2587         ModestMailOperation *self = NULL;
2588         ModestMailOperationPrivate *priv = NULL;
2589
2590         helper = (RefreshAsyncHelper *) user_data;
2591         self = helper->mail_op;
2592         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2593
2594         if (*error) {
2595                 priv->error = g_error_copy (*error);
2596                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2597                 goto out;
2598         }
2599
2600         if (cancelled) {
2601                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2602                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2603                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2604                              _("Error trying to refresh the contents of %s"),
2605                              tny_folder_get_name (folder));
2606                 goto out;
2607         }
2608
2609         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2610  out:
2611         /* Call user defined callback, if it exists */
2612         if (helper->user_callback) {
2613                 gdk_threads_enter ();
2614                 helper->user_callback (self, folder, helper->user_data);
2615                 gdk_threads_leave ();
2616         }
2617
2618         /* Free */
2619 /*      g_object_unref (helper->mail_op); */
2620         g_slice_free   (RefreshAsyncHelper, helper);
2621
2622         /* Notify about operation end */
2623         modest_mail_operation_notify_end (self);
2624 }
2625
2626 static void
2627 on_refresh_folder_status_update (GObject *obj,
2628                                  TnyStatus *status,
2629                                  gpointer user_data)
2630 {
2631         RefreshAsyncHelper *helper = NULL;
2632         ModestMailOperation *self = NULL;
2633         ModestMailOperationPrivate *priv = NULL;
2634         ModestMailOperationState *state;
2635
2636         g_return_if_fail (user_data != NULL);
2637         g_return_if_fail (status != NULL);
2638         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2639
2640         helper = (RefreshAsyncHelper *) user_data;
2641         self = helper->mail_op;
2642         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2643
2644         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2645
2646         priv->done = status->position;
2647         priv->total = status->of_total;
2648
2649         state = modest_mail_operation_clone_state (self);
2650 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2651         gdk_threads_enter ();
2652 #endif
2653         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2654 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2655         gdk_threads_leave ();
2656 #endif
2657         g_slice_free (ModestMailOperationState, state);
2658 }
2659
2660 void 
2661 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2662                                        TnyFolder *folder,
2663                                        RefreshAsyncUserCallback user_callback,
2664                                        gpointer user_data)
2665 {
2666         ModestMailOperationPrivate *priv = NULL;
2667         RefreshAsyncHelper *helper = NULL;
2668
2669         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2670
2671         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2672
2673         /* Get account and set it into mail_operation */
2674         priv->account = modest_tny_folder_get_account  (folder);
2675
2676         /* Create the helper */
2677         helper = g_slice_new0 (RefreshAsyncHelper);
2678         helper->mail_op = g_object_ref (self);
2679         helper->user_callback = user_callback;
2680         helper->user_data = user_data;
2681
2682         /* Refresh the folder. TODO: tinymail could issue a status
2683            updates before the callback call then this could happen. We
2684            must review the design */
2685         tny_folder_refresh_async (folder,
2686                                   on_refresh_folder,
2687                                   on_refresh_folder_status_update,
2688                                   helper);
2689 }
2690
2691 /**
2692  *
2693  * It's used by the mail operation queue to notify the observers
2694  * attached to that signal that the operation finished. We need to use
2695  * that because tinymail does not give us the progress of a given
2696  * operation when it finishes (it directly calls the operation
2697  * callback).
2698  */
2699 static void
2700 modest_mail_operation_notify_end (ModestMailOperation *self)
2701 {
2702         ModestMailOperationState *state;
2703         ModestMailOperationPrivate *priv = NULL;
2704
2705         g_return_if_fail (self);
2706
2707         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2708
2709         /* Set the account back to not busy */
2710         if (priv->account_name) {
2711                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2712                                                      priv->account_name, FALSE);
2713                 g_free(priv->account_name);
2714                 priv->account_name = NULL;
2715         }
2716         
2717         /* Notify the observers about the mail operation end */
2718         /* We do not wrapp this emission because we assume that this
2719            function is always called from within the main lock */
2720         state = modest_mail_operation_clone_state (self);
2721         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2722         g_slice_free (ModestMailOperationState, state);
2723 }