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