Work to improve drafts handling, mainly from the UI pov. This, with
[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 TnyMsg*
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_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
613         g_return_val_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account), NULL);
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 (folder)
666                 g_object_unref (G_OBJECT(folder));
667
668         modest_mail_operation_notify_end (self);
669         return msg;
670 }
671
672 typedef struct 
673 {
674         ModestMailOperation *mail_op;
675         TnyStoreAccount *account;
676         TnyTransportAccount *transport_account;
677         gint max_size;
678         gint retrieve_limit;
679         gchar *retrieve_type;
680         gchar *account_name;
681         UpdateAccountCallback callback;
682         gpointer user_data;
683         gint new_headers;
684 } UpdateAccountInfo;
685
686 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
687 /* We use this folder observer to track the headers that have been
688  * added to a folder */
689 typedef struct {
690         GObject parent;
691         TnyList *new_headers;
692 } InternalFolderObserver;
693
694 typedef struct {
695         GObjectClass parent;
696 } InternalFolderObserverClass;
697
698 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
699
700 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
701                          internal_folder_observer,
702                          G_TYPE_OBJECT,
703                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
704
705
706 static void
707 foreach_add_item (gpointer header, gpointer user_data)
708 {
709         /* printf("DEBUG: %s: header subject=%s\n", 
710          * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
711          */
712         tny_list_prepend (TNY_LIST (user_data), 
713                           g_object_ref (G_OBJECT (header)));
714 }
715
716 /* This is the method that looks for new messages in a folder */
717 static void
718 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
719 {
720         InternalFolderObserver *derived = (InternalFolderObserver *)self;
721         
722         TnyFolderChangeChanged changed;
723
724         changed = tny_folder_change_get_changed (change);
725
726         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
727                 TnyList *list;
728
729                 /* Get added headers */
730                 list = tny_simple_list_new ();
731                 tny_folder_change_get_added_headers (change, list);
732
733                 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n", 
734                  *      __FUNCTION__, tny_list_get_length(list));
735                  */
736                  
737                 /* Add them to the folder observer */
738                 tny_list_foreach (list, foreach_add_item, 
739                                   derived->new_headers);
740
741                 g_object_unref (G_OBJECT (list));
742         }
743 }
744
745 static void
746 internal_folder_observer_init (InternalFolderObserver *self) 
747 {
748         self->new_headers = tny_simple_list_new ();
749 }
750 static void
751 internal_folder_observer_finalize (GObject *object) 
752 {
753         InternalFolderObserver *self;
754
755         self = (InternalFolderObserver *) object;
756         g_object_unref (self->new_headers);
757
758         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
759 }
760 static void
761 tny_folder_observer_init (TnyFolderObserverIface *iface) 
762 {
763         iface->update_func = internal_folder_observer_update;
764 }
765 static void
766 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
767 {
768         GObjectClass *object_class;
769
770         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
771         object_class = (GObjectClass*) klass;
772         object_class->finalize = internal_folder_observer_finalize;
773 }
774
775 /*****************/
776
777 static void
778 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
779 {
780         TnyIterator *iter;
781         TnyList *folders = tny_simple_list_new ();
782
783         tny_folder_store_get_folders (store, folders, query, NULL);
784         iter = tny_list_create_iterator (folders);
785
786         while (!tny_iterator_is_done (iter)) {
787
788                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
789
790                 tny_list_prepend (all_folders, G_OBJECT (folder));
791                 recurse_folders (folder, query, all_folders);    
792                 g_object_unref (G_OBJECT (folder));
793
794                 tny_iterator_next (iter);
795         }
796          g_object_unref (G_OBJECT (iter));
797          g_object_unref (G_OBJECT (folders));
798 }
799
800 /* 
801  * Issues the "progress-changed" signal. The timer won't be removed,
802  * so you must call g_source_remove to stop the signal emission
803  */
804 static gboolean
805 idle_notify_progress (gpointer data)
806 {
807         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
808         ModestMailOperationState *state;
809
810         state = modest_mail_operation_clone_state (mail_op);
811         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
812         g_slice_free (ModestMailOperationState, state);
813         
814         return TRUE;
815 }
816
817 /* 
818  * Issues the "progress-changed" signal and removes the timer. It uses
819  * a lock to ensure that the progress information of the mail
820  * operation is not modified while there are notifications pending
821  */
822 static gboolean
823 idle_notify_progress_once (gpointer data)
824 {
825         ModestPair *pair;
826
827         pair = (ModestPair *) data;
828
829         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
830
831         /* Free the state and the reference to the mail operation */
832         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
833         g_object_unref (pair->first);
834
835         return FALSE;
836 }
837
838 /* 
839  * Used by update_account_thread to notify the queue from the main
840  * loop. We call it inside an idle call to achieve that
841  */
842 static gboolean
843 idle_notify_update_account_queue (gpointer data)
844 {
845         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
846         ModestMailOperationPrivate *priv = NULL;
847
848         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
849
850         /* Do not need to block, the notify end will do it for us */    
851         modest_mail_operation_notify_end (mail_op);
852         g_object_unref (mail_op);
853
854         return FALSE;
855 }
856
857 static int
858 compare_headers_by_date (gconstpointer a, 
859                          gconstpointer b)
860 {
861         TnyHeader **header1, **header2;
862         time_t sent1, sent2;
863
864         header1 = (TnyHeader **) a;
865         header2 = (TnyHeader **) b;
866
867         sent1 = tny_header_get_date_sent (*header1);
868         sent2 = tny_header_get_date_sent (*header2);
869
870         /* We want the most recent ones (greater time_t) at the
871            beginning */
872         if (sent1 < sent2)
873                 return 1;
874         else
875                 return -1;
876 }
877
878 static gboolean 
879 set_last_updated_idle (gpointer data)
880 {
881         gdk_threads_enter ();
882
883         /* It does not matter if the time is not exactly the same than
884            the time when this idle was called, it's just an
885            approximation and it won't be very different */
886         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
887                                     (gchar *) data, 
888                                     MODEST_ACCOUNT_LAST_UPDATED, 
889                                     time(NULL), 
890                                     TRUE);
891
892         gdk_threads_leave ();
893
894         return FALSE;
895 }
896
897 static gboolean
898 idle_update_account_cb (gpointer data)
899 {
900         UpdateAccountInfo *idle_info;
901
902         idle_info = (UpdateAccountInfo *) data;
903
904         gdk_threads_enter ();
905         idle_info->callback (idle_info->mail_op,
906                              idle_info->new_headers,
907                              idle_info->user_data);
908         gdk_threads_leave ();
909
910         /* Frees */
911         g_object_unref (idle_info->mail_op);
912         g_free (idle_info);
913
914         return FALSE;
915 }
916
917
918 static gpointer
919 update_account_thread (gpointer thr_user_data)
920 {
921         static gboolean first_time = TRUE;
922         UpdateAccountInfo *info;
923         TnyList *all_folders = NULL;
924         GPtrArray *new_headers = NULL;
925         TnyIterator *iter = NULL;
926         TnyFolderStoreQuery *query = NULL;
927         ModestMailOperationPrivate *priv = NULL;
928         ModestTnySendQueue *send_queue = NULL;
929
930         info = (UpdateAccountInfo *) thr_user_data;
931         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
932
933         /* Get account and set it into mail_operation */
934         priv->account = g_object_ref (info->account);
935
936         /*
937          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
938          * show any updates unless we do that
939          */
940         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
941                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
942
943         /* Get all the folders. We can do it synchronously because
944            we're already running in a different thread than the UI */
945         all_folders = tny_simple_list_new ();
946         query = tny_folder_store_query_new ();
947         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
948         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
949                                       all_folders,
950                                       query,
951                                       &(priv->error));
952         if (priv->error) {
953                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
954                 goto out;
955         }
956
957         iter = tny_list_create_iterator (all_folders);
958         while (!tny_iterator_is_done (iter)) {
959                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
960
961                 recurse_folders (folder, query, all_folders);
962                 tny_iterator_next (iter);
963         }
964         g_object_unref (G_OBJECT (iter));
965
966         /* Update status and notify. We need to call the notification
967            with a source function in order to call it from the main
968            loop. We need that in order not to get into trouble with
969            Gtk+. We use a timeout in order to provide more status
970            information, because the sync tinymail call does not
971            provide it for the moment */
972         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
973
974         /* Refresh folders */
975         new_headers = g_ptr_array_new ();
976         iter = tny_list_create_iterator (all_folders);
977
978         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
979
980                 InternalFolderObserver *observer;
981                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
982
983                 /* Refresh the folder */
984                 /* Our observer receives notification of new emails during folder refreshes,
985                  * so we can use observer->new_headers.
986                  */
987                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
988                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
989                 
990                 /* This gets the status information (headers) from the server.
991                  * We use the blocking version, because we are already in a separate 
992                  * thread.
993                  */
994
995                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
996                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
997                         TnyIterator *iter;
998
999                         /* If the retrieve type is full messages, refresh and get the messages */
1000                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1001
1002                         iter = tny_list_create_iterator (observer->new_headers);
1003                         while (!tny_iterator_is_done (iter)) {
1004                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1005                                  
1006                                 /* Apply per-message size limits */
1007                                 if (tny_header_get_message_size (header) < info->max_size)
1008                                         g_ptr_array_add (new_headers, g_object_ref (header));
1009
1010                                 g_object_unref (header);
1011                                 tny_iterator_next (iter);
1012                         }
1013                         g_object_unref (iter);
1014                 } else {
1015                         /* We do not need to do it the first time
1016                            because it's automatically done by the tree
1017                            model */
1018                         if (G_UNLIKELY (!first_time))
1019                                 tny_folder_poke_status (TNY_FOLDER (folder));
1020                 }
1021                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1022                 g_object_unref (observer);
1023                 observer = NULL;                        
1024
1025                 g_object_unref (G_OBJECT (folder));
1026                 if (priv->error)
1027                 {
1028                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1029                         goto out;
1030                 }
1031                 
1032                 tny_iterator_next (iter);
1033         }
1034
1035         did_a_cancel = FALSE;
1036
1037         g_object_unref (G_OBJECT (iter));
1038         g_source_remove (timeout);
1039
1040         if (new_headers->len > 0) {
1041                 gint msg_num = 0;
1042
1043                 /* Order by date */
1044                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1045
1046                 /* Apply message count limit */
1047                 /* If the number of messages exceeds the maximum, ask the
1048                  * user to download them all,
1049                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1050                  */
1051                 if (new_headers->len > info->retrieve_limit) {
1052                         /* TODO: Ask the user, instead of just
1053                          * failing, showing
1054                          * mail_nc_msg_count_limit_exceeded, with 'Get
1055                          * all' and 'Newest only' buttons. */
1056                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1057                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1058                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1059                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1060                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1061                         goto out;
1062                 }
1063                 
1064                 priv->done = 0;
1065                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1066                 while (msg_num < priv->total) {
1067
1068                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1069                         TnyFolder *folder = tny_header_get_folder (header);
1070                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1071                         ModestMailOperationState *state;
1072                         ModestPair* pair;
1073
1074                         priv->done++;
1075                         /* We can not just use the mail operation because the
1076                            values of done and total could change before the
1077                            idle is called */
1078                         state = modest_mail_operation_clone_state (info->mail_op);
1079                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1080                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1081                                          pair, (GDestroyNotify) modest_pair_free);
1082
1083                         g_object_unref (msg);
1084                         g_object_unref (folder);
1085
1086                         msg_num++;
1087                 }
1088                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1089                 g_ptr_array_free (new_headers, FALSE);
1090         }
1091         
1092         /* Perform send */
1093         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1094         priv->done = 0;
1095         priv->total = 0;
1096         if (priv->account != NULL) 
1097                 g_object_unref (priv->account);
1098         priv->account = g_object_ref (info->transport_account);
1099         
1100         send_queue = modest_runtime_get_send_queue (info->transport_account);
1101         if (send_queue) {
1102                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1103                 modest_tny_send_queue_try_to_send (send_queue);
1104                 g_source_remove (timeout);
1105         } else {
1106                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1107                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1108                              "cannot create a send queue for %s\n", 
1109                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1110                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1111         }
1112         
1113         /* Check if the operation was a success */
1114         if (!priv->error) {
1115                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1116
1117                 /* Update the last updated key */
1118                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1119                                  set_last_updated_idle, 
1120                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1121                                  (GDestroyNotify) g_free);
1122         }
1123
1124  out:
1125
1126         if (info->callback) {
1127                 UpdateAccountInfo *idle_info;
1128
1129                 /* This thread is not in the main lock */
1130                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1131                 idle_info->mail_op = g_object_ref (info->mail_op);
1132                 idle_info->new_headers = (new_headers) ? new_headers->len : 0;
1133                 idle_info->callback = info->callback;
1134                 g_idle_add (idle_update_account_cb, idle_info);
1135         }
1136
1137         /* Notify about operation end. Note that the info could be
1138            freed before this idle happens, but the mail operation will
1139            be still alive */
1140         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1141
1142         /* Frees */
1143         g_object_unref (query);
1144         g_object_unref (all_folders);
1145         g_object_unref (info->account);
1146         g_object_unref (info->transport_account);
1147         g_free (info->retrieve_type);
1148         g_slice_free (UpdateAccountInfo, info);
1149
1150         first_time = FALSE;
1151
1152         return NULL;
1153 }
1154
1155 gboolean
1156 modest_mail_operation_update_account (ModestMailOperation *self,
1157                                       const gchar *account_name,
1158                                       UpdateAccountCallback callback,
1159                                       gpointer user_data)
1160 {
1161         GThread *thread;
1162         UpdateAccountInfo *info;
1163         ModestMailOperationPrivate *priv;
1164         ModestAccountMgr *mgr;
1165         TnyStoreAccount *modest_account;
1166         TnyTransportAccount *transport_account;
1167
1168         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1169         g_return_val_if_fail (account_name, FALSE);
1170
1171         /* Init mail operation. Set total and done to 0, and do not
1172            update them, this way the progress objects will know that
1173            we have no clue about the number of the objects */
1174         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1175         priv->total = 0;
1176         priv->done  = 0;
1177         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1178
1179         /* Make sure that we have a connection, and request one 
1180          * if necessary:
1181          * TODO: Is there some way to trigger this for every attempt to 
1182          * use the network? */
1183         if (!modest_platform_connect_and_wait (NULL))
1184                 goto error;
1185
1186         /* Get the Modest account */
1187         modest_account = (TnyStoreAccount *)
1188                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1189                                                                      account_name,
1190                                                                      TNY_ACCOUNT_TYPE_STORE);
1191
1192         if (!modest_account) {
1193                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1194                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1195                              "cannot get tny store account for %s\n", account_name);
1196                 goto error;
1197         }
1198
1199         
1200         /* Get the transport account, we can not do it in the thread
1201            due to some problems with dbus */
1202         transport_account = (TnyTransportAccount *)
1203                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1204                                                                                     account_name);
1205         if (!transport_account) {
1206                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1207                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1208                              "cannot get tny transport account for %s\n", account_name);
1209                 goto error;
1210         }
1211
1212         /* Create the helper object */
1213         info = g_slice_new (UpdateAccountInfo);
1214         info->mail_op = self;
1215         info->account = modest_account;
1216         info->transport_account = transport_account;
1217         info->callback = callback;
1218         info->user_data = user_data;
1219
1220         /* Get the message size limit */
1221         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1222                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1223         if (info->max_size == 0)
1224                 info->max_size = G_MAXINT;
1225         else
1226                 info->max_size = info->max_size * KB;
1227
1228         /* Get per-account retrieval type */
1229         mgr = modest_runtime_get_account_mgr ();
1230         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1231                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1232
1233         /* Get per-account message amount retrieval limit */
1234         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1235                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1236         if (info->retrieve_limit == 0)
1237                 info->retrieve_limit = G_MAXINT;
1238                 
1239         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1240
1241         /* Set account busy */
1242         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1243         priv->account_name = g_strdup(account_name);
1244         
1245         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1246
1247         return TRUE;
1248
1249  error:
1250         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1251         if (callback) 
1252                 callback (self, 0, user_data);
1253         modest_mail_operation_notify_end (self);
1254         return FALSE;
1255 }
1256
1257 /* ******************************************************************* */
1258 /* ************************** STORE  ACTIONS ************************* */
1259 /* ******************************************************************* */
1260
1261
1262 TnyFolder *
1263 modest_mail_operation_create_folder (ModestMailOperation *self,
1264                                      TnyFolderStore *parent,
1265                                      const gchar *name)
1266 {
1267         ModestMailOperationPrivate *priv;
1268         TnyFolder *new_folder = NULL;
1269
1270         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1271         g_return_val_if_fail (name, NULL);
1272         
1273         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1274
1275         /* Check parent */
1276         if (TNY_IS_FOLDER (parent)) {
1277                 /* Check folder rules */
1278                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1279                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1280                         /* Set status failed and set an error */
1281                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1282                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1283                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1284                                      _("mail_in_ui_folder_create_error"));
1285                 }
1286         }
1287
1288         if (!strcmp (name, " ") || strchr (name, '/')) {
1289                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1290                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1291                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1292                              _("mail_in_ui_folder_create_error"));
1293         }
1294
1295         if (!priv->error) {
1296                 /* Create the folder */
1297                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1298                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1299                 if (!priv->error)
1300                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1301         }
1302
1303         /* Notify about operation end */
1304         modest_mail_operation_notify_end (self);
1305
1306         return new_folder;
1307 }
1308
1309 void
1310 modest_mail_operation_remove_folder (ModestMailOperation *self,
1311                                      TnyFolder           *folder,
1312                                      gboolean             remove_to_trash)
1313 {
1314         TnyAccount *account;
1315         ModestMailOperationPrivate *priv;
1316         ModestTnyFolderRules rules;
1317
1318         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1319         g_return_if_fail (TNY_IS_FOLDER (folder));
1320         
1321         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1322         
1323         /* Check folder rules */
1324         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1325         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1326                 /* Set status failed and set an error */
1327                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1328                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1329                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1330                              _("mail_in_ui_folder_delete_error"));
1331                 goto end;
1332         }
1333
1334         /* Get the account */
1335         account = modest_tny_folder_get_account (folder);
1336         priv->account = g_object_ref(account);
1337
1338         /* Delete folder or move to trash */
1339         if (remove_to_trash) {
1340                 TnyFolder *trash_folder = NULL;
1341                 trash_folder = modest_tny_account_get_special_folder (account,
1342                                                                       TNY_FOLDER_TYPE_TRASH);
1343                 /* TODO: error_handling */
1344                  modest_mail_operation_xfer_folder (self, folder,
1345                                                     TNY_FOLDER_STORE (trash_folder), 
1346                                                     TRUE, NULL, NULL);
1347         } else {
1348                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1349
1350                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1351                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1352
1353                 if (parent)
1354                         g_object_unref (G_OBJECT (parent));
1355         }
1356         g_object_unref (G_OBJECT (account));
1357
1358  end:
1359         /* Notify about operation end */
1360         modest_mail_operation_notify_end (self);
1361 }
1362
1363 static void
1364 transfer_folder_status_cb (GObject *obj,
1365                            TnyStatus *status,
1366                            gpointer user_data)
1367 {
1368         ModestMailOperation *self;
1369         ModestMailOperationPrivate *priv;
1370         ModestMailOperationState *state;
1371         XFerMsgAsyncHelper *helper;
1372
1373         g_return_if_fail (status != NULL);
1374         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1375
1376         helper = (XFerMsgAsyncHelper *) user_data;
1377         g_return_if_fail (helper != NULL);       
1378
1379         self = helper->mail_op;
1380         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1381
1382         priv->done = status->position;
1383         priv->total = status->of_total;
1384
1385         state = modest_mail_operation_clone_state (self);
1386         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1387         g_slice_free (ModestMailOperationState, state);
1388 }
1389
1390
1391 static void
1392 transfer_folder_cb (TnyFolder *folder, 
1393                     TnyFolderStore *into, 
1394                     gboolean cancelled, 
1395                     TnyFolder *new_folder, 
1396                     GError **err, 
1397                     gpointer user_data)
1398 {
1399         XFerMsgAsyncHelper *helper;
1400         ModestMailOperation *self = NULL;
1401         ModestMailOperationPrivate *priv = NULL;
1402
1403         helper = (XFerMsgAsyncHelper *) user_data;
1404         g_return_if_fail (helper != NULL);       
1405
1406         self = helper->mail_op;
1407         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1408
1409         if (*err) {
1410                 priv->error = g_error_copy (*err);
1411                 priv->done = 0;
1412                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1413         } else if (cancelled) {
1414                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1415                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1416                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1417                              _("Transference of %s was cancelled."),
1418                              tny_folder_get_name (folder));
1419         } else {
1420                 priv->done = 1;
1421                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1422         }
1423                 
1424         /* Notify about operation end */
1425         modest_mail_operation_notify_end (self);
1426
1427         /* If user defined callback function was defined, call it */
1428         if (helper->user_callback) {
1429                 gdk_threads_enter ();
1430                 helper->user_callback (priv->source, helper->user_data);
1431                 gdk_threads_leave ();
1432         }
1433
1434         /* Free */
1435         g_object_unref (helper->mail_op);
1436         g_slice_free   (XFerMsgAsyncHelper, helper);
1437         g_object_unref (folder);
1438         g_object_unref (into);
1439 }
1440
1441 void
1442 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1443                                    TnyFolder *folder,
1444                                    TnyFolderStore *parent,
1445                                    gboolean delete_original,
1446                                    XferMsgsAsynUserCallback user_callback,
1447                                    gpointer user_data)
1448 {
1449         ModestMailOperationPrivate *priv = NULL;
1450         ModestTnyFolderRules parent_rules = 0, rules; 
1451         XFerMsgAsyncHelper *helper = NULL;
1452
1453         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1454         g_return_if_fail (TNY_IS_FOLDER (folder));
1455
1456         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1457
1458         /* Get account and set it into mail_operation */
1459         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1460         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1461
1462         /* Get folder rules */
1463         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1464         if (TNY_IS_FOLDER (parent))
1465                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1466         
1467         /* The moveable restriction is applied also to copy operation */
1468         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1469                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1470                 /* Set status failed and set an error */
1471                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1472                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1473                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1474                              _("mail_in_ui_folder_move_target_error"));
1475
1476                 /* Notify the queue */
1477                 modest_mail_operation_notify_end (self);
1478         } else if (TNY_IS_FOLDER (parent) && 
1479                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1480                 /* Set status failed and set an error */
1481                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1482                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1483                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1484                              _("FIXME: parent folder does not accept new folders"));
1485
1486                 /* Notify the queue */
1487                 modest_mail_operation_notify_end (self);
1488         } else {
1489                 /* Pick references for async calls */
1490                 g_object_ref (folder);
1491                 g_object_ref (parent);
1492
1493                 /* Create the helper */
1494                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1495                 helper->mail_op = g_object_ref(self);
1496                 helper->dest_folder = NULL;
1497                 helper->headers = NULL;
1498                 helper->user_callback = user_callback;
1499                 helper->user_data = user_data;
1500                 
1501                 /* Move/Copy folder */          
1502                 tny_folder_copy_async (folder,
1503                                        parent,
1504                                        tny_folder_get_name (folder),
1505                                        delete_original,
1506                                        transfer_folder_cb,
1507                                        transfer_folder_status_cb,
1508                                        helper);
1509 /*                                     self); */
1510         }
1511 }
1512
1513 void
1514 modest_mail_operation_rename_folder (ModestMailOperation *self,
1515                                      TnyFolder *folder,
1516                                      const gchar *name)
1517 {
1518         ModestMailOperationPrivate *priv;
1519         ModestTnyFolderRules rules;
1520         XFerMsgAsyncHelper *helper;
1521
1522         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1523         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1524         g_return_if_fail (name);
1525         
1526         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1527
1528         /* Get account and set it into mail_operation */
1529         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1530
1531         /* Check folder rules */
1532         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1533         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1534                 /* Set status failed and set an error */
1535                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1536                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1537                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1538                              _("FIXME: unable to rename"));
1539
1540                 /* Notify about operation end */
1541                 modest_mail_operation_notify_end (self);
1542         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1543                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1544                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1545                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1546                              _("FIXME: unable to rename"));
1547                 /* Notify about operation end */
1548                 modest_mail_operation_notify_end (self);
1549         } else {
1550                 TnyFolderStore *into;
1551
1552                 /* Create the helper */
1553                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1554                 helper->mail_op = g_object_ref(self);
1555                 helper->dest_folder = NULL;
1556                 helper->headers = NULL;
1557                 helper->user_callback = NULL;
1558                 helper->user_data = NULL;
1559
1560                 /* Rename. Camel handles folder subscription/unsubscription */
1561                 into = tny_folder_get_folder_store (folder);
1562                 tny_folder_copy_async (folder, into, name, TRUE,
1563                                  transfer_folder_cb,
1564                                  transfer_folder_status_cb,
1565                                  helper);
1566 /*                               self); */
1567                 if (into)
1568                         g_object_unref (into);          
1569         }
1570  }
1571
1572 /* ******************************************************************* */
1573 /* **************************  MSG  ACTIONS  ************************* */
1574 /* ******************************************************************* */
1575
1576 void modest_mail_operation_get_msg (ModestMailOperation *self,
1577                                     TnyHeader *header,
1578                                     GetMsgAsyncUserCallback user_callback,
1579                                     gpointer user_data)
1580 {
1581         GetMsgAsyncHelper *helper = NULL;
1582         TnyFolder *folder;
1583         ModestMailOperationPrivate *priv;
1584         
1585         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1586         g_return_if_fail (TNY_IS_HEADER (header));
1587         
1588         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1589         folder = tny_header_get_folder (header);
1590
1591         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1592
1593         /* Get message from folder */
1594         if (folder) {
1595                 /* Get account and set it into mail_operation */
1596                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1597
1598                 helper = g_slice_new0 (GetMsgAsyncHelper);
1599                 helper->mail_op = self;
1600                 helper->user_callback = user_callback;
1601                 helper->user_data = user_data;
1602                 helper->header = g_object_ref (header);
1603
1604                 // The callback's reference so that the mail op is not
1605                 // finalized until the async operation is completed even if
1606                 // the user canceled the request meanwhile.
1607                 g_object_ref (G_OBJECT (helper->mail_op));
1608
1609                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1610
1611                 g_object_unref (G_OBJECT (folder));
1612         } else {
1613                 /* Set status failed and set an error */
1614                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1615                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1616                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1617                              _("Error trying to get a message. No folder found for header"));
1618
1619                 /* Notify the queue */
1620                 modest_mail_operation_notify_end (self);
1621         }
1622 }
1623
1624 static void
1625 get_msg_cb (TnyFolder *folder, 
1626             gboolean cancelled, 
1627             TnyMsg *msg, 
1628             GError **error, 
1629             gpointer user_data)
1630 {
1631         GetMsgAsyncHelper *helper = NULL;
1632         ModestMailOperation *self = NULL;
1633         ModestMailOperationPrivate *priv = NULL;
1634
1635         helper = (GetMsgAsyncHelper *) user_data;
1636         g_return_if_fail (helper != NULL);       
1637         self = helper->mail_op;
1638         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1639         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1640
1641         /* Check errors and cancel */
1642         if (*error) {
1643                 priv->error = g_error_copy (*error);
1644                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1645         } else if (cancelled) {
1646                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1647                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1648                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1649                              _("Error trying to refresh the contents of %s"),
1650                              tny_folder_get_name (folder));
1651         } else {
1652                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1653         }
1654
1655         /* If user defined callback function was defined, call it even
1656            if the operation failed*/
1657         if (helper->user_callback) {
1658                 /* This callback is called into an iddle by tinymail,
1659                    and idles are not in the main lock */
1660                 gdk_threads_enter ();
1661                 helper->user_callback (self, helper->header, msg, helper->user_data);
1662                 gdk_threads_leave ();   
1663         }
1664
1665         /* Free */
1666         g_object_unref (helper->mail_op);
1667         g_object_unref (helper->header);
1668         g_slice_free (GetMsgAsyncHelper, helper);
1669                 
1670         /* Notify about operation end */
1671         modest_mail_operation_notify_end (self);
1672 }
1673
1674 static void     
1675 get_msg_status_cb (GObject *obj,
1676                    TnyStatus *status,  
1677                    gpointer user_data)
1678 {
1679         GetMsgAsyncHelper *helper = NULL;
1680         ModestMailOperation *self;
1681         ModestMailOperationPrivate *priv;
1682         ModestMailOperationState *state;
1683
1684         g_return_if_fail (status != NULL);
1685         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1686
1687         helper = (GetMsgAsyncHelper *) user_data;
1688         g_return_if_fail (helper != NULL);       
1689
1690         self = helper->mail_op;
1691         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1692
1693         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1694                 return;
1695
1696         priv->done = 1;
1697         priv->total = 1;
1698
1699         state = modest_mail_operation_clone_state (self);
1700         state->bytes_done = status->position;
1701         state->bytes_total = status->of_total;
1702         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1703         g_slice_free (ModestMailOperationState, state);
1704 }
1705
1706 /****************************************************/
1707 typedef struct {
1708         ModestMailOperation *mail_op;
1709         TnyList *headers;
1710         GetMsgAsyncUserCallback user_callback;
1711         gpointer user_data;
1712         GDestroyNotify notify;
1713 } GetFullMsgsInfo;
1714
1715 typedef struct {
1716         GetMsgAsyncUserCallback user_callback;
1717         TnyHeader *header;
1718         TnyMsg *msg;
1719         gpointer user_data;
1720         ModestMailOperation *mail_op;
1721 } NotifyGetMsgsInfo;
1722
1723
1724 /* 
1725  * Used by get_msgs_full_thread to call the user_callback for each
1726  * message that has been read
1727  */
1728 static gboolean
1729 notify_get_msgs_full (gpointer data)
1730 {
1731         NotifyGetMsgsInfo *info;
1732
1733         info = (NotifyGetMsgsInfo *) data;      
1734
1735         /* Call the user callback. Idles are not in the main lock, so
1736            lock it */
1737         gdk_threads_enter ();
1738         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1739         gdk_threads_leave ();
1740
1741         g_slice_free (NotifyGetMsgsInfo, info);
1742
1743         return FALSE;
1744 }
1745
1746 /* 
1747  * Used by get_msgs_full_thread to free al the thread resources and to
1748  * call the destroy function for the passed user_data
1749  */
1750 static gboolean
1751 get_msgs_full_destroyer (gpointer data)
1752 {
1753         GetFullMsgsInfo *info;
1754
1755         info = (GetFullMsgsInfo *) data;
1756
1757         if (info->notify) {
1758                 gdk_threads_enter ();   
1759                 info->notify (info->user_data);
1760                 gdk_threads_leave ();
1761         }
1762
1763         /* free */
1764         g_object_unref (info->headers);
1765         g_slice_free (GetFullMsgsInfo, info);
1766
1767         return FALSE;
1768 }
1769
1770 static gpointer
1771 get_msgs_full_thread (gpointer thr_user_data)
1772 {
1773         GetFullMsgsInfo *info;
1774         ModestMailOperationPrivate *priv = NULL;
1775         TnyIterator *iter = NULL;
1776         
1777         info = (GetFullMsgsInfo *) thr_user_data;       
1778         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1779
1780         iter = tny_list_create_iterator (info->headers);
1781         while (!tny_iterator_is_done (iter)) { 
1782                 TnyHeader *header;
1783                 TnyFolder *folder;
1784                 
1785                 header = TNY_HEADER (tny_iterator_get_current (iter));
1786                 folder = tny_header_get_folder (header);
1787                                 
1788                 /* Get message from folder */
1789                 if (folder) {
1790                         TnyMsg *msg;
1791                         /* The callback will call it per each header */
1792                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1793
1794                         if (msg) {
1795                                 ModestMailOperationState *state;
1796                                 ModestPair *pair;
1797
1798                                 priv->done++;
1799
1800                                 /* notify progress */
1801                                 state = modest_mail_operation_clone_state (info->mail_op);
1802                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1803                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1804                                                  pair, (GDestroyNotify) modest_pair_free);
1805
1806                                 /* The callback is the responsible for
1807                                    freeing the message */
1808                                 if (info->user_callback) {
1809                                         NotifyGetMsgsInfo *info_notify;
1810                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1811                                         info_notify->user_callback = info->user_callback;
1812                                         info_notify->mail_op = info->mail_op;
1813                                         info_notify->header = g_object_ref (header);
1814                                         info_notify->msg = g_object_ref (msg);
1815                                         info_notify->user_data = info->user_data;
1816                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1817                                                          notify_get_msgs_full, 
1818                                                          info_notify, NULL);
1819                                 }
1820                                 g_object_unref (msg);
1821                         }
1822                 } else {
1823                         /* Set status failed and set an error */
1824                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1825                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1826                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1827                                      "Error trying to get a message. No folder found for header");
1828                 }
1829                 g_object_unref (header);                
1830                 tny_iterator_next (iter);
1831         }
1832
1833         /* Set operation status */
1834         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1835                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1836
1837         /* Notify about operation end */
1838         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1839
1840         /* Free thread resources. Will be called after all previous idles */
1841         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1842
1843         return NULL;
1844 }
1845
1846 void 
1847 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1848                                      TnyList *header_list, 
1849                                      GetMsgAsyncUserCallback user_callback,
1850                                      gpointer user_data,
1851                                      GDestroyNotify notify)
1852 {
1853         TnyHeader *header = NULL;
1854         TnyFolder *folder = NULL;
1855         GThread *thread;
1856         ModestMailOperationPrivate *priv = NULL;
1857         GetFullMsgsInfo *info = NULL;
1858         gboolean size_ok = TRUE;
1859         gint max_size;
1860         TnyIterator *iter = NULL;
1861         
1862         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1863         
1864         /* Init mail operation */
1865         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1866         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1867         priv->done = 0;
1868         priv->total = tny_list_get_length(header_list);
1869
1870         /* Get account and set it into mail_operation */
1871         if (tny_list_get_length (header_list) >= 1) {
1872                 iter = tny_list_create_iterator (header_list);
1873                 header = TNY_HEADER (tny_iterator_get_current (iter));
1874                 folder = tny_header_get_folder (header);                
1875                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1876                 g_object_unref (header);
1877                 g_object_unref (folder);
1878
1879                 if (tny_list_get_length (header_list) == 1) {
1880                         g_object_unref (iter);
1881                         iter = NULL;
1882                 }
1883         }
1884
1885         /* Get msg size limit */
1886         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1887                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1888                                          &(priv->error));
1889         if (priv->error) {
1890                 g_clear_error (&(priv->error));
1891                 max_size = G_MAXINT;
1892         } else {
1893                 max_size = max_size * KB;
1894         }
1895
1896         /* Check message size limits. If there is only one message
1897            always retrieve it */
1898         if (iter != NULL) {
1899                 while (!tny_iterator_is_done (iter) && size_ok) {
1900                         header = TNY_HEADER (tny_iterator_get_current (iter));
1901                         if (tny_header_get_message_size (header) >= max_size)
1902                                 size_ok = FALSE;
1903                         g_object_unref (header);
1904                         tny_iterator_next (iter);
1905                 }
1906                 g_object_unref (iter);
1907         }
1908
1909         if (size_ok) {
1910                 /* Create the info */
1911                 info = g_slice_new0 (GetFullMsgsInfo);
1912                 info->mail_op = self;
1913                 info->user_callback = user_callback;
1914                 info->user_data = user_data;
1915                 info->headers = g_object_ref (header_list);
1916                 info->notify = notify;
1917
1918                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1919         } else {
1920                 /* Set status failed and set an error */
1921                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1922                 /* FIXME: the error msg is different for pop */
1923                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1924                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1925                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1926                 /* Remove from queue and free resources */
1927                 modest_mail_operation_notify_end (self);
1928                 if (notify)
1929                         notify (user_data);
1930         }
1931 }
1932
1933
1934 void 
1935 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
1936                                   gboolean remove_to_trash /*ignored*/)
1937 {
1938         TnyFolder *folder;
1939         ModestMailOperationPrivate *priv;
1940
1941         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1942         g_return_if_fail (TNY_IS_HEADER (header));
1943
1944         if (remove_to_trash)
1945                 g_warning ("remove to trash is not implemented");
1946
1947         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1948         folder = tny_header_get_folder (header);
1949
1950         /* Get account and set it into mail_operation */
1951         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1952
1953         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1954
1955
1956         tny_folder_remove_msg (folder, header, &(priv->error));
1957         if (!priv->error) {
1958                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1959
1960                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
1961                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
1962                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
1963                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1964                 else
1965                         /* lcoal folders */
1966                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1967         }
1968         
1969         
1970         /* Set status */
1971         if (!priv->error)
1972                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1973         else
1974                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1975
1976         /* Free */
1977         g_object_unref (G_OBJECT (folder));
1978
1979         /* Notify about operation end */
1980         modest_mail_operation_notify_end (self);
1981 }
1982
1983 static void
1984 transfer_msgs_status_cb (GObject *obj,
1985                          TnyStatus *status,  
1986                          gpointer user_data)
1987 {
1988         XFerMsgAsyncHelper *helper = NULL;
1989         ModestMailOperation *self;
1990         ModestMailOperationPrivate *priv;
1991         ModestMailOperationState *state;
1992
1993
1994         g_return_if_fail (status != NULL);
1995         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1996
1997         helper = (XFerMsgAsyncHelper *) user_data;
1998         g_return_if_fail (helper != NULL);       
1999
2000         self = helper->mail_op;
2001         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2002
2003         priv->done = status->position;
2004         priv->total = status->of_total;
2005
2006         state = modest_mail_operation_clone_state (self);
2007         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2008         g_slice_free (ModestMailOperationState, state);
2009 }
2010
2011
2012 static void
2013 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2014 {
2015         XFerMsgAsyncHelper *helper;
2016         ModestMailOperation *self;
2017         ModestMailOperationPrivate *priv;
2018
2019         helper = (XFerMsgAsyncHelper *) user_data;
2020         self = helper->mail_op;
2021
2022         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2023
2024         if (*err) {
2025                 priv->error = g_error_copy (*err);
2026                 priv->done = 0;
2027                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2028         } else if (cancelled) {
2029                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2030                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2031                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2032                              _("Error trying to refresh the contents of %s"),
2033                              tny_folder_get_name (folder));
2034         } else {
2035                 priv->done = 1;
2036                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2037         }
2038
2039         /* Notify about operation end */
2040         modest_mail_operation_notify_end (self);
2041
2042         /* If user defined callback function was defined, call it */
2043         if (helper->user_callback) {
2044                 gdk_threads_enter ();
2045                 helper->user_callback (priv->source, helper->user_data);
2046                 gdk_threads_leave ();
2047         }
2048
2049         /* Free */
2050         g_object_unref (helper->headers);
2051         g_object_unref (helper->dest_folder);
2052         g_object_unref (helper->mail_op);
2053         g_slice_free   (XFerMsgAsyncHelper, helper);
2054         g_object_unref (folder);
2055
2056 }
2057
2058 void
2059 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2060                                  TnyList *headers, 
2061                                  TnyFolder *folder, 
2062                                  gboolean delete_original,
2063                                  XferMsgsAsynUserCallback user_callback,
2064                                  gpointer user_data)
2065 {
2066         ModestMailOperationPrivate *priv;
2067         TnyIterator *iter;
2068         TnyFolder *src_folder;
2069         XFerMsgAsyncHelper *helper;
2070         TnyHeader *header;
2071         ModestTnyFolderRules rules;
2072         const gchar *id1 = NULL;
2073         const gchar *id2 = NULL;
2074         gboolean same_folder = FALSE;
2075
2076         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2077         g_return_if_fail (TNY_IS_LIST (headers));
2078         g_return_if_fail (TNY_IS_FOLDER (folder));
2079
2080         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2081         priv->total = 1;
2082         priv->done = 0;
2083         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2084
2085         /* Apply folder rules */
2086         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2087         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2088                 /* Set status failed and set an error */
2089                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2090                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2091                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2092                              _("ckct_ib_unable_to_paste_here"));
2093                 /* Notify the queue */
2094                 modest_mail_operation_notify_end (self);
2095                 return;
2096         }
2097                 
2098         /* Get source folder */
2099         iter = tny_list_create_iterator (headers);
2100         header = TNY_HEADER (tny_iterator_get_current (iter));
2101         src_folder = tny_header_get_folder (header);
2102         g_object_unref (header);
2103         g_object_unref (iter);
2104
2105         /* Check folder source and destination */
2106         id1 = tny_folder_get_id (src_folder);
2107         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2108         same_folder = !g_ascii_strcasecmp (id1, id2);
2109         if (same_folder) {
2110                 /* Set status failed and set an error */
2111                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2112                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2113                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2114                              _("mcen_ib_unable_to_copy_samefolder"));
2115                 
2116                 /* Notify the queue */
2117                 modest_mail_operation_notify_end (self);
2118                 
2119                 /* Free */
2120                 g_object_unref (src_folder);            
2121                 return;
2122         }
2123
2124         /* Create the helper */
2125         helper = g_slice_new0 (XFerMsgAsyncHelper);
2126         helper->mail_op = g_object_ref(self);
2127         helper->dest_folder = g_object_ref(folder);
2128         helper->headers = g_object_ref(headers);
2129         helper->user_callback = user_callback;
2130         helper->user_data = user_data;
2131
2132         /* Get account and set it into mail_operation */
2133         priv->account = modest_tny_folder_get_account (src_folder);
2134
2135         /* Transfer messages */
2136         tny_folder_transfer_msgs_async (src_folder, 
2137                                         headers, 
2138                                         folder, 
2139                                         delete_original, 
2140                                         transfer_msgs_cb, 
2141                                         transfer_msgs_status_cb,
2142                                         helper);
2143 }
2144
2145
2146 static void
2147 on_refresh_folder (TnyFolder   *folder, 
2148                    gboolean     cancelled, 
2149                    GError     **error,
2150                    gpointer     user_data)
2151 {
2152         RefreshAsyncHelper *helper = NULL;
2153         ModestMailOperation *self = NULL;
2154         ModestMailOperationPrivate *priv = NULL;
2155
2156         helper = (RefreshAsyncHelper *) user_data;
2157         self = helper->mail_op;
2158         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2159
2160         if (*error) {
2161                 priv->error = g_error_copy (*error);
2162                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2163                 goto out;
2164         }
2165
2166         if (cancelled) {
2167                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2168                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2169                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2170                              _("Error trying to refresh the contents of %s"),
2171                              tny_folder_get_name (folder));
2172                 goto out;
2173         }
2174
2175         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2176
2177  out:
2178         /* Call user defined callback, if it exists */
2179         if (helper->user_callback) {
2180                 gdk_threads_enter ();
2181                 helper->user_callback (self, folder, helper->user_data);
2182                 gdk_threads_leave ();
2183         }
2184
2185         /* Free */
2186         g_object_unref (helper->mail_op);
2187         g_slice_free   (RefreshAsyncHelper, helper);
2188         g_object_unref (folder);
2189
2190         /* Notify about operation end */
2191         modest_mail_operation_notify_end (self);
2192 }
2193
2194 static void
2195 on_refresh_folder_status_update (GObject *obj,
2196                                  TnyStatus *status,
2197                                  gpointer user_data)
2198 {
2199         RefreshAsyncHelper *helper = NULL;
2200         ModestMailOperation *self = NULL;
2201         ModestMailOperationPrivate *priv = NULL;
2202         ModestMailOperationState *state;
2203
2204         g_return_if_fail (user_data != NULL);
2205         g_return_if_fail (status != NULL);
2206         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2207
2208         helper = (RefreshAsyncHelper *) user_data;
2209         self = helper->mail_op;
2210         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2211
2212         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2213
2214         priv->done = status->position;
2215         priv->total = status->of_total;
2216
2217         state = modest_mail_operation_clone_state (self);
2218         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2219         g_slice_free (ModestMailOperationState, state);
2220 }
2221
2222 void 
2223 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2224                                        TnyFolder *folder,
2225                                        RefreshAsyncUserCallback user_callback,
2226                                        gpointer user_data)
2227 {
2228         ModestMailOperationPrivate *priv = NULL;
2229         RefreshAsyncHelper *helper = NULL;
2230
2231         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2232
2233         /* Pick a reference */
2234         g_object_ref (folder);
2235
2236         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2237
2238         /* Get account and set it into mail_operation */
2239         priv->account = modest_tny_folder_get_account  (folder);
2240
2241         /* Create the helper */
2242         helper = g_slice_new0 (RefreshAsyncHelper);
2243         helper->mail_op = g_object_ref(self);
2244         helper->user_callback = user_callback;
2245         helper->user_data = user_data;
2246
2247         /* Refresh the folder. TODO: tinymail could issue a status
2248            updates before the callback call then this could happen. We
2249            must review the design */
2250         tny_folder_refresh_async (folder,
2251                                   on_refresh_folder,
2252                                   on_refresh_folder_status_update,
2253                                   helper);
2254 }
2255
2256 /**
2257  *
2258  * It's used by the mail operation queue to notify the observers
2259  * attached to that signal that the operation finished. We need to use
2260  * that because tinymail does not give us the progress of a given
2261  * operation when it finishes (it directly calls the operation
2262  * callback).
2263  */
2264 static void
2265 modest_mail_operation_notify_end (ModestMailOperation *self)
2266 {
2267         ModestMailOperationState *state;
2268         ModestMailOperationPrivate *priv = NULL;
2269
2270         g_return_if_fail (self);
2271
2272         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2273
2274         if (!priv) {
2275                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2276                 return;
2277         }
2278         
2279         /* Set the account back to not busy */
2280         if (priv->account_name) {
2281                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2282                                                      priv->account_name, FALSE);
2283                 g_free(priv->account_name);
2284                 priv->account_name = NULL;
2285         }
2286         
2287         /* Notify the observers about the mail opertation end */
2288         state = modest_mail_operation_clone_state (self);
2289         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2290         g_slice_free (ModestMailOperationState, state);
2291 }