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