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