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