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