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