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