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