* src/maemo/modest-msg-edit-window.c:
[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-simple-list.h>
41 #include <tny-send-queue.h>
42 #include <tny-status.h>
43 #include <tny-folder-observer.h>
44 #include <camel/camel-stream-mem.h>
45 #include <glib/gi18n.h>
46 #include "modest-platform.h"
47 #include <modest-tny-account.h>
48 #include <modest-tny-send-queue.h>
49 #include <modest-runtime.h>
50 #include "modest-text-utils.h"
51 #include "modest-tny-msg.h"
52 #include "modest-tny-folder.h"
53 #include "modest-tny-platform-factory.h"
54 #include "modest-marshal.h"
55 #include "modest-error.h"
56 #include "modest-mail-operation.h"
57
58 #define KB 1024
59
60 /* 'private'/'protected' functions */
61 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
62 static void modest_mail_operation_init       (ModestMailOperation *obj);
63 static void modest_mail_operation_finalize   (GObject *obj);
64
65 static void     get_msg_cb (TnyFolder *folder, 
66                             gboolean cancelled, 
67                             TnyMsg *msg, 
68                             GError **err, 
69                             gpointer user_data);
70
71 static void     get_msg_status_cb (GObject *obj,
72                                    TnyStatus *status,  
73                                    gpointer user_data);
74
75 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
76
77 static gboolean did_a_cancel = FALSE;
78
79 enum _ModestMailOperationSignals 
80 {
81         PROGRESS_CHANGED_SIGNAL,
82
83         NUM_SIGNALS
84 };
85
86 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
87 struct _ModestMailOperationPrivate {
88         TnyAccount                 *account;
89         gchar                                                                            *account_name;
90         guint                      done;
91         guint                      total;
92         GObject                   *source;
93         GError                    *error;
94         ErrorCheckingUserCallback  error_checking;
95         gpointer                   error_checking_user_data;
96         ModestMailOperationStatus  status;      
97         ModestMailOperationTypeOperation op_type;
98 };
99
100 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
101                                                    MODEST_TYPE_MAIL_OPERATION, \
102                                                    ModestMailOperationPrivate))
103
104 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
105                                                    priv->status = new_status;\
106                                                }
107
108 typedef struct _GetMsgAsyncHelper {     
109         ModestMailOperation *mail_op;
110         TnyHeader *header;
111         GetMsgAsyncUserCallback user_callback;  
112         gpointer user_data;
113 } GetMsgAsyncHelper;
114
115 typedef struct _RefreshAsyncHelper {    
116         ModestMailOperation *mail_op;
117         RefreshAsyncUserCallback user_callback; 
118         gpointer user_data;
119 } RefreshAsyncHelper;
120
121 typedef struct _XFerMsgAsyncHelper
122 {
123         ModestMailOperation *mail_op;
124         TnyList *headers;
125         TnyFolder *dest_folder;
126         XferMsgsAsynUserCallback user_callback; 
127         gpointer user_data;
128 } XFerMsgAsyncHelper;
129
130 /* globals */
131 static GObjectClass *parent_class = NULL;
132
133 static guint signals[NUM_SIGNALS] = {0};
134
135 GType
136 modest_mail_operation_get_type (void)
137 {
138         static GType my_type = 0;
139         if (!my_type) {
140                 static const GTypeInfo my_info = {
141                         sizeof(ModestMailOperationClass),
142                         NULL,           /* base init */
143                         NULL,           /* base finalize */
144                         (GClassInitFunc) modest_mail_operation_class_init,
145                         NULL,           /* class finalize */
146                         NULL,           /* class data */
147                         sizeof(ModestMailOperation),
148                         1,              /* n_preallocs */
149                         (GInstanceInitFunc) modest_mail_operation_init,
150                         NULL
151                 };
152                 my_type = g_type_register_static (G_TYPE_OBJECT,
153                                                   "ModestMailOperation",
154                                                   &my_info, 0);
155         }
156         return my_type;
157 }
158
159 static void
160 modest_mail_operation_class_init (ModestMailOperationClass *klass)
161 {
162         GObjectClass *gobject_class;
163         gobject_class = (GObjectClass*) klass;
164
165         parent_class            = g_type_class_peek_parent (klass);
166         gobject_class->finalize = modest_mail_operation_finalize;
167
168         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
169
170         /**
171          * ModestMailOperation::progress-changed
172          * @self: the #MailOperation that emits the signal
173          * @user_data: user data set when the signal handler was connected
174          *
175          * Emitted when the progress of a mail operation changes
176          */
177         signals[PROGRESS_CHANGED_SIGNAL] = 
178                 g_signal_new ("progress-changed",
179                               G_TYPE_FROM_CLASS (gobject_class),
180                               G_SIGNAL_RUN_FIRST,
181                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
182                               NULL, NULL,
183                               g_cclosure_marshal_VOID__POINTER,
184                               G_TYPE_NONE, 1, G_TYPE_POINTER);
185
186 }
187
188 static void
189 modest_mail_operation_init (ModestMailOperation *obj)
190 {
191         ModestMailOperationPrivate *priv;
192
193         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
194
195         priv->account        = NULL;
196         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
197         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
198         priv->error          = NULL;
199         priv->done           = 0;
200         priv->total          = 0;
201         priv->source         = NULL;
202         priv->error_checking = NULL;
203         priv->error_checking_user_data = NULL;
204 }
205
206 static void
207 modest_mail_operation_finalize (GObject *obj)
208 {
209         ModestMailOperationPrivate *priv;
210
211         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
212
213         
214         
215         if (priv->error) {
216                 g_error_free (priv->error);
217                 priv->error = NULL;
218         }
219         if (priv->source) {
220                 g_object_unref (priv->source);
221                 priv->source = NULL;
222         }
223         if (priv->account) {
224                 g_object_unref (priv->account);
225                 priv->account = NULL;
226         }
227
228
229         G_OBJECT_CLASS(parent_class)->finalize (obj);
230 }
231
232 ModestMailOperation*
233 modest_mail_operation_new (ModestMailOperationTypeOperation op_type, 
234                            GObject *source)
235 {
236         ModestMailOperation *obj;
237         ModestMailOperationPrivate *priv;
238                 
239         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
240         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
241
242         priv->op_type = op_type;
243         if (source != NULL)
244                 priv->source = g_object_ref(source);
245
246         return obj;
247 }
248
249 ModestMailOperation*
250 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
251                                                GObject *source,
252                                                ErrorCheckingUserCallback error_handler,
253                                                gpointer user_data)
254 {
255         ModestMailOperation *obj;
256         ModestMailOperationPrivate *priv;
257                 
258         obj = modest_mail_operation_new (op_type, source);
259         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
260         
261         g_return_val_if_fail (error_handler != NULL, obj);
262         priv->error_checking = error_handler;
263
264         return obj;
265 }
266
267 void
268 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
269 {
270         ModestMailOperationPrivate *priv;
271         
272         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
273         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
274
275         if (priv->error_checking != NULL)
276                 priv->error_checking (self, priv->error_checking_user_data);
277 }
278
279
280 ModestMailOperationTypeOperation
281 modest_mail_operation_get_type_operation (ModestMailOperation *self)
282 {
283         ModestMailOperationPrivate *priv;
284
285         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
286         
287         return priv->op_type;
288 }
289
290 gboolean 
291 modest_mail_operation_is_mine (ModestMailOperation *self, 
292                                GObject *me)
293 {
294         ModestMailOperationPrivate *priv;
295
296         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
297         if (priv->source == NULL) return FALSE;
298
299         return priv->source == me;
300 }
301
302 GObject *
303 modest_mail_operation_get_source (ModestMailOperation *self)
304 {
305         ModestMailOperationPrivate *priv;
306
307         g_return_val_if_fail (self, NULL);
308         
309         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
310         if (!priv) {
311                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
312                 return NULL;
313         }
314         
315         return g_object_ref (priv->source);
316 }
317
318 ModestMailOperationStatus
319 modest_mail_operation_get_status (ModestMailOperation *self)
320 {
321         ModestMailOperationPrivate *priv;
322
323         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
324         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
325                               MODEST_MAIL_OPERATION_STATUS_INVALID);
326
327         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
328         if (!priv) {
329                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
330                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
331         }
332         
333         return priv->status;
334 }
335
336 const GError *
337 modest_mail_operation_get_error (ModestMailOperation *self)
338 {
339         ModestMailOperationPrivate *priv;
340
341         g_return_val_if_fail (self, NULL);
342         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
343
344         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
345
346         if (!priv) {
347                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
348                 return NULL;
349         }
350
351         return priv->error;
352 }
353
354 gboolean 
355 modest_mail_operation_cancel (ModestMailOperation *self)
356 {
357         ModestMailOperationPrivate *priv;
358
359         if (!MODEST_IS_MAIL_OPERATION (self)) {
360                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
361                 return FALSE;
362         }
363
364         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
365         if (!priv) {
366                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
367                 return FALSE;
368         }
369
370         did_a_cancel = TRUE;
371
372         /* Set new status */
373         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
374
375         /* This emits progress-changed on which the mail operation queue is
376          * listening, so the mail operation is correctly removed from the
377          * queue without further explicit calls. */
378         modest_mail_operation_notify_end (self);
379         
380         return TRUE;
381 }
382
383 guint 
384 modest_mail_operation_get_task_done (ModestMailOperation *self)
385 {
386         ModestMailOperationPrivate *priv;
387
388         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
389
390         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
391         return priv->done;
392 }
393
394 guint 
395 modest_mail_operation_get_task_total (ModestMailOperation *self)
396 {
397         ModestMailOperationPrivate *priv;
398
399         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
400
401         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
402         return priv->total;
403 }
404
405 gboolean
406 modest_mail_operation_is_finished (ModestMailOperation *self)
407 {
408         ModestMailOperationPrivate *priv;
409         gboolean retval = FALSE;
410
411         if (!MODEST_IS_MAIL_OPERATION (self)) {
412                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
413                 return retval;
414         }
415
416         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
417
418         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
419             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
420             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
421             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
422                 retval = TRUE;
423         } else {
424                 retval = FALSE;
425         }
426
427         return retval;
428 }
429
430 guint
431 modest_mail_operation_get_id (ModestMailOperation *self)
432 {
433         ModestMailOperationPrivate *priv;
434
435         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
436
437         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
438         return priv->done;
439 }
440
441 guint 
442 modest_mail_operation_set_id (ModestMailOperation *self,
443                               guint id)
444 {
445         ModestMailOperationPrivate *priv;
446
447         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
448
449         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
450         return priv->done;
451 }
452
453 /*
454  * Creates an image of the current state of a mail operation, the
455  * caller must free it
456  */
457 static ModestMailOperationState *
458 modest_mail_operation_clone_state (ModestMailOperation *self)
459 {
460         ModestMailOperationState *state;
461         ModestMailOperationPrivate *priv;
462
463         /* FIXME: this should be fixed properly
464          * 
465          * in some cases, priv was NULL, so checking here to
466          * make sure.
467          */
468         g_return_val_if_fail (self, NULL);
469         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
470         g_return_val_if_fail (priv, NULL);
471
472         if (!priv)
473                 return NULL;
474
475         state = g_slice_new (ModestMailOperationState);
476
477         state->status = priv->status;
478         state->op_type = priv->op_type;
479         state->done = priv->done;
480         state->total = priv->total;
481         state->finished = modest_mail_operation_is_finished (self);
482         state->bytes_done = 0;
483         state->bytes_total = 0;
484
485         return state;
486 }
487
488 /* ******************************************************************* */
489 /* ************************** SEND   ACTIONS ************************* */
490 /* ******************************************************************* */
491
492 void
493 modest_mail_operation_send_mail (ModestMailOperation *self,
494                                  TnyTransportAccount *transport_account,
495                                  TnyMsg* msg)
496 {
497         TnySendQueue *send_queue = NULL;
498         ModestMailOperationPrivate *priv;
499         
500         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
501         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
502         g_return_if_fail (TNY_IS_MSG (msg));
503         
504         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
505
506         /* Get account and set it into mail_operation */
507         priv->account = g_object_ref (transport_account);
508         priv->done = 1;
509         priv->total = 1;
510
511         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
512         if (!TNY_IS_SEND_QUEUE(send_queue)) {
513                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
514                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
515                              "modest: could not find send queue for account\n");
516         } else {
517                 /* TODO: connect to the msg-sent in order to know when
518                    the mail operation is finished */
519                 tny_send_queue_add (send_queue, msg, &(priv->error));
520                 /* TODO: we're setting always success, do the check in
521                    the handler */
522                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
523         }
524
525         /* TODO: do this in the handler of the "msg-sent"
526            signal.Notify about operation end */
527         modest_mail_operation_notify_end (self);
528 }
529
530 void
531 modest_mail_operation_send_new_mail (ModestMailOperation *self,
532                                      TnyTransportAccount *transport_account,
533                                      TnyMsg *draft_msg,
534                                      const gchar *from,  const gchar *to,
535                                      const gchar *cc,  const gchar *bcc,
536                                      const gchar *subject, const gchar *plain_body,
537                                      const gchar *html_body,
538                                      const GList *attachments_list,
539                                      TnyHeaderFlags priority_flags)
540 {
541         TnyMsg *new_msg = NULL;
542         TnyFolder *folder = NULL;
543         TnyHeader *header = NULL;
544         ModestMailOperationPrivate *priv = NULL;
545
546         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
547         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
548
549         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
550
551         /* Check parametters */
552         if (to == NULL) {
553                 /* Set status failed and set an error */
554                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
555                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
556                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
557                              _("Error trying to send a mail. You need to set at least one recipient"));
558                 return;
559         }
560
561         if (html_body == NULL) {
562                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
563         } else {
564                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
565         }
566         if (!new_msg) {
567                 g_printerr ("modest: failed to create a new msg\n");
568                 return;
569         }
570
571         /* Set priority flags in message */
572         header = tny_msg_get_header (new_msg);
573         if (priority_flags != 0)
574                 tny_header_set_flags (header, priority_flags);
575         g_object_unref (G_OBJECT(header));
576
577         /* Call mail operation */
578         modest_mail_operation_send_mail (self, transport_account, new_msg);
579
580         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
581         if (folder) {
582                 if (draft_msg != NULL) {
583                         header = tny_msg_get_header (draft_msg);
584                         /* Note: This can fail (with a warning) if the message is not really already in a folder,
585                          * because this function requires it to have a UID. */
586                         tny_folder_remove_msg (folder, header, NULL);
587                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
588                         g_object_unref (header);
589                 }
590         }
591
592         /* Free */
593         g_object_unref (G_OBJECT (new_msg));
594 }
595
596 void
597 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
598                                       TnyTransportAccount *transport_account,
599                                       TnyMsg *draft_msg,
600                                       const gchar *from,  const gchar *to,
601                                       const gchar *cc,  const gchar *bcc,
602                                       const gchar *subject, const gchar *plain_body,
603                                       const gchar *html_body,
604                                       const GList *attachments_list,
605                                       TnyHeaderFlags priority_flags)
606 {
607         TnyMsg *msg = NULL;
608         TnyFolder *folder = NULL;
609         TnyHeader *header = NULL;
610         ModestMailOperationPrivate *priv = NULL;
611
612         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
613         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
614
615         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
616
617         /* Get account and set it into mail_operation */
618         priv->account = g_object_ref (transport_account);
619
620         if (html_body == NULL) {
621                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
622         } else {
623                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
624         }
625         if (!msg) {
626                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
627                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
628                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
629                              "modest: failed to create a new msg\n");
630                 goto end;
631         }
632
633         /* add priority flags */
634         header = tny_msg_get_header (msg);
635         tny_header_set_flags (header, priority_flags);
636         g_object_unref (G_OBJECT(header));
637
638         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
639         if (!folder) {
640                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
641                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
642                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
643                              "modest: failed to create a new msg\n");
644                 goto end;
645         }
646
647         if (draft_msg != NULL) {
648                 header = tny_msg_get_header (draft_msg);
649                 /* Remove the old draft expunging it */
650                 tny_folder_remove_msg (folder, header, NULL);
651                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
652                 tny_folder_sync (folder, FALSE, &(priv->error));  /* FALSE --> don't expunge */
653                 g_object_unref (header);
654         }
655         
656         if (!priv->error)
657                 tny_folder_add_msg (folder, msg, &(priv->error));
658
659         if (!priv->error)
660                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
661         else
662                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
663
664 end:
665         if (msg)
666                 g_object_unref (G_OBJECT(msg));
667         if (folder)
668                 g_object_unref (G_OBJECT(folder));
669
670         modest_mail_operation_notify_end (self);
671 }
672
673 typedef struct 
674 {
675         ModestMailOperation *mail_op;
676         TnyStoreAccount *account;
677         TnyTransportAccount *transport_account;
678         gint max_size;
679         gint retrieve_limit;
680         gchar *retrieve_type;
681         gchar *account_name;
682         UpdateAccountCallback callback;
683         gpointer user_data;
684         gint new_headers;
685 } UpdateAccountInfo;
686
687 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
688 /* We use this folder observer to track the headers that have been
689  * added to a folder */
690 typedef struct {
691         GObject parent;
692         TnyList *new_headers;
693 } InternalFolderObserver;
694
695 typedef struct {
696         GObjectClass parent;
697 } InternalFolderObserverClass;
698
699 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
700
701 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
702                          internal_folder_observer,
703                          G_TYPE_OBJECT,
704                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
705
706
707 static void
708 foreach_add_item (gpointer header, gpointer user_data)
709 {
710         /* printf("DEBUG: %s: header subject=%s\n", 
711          * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
712          */
713         tny_list_prepend (TNY_LIST (user_data), 
714                           g_object_ref (G_OBJECT (header)));
715 }
716
717 /* This is the method that looks for new messages in a folder */
718 static void
719 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
720 {
721         InternalFolderObserver *derived = (InternalFolderObserver *)self;
722         
723         TnyFolderChangeChanged changed;
724
725         changed = tny_folder_change_get_changed (change);
726
727         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
728                 TnyList *list;
729
730                 /* Get added headers */
731                 list = tny_simple_list_new ();
732                 tny_folder_change_get_added_headers (change, list);
733
734                 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n", 
735                  *      __FUNCTION__, tny_list_get_length(list));
736                  */
737                  
738                 /* Add them to the folder observer */
739                 tny_list_foreach (list, foreach_add_item, 
740                                   derived->new_headers);
741
742                 g_object_unref (G_OBJECT (list));
743         }
744 }
745
746 static void
747 internal_folder_observer_init (InternalFolderObserver *self) 
748 {
749         self->new_headers = tny_simple_list_new ();
750 }
751 static void
752 internal_folder_observer_finalize (GObject *object) 
753 {
754         InternalFolderObserver *self;
755
756         self = (InternalFolderObserver *) object;
757         g_object_unref (self->new_headers);
758
759         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
760 }
761 static void
762 tny_folder_observer_init (TnyFolderObserverIface *iface) 
763 {
764         iface->update_func = internal_folder_observer_update;
765 }
766 static void
767 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
768 {
769         GObjectClass *object_class;
770
771         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
772         object_class = (GObjectClass*) klass;
773         object_class->finalize = internal_folder_observer_finalize;
774 }
775
776 /*****************/
777
778 static void
779 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
780 {
781         TnyIterator *iter;
782         TnyList *folders = tny_simple_list_new ();
783
784         tny_folder_store_get_folders (store, folders, query, NULL);
785         iter = tny_list_create_iterator (folders);
786
787         while (!tny_iterator_is_done (iter)) {
788
789                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
790
791                 tny_list_prepend (all_folders, G_OBJECT (folder));
792                 recurse_folders (folder, query, all_folders);    
793                 g_object_unref (G_OBJECT (folder));
794
795                 tny_iterator_next (iter);
796         }
797          g_object_unref (G_OBJECT (iter));
798          g_object_unref (G_OBJECT (folders));
799 }
800
801 /* 
802  * Issues the "progress-changed" signal. The timer won't be removed,
803  * so you must call g_source_remove to stop the signal emission
804  */
805 static gboolean
806 idle_notify_progress (gpointer data)
807 {
808         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
809         ModestMailOperationState *state;
810
811         state = modest_mail_operation_clone_state (mail_op);
812         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
813         g_slice_free (ModestMailOperationState, state);
814         
815         return TRUE;
816 }
817
818 /* 
819  * Issues the "progress-changed" signal and removes the timer. It uses
820  * a lock to ensure that the progress information of the mail
821  * operation is not modified while there are notifications pending
822  */
823 static gboolean
824 idle_notify_progress_once (gpointer data)
825 {
826         ModestPair *pair;
827
828         pair = (ModestPair *) data;
829
830         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
831
832         /* Free the state and the reference to the mail operation */
833         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
834         g_object_unref (pair->first);
835
836         return FALSE;
837 }
838
839 /* 
840  * Used by update_account_thread to notify the queue from the main
841  * loop. We call it inside an idle call to achieve that
842  */
843 static gboolean
844 idle_notify_update_account_queue (gpointer data)
845 {
846         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
847         ModestMailOperationPrivate *priv = NULL;
848
849         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
850
851         /* Do not need to block, the notify end will do it for us */    
852         modest_mail_operation_notify_end (mail_op);
853         g_object_unref (mail_op);
854
855         return FALSE;
856 }
857
858 static int
859 compare_headers_by_date (gconstpointer a, 
860                          gconstpointer b)
861 {
862         TnyHeader **header1, **header2;
863         time_t sent1, sent2;
864
865         header1 = (TnyHeader **) a;
866         header2 = (TnyHeader **) b;
867
868         sent1 = tny_header_get_date_sent (*header1);
869         sent2 = tny_header_get_date_sent (*header2);
870
871         /* We want the most recent ones (greater time_t) at the
872            beginning */
873         if (sent1 < sent2)
874                 return 1;
875         else
876                 return -1;
877 }
878
879 static gboolean 
880 set_last_updated_idle (gpointer data)
881 {
882         gdk_threads_enter ();
883
884         /* It does not matter if the time is not exactly the same than
885            the time when this idle was called, it's just an
886            approximation and it won't be very different */
887         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
888                                     (gchar *) data, 
889                                     MODEST_ACCOUNT_LAST_UPDATED, 
890                                     time(NULL), 
891                                     TRUE);
892
893         gdk_threads_leave ();
894
895         return FALSE;
896 }
897
898 static gboolean
899 idle_update_account_cb (gpointer data)
900 {
901         UpdateAccountInfo *idle_info;
902
903         idle_info = (UpdateAccountInfo *) data;
904
905         gdk_threads_enter ();
906         idle_info->callback (idle_info->mail_op,
907                              idle_info->new_headers,
908                              idle_info->user_data);
909         gdk_threads_leave ();
910
911         /* Frees */
912         g_object_unref (idle_info->mail_op);
913         g_free (idle_info);
914
915         return FALSE;
916 }
917
918
919 static gpointer
920 update_account_thread (gpointer thr_user_data)
921 {
922         static gboolean first_time = TRUE;
923         UpdateAccountInfo *info;
924         TnyList *all_folders = NULL;
925         GPtrArray *new_headers = NULL;
926         TnyIterator *iter = NULL;
927         TnyFolderStoreQuery *query = NULL;
928         ModestMailOperationPrivate *priv = NULL;
929         ModestTnySendQueue *send_queue = NULL;
930
931         info = (UpdateAccountInfo *) thr_user_data;
932         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
933
934         /* Get account and set it into mail_operation */
935         priv->account = g_object_ref (info->account);
936
937         /*
938          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
939          * show any updates unless we do that
940          */
941         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
942                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
943
944         /* Get all the folders. We can do it synchronously because
945            we're already running in a different thread than the UI */
946         all_folders = tny_simple_list_new ();
947         query = tny_folder_store_query_new ();
948         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
949         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
950                                       all_folders,
951                                       query,
952                                       &(priv->error));
953         if (priv->error) {
954                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
955                 goto out;
956         }
957
958         iter = tny_list_create_iterator (all_folders);
959         while (!tny_iterator_is_done (iter)) {
960                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
961
962                 recurse_folders (folder, query, all_folders);
963                 tny_iterator_next (iter);
964         }
965         g_object_unref (G_OBJECT (iter));
966
967         /* Update status and notify. We need to call the notification
968            with a source function in order to call it from the main
969            loop. We need that in order not to get into trouble with
970            Gtk+. We use a timeout in order to provide more status
971            information, because the sync tinymail call does not
972            provide it for the moment */
973         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
974
975         /* Refresh folders */
976         new_headers = g_ptr_array_new ();
977         iter = tny_list_create_iterator (all_folders);
978
979         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
980
981                 InternalFolderObserver *observer;
982                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
983
984                 /* Refresh the folder */
985                 /* Our observer receives notification of new emails during folder refreshes,
986                  * so we can use observer->new_headers.
987                  */
988                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
989                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
990                 
991                 /* This gets the status information (headers) from the server.
992                  * We use the blocking version, because we are already in a separate 
993                  * thread.
994                  */
995
996                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
997                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
998                         TnyIterator *iter;
999
1000                         /* If the retrieve type is full messages, refresh and get the messages */
1001                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1002
1003                         iter = tny_list_create_iterator (observer->new_headers);
1004                         while (!tny_iterator_is_done (iter)) {
1005                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1006                                  
1007                                 /* Apply per-message size limits */
1008                                 if (tny_header_get_message_size (header) < info->max_size)
1009                                         g_ptr_array_add (new_headers, g_object_ref (header));
1010
1011                                 g_object_unref (header);
1012                                 tny_iterator_next (iter);
1013                         }
1014                         g_object_unref (iter);
1015                 } else {
1016                         /* We do not need to do it the first time
1017                            because it's automatically done by the tree
1018                            model */
1019                         if (G_UNLIKELY (!first_time))
1020                                 tny_folder_poke_status (TNY_FOLDER (folder));
1021                 }
1022                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1023                 g_object_unref (observer);
1024                 observer = NULL;                        
1025
1026                 g_object_unref (G_OBJECT (folder));
1027                 if (priv->error)
1028                 {
1029                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1030                         goto out;
1031                 }
1032                 
1033                 tny_iterator_next (iter);
1034         }
1035
1036         did_a_cancel = FALSE;
1037
1038         g_object_unref (G_OBJECT (iter));
1039         g_source_remove (timeout);
1040
1041         if (new_headers->len > 0) {
1042                 gint msg_num = 0;
1043
1044                 /* Order by date */
1045                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1046
1047                 /* Apply message count limit */
1048                 /* If the number of messages exceeds the maximum, ask the
1049                  * user to download them all,
1050                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1051                  */
1052                 if (new_headers->len > info->retrieve_limit) {
1053                         /* TODO: Ask the user, instead of just
1054                          * failing, showing
1055                          * mail_nc_msg_count_limit_exceeded, with 'Get
1056                          * all' and 'Newest only' buttons. */
1057                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1058                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1059                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1060                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1061                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1062                         goto out;
1063                 }
1064                 
1065                 priv->done = 0;
1066                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1067                 while (msg_num < priv->total) {
1068
1069                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1070                         TnyFolder *folder = tny_header_get_folder (header);
1071                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1072                         ModestMailOperationState *state;
1073                         ModestPair* pair;
1074
1075                         priv->done++;
1076                         /* We can not just use the mail operation because the
1077                            values of done and total could change before the
1078                            idle is called */
1079                         state = modest_mail_operation_clone_state (info->mail_op);
1080                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1081                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1082                                          pair, (GDestroyNotify) modest_pair_free);
1083
1084                         g_object_unref (msg);
1085                         g_object_unref (folder);
1086
1087                         msg_num++;
1088                 }
1089                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1090                 g_ptr_array_free (new_headers, FALSE);
1091         }
1092         
1093         /* Perform send */
1094         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1095         priv->done = 0;
1096         priv->total = 0;
1097         if (priv->account != NULL) 
1098                 g_object_unref (priv->account);
1099         priv->account = g_object_ref (info->transport_account);
1100         
1101         send_queue = modest_runtime_get_send_queue (info->transport_account);
1102         if (send_queue) {
1103                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1104                 modest_tny_send_queue_try_to_send (send_queue);
1105                 g_source_remove (timeout);
1106         } else {
1107                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1108                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1109                              "cannot create a send queue for %s\n", 
1110                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1111                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1112         }
1113         
1114         /* Check if the operation was a success */
1115         if (!priv->error) {
1116                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1117
1118                 /* Update the last updated key */
1119                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1120                                  set_last_updated_idle, 
1121                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1122                                  (GDestroyNotify) g_free);
1123         }
1124
1125  out:
1126
1127         if (info->callback) {
1128                 UpdateAccountInfo *idle_info;
1129
1130                 /* This thread is not in the main lock */
1131                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1132                 idle_info->mail_op = g_object_ref (info->mail_op);
1133                 idle_info->new_headers = (new_headers) ? new_headers->len : 0;
1134                 idle_info->callback = info->callback;
1135                 g_idle_add (idle_update_account_cb, idle_info);
1136         }
1137
1138         /* Notify about operation end. Note that the info could be
1139            freed before this idle happens, but the mail operation will
1140            be still alive */
1141         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1142
1143         /* Frees */
1144         g_object_unref (query);
1145         g_object_unref (all_folders);
1146         g_object_unref (info->account);
1147         g_object_unref (info->transport_account);
1148         g_free (info->retrieve_type);
1149         g_slice_free (UpdateAccountInfo, info);
1150
1151         first_time = FALSE;
1152
1153         return NULL;
1154 }
1155
1156 gboolean
1157 modest_mail_operation_update_account (ModestMailOperation *self,
1158                                       const gchar *account_name,
1159                                       UpdateAccountCallback callback,
1160                                       gpointer user_data)
1161 {
1162         GThread *thread;
1163         UpdateAccountInfo *info;
1164         ModestMailOperationPrivate *priv;
1165         ModestAccountMgr *mgr;
1166         TnyStoreAccount *modest_account;
1167         TnyTransportAccount *transport_account;
1168
1169         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1170         g_return_val_if_fail (account_name, FALSE);
1171
1172         /* Init mail operation. Set total and done to 0, and do not
1173            update them, this way the progress objects will know that
1174            we have no clue about the number of the objects */
1175         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1176         priv->total = 0;
1177         priv->done  = 0;
1178         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1179
1180         /* Make sure that we have a connection, and request one 
1181          * if necessary:
1182          * TODO: Is there some way to trigger this for every attempt to 
1183          * use the network? */
1184         if (!modest_platform_connect_and_wait (NULL))
1185                 goto error;
1186
1187         /* Get the Modest account */
1188         modest_account = (TnyStoreAccount *)
1189                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1190                                                                      account_name,
1191                                                                      TNY_ACCOUNT_TYPE_STORE);
1192
1193         if (!modest_account) {
1194                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1195                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1196                              "cannot get tny store account for %s\n", account_name);
1197                 goto error;
1198         }
1199
1200         
1201         /* Get the transport account, we can not do it in the thread
1202            due to some problems with dbus */
1203         transport_account = (TnyTransportAccount *)
1204                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1205                                                                                     account_name);
1206         if (!transport_account) {
1207                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1208                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1209                              "cannot get tny transport account for %s\n", account_name);
1210                 goto error;
1211         }
1212
1213         /* Create the helper object */
1214         info = g_slice_new (UpdateAccountInfo);
1215         info->mail_op = self;
1216         info->account = modest_account;
1217         info->transport_account = transport_account;
1218         info->callback = callback;
1219         info->user_data = user_data;
1220
1221         /* Get the message size limit */
1222         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1223                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1224         if (info->max_size == 0)
1225                 info->max_size = G_MAXINT;
1226         else
1227                 info->max_size = info->max_size * KB;
1228
1229         /* Get per-account retrieval type */
1230         mgr = modest_runtime_get_account_mgr ();
1231         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1232                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1233
1234         /* Get per-account message amount retrieval limit */
1235         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1236                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1237         if (info->retrieve_limit == 0)
1238                 info->retrieve_limit = G_MAXINT;
1239                 
1240         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1241
1242         /* Set account busy */
1243         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1244         priv->account_name = g_strdup(account_name);
1245         
1246         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1247
1248         return TRUE;
1249
1250  error:
1251         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1252         if (callback) 
1253                 callback (self, 0, user_data);
1254         modest_mail_operation_notify_end (self);
1255         return FALSE;
1256 }
1257
1258 /* ******************************************************************* */
1259 /* ************************** STORE  ACTIONS ************************* */
1260 /* ******************************************************************* */
1261
1262
1263 TnyFolder *
1264 modest_mail_operation_create_folder (ModestMailOperation *self,
1265                                      TnyFolderStore *parent,
1266                                      const gchar *name)
1267 {
1268         ModestMailOperationPrivate *priv;
1269         TnyFolder *new_folder = NULL;
1270
1271         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1272         g_return_val_if_fail (name, NULL);
1273         
1274         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1275
1276         /* Check parent */
1277         if (TNY_IS_FOLDER (parent)) {
1278                 /* Check folder rules */
1279                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1280                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1281                         /* Set status failed and set an error */
1282                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1283                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1284                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1285                                      _("mail_in_ui_folder_create_error"));
1286                 }
1287         }
1288
1289         if (!strcmp (name, " ") || strchr (name, '/')) {
1290                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1291                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1292                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1293                              _("mail_in_ui_folder_create_error"));
1294         }
1295
1296         if (!priv->error) {
1297                 /* Create the folder */
1298                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1299                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1300                 if (!priv->error)
1301                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1302         }
1303
1304         /* Notify about operation end */
1305         modest_mail_operation_notify_end (self);
1306
1307         return new_folder;
1308 }
1309
1310 void
1311 modest_mail_operation_remove_folder (ModestMailOperation *self,
1312                                      TnyFolder           *folder,
1313                                      gboolean             remove_to_trash)
1314 {
1315         TnyAccount *account;
1316         ModestMailOperationPrivate *priv;
1317         ModestTnyFolderRules rules;
1318
1319         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1320         g_return_if_fail (TNY_IS_FOLDER (folder));
1321         
1322         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1323         
1324         /* Check folder rules */
1325         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1326         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1327                 /* Set status failed and set an error */
1328                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1329                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1330                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1331                              _("mail_in_ui_folder_delete_error"));
1332                 goto end;
1333         }
1334
1335         /* Get the account */
1336         account = modest_tny_folder_get_account (folder);
1337         priv->account = g_object_ref(account);
1338
1339         /* Delete folder or move to trash */
1340         if (remove_to_trash) {
1341                 TnyFolder *trash_folder = NULL;
1342                 trash_folder = modest_tny_account_get_special_folder (account,
1343                                                                       TNY_FOLDER_TYPE_TRASH);
1344                 /* TODO: error_handling */
1345                  modest_mail_operation_xfer_folder (self, folder,
1346                                                     TNY_FOLDER_STORE (trash_folder), 
1347                                                     TRUE, NULL, NULL);
1348         } else {
1349                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1350
1351                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1352                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1353
1354                 if (parent)
1355                         g_object_unref (G_OBJECT (parent));
1356         }
1357         g_object_unref (G_OBJECT (account));
1358
1359  end:
1360         /* Notify about operation end */
1361         modest_mail_operation_notify_end (self);
1362 }
1363
1364 static void
1365 transfer_folder_status_cb (GObject *obj,
1366                            TnyStatus *status,
1367                            gpointer user_data)
1368 {
1369         ModestMailOperation *self;
1370         ModestMailOperationPrivate *priv;
1371         ModestMailOperationState *state;
1372         XFerMsgAsyncHelper *helper;
1373
1374         g_return_if_fail (status != NULL);
1375         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1376
1377         helper = (XFerMsgAsyncHelper *) user_data;
1378         g_return_if_fail (helper != NULL);       
1379
1380         self = helper->mail_op;
1381         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1382
1383         priv->done = status->position;
1384         priv->total = status->of_total;
1385
1386         state = modest_mail_operation_clone_state (self);
1387         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1388         g_slice_free (ModestMailOperationState, state);
1389 }
1390
1391
1392 static void
1393 transfer_folder_cb (TnyFolder *folder, 
1394                     TnyFolderStore *into, 
1395                     gboolean cancelled, 
1396                     TnyFolder *new_folder, 
1397                     GError **err, 
1398                     gpointer user_data)
1399 {
1400         XFerMsgAsyncHelper *helper;
1401         ModestMailOperation *self = NULL;
1402         ModestMailOperationPrivate *priv = NULL;
1403
1404         helper = (XFerMsgAsyncHelper *) user_data;
1405         g_return_if_fail (helper != NULL);       
1406
1407         self = helper->mail_op;
1408         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1409
1410         if (*err) {
1411                 priv->error = g_error_copy (*err);
1412                 priv->done = 0;
1413                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1414         } else if (cancelled) {
1415                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1416                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1417                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1418                              _("Transference of %s was cancelled."),
1419                              tny_folder_get_name (folder));
1420         } else {
1421                 priv->done = 1;
1422                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1423         }
1424                 
1425         /* Notify about operation end */
1426         modest_mail_operation_notify_end (self);
1427
1428         /* If user defined callback function was defined, call it */
1429         if (helper->user_callback) {
1430                 gdk_threads_enter ();
1431                 helper->user_callback (priv->source, helper->user_data);
1432                 gdk_threads_leave ();
1433         }
1434
1435         /* Free */
1436         g_object_unref (helper->mail_op);
1437         g_slice_free   (XFerMsgAsyncHelper, helper);
1438         g_object_unref (folder);
1439         g_object_unref (into);
1440 }
1441
1442 void
1443 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1444                                    TnyFolder *folder,
1445                                    TnyFolderStore *parent,
1446                                    gboolean delete_original,
1447                                    XferMsgsAsynUserCallback user_callback,
1448                                    gpointer user_data)
1449 {
1450         ModestMailOperationPrivate *priv = NULL;
1451         ModestTnyFolderRules parent_rules = 0, rules; 
1452         XFerMsgAsyncHelper *helper = NULL;
1453
1454         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1455         g_return_if_fail (TNY_IS_FOLDER (folder));
1456
1457         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1458
1459         /* Get account and set it into mail_operation */
1460         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1461         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1462
1463         /* Get folder rules */
1464         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1465         if (TNY_IS_FOLDER (parent))
1466                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1467         
1468         /* The moveable restriction is applied also to copy operation */
1469         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1470                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1471                 /* Set status failed and set an error */
1472                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1473                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1474                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1475                              _("mail_in_ui_folder_move_target_error"));
1476
1477                 /* Notify the queue */
1478                 modest_mail_operation_notify_end (self);
1479         } else if (TNY_IS_FOLDER (parent) && 
1480                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1481                 /* Set status failed and set an error */
1482                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1483                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1484                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1485                              _("FIXME: parent folder does not accept new folders"));
1486
1487                 /* Notify the queue */
1488                 modest_mail_operation_notify_end (self);
1489         } else {
1490                 /* Pick references for async calls */
1491                 g_object_ref (folder);
1492                 g_object_ref (parent);
1493
1494                 /* Create the helper */
1495                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1496                 helper->mail_op = g_object_ref(self);
1497                 helper->dest_folder = NULL;
1498                 helper->headers = NULL;
1499                 helper->user_callback = user_callback;
1500                 helper->user_data = user_data;
1501                 
1502                 /* Move/Copy folder */          
1503                 tny_folder_copy_async (folder,
1504                                        parent,
1505                                        tny_folder_get_name (folder),
1506                                        delete_original,
1507                                        transfer_folder_cb,
1508                                        transfer_folder_status_cb,
1509                                        helper);
1510 /*                                     self); */
1511         }
1512 }
1513
1514 void
1515 modest_mail_operation_rename_folder (ModestMailOperation *self,
1516                                      TnyFolder *folder,
1517                                      const gchar *name)
1518 {
1519         ModestMailOperationPrivate *priv;
1520         ModestTnyFolderRules rules;
1521         XFerMsgAsyncHelper *helper;
1522
1523         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1524         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1525         g_return_if_fail (name);
1526         
1527         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1528
1529         /* Get account and set it into mail_operation */
1530         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1531
1532         /* Check folder rules */
1533         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1534         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1535                 /* Set status failed and set an error */
1536                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1537                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1538                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1539                              _("FIXME: unable to rename"));
1540
1541                 /* Notify about operation end */
1542                 modest_mail_operation_notify_end (self);
1543         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1544                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1545                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1546                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1547                              _("FIXME: unable to rename"));
1548                 /* Notify about operation end */
1549                 modest_mail_operation_notify_end (self);
1550         } else {
1551                 TnyFolderStore *into;
1552
1553                 /* Create the helper */
1554                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1555                 helper->mail_op = g_object_ref(self);
1556                 helper->dest_folder = NULL;
1557                 helper->headers = NULL;
1558                 helper->user_callback = NULL;
1559                 helper->user_data = NULL;
1560
1561                 /* Rename. Camel handles folder subscription/unsubscription */
1562                 into = tny_folder_get_folder_store (folder);
1563                 tny_folder_copy_async (folder, into, name, TRUE,
1564                                  transfer_folder_cb,
1565                                  transfer_folder_status_cb,
1566                                  helper);
1567 /*                               self); */
1568                 if (into)
1569                         g_object_unref (into);          
1570         }
1571  }
1572
1573 /* ******************************************************************* */
1574 /* **************************  MSG  ACTIONS  ************************* */
1575 /* ******************************************************************* */
1576
1577 void modest_mail_operation_get_msg (ModestMailOperation *self,
1578                                     TnyHeader *header,
1579                                     GetMsgAsyncUserCallback user_callback,
1580                                     gpointer user_data)
1581 {
1582         GetMsgAsyncHelper *helper = NULL;
1583         TnyFolder *folder;
1584         ModestMailOperationPrivate *priv;
1585         
1586         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1587         g_return_if_fail (TNY_IS_HEADER (header));
1588         
1589         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1590         folder = tny_header_get_folder (header);
1591
1592         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1593
1594         /* Get message from folder */
1595         if (folder) {
1596                 /* Get account and set it into mail_operation */
1597                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1598
1599                 helper = g_slice_new0 (GetMsgAsyncHelper);
1600                 helper->mail_op = self;
1601                 helper->user_callback = user_callback;
1602                 helper->user_data = user_data;
1603                 helper->header = g_object_ref (header);
1604
1605                 // The callback's reference so that the mail op is not
1606                 // finalized until the async operation is completed even if
1607                 // the user canceled the request meanwhile.
1608                 g_object_ref (G_OBJECT (helper->mail_op));
1609
1610                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1611
1612                 g_object_unref (G_OBJECT (folder));
1613         } else {
1614                 /* Set status failed and set an error */
1615                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1616                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1617                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1618                              _("Error trying to get a message. No folder found for header"));
1619
1620                 /* Notify the queue */
1621                 modest_mail_operation_notify_end (self);
1622         }
1623 }
1624
1625 static void
1626 get_msg_cb (TnyFolder *folder, 
1627             gboolean cancelled, 
1628             TnyMsg *msg, 
1629             GError **error, 
1630             gpointer user_data)
1631 {
1632         GetMsgAsyncHelper *helper = NULL;
1633         ModestMailOperation *self = NULL;
1634         ModestMailOperationPrivate *priv = NULL;
1635
1636         helper = (GetMsgAsyncHelper *) user_data;
1637         g_return_if_fail (helper != NULL);       
1638         self = helper->mail_op;
1639         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1640         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1641
1642         /* Check errors and cancel */
1643         if (*error) {
1644                 priv->error = g_error_copy (*error);
1645                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1646         } else if (cancelled) {
1647                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1648                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1649                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1650                              _("Error trying to refresh the contents of %s"),
1651                              tny_folder_get_name (folder));
1652         } else {
1653                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1654         }
1655
1656         /* If user defined callback function was defined, call it even
1657            if the operation failed*/
1658         if (helper->user_callback) {
1659                 /* This callback is called into an iddle by tinymail,
1660                    and idles are not in the main lock */
1661                 gdk_threads_enter ();
1662                 helper->user_callback (self, helper->header, msg, helper->user_data);
1663                 gdk_threads_leave ();   
1664         }
1665
1666         /* Free */
1667         g_object_unref (helper->mail_op);
1668         g_object_unref (helper->header);
1669         g_slice_free (GetMsgAsyncHelper, helper);
1670                 
1671         /* Notify about operation end */
1672         modest_mail_operation_notify_end (self);
1673 }
1674
1675 static void     
1676 get_msg_status_cb (GObject *obj,
1677                    TnyStatus *status,  
1678                    gpointer user_data)
1679 {
1680         GetMsgAsyncHelper *helper = NULL;
1681         ModestMailOperation *self;
1682         ModestMailOperationPrivate *priv;
1683         ModestMailOperationState *state;
1684
1685         g_return_if_fail (status != NULL);
1686         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1687
1688         helper = (GetMsgAsyncHelper *) user_data;
1689         g_return_if_fail (helper != NULL);       
1690
1691         self = helper->mail_op;
1692         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1693
1694         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1695                 return;
1696
1697         priv->done = 1;
1698         priv->total = 1;
1699
1700         state = modest_mail_operation_clone_state (self);
1701         state->bytes_done = status->position;
1702         state->bytes_total = status->of_total;
1703         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1704         g_slice_free (ModestMailOperationState, state);
1705 }
1706
1707 /****************************************************/
1708 typedef struct {
1709         ModestMailOperation *mail_op;
1710         TnyList *headers;
1711         GetMsgAsyncUserCallback user_callback;
1712         gpointer user_data;
1713         GDestroyNotify notify;
1714 } GetFullMsgsInfo;
1715
1716 typedef struct {
1717         GetMsgAsyncUserCallback user_callback;
1718         TnyHeader *header;
1719         TnyMsg *msg;
1720         gpointer user_data;
1721         ModestMailOperation *mail_op;
1722 } NotifyGetMsgsInfo;
1723
1724
1725 /* 
1726  * Used by get_msgs_full_thread to call the user_callback for each
1727  * message that has been read
1728  */
1729 static gboolean
1730 notify_get_msgs_full (gpointer data)
1731 {
1732         NotifyGetMsgsInfo *info;
1733
1734         info = (NotifyGetMsgsInfo *) data;      
1735
1736         /* Call the user callback. Idles are not in the main lock, so
1737            lock it */
1738         gdk_threads_enter ();
1739         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1740         gdk_threads_leave ();
1741
1742         g_slice_free (NotifyGetMsgsInfo, info);
1743
1744         return FALSE;
1745 }
1746
1747 /* 
1748  * Used by get_msgs_full_thread to free al the thread resources and to
1749  * call the destroy function for the passed user_data
1750  */
1751 static gboolean
1752 get_msgs_full_destroyer (gpointer data)
1753 {
1754         GetFullMsgsInfo *info;
1755
1756         info = (GetFullMsgsInfo *) data;
1757
1758         if (info->notify) {
1759                 gdk_threads_enter ();   
1760                 info->notify (info->user_data);
1761                 gdk_threads_leave ();
1762         }
1763
1764         /* free */
1765         g_object_unref (info->headers);
1766         g_slice_free (GetFullMsgsInfo, info);
1767
1768         return FALSE;
1769 }
1770
1771 static gpointer
1772 get_msgs_full_thread (gpointer thr_user_data)
1773 {
1774         GetFullMsgsInfo *info;
1775         ModestMailOperationPrivate *priv = NULL;
1776         TnyIterator *iter = NULL;
1777         
1778         info = (GetFullMsgsInfo *) thr_user_data;       
1779         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1780
1781         iter = tny_list_create_iterator (info->headers);
1782         while (!tny_iterator_is_done (iter)) { 
1783                 TnyHeader *header;
1784                 TnyFolder *folder;
1785                 
1786                 header = TNY_HEADER (tny_iterator_get_current (iter));
1787                 folder = tny_header_get_folder (header);
1788                                 
1789                 /* Get message from folder */
1790                 if (folder) {
1791                         TnyMsg *msg;
1792                         /* The callback will call it per each header */
1793                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1794
1795                         if (msg) {
1796                                 ModestMailOperationState *state;
1797                                 ModestPair *pair;
1798
1799                                 priv->done++;
1800
1801                                 /* notify progress */
1802                                 state = modest_mail_operation_clone_state (info->mail_op);
1803                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1804                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1805                                                  pair, (GDestroyNotify) modest_pair_free);
1806
1807                                 /* The callback is the responsible for
1808                                    freeing the message */
1809                                 if (info->user_callback) {
1810                                         NotifyGetMsgsInfo *info_notify;
1811                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1812                                         info_notify->user_callback = info->user_callback;
1813                                         info_notify->mail_op = info->mail_op;
1814                                         info_notify->header = g_object_ref (header);
1815                                         info_notify->msg = g_object_ref (msg);
1816                                         info_notify->user_data = info->user_data;
1817                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1818                                                          notify_get_msgs_full, 
1819                                                          info_notify, NULL);
1820                                 }
1821                                 g_object_unref (msg);
1822                         }
1823                 } else {
1824                         /* Set status failed and set an error */
1825                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1826                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1827                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1828                                      "Error trying to get a message. No folder found for header");
1829                 }
1830                 g_object_unref (header);                
1831                 tny_iterator_next (iter);
1832         }
1833
1834         /* Set operation status */
1835         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1836                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1837
1838         /* Notify about operation end */
1839         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1840
1841         /* Free thread resources. Will be called after all previous idles */
1842         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1843
1844         return NULL;
1845 }
1846
1847 void 
1848 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1849                                      TnyList *header_list, 
1850                                      GetMsgAsyncUserCallback user_callback,
1851                                      gpointer user_data,
1852                                      GDestroyNotify notify)
1853 {
1854         TnyHeader *header = NULL;
1855         TnyFolder *folder = NULL;
1856         GThread *thread;
1857         ModestMailOperationPrivate *priv = NULL;
1858         GetFullMsgsInfo *info = NULL;
1859         gboolean size_ok = TRUE;
1860         gint max_size;
1861         TnyIterator *iter = NULL;
1862         
1863         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1864         
1865         /* Init mail operation */
1866         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1867         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1868         priv->done = 0;
1869         priv->total = tny_list_get_length(header_list);
1870
1871         /* Get account and set it into mail_operation */
1872         if (tny_list_get_length (header_list) >= 1) {
1873                 iter = tny_list_create_iterator (header_list);
1874                 header = TNY_HEADER (tny_iterator_get_current (iter));
1875                 folder = tny_header_get_folder (header);                
1876                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1877                 g_object_unref (header);
1878                 g_object_unref (folder);
1879
1880                 if (tny_list_get_length (header_list) == 1) {
1881                         g_object_unref (iter);
1882                         iter = NULL;
1883                 }
1884         }
1885
1886         /* Get msg size limit */
1887         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1888                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1889                                          &(priv->error));
1890         if (priv->error) {
1891                 g_clear_error (&(priv->error));
1892                 max_size = G_MAXINT;
1893         } else {
1894                 max_size = max_size * KB;
1895         }
1896
1897         /* Check message size limits. If there is only one message
1898            always retrieve it */
1899         if (iter != NULL) {
1900                 while (!tny_iterator_is_done (iter) && size_ok) {
1901                         header = TNY_HEADER (tny_iterator_get_current (iter));
1902                         if (tny_header_get_message_size (header) >= max_size)
1903                                 size_ok = FALSE;
1904                         g_object_unref (header);
1905                         tny_iterator_next (iter);
1906                 }
1907                 g_object_unref (iter);
1908         }
1909
1910         if (size_ok) {
1911                 /* Create the info */
1912                 info = g_slice_new0 (GetFullMsgsInfo);
1913                 info->mail_op = self;
1914                 info->user_callback = user_callback;
1915                 info->user_data = user_data;
1916                 info->headers = g_object_ref (header_list);
1917                 info->notify = notify;
1918
1919                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1920         } else {
1921                 /* Set status failed and set an error */
1922                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1923                 /* FIXME: the error msg is different for pop */
1924                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1925                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1926                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1927                 /* Remove from queue and free resources */
1928                 modest_mail_operation_notify_end (self);
1929                 if (notify)
1930                         notify (user_data);
1931         }
1932 }
1933
1934
1935 void 
1936 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
1937                                   gboolean remove_to_trash /*ignored*/)
1938 {
1939         TnyFolder *folder;
1940         ModestMailOperationPrivate *priv;
1941
1942         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1943         g_return_if_fail (TNY_IS_HEADER (header));
1944
1945         if (remove_to_trash)
1946                 g_warning ("remove to trash is not implemented");
1947
1948         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1949         folder = tny_header_get_folder (header);
1950
1951         /* Get account and set it into mail_operation */
1952         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1953
1954         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1955
1956
1957         tny_folder_remove_msg (folder, header, &(priv->error));
1958         if (!priv->error) {
1959                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1960
1961                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
1962                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
1963                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
1964                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1965                 else
1966                         /* lcoal folders */
1967                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1968         }
1969         
1970         
1971         /* Set status */
1972         if (!priv->error)
1973                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1974         else
1975                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1976
1977         /* Free */
1978         g_object_unref (G_OBJECT (folder));
1979
1980         /* Notify about operation end */
1981         modest_mail_operation_notify_end (self);
1982 }
1983
1984 static void
1985 transfer_msgs_status_cb (GObject *obj,
1986                          TnyStatus *status,  
1987                          gpointer user_data)
1988 {
1989         XFerMsgAsyncHelper *helper = NULL;
1990         ModestMailOperation *self;
1991         ModestMailOperationPrivate *priv;
1992         ModestMailOperationState *state;
1993
1994
1995         g_return_if_fail (status != NULL);
1996         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1997
1998         helper = (XFerMsgAsyncHelper *) user_data;
1999         g_return_if_fail (helper != NULL);       
2000
2001         self = helper->mail_op;
2002         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2003
2004         priv->done = status->position;
2005         priv->total = status->of_total;
2006
2007         state = modest_mail_operation_clone_state (self);
2008         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2009         g_slice_free (ModestMailOperationState, state);
2010 }
2011
2012
2013 static void
2014 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2015 {
2016         XFerMsgAsyncHelper *helper;
2017         ModestMailOperation *self;
2018         ModestMailOperationPrivate *priv;
2019
2020         helper = (XFerMsgAsyncHelper *) user_data;
2021         self = helper->mail_op;
2022
2023         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2024
2025         if (*err) {
2026                 priv->error = g_error_copy (*err);
2027                 priv->done = 0;
2028                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2029         } else if (cancelled) {
2030                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2031                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2032                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2033                              _("Error trying to refresh the contents of %s"),
2034                              tny_folder_get_name (folder));
2035         } else {
2036                 priv->done = 1;
2037                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2038         }
2039
2040         /* Notify about operation end */
2041         modest_mail_operation_notify_end (self);
2042
2043         /* If user defined callback function was defined, call it */
2044         if (helper->user_callback) {
2045                 gdk_threads_enter ();
2046                 helper->user_callback (priv->source, helper->user_data);
2047                 gdk_threads_leave ();
2048         }
2049
2050         /* Free */
2051         g_object_unref (helper->headers);
2052         g_object_unref (helper->dest_folder);
2053         g_object_unref (helper->mail_op);
2054         g_slice_free   (XFerMsgAsyncHelper, helper);
2055         g_object_unref (folder);
2056
2057 }
2058
2059 void
2060 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2061                                  TnyList *headers, 
2062                                  TnyFolder *folder, 
2063                                  gboolean delete_original,
2064                                  XferMsgsAsynUserCallback user_callback,
2065                                  gpointer user_data)
2066 {
2067         ModestMailOperationPrivate *priv;
2068         TnyIterator *iter;
2069         TnyFolder *src_folder;
2070         XFerMsgAsyncHelper *helper;
2071         TnyHeader *header;
2072         ModestTnyFolderRules rules;
2073         const gchar *id1 = NULL;
2074         const gchar *id2 = NULL;
2075         gboolean same_folder = FALSE;
2076
2077         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2078         g_return_if_fail (TNY_IS_LIST (headers));
2079         g_return_if_fail (TNY_IS_FOLDER (folder));
2080
2081         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2082         priv->total = 1;
2083         priv->done = 0;
2084         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2085
2086         /* Apply folder rules */
2087         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2088         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2089                 /* Set status failed and set an error */
2090                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2091                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2092                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2093                              _("ckct_ib_unable_to_paste_here"));
2094                 /* Notify the queue */
2095                 modest_mail_operation_notify_end (self);
2096                 return;
2097         }
2098                 
2099         /* Get source folder */
2100         iter = tny_list_create_iterator (headers);
2101         header = TNY_HEADER (tny_iterator_get_current (iter));
2102         src_folder = tny_header_get_folder (header);
2103         g_object_unref (header);
2104         g_object_unref (iter);
2105
2106         /* Check folder source and destination */
2107         id1 = tny_folder_get_id (src_folder);
2108         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2109         same_folder = !g_ascii_strcasecmp (id1, id2);
2110         if (same_folder) {
2111                 /* Set status failed and set an error */
2112                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2113                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2114                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2115                              _("mcen_ib_unable_to_copy_samefolder"));
2116                 
2117                 /* Notify the queue */
2118                 modest_mail_operation_notify_end (self);
2119                 
2120                 /* Free */
2121                 g_object_unref (src_folder);            
2122                 return;
2123         }
2124
2125         /* Create the helper */
2126         helper = g_slice_new0 (XFerMsgAsyncHelper);
2127         helper->mail_op = g_object_ref(self);
2128         helper->dest_folder = g_object_ref(folder);
2129         helper->headers = g_object_ref(headers);
2130         helper->user_callback = user_callback;
2131         helper->user_data = user_data;
2132
2133         /* Get account and set it into mail_operation */
2134         priv->account = modest_tny_folder_get_account (src_folder);
2135
2136         /* Transfer messages */
2137         tny_folder_transfer_msgs_async (src_folder, 
2138                                         headers, 
2139                                         folder, 
2140                                         delete_original, 
2141                                         transfer_msgs_cb, 
2142                                         transfer_msgs_status_cb,
2143                                         helper);
2144 }
2145
2146
2147 static void
2148 on_refresh_folder (TnyFolder   *folder, 
2149                    gboolean     cancelled, 
2150                    GError     **error,
2151                    gpointer     user_data)
2152 {
2153         RefreshAsyncHelper *helper = NULL;
2154         ModestMailOperation *self = NULL;
2155         ModestMailOperationPrivate *priv = NULL;
2156
2157         helper = (RefreshAsyncHelper *) user_data;
2158         self = helper->mail_op;
2159         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2160
2161         if (*error) {
2162                 priv->error = g_error_copy (*error);
2163                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2164                 goto out;
2165         }
2166
2167         if (cancelled) {
2168                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2169                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2170                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2171                              _("Error trying to refresh the contents of %s"),
2172                              tny_folder_get_name (folder));
2173                 goto out;
2174         }
2175
2176         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2177
2178  out:
2179         /* Call user defined callback, if it exists */
2180         if (helper->user_callback) {
2181                 gdk_threads_enter ();
2182                 helper->user_callback (self, folder, helper->user_data);
2183                 gdk_threads_leave ();
2184         }
2185
2186         /* Free */
2187         g_object_unref (helper->mail_op);
2188         g_slice_free   (RefreshAsyncHelper, helper);
2189         g_object_unref (folder);
2190
2191         /* Notify about operation end */
2192         modest_mail_operation_notify_end (self);
2193 }
2194
2195 static void
2196 on_refresh_folder_status_update (GObject *obj,
2197                                  TnyStatus *status,
2198                                  gpointer user_data)
2199 {
2200         RefreshAsyncHelper *helper = NULL;
2201         ModestMailOperation *self = NULL;
2202         ModestMailOperationPrivate *priv = NULL;
2203         ModestMailOperationState *state;
2204
2205         g_return_if_fail (user_data != NULL);
2206         g_return_if_fail (status != NULL);
2207         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2208
2209         helper = (RefreshAsyncHelper *) user_data;
2210         self = helper->mail_op;
2211         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2212
2213         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2214
2215         priv->done = status->position;
2216         priv->total = status->of_total;
2217
2218         state = modest_mail_operation_clone_state (self);
2219         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2220         g_slice_free (ModestMailOperationState, state);
2221 }
2222
2223 void 
2224 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2225                                        TnyFolder *folder,
2226                                        RefreshAsyncUserCallback user_callback,
2227                                        gpointer user_data)
2228 {
2229         ModestMailOperationPrivate *priv = NULL;
2230         RefreshAsyncHelper *helper = NULL;
2231
2232         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2233
2234         /* Pick a reference */
2235         g_object_ref (folder);
2236
2237         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2238
2239         /* Get account and set it into mail_operation */
2240         priv->account = modest_tny_folder_get_account  (folder);
2241
2242         /* Create the helper */
2243         helper = g_slice_new0 (RefreshAsyncHelper);
2244         helper->mail_op = g_object_ref(self);
2245         helper->user_callback = user_callback;
2246         helper->user_data = user_data;
2247
2248         /* Refresh the folder. TODO: tinymail could issue a status
2249            updates before the callback call then this could happen. We
2250            must review the design */
2251         tny_folder_refresh_async (folder,
2252                                   on_refresh_folder,
2253                                   on_refresh_folder_status_update,
2254                                   helper);
2255 }
2256
2257 /**
2258  *
2259  * It's used by the mail operation queue to notify the observers
2260  * attached to that signal that the operation finished. We need to use
2261  * that because tinymail does not give us the progress of a given
2262  * operation when it finishes (it directly calls the operation
2263  * callback).
2264  */
2265 static void
2266 modest_mail_operation_notify_end (ModestMailOperation *self)
2267 {
2268         ModestMailOperationState *state;
2269         ModestMailOperationPrivate *priv = NULL;
2270
2271         g_return_if_fail (self);
2272
2273         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2274
2275         if (!priv) {
2276                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2277                 return;
2278         }
2279         
2280         /* Set the account back to not busy */
2281         if (priv->account_name) {
2282                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2283                                                      priv->account_name, FALSE);
2284                 g_free(priv->account_name);
2285                 priv->account_name = NULL;
2286         }
2287         
2288         /* Notify the observers about the mail opertation end */
2289         state = modest_mail_operation_clone_state (self);
2290         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2291         g_slice_free (ModestMailOperationState, state);
2292 }