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