9dfcaf81ccaadb1c31c50b01314ad957dd619913
[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 (if operation was not cancelled) */
1098         if (did_a_cancel) goto out;             
1099         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1100         priv->done = 0;
1101         priv->total = 0;
1102         if (priv->account != NULL) 
1103                 g_object_unref (priv->account);
1104         priv->account = g_object_ref (info->transport_account);
1105         
1106         send_queue = modest_runtime_get_send_queue (info->transport_account);
1107         if (send_queue) {
1108                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1109                 modest_tny_send_queue_try_to_send (send_queue);
1110                 g_source_remove (timeout);
1111         } else {
1112                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1113                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1114                              "cannot create a send queue for %s\n", 
1115                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1116                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1117         }
1118         
1119         /* Check if the operation was a success */
1120         if (!priv->error) {
1121                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1122
1123                 /* Update the last updated key */
1124                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1125                                  set_last_updated_idle, 
1126                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1127                                  (GDestroyNotify) g_free);
1128         }
1129
1130  out:
1131
1132         if (info->callback) {
1133                 UpdateAccountInfo *idle_info;
1134
1135                 /* This thread is not in the main lock */
1136                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1137                 idle_info->mail_op = g_object_ref (info->mail_op);
1138                 idle_info->new_headers = (new_headers) ? new_headers->len : 0;
1139                 idle_info->callback = info->callback;
1140                 g_idle_add (idle_update_account_cb, idle_info);
1141         }
1142
1143         /* Notify about operation end. Note that the info could be
1144            freed before this idle happens, but the mail operation will
1145            be still alive */
1146         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1147
1148         /* Frees */
1149         g_object_unref (query);
1150         g_object_unref (all_folders);
1151         g_object_unref (info->account);
1152         g_object_unref (info->transport_account);
1153         g_free (info->retrieve_type);
1154         g_slice_free (UpdateAccountInfo, info);
1155
1156         first_time = FALSE;
1157
1158         return NULL;
1159 }
1160
1161 gboolean
1162 modest_mail_operation_update_account (ModestMailOperation *self,
1163                                       const gchar *account_name,
1164                                       UpdateAccountCallback callback,
1165                                       gpointer user_data)
1166 {
1167         GThread *thread;
1168         UpdateAccountInfo *info;
1169         ModestMailOperationPrivate *priv;
1170         ModestAccountMgr *mgr;
1171         TnyStoreAccount *modest_account;
1172         TnyTransportAccount *transport_account;
1173
1174         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1175         g_return_val_if_fail (account_name, FALSE);
1176
1177         /* Init mail operation. Set total and done to 0, and do not
1178            update them, this way the progress objects will know that
1179            we have no clue about the number of the objects */
1180         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1181         priv->total = 0;
1182         priv->done  = 0;
1183         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1184
1185         /* Make sure that we have a connection, and request one 
1186          * if necessary:
1187          * TODO: Is there some way to trigger this for every attempt to 
1188          * use the network? */
1189         if (!modest_platform_connect_and_wait (NULL))
1190                 goto error;
1191
1192         /* Get the Modest account */
1193         modest_account = (TnyStoreAccount *)
1194                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1195                                                                      account_name,
1196                                                                      TNY_ACCOUNT_TYPE_STORE);
1197
1198         if (!modest_account) {
1199                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1200                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1201                              "cannot get tny store account for %s\n", account_name);
1202                 goto error;
1203         }
1204
1205         
1206         /* Get the transport account, we can not do it in the thread
1207            due to some problems with dbus */
1208         transport_account = (TnyTransportAccount *)
1209                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1210                                                                                     account_name);
1211         if (!transport_account) {
1212                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1213                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1214                              "cannot get tny transport account for %s\n", account_name);
1215                 goto error;
1216         }
1217
1218         /* Create the helper object */
1219         info = g_slice_new (UpdateAccountInfo);
1220         info->mail_op = self;
1221         info->account = modest_account;
1222         info->transport_account = transport_account;
1223         info->callback = callback;
1224         info->user_data = user_data;
1225
1226         /* Get the message size limit */
1227         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1228                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1229         if (info->max_size == 0)
1230                 info->max_size = G_MAXINT;
1231         else
1232                 info->max_size = info->max_size * KB;
1233
1234         /* Get per-account retrieval type */
1235         mgr = modest_runtime_get_account_mgr ();
1236         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1237                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1238
1239         /* Get per-account message amount retrieval limit */
1240         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1241                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1242         if (info->retrieve_limit == 0)
1243                 info->retrieve_limit = G_MAXINT;
1244                 
1245         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1246
1247         /* Set account busy */
1248         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1249         priv->account_name = g_strdup(account_name);
1250         
1251         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1252
1253         return TRUE;
1254
1255  error:
1256         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1257         if (callback) 
1258                 callback (self, 0, user_data);
1259         modest_mail_operation_notify_end (self);
1260         return FALSE;
1261 }
1262
1263 /* ******************************************************************* */
1264 /* ************************** STORE  ACTIONS ************************* */
1265 /* ******************************************************************* */
1266
1267
1268 TnyFolder *
1269 modest_mail_operation_create_folder (ModestMailOperation *self,
1270                                      TnyFolderStore *parent,
1271                                      const gchar *name)
1272 {
1273         ModestMailOperationPrivate *priv;
1274         TnyFolder *new_folder = NULL;
1275
1276         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1277         g_return_val_if_fail (name, NULL);
1278         
1279         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1280
1281         /* Check parent */
1282         if (TNY_IS_FOLDER (parent)) {
1283                 /* Check folder rules */
1284                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1285                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1286                         /* Set status failed and set an error */
1287                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1288                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1289                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1290                                      _("mail_in_ui_folder_create_error"));
1291                 }
1292         }
1293
1294         if (!strcmp (name, " ") || strchr (name, '/')) {
1295                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1296                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1297                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1298                              _("mail_in_ui_folder_create_error"));
1299         }
1300
1301         if (!priv->error) {
1302                 /* Create the folder */
1303                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1304                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1305                 if (!priv->error)
1306                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1307         }
1308
1309         /* Notify about operation end */
1310         modest_mail_operation_notify_end (self);
1311
1312         return new_folder;
1313 }
1314
1315 void
1316 modest_mail_operation_remove_folder (ModestMailOperation *self,
1317                                      TnyFolder           *folder,
1318                                      gboolean             remove_to_trash)
1319 {
1320         TnyAccount *account;
1321         ModestMailOperationPrivate *priv;
1322         ModestTnyFolderRules rules;
1323
1324         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1325         g_return_if_fail (TNY_IS_FOLDER (folder));
1326         
1327         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1328         
1329         /* Check folder rules */
1330         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1331         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1332                 /* Set status failed and set an error */
1333                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1334                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1335                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1336                              _("mail_in_ui_folder_delete_error"));
1337                 goto end;
1338         }
1339
1340         /* Get the account */
1341         account = modest_tny_folder_get_account (folder);
1342         priv->account = g_object_ref(account);
1343
1344         /* Delete folder or move to trash */
1345         if (remove_to_trash) {
1346                 TnyFolder *trash_folder = NULL;
1347                 trash_folder = modest_tny_account_get_special_folder (account,
1348                                                                       TNY_FOLDER_TYPE_TRASH);
1349                 /* TODO: error_handling */
1350                  modest_mail_operation_xfer_folder (self, folder,
1351                                                     TNY_FOLDER_STORE (trash_folder), 
1352                                                     TRUE, NULL, NULL);
1353         } else {
1354                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1355
1356                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1357                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1358
1359                 if (parent)
1360                         g_object_unref (G_OBJECT (parent));
1361         }
1362         g_object_unref (G_OBJECT (account));
1363
1364  end:
1365         /* Notify about operation end */
1366         modest_mail_operation_notify_end (self);
1367 }
1368
1369 static void
1370 transfer_folder_status_cb (GObject *obj,
1371                            TnyStatus *status,
1372                            gpointer user_data)
1373 {
1374         ModestMailOperation *self;
1375         ModestMailOperationPrivate *priv;
1376         ModestMailOperationState *state;
1377         XFerMsgAsyncHelper *helper;
1378
1379         g_return_if_fail (status != NULL);
1380         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1381
1382         helper = (XFerMsgAsyncHelper *) user_data;
1383         g_return_if_fail (helper != NULL);
1384
1385         self = helper->mail_op;
1386         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1387
1388         priv->done = status->position;
1389         priv->total = status->of_total;
1390
1391         state = modest_mail_operation_clone_state (self);
1392         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1393         g_slice_free (ModestMailOperationState, state);
1394 }
1395
1396
1397 static void
1398 transfer_folder_cb (TnyFolder *folder, 
1399                     TnyFolderStore *into, 
1400                     gboolean cancelled, 
1401                     TnyFolder *new_folder, 
1402                     GError **err, 
1403                     gpointer user_data)
1404 {
1405         XFerMsgAsyncHelper *helper;
1406         ModestMailOperation *self = NULL;
1407         ModestMailOperationPrivate *priv = NULL;
1408
1409         helper = (XFerMsgAsyncHelper *) user_data;
1410         g_return_if_fail (helper != NULL);       
1411
1412         self = helper->mail_op;
1413         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1414
1415         if (*err) {
1416                 priv->error = g_error_copy (*err);
1417                 priv->done = 0;
1418                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1419         } else if (cancelled) {
1420                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1421                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1422                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1423                              _("Transference of %s was cancelled."),
1424                              tny_folder_get_name (folder));
1425         } else {
1426                 priv->done = 1;
1427                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1428         }
1429                 
1430         /* Notify about operation end */
1431         modest_mail_operation_notify_end (self);
1432
1433         /* If user defined callback function was defined, call it */
1434         if (helper->user_callback) {
1435                 gdk_threads_enter ();
1436                 helper->user_callback (priv->source, helper->user_data);
1437                 gdk_threads_leave ();
1438         }
1439
1440         /* Free */
1441         g_object_unref (helper->mail_op);
1442         g_slice_free   (XFerMsgAsyncHelper, helper);
1443         g_object_unref (folder);
1444         g_object_unref (into);
1445 }
1446
1447 void
1448 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1449                                    TnyFolder *folder,
1450                                    TnyFolderStore *parent,
1451                                    gboolean delete_original,
1452                                    XferMsgsAsynUserCallback user_callback,
1453                                    gpointer user_data)
1454 {
1455         ModestMailOperationPrivate *priv = NULL;
1456         ModestTnyFolderRules parent_rules = 0, rules; 
1457         XFerMsgAsyncHelper *helper = NULL;
1458
1459         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1460         g_return_if_fail (TNY_IS_FOLDER (folder));
1461
1462         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1463
1464         /* Get account and set it into mail_operation */
1465         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1466         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1467
1468         /* Get folder rules */
1469         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1470         if (TNY_IS_FOLDER (parent))
1471                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1472         
1473         /* The moveable restriction is applied also to copy operation */
1474         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1475                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1476                 /* Set status failed and set an error */
1477                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1478                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1479                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1480                              _("mail_in_ui_folder_move_target_error"));
1481
1482                 /* Notify the queue */
1483                 modest_mail_operation_notify_end (self);
1484         } else if (TNY_IS_FOLDER (parent) && 
1485                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1486                 /* Set status failed and set an error */
1487                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1488                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1489                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1490                              _("FIXME: parent folder does not accept new folders"));
1491
1492                 /* Notify the queue */
1493                 modest_mail_operation_notify_end (self);
1494         } else {
1495                 /* Pick references for async calls */
1496                 g_object_ref (folder);
1497                 g_object_ref (parent);
1498
1499                 /* Create the helper */
1500                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1501                 helper->mail_op = g_object_ref(self);
1502                 helper->dest_folder = NULL;
1503                 helper->headers = NULL;
1504                 helper->user_callback = user_callback;
1505                 helper->user_data = user_data;
1506                 
1507                 /* Move/Copy folder */          
1508                 tny_folder_copy_async (folder,
1509                                        parent,
1510                                        tny_folder_get_name (folder),
1511                                        delete_original,
1512                                        transfer_folder_cb,
1513                                        transfer_folder_status_cb,
1514                                        helper);
1515 /*                                     self); */
1516         }
1517 }
1518
1519 void
1520 modest_mail_operation_rename_folder (ModestMailOperation *self,
1521                                      TnyFolder *folder,
1522                                      const gchar *name)
1523 {
1524         ModestMailOperationPrivate *priv;
1525         ModestTnyFolderRules rules;
1526         XFerMsgAsyncHelper *helper;
1527
1528         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1529         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1530         g_return_if_fail (name);
1531         
1532         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1533
1534         /* Get account and set it into mail_operation */
1535         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1536
1537         /* Check folder rules */
1538         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1539         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1540                 /* Set status failed and set an error */
1541                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1542                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1543                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1544                              _("FIXME: unable to rename"));
1545
1546                 /* Notify about operation end */
1547                 modest_mail_operation_notify_end (self);
1548         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1549                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1550                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1551                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1552                              _("FIXME: unable to rename"));
1553                 /* Notify about operation end */
1554                 modest_mail_operation_notify_end (self);
1555         } else {
1556                 TnyFolderStore *into;
1557
1558                 /* Create the helper */
1559                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1560                 helper->mail_op = g_object_ref(self);
1561                 helper->dest_folder = NULL;
1562                 helper->headers = NULL;
1563                 helper->user_callback = NULL;
1564                 helper->user_data = NULL;
1565
1566                 /* Rename. Camel handles folder subscription/unsubscription */
1567                 into = tny_folder_get_folder_store (folder);
1568                 tny_folder_copy_async (folder, into, name, TRUE,
1569                                  transfer_folder_cb,
1570                                  transfer_folder_status_cb,
1571                                  helper);
1572 /*                               self); */
1573                 if (into)
1574                         g_object_unref (into);          
1575         }
1576  }
1577
1578 /* ******************************************************************* */
1579 /* **************************  MSG  ACTIONS  ************************* */
1580 /* ******************************************************************* */
1581
1582 void modest_mail_operation_get_msg (ModestMailOperation *self,
1583                                     TnyHeader *header,
1584                                     GetMsgAsyncUserCallback user_callback,
1585                                     gpointer user_data)
1586 {
1587         GetMsgAsyncHelper *helper = NULL;
1588         TnyFolder *folder;
1589         ModestMailOperationPrivate *priv;
1590         
1591         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1592         g_return_if_fail (TNY_IS_HEADER (header));
1593         
1594         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1595         folder = tny_header_get_folder (header);
1596
1597         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1598
1599         /* Get message from folder */
1600         if (folder) {
1601                 /* Get account and set it into mail_operation */
1602                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1603
1604                 helper = g_slice_new0 (GetMsgAsyncHelper);
1605                 helper->mail_op = self;
1606                 helper->user_callback = user_callback;
1607                 helper->user_data = user_data;
1608                 helper->header = g_object_ref (header);
1609
1610                 // The callback's reference so that the mail op is not
1611                 // finalized until the async operation is completed even if
1612                 // the user canceled the request meanwhile.
1613                 g_object_ref (G_OBJECT (helper->mail_op));
1614
1615                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1616
1617                 g_object_unref (G_OBJECT (folder));
1618         } else {
1619                 /* Set status failed and set an error */
1620                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1621                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1622                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1623                              _("Error trying to get a message. No folder found for header"));
1624
1625                 /* Notify the queue */
1626                 modest_mail_operation_notify_end (self);
1627         }
1628 }
1629
1630 static void
1631 get_msg_cb (TnyFolder *folder, 
1632             gboolean cancelled, 
1633             TnyMsg *msg, 
1634             GError **error, 
1635             gpointer user_data)
1636 {
1637         GetMsgAsyncHelper *helper = NULL;
1638         ModestMailOperation *self = NULL;
1639         ModestMailOperationPrivate *priv = NULL;
1640
1641         helper = (GetMsgAsyncHelper *) user_data;
1642         g_return_if_fail (helper != NULL);       
1643         self = helper->mail_op;
1644         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1645         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1646
1647         /* Check errors and cancel */
1648         if (*error) {
1649                 priv->error = g_error_copy (*error);
1650                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1651         } else if (cancelled) {
1652                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1653                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1654                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1655                              _("Error trying to refresh the contents of %s"),
1656                              tny_folder_get_name (folder));
1657         } else {
1658                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1659         }
1660
1661         /* If user defined callback function was defined, call it even
1662            if the operation failed*/
1663         if (helper->user_callback) {
1664                 /* This callback is called into an iddle by tinymail,
1665                    and idles are not in the main lock */
1666                 gdk_threads_enter ();
1667                 helper->user_callback (self, helper->header, msg, helper->user_data);
1668                 gdk_threads_leave ();   
1669         }
1670
1671         /* Free */
1672         g_object_unref (helper->mail_op);
1673         g_object_unref (helper->header);
1674         g_slice_free (GetMsgAsyncHelper, helper);
1675                 
1676         /* Notify about operation end */
1677         modest_mail_operation_notify_end (self);
1678 }
1679
1680 static void     
1681 get_msg_status_cb (GObject *obj,
1682                    TnyStatus *status,  
1683                    gpointer user_data)
1684 {
1685         GetMsgAsyncHelper *helper = NULL;
1686         ModestMailOperation *self;
1687         ModestMailOperationPrivate *priv;
1688         ModestMailOperationState *state;
1689
1690         g_return_if_fail (status != NULL);
1691         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1692
1693         helper = (GetMsgAsyncHelper *) user_data;
1694         g_return_if_fail (helper != NULL);       
1695
1696         self = helper->mail_op;
1697         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1698
1699         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1700                 return;
1701
1702         priv->done = 1;
1703         priv->total = 1;
1704
1705         state = modest_mail_operation_clone_state (self);
1706         state->bytes_done = status->position;
1707         state->bytes_total = status->of_total;
1708         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1709         g_slice_free (ModestMailOperationState, state);
1710 }
1711
1712 /****************************************************/
1713 typedef struct {
1714         ModestMailOperation *mail_op;
1715         TnyList *headers;
1716         GetMsgAsyncUserCallback user_callback;
1717         gpointer user_data;
1718         GDestroyNotify notify;
1719 } GetFullMsgsInfo;
1720
1721 typedef struct {
1722         GetMsgAsyncUserCallback user_callback;
1723         TnyHeader *header;
1724         TnyMsg *msg;
1725         gpointer user_data;
1726         ModestMailOperation *mail_op;
1727 } NotifyGetMsgsInfo;
1728
1729
1730 /* 
1731  * Used by get_msgs_full_thread to call the user_callback for each
1732  * message that has been read
1733  */
1734 static gboolean
1735 notify_get_msgs_full (gpointer data)
1736 {
1737         NotifyGetMsgsInfo *info;
1738
1739         info = (NotifyGetMsgsInfo *) data;      
1740
1741         /* Call the user callback. Idles are not in the main lock, so
1742            lock it */
1743         gdk_threads_enter ();
1744         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1745         gdk_threads_leave ();
1746
1747         g_slice_free (NotifyGetMsgsInfo, info);
1748
1749         return FALSE;
1750 }
1751
1752 /* 
1753  * Used by get_msgs_full_thread to free al the thread resources and to
1754  * call the destroy function for the passed user_data
1755  */
1756 static gboolean
1757 get_msgs_full_destroyer (gpointer data)
1758 {
1759         GetFullMsgsInfo *info;
1760
1761         info = (GetFullMsgsInfo *) data;
1762
1763         if (info->notify) {
1764                 gdk_threads_enter ();   
1765                 info->notify (info->user_data);
1766                 gdk_threads_leave ();
1767         }
1768
1769         /* free */
1770         g_object_unref (info->headers);
1771         g_slice_free (GetFullMsgsInfo, info);
1772
1773         return FALSE;
1774 }
1775
1776 static gpointer
1777 get_msgs_full_thread (gpointer thr_user_data)
1778 {
1779         GetFullMsgsInfo *info;
1780         ModestMailOperationPrivate *priv = NULL;
1781         TnyIterator *iter = NULL;
1782         
1783         info = (GetFullMsgsInfo *) thr_user_data;       
1784         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1785
1786         iter = tny_list_create_iterator (info->headers);
1787         while (!tny_iterator_is_done (iter)) { 
1788                 TnyHeader *header;
1789                 TnyFolder *folder;
1790                 
1791                 header = TNY_HEADER (tny_iterator_get_current (iter));
1792                 folder = tny_header_get_folder (header);
1793                                 
1794                 /* Get message from folder */
1795                 if (folder) {
1796                         TnyMsg *msg;
1797                         /* The callback will call it per each header */
1798                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1799
1800                         if (msg) {
1801                                 ModestMailOperationState *state;
1802                                 ModestPair *pair;
1803
1804                                 priv->done++;
1805
1806                                 /* notify progress */
1807                                 state = modest_mail_operation_clone_state (info->mail_op);
1808                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1809                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1810                                                  pair, (GDestroyNotify) modest_pair_free);
1811
1812                                 /* The callback is the responsible for
1813                                    freeing the message */
1814                                 if (info->user_callback) {
1815                                         NotifyGetMsgsInfo *info_notify;
1816                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1817                                         info_notify->user_callback = info->user_callback;
1818                                         info_notify->mail_op = info->mail_op;
1819                                         info_notify->header = g_object_ref (header);
1820                                         info_notify->msg = g_object_ref (msg);
1821                                         info_notify->user_data = info->user_data;
1822                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1823                                                          notify_get_msgs_full, 
1824                                                          info_notify, NULL);
1825                                 }
1826                                 g_object_unref (msg);
1827                         }
1828                 } else {
1829                         /* Set status failed and set an error */
1830                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1831                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1832                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1833                                      "Error trying to get a message. No folder found for header");
1834                 }
1835                 g_object_unref (header);                
1836                 tny_iterator_next (iter);
1837         }
1838
1839         /* Set operation status */
1840         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1841                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1842
1843         /* Notify about operation end */
1844         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1845
1846         /* Free thread resources. Will be called after all previous idles */
1847         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1848
1849         return NULL;
1850 }
1851
1852 void 
1853 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1854                                      TnyList *header_list, 
1855                                      GetMsgAsyncUserCallback user_callback,
1856                                      gpointer user_data,
1857                                      GDestroyNotify notify)
1858 {
1859         TnyHeader *header = NULL;
1860         TnyFolder *folder = NULL;
1861         GThread *thread;
1862         ModestMailOperationPrivate *priv = NULL;
1863         GetFullMsgsInfo *info = NULL;
1864         gboolean size_ok = TRUE;
1865         gint max_size;
1866         TnyIterator *iter = NULL;
1867         
1868         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1869         
1870         /* Init mail operation */
1871         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1872         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1873         priv->done = 0;
1874         priv->total = tny_list_get_length(header_list);
1875
1876         /* Get account and set it into mail_operation */
1877         if (tny_list_get_length (header_list) >= 1) {
1878                 iter = tny_list_create_iterator (header_list);
1879                 header = TNY_HEADER (tny_iterator_get_current (iter));
1880                 folder = tny_header_get_folder (header);                
1881                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1882                 g_object_unref (header);
1883                 g_object_unref (folder);
1884
1885                 if (tny_list_get_length (header_list) == 1) {
1886                         g_object_unref (iter);
1887                         iter = NULL;
1888                 }
1889         }
1890
1891         /* Get msg size limit */
1892         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1893                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1894                                          &(priv->error));
1895         if (priv->error) {
1896                 g_clear_error (&(priv->error));
1897                 max_size = G_MAXINT;
1898         } else {
1899                 max_size = max_size * KB;
1900         }
1901
1902         /* Check message size limits. If there is only one message
1903            always retrieve it */
1904         if (iter != NULL) {
1905                 while (!tny_iterator_is_done (iter) && size_ok) {
1906                         header = TNY_HEADER (tny_iterator_get_current (iter));
1907                         if (tny_header_get_message_size (header) >= max_size)
1908                                 size_ok = FALSE;
1909                         g_object_unref (header);
1910                         tny_iterator_next (iter);
1911                 }
1912                 g_object_unref (iter);
1913         }
1914
1915         if (size_ok) {
1916                 /* Create the info */
1917                 info = g_slice_new0 (GetFullMsgsInfo);
1918                 info->mail_op = self;
1919                 info->user_callback = user_callback;
1920                 info->user_data = user_data;
1921                 info->headers = g_object_ref (header_list);
1922                 info->notify = notify;
1923
1924                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1925         } else {
1926                 /* Set status failed and set an error */
1927                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1928                 /* FIXME: the error msg is different for pop */
1929                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1930                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1931                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1932                 /* Remove from queue and free resources */
1933                 modest_mail_operation_notify_end (self);
1934                 if (notify)
1935                         notify (user_data);
1936         }
1937 }
1938
1939
1940 void 
1941 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
1942                                   gboolean remove_to_trash /*ignored*/)
1943 {
1944         TnyFolder *folder;
1945         ModestMailOperationPrivate *priv;
1946
1947         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1948         g_return_if_fail (TNY_IS_HEADER (header));
1949
1950         if (remove_to_trash)
1951                 g_warning ("remove to trash is not implemented");
1952
1953         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1954         folder = tny_header_get_folder (header);
1955
1956         /* Get account and set it into mail_operation */
1957         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1958
1959         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1960
1961
1962         tny_folder_remove_msg (folder, header, &(priv->error));
1963         if (!priv->error) {
1964                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1965
1966                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
1967                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
1968                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
1969                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1970                 else
1971                         /* lcoal folders */
1972                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1973         }
1974         
1975         
1976         /* Set status */
1977         if (!priv->error)
1978                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1979         else
1980                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1981
1982         /* Free */
1983         g_object_unref (G_OBJECT (folder));
1984
1985         /* Notify about operation end */
1986         modest_mail_operation_notify_end (self);
1987 }
1988
1989 static void
1990 transfer_msgs_status_cb (GObject *obj,
1991                          TnyStatus *status,  
1992                          gpointer user_data)
1993 {
1994         XFerMsgAsyncHelper *helper = NULL;
1995         ModestMailOperation *self;
1996         ModestMailOperationPrivate *priv;
1997         ModestMailOperationState *state;
1998
1999
2000         g_return_if_fail (status != NULL);
2001         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2002
2003         helper = (XFerMsgAsyncHelper *) user_data;
2004         g_return_if_fail (helper != NULL);       
2005
2006         self = helper->mail_op;
2007         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2008
2009         priv->done = status->position;
2010         priv->total = status->of_total;
2011
2012         state = modest_mail_operation_clone_state (self);
2013         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2014         g_slice_free (ModestMailOperationState, state);
2015 }
2016
2017
2018 static void
2019 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2020 {
2021         XFerMsgAsyncHelper *helper;
2022         ModestMailOperation *self;
2023         ModestMailOperationPrivate *priv;
2024
2025         helper = (XFerMsgAsyncHelper *) user_data;
2026         self = helper->mail_op;
2027
2028         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2029
2030         if (*err) {
2031                 priv->error = g_error_copy (*err);
2032                 priv->done = 0;
2033                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2034         } else if (cancelled) {
2035                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2036                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2037                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2038                              _("Error trying to refresh the contents of %s"),
2039                              tny_folder_get_name (folder));
2040         } else {
2041                 priv->done = 1;
2042                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2043         }
2044
2045         /* Notify about operation end */
2046         modest_mail_operation_notify_end (self);
2047
2048         /* If user defined callback function was defined, call it */
2049         if (helper->user_callback) {
2050                 gdk_threads_enter ();
2051                 helper->user_callback (priv->source, helper->user_data);
2052                 gdk_threads_leave ();
2053         }
2054
2055         /* Free */
2056         g_object_unref (helper->headers);
2057         g_object_unref (helper->dest_folder);
2058         g_object_unref (helper->mail_op);
2059         g_slice_free   (XFerMsgAsyncHelper, helper);
2060         g_object_unref (folder);
2061
2062 }
2063
2064 void
2065 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2066                                  TnyList *headers, 
2067                                  TnyFolder *folder, 
2068                                  gboolean delete_original,
2069                                  XferMsgsAsynUserCallback user_callback,
2070                                  gpointer user_data)
2071 {
2072         ModestMailOperationPrivate *priv;
2073         TnyIterator *iter;
2074         TnyFolder *src_folder;
2075         XFerMsgAsyncHelper *helper;
2076         TnyHeader *header;
2077         ModestTnyFolderRules rules;
2078         const gchar *id1 = NULL;
2079         const gchar *id2 = NULL;
2080         gboolean same_folder = FALSE;
2081
2082         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2083         g_return_if_fail (TNY_IS_LIST (headers));
2084         g_return_if_fail (TNY_IS_FOLDER (folder));
2085
2086         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2087         priv->total = 1;
2088         priv->done = 0;
2089         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2090
2091         /* Apply folder rules */
2092         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2093         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2094                 /* Set status failed and set an error */
2095                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2096                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2097                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2098                              _("ckct_ib_unable_to_paste_here"));
2099                 /* Notify the queue */
2100                 modest_mail_operation_notify_end (self);
2101                 return;
2102         }
2103                 
2104         /* Get source folder */
2105         iter = tny_list_create_iterator (headers);
2106         header = TNY_HEADER (tny_iterator_get_current (iter));
2107         src_folder = tny_header_get_folder (header);
2108         g_object_unref (header);
2109         g_object_unref (iter);
2110
2111         /* Check folder source and destination */
2112         id1 = tny_folder_get_id (src_folder);
2113         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2114         same_folder = !g_ascii_strcasecmp (id1, id2);
2115         if (same_folder) {
2116                 /* Set status failed and set an error */
2117                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2118                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2119                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2120                              _("mcen_ib_unable_to_copy_samefolder"));
2121                 
2122                 /* Notify the queue */
2123                 modest_mail_operation_notify_end (self);
2124                 
2125                 /* Free */
2126                 g_object_unref (src_folder);            
2127                 return;
2128         }
2129
2130         /* Create the helper */
2131         helper = g_slice_new0 (XFerMsgAsyncHelper);
2132         helper->mail_op = g_object_ref(self);
2133         helper->dest_folder = g_object_ref(folder);
2134         helper->headers = g_object_ref(headers);
2135         helper->user_callback = user_callback;
2136         helper->user_data = user_data;
2137
2138         /* Get account and set it into mail_operation */
2139         priv->account = modest_tny_folder_get_account (src_folder);
2140
2141         /* Transfer messages */
2142         tny_folder_transfer_msgs_async (src_folder, 
2143                                         headers, 
2144                                         folder, 
2145                                         delete_original, 
2146                                         transfer_msgs_cb, 
2147                                         transfer_msgs_status_cb,
2148                                         helper);
2149 }
2150
2151
2152 static void
2153 on_refresh_folder (TnyFolder   *folder, 
2154                    gboolean     cancelled, 
2155                    GError     **error,
2156                    gpointer     user_data)
2157 {
2158         RefreshAsyncHelper *helper = NULL;
2159         ModestMailOperation *self = NULL;
2160         ModestMailOperationPrivate *priv = NULL;
2161
2162         helper = (RefreshAsyncHelper *) user_data;
2163         self = helper->mail_op;
2164         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2165
2166         if (*error) {
2167                 priv->error = g_error_copy (*error);
2168                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2169                 goto out;
2170         }
2171
2172         if (cancelled) {
2173                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2174                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2175                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2176                              _("Error trying to refresh the contents of %s"),
2177                              tny_folder_get_name (folder));
2178                 goto out;
2179         }
2180
2181         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
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         /* Set the account back to not busy */
2280         if (priv->account_name) {
2281                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2282                                                      priv->account_name, FALSE);
2283                 g_free(priv->account_name);
2284                 priv->account_name = NULL;
2285         }
2286         
2287         /* Notify the observers about the mail opertation end */
2288         state = modest_mail_operation_clone_state (self);
2289         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2290         g_slice_free (ModestMailOperationState, state);
2291 }