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