77e42d684441c8941902c3854f4b1d8206b63d01
[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         if (new_folder != NULL)
1391                 g_object_unref (new_folder);
1392
1393         /* Notify about operation end */
1394         modest_mail_operation_notify_end (self, TRUE);
1395 }
1396
1397 void
1398 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1399                                    TnyFolder *folder,
1400                                    TnyFolderStore *parent,
1401                                    gboolean delete_original)
1402 {
1403         ModestMailOperationPrivate *priv = NULL;
1404         ModestTnyFolderRules parent_rules, rules;
1405
1406         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1407         g_return_if_fail (TNY_IS_FOLDER (folder));
1408         g_return_if_fail (TNY_IS_FOLDER (parent));
1409
1410         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1411
1412         /* Get account and set it into mail_operation */
1413         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1414         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1415
1416         /* Get folder rules */
1417         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1418         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1419
1420         if (!TNY_IS_FOLDER_STORE (parent)) {
1421                 
1422         }
1423         
1424         /* The moveable restriction is applied also to copy operation */
1425         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1426                 /* Set status failed and set an error */
1427                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1428                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1429                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1430                              _("mail_in_ui_folder_move_target_error"));
1431
1432                 /* Notify the queue */
1433                 modest_mail_operation_notify_end (self, FALSE);
1434         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1435                 /* Set status failed and set an error */
1436                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1437                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1438                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1439                              _("FIXME: parent folder does not accept new folders"));
1440
1441                 /* Notify the queue */
1442                 modest_mail_operation_notify_end (self, FALSE);
1443         } else {
1444                 /* Pick references for async calls */
1445                 g_object_ref (folder);
1446                 g_object_ref (parent);
1447
1448                 /* Move/Copy folder */          
1449                 tny_folder_copy_async (folder,
1450                                        parent,
1451                                        tny_folder_get_name (folder),
1452                                        delete_original,
1453                                        transfer_folder_cb,
1454                                        transfer_folder_status_cb,
1455                                        self);
1456         }
1457 }
1458
1459 void
1460 modest_mail_operation_rename_folder (ModestMailOperation *self,
1461                                      TnyFolder *folder,
1462                                      const gchar *name)
1463 {
1464         ModestMailOperationPrivate *priv;
1465         ModestTnyFolderRules rules;
1466
1467         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1468         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1469         g_return_if_fail (name);
1470         
1471         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1472
1473         /* Get account and set it into mail_operation */
1474         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1475
1476         /* Check folder rules */
1477         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1478         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1479                 /* Set status failed and set an error */
1480                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1481                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1482                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1483                              _("FIXME: unable to rename"));
1484
1485                 /* Notify about operation end */
1486                 modest_mail_operation_notify_end (self, FALSE);
1487         } else {
1488                 /* Rename. Camel handles folder subscription/unsubscription */
1489                 TnyFolderStore *into;
1490
1491                 into = tny_folder_get_folder_store (folder);
1492                 tny_folder_copy_async (folder, into, name, TRUE,
1493                                  transfer_folder_cb,
1494                                  transfer_folder_status_cb,
1495                                  self);
1496                 if (into)
1497                         g_object_unref (into);
1498                 
1499         }
1500  }
1501
1502 /* ******************************************************************* */
1503 /* **************************  MSG  ACTIONS  ************************* */
1504 /* ******************************************************************* */
1505
1506 void modest_mail_operation_get_msg (ModestMailOperation *self,
1507                                     TnyHeader *header,
1508                                     GetMsgAsyncUserCallback user_callback,
1509                                     gpointer user_data)
1510 {
1511         GetMsgAsyncHelper *helper = NULL;
1512         TnyFolder *folder;
1513         ModestMailOperationPrivate *priv;
1514         
1515         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1516         g_return_if_fail (TNY_IS_HEADER (header));
1517         
1518         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1519         folder = tny_header_get_folder (header);
1520
1521         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1522
1523         /* Get message from folder */
1524         if (folder) {
1525                 /* Get account and set it into mail_operation */
1526                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1527
1528                 helper = g_slice_new0 (GetMsgAsyncHelper);
1529                 helper->mail_op = self;
1530                 helper->user_callback = user_callback;
1531                 helper->user_data = user_data;
1532                 helper->header = g_object_ref (header);
1533
1534                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1535
1536                 g_object_unref (G_OBJECT (folder));
1537         } else {
1538                 /* Set status failed and set an error */
1539                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1540                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1541                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1542                              _("Error trying to get a message. No folder found for header"));
1543
1544                 /* Notify the queue */
1545                 modest_mail_operation_notify_end (self, FALSE);
1546         }
1547 }
1548
1549 static void
1550 get_msg_cb (TnyFolder *folder, 
1551             gboolean cancelled, 
1552             TnyMsg *msg, 
1553             GError **error, 
1554             gpointer user_data)
1555 {
1556         GetMsgAsyncHelper *helper = NULL;
1557         ModestMailOperation *self = NULL;
1558         ModestMailOperationPrivate *priv = NULL;
1559
1560         helper = (GetMsgAsyncHelper *) user_data;
1561         g_return_if_fail (helper != NULL);       
1562         self = helper->mail_op;
1563         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1564         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1565
1566         /* Check errors and cancel */
1567         if (*error) {
1568                 priv->error = g_error_copy (*error);
1569                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1570                 goto out;
1571         }
1572         if (cancelled) {
1573                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1574                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1575                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1576                              _("Error trying to refresh the contents of %s"),
1577                              tny_folder_get_name (folder));
1578                 goto out;
1579         }
1580
1581         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1582
1583         /* If user defined callback function was defined, call it */
1584         if (helper->user_callback) {
1585                 /* This callback is called into an iddle by tinymail,
1586                    and idles are not in the main lock */
1587                 gdk_threads_enter ();
1588                 helper->user_callback (self, helper->header, msg, helper->user_data);
1589                 gdk_threads_leave ();
1590         }
1591
1592  out:
1593         /* Free */
1594         g_object_unref (helper->header);
1595         g_slice_free (GetMsgAsyncHelper, helper);
1596                 
1597         /* Notify about operation end */
1598         modest_mail_operation_notify_end (self, TRUE);
1599 }
1600
1601 static void     
1602 get_msg_status_cb (GObject *obj,
1603                    TnyStatus *status,  
1604                    gpointer user_data)
1605 {
1606         GetMsgAsyncHelper *helper = NULL;
1607         ModestMailOperation *self;
1608         ModestMailOperationPrivate *priv;
1609         ModestMailOperationState *state;
1610
1611         g_return_if_fail (status != NULL);
1612         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1613
1614         helper = (GetMsgAsyncHelper *) user_data;
1615         g_return_if_fail (helper != NULL);       
1616
1617         self = helper->mail_op;
1618         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1619
1620         if ((status->position == 1) && (status->of_total == 100))
1621                 return;
1622
1623         priv->done = 1;
1624         priv->total = 1;
1625
1626         state = modest_mail_operation_clone_state (self);
1627         state->bytes_done = status->position;
1628         state->bytes_total = status->of_total;
1629         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1630         g_slice_free (ModestMailOperationState, state);
1631 }
1632
1633 /****************************************************/
1634 typedef struct {
1635         ModestMailOperation *mail_op;
1636         TnyList *headers;
1637         GetMsgAsyncUserCallback user_callback;
1638         gpointer user_data;
1639         GDestroyNotify notify;
1640 } GetFullMsgsInfo;
1641
1642 typedef struct {
1643         GetMsgAsyncUserCallback user_callback;
1644         TnyHeader *header;
1645         TnyMsg *msg;
1646         gpointer user_data;
1647         ModestMailOperation *mail_op;
1648 } NotifyGetMsgsInfo;
1649
1650
1651 /* 
1652  * Used by get_msgs_full_thread to call the user_callback for each
1653  * message that has been read
1654  */
1655 static gboolean
1656 notify_get_msgs_full (gpointer data)
1657 {
1658         NotifyGetMsgsInfo *info;
1659
1660         info = (NotifyGetMsgsInfo *) data;      
1661
1662         /* Call the user callback. Idles are not in the main lock, so
1663            lock it */
1664         gdk_threads_enter ();
1665         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1666         gdk_threads_leave ();
1667
1668         g_slice_free (NotifyGetMsgsInfo, info);
1669
1670         return FALSE;
1671 }
1672
1673 /* 
1674  * Used by get_msgs_full_thread to free al the thread resources and to
1675  * call the destroy function for the passed user_data
1676  */
1677 static gboolean
1678 get_msgs_full_destroyer (gpointer data)
1679 {
1680         GetFullMsgsInfo *info;
1681
1682         info = (GetFullMsgsInfo *) data;
1683
1684         if (info->notify) {
1685                 gdk_threads_enter ();   
1686                 info->notify (info->user_data);
1687                 gdk_threads_leave ();
1688         }
1689
1690         /* free */
1691         g_object_unref (info->headers);
1692         g_slice_free (GetFullMsgsInfo, info);
1693
1694         return FALSE;
1695 }
1696
1697 static gpointer
1698 get_msgs_full_thread (gpointer thr_user_data)
1699 {
1700         GetFullMsgsInfo *info;
1701         ModestMailOperationPrivate *priv = NULL;
1702         TnyIterator *iter = NULL;
1703         
1704         info = (GetFullMsgsInfo *) thr_user_data;       
1705         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1706
1707         iter = tny_list_create_iterator (info->headers);
1708         while (!tny_iterator_is_done (iter)) { 
1709                 TnyHeader *header;
1710                 TnyFolder *folder;
1711                 
1712                 header = TNY_HEADER (tny_iterator_get_current (iter));
1713                 folder = tny_header_get_folder (header);
1714                                 
1715                 /* Get message from folder */
1716                 if (folder) {
1717                         TnyMsg *msg;
1718                         /* The callback will call it per each header */
1719                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1720
1721                         if (msg) {
1722                                 ModestMailOperationState *state;
1723                                 ModestPair *pair;
1724
1725                                 priv->done++;
1726
1727                                 /* notify progress */
1728                                 state = modest_mail_operation_clone_state (info->mail_op);
1729                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1730                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1731                                                  pair, (GDestroyNotify) modest_pair_free);
1732
1733                                 /* The callback is the responsible for
1734                                    freeing the message */
1735                                 if (info->user_callback) {
1736                                         NotifyGetMsgsInfo *info_notify;
1737                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1738                                         info_notify->user_callback = info->user_callback;
1739                                         info_notify->mail_op = info->mail_op;
1740                                         info_notify->header = g_object_ref (header);
1741                                         info_notify->msg = g_object_ref (msg);
1742                                         info_notify->user_data = info->user_data;
1743                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1744                                                          notify_get_msgs_full, 
1745                                                          info_notify, NULL);
1746                                 }
1747                                 g_object_unref (msg);
1748                         }
1749                 } else {
1750                         /* Set status failed and set an error */
1751                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1752                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1753                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1754                                      "Error trying to get a message. No folder found for header");
1755                 }
1756                 g_object_unref (header);                
1757                 tny_iterator_next (iter);
1758         }
1759
1760         /* Set operation status */
1761         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1762                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1763
1764         /* Notify about operation end */
1765         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1766
1767         /* Free thread resources. Will be called after all previous idles */
1768         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1769
1770         return NULL;
1771 }
1772
1773 void 
1774 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1775                                      TnyList *header_list, 
1776                                      GetMsgAsyncUserCallback user_callback,
1777                                      gpointer user_data,
1778                                      GDestroyNotify notify)
1779 {
1780         TnyHeader *header = NULL;
1781         TnyFolder *folder = NULL;
1782         GThread *thread;
1783         ModestMailOperationPrivate *priv = NULL;
1784         GetFullMsgsInfo *info = NULL;
1785         gboolean size_ok = TRUE;
1786         gint max_size;
1787         TnyIterator *iter = NULL;
1788         
1789         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1790         
1791         /* Init mail operation */
1792         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1793         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1794         priv->done = 0;
1795         priv->total = tny_list_get_length(header_list);
1796
1797         /* Get account and set it into mail_operation */
1798         if (tny_list_get_length (header_list) >= 1) {
1799                 iter = tny_list_create_iterator (header_list);
1800                 header = TNY_HEADER (tny_iterator_get_current (iter));
1801                 folder = tny_header_get_folder (header);                
1802                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1803                 g_object_unref (header);
1804                 g_object_unref (folder);
1805
1806                 if (tny_list_get_length (header_list) == 1) {
1807                         g_object_unref (iter);
1808                         iter = NULL;
1809                 }
1810         }
1811
1812         /* Get msg size limit */
1813         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1814                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1815                                          &(priv->error));
1816         if (priv->error) {
1817                 g_clear_error (&(priv->error));
1818                 max_size = G_MAXINT;
1819         } else {
1820                 max_size = max_size * KB;
1821         }
1822
1823         /* Check message size limits. If there is only one message
1824            always retrieve it */
1825         if (iter != NULL) {
1826                 while (!tny_iterator_is_done (iter) && size_ok) {
1827                         header = TNY_HEADER (tny_iterator_get_current (iter));
1828                         if (tny_header_get_message_size (header) >= max_size)
1829                                 size_ok = FALSE;
1830                         g_object_unref (header);
1831                         tny_iterator_next (iter);
1832                 }
1833                 g_object_unref (iter);
1834         }
1835
1836         if (size_ok) {
1837                 /* Create the info */
1838                 info = g_slice_new0 (GetFullMsgsInfo);
1839                 info->mail_op = self;
1840                 info->user_callback = user_callback;
1841                 info->user_data = user_data;
1842                 info->headers = g_object_ref (header_list);
1843                 info->notify = notify;
1844
1845                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1846         } else {
1847                 /* Set status failed and set an error */
1848                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1849                 /* FIXME: the error msg is different for pop */
1850                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1851                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1852                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1853                 /* Remove from queue and free resources */
1854                 modest_mail_operation_notify_end (self, FALSE);
1855                 if (notify)
1856                         notify (user_data);
1857         }
1858 }
1859
1860
1861 void 
1862 modest_mail_operation_remove_msg (ModestMailOperation *self,
1863                                   TnyHeader *header,
1864                                   gboolean remove_to_trash)
1865 {
1866         TnyFolder *folder;
1867         ModestMailOperationPrivate *priv;
1868
1869         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1870         g_return_if_fail (TNY_IS_HEADER (header));
1871
1872         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1873         folder = tny_header_get_folder (header);
1874
1875         /* Get account and set it into mail_operation */
1876         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1877
1878         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1879
1880         /* Delete or move to trash */
1881         if (remove_to_trash) {
1882                 TnyFolder *trash_folder;
1883                 TnyStoreAccount *store_account;
1884
1885                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1886                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1887                                                                       TNY_FOLDER_TYPE_TRASH);
1888                 if (trash_folder) {
1889                         TnyList *headers;
1890
1891                         /* Create list */
1892                         headers = tny_simple_list_new ();
1893                         tny_list_append (headers, G_OBJECT (header));
1894                         g_object_unref (header);
1895
1896                         /* Move to trash */
1897                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1898                         g_object_unref (headers);
1899 /*                      g_object_unref (trash_folder); */
1900                 } else {
1901                         ModestMailOperationPrivate *priv;
1902
1903                         /* Set status failed and set an error */
1904                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1905                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1906                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1907                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1908                                      _("Error trying to delete a message. Trash folder not found"));
1909                 }
1910
1911                 g_object_unref (G_OBJECT (store_account));
1912         } else {
1913                 tny_folder_remove_msg (folder, header, &(priv->error));
1914                 if (!priv->error)
1915                         tny_folder_sync(folder, TRUE, &(priv->error));
1916         }
1917
1918         /* Set status */
1919         if (!priv->error)
1920                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1921         else
1922                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1923
1924         /* Free */
1925         g_object_unref (G_OBJECT (folder));
1926
1927         /* Notify about operation end */
1928         modest_mail_operation_notify_end (self, FALSE);
1929 }
1930
1931 static void
1932 transfer_msgs_status_cb (GObject *obj,
1933                          TnyStatus *status,  
1934                          gpointer user_data)
1935 {
1936         XFerMsgAsyncHelper *helper = NULL;
1937         ModestMailOperation *self;
1938         ModestMailOperationPrivate *priv;
1939         ModestMailOperationState *state;
1940
1941
1942         g_return_if_fail (status != NULL);
1943         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1944
1945         helper = (XFerMsgAsyncHelper *) user_data;
1946         g_return_if_fail (helper != NULL);       
1947
1948         self = helper->mail_op;
1949         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1950
1951         if ((status->position == 1) && (status->of_total == 100))
1952                 return;
1953
1954         priv->done = status->position;
1955         priv->total = status->of_total;
1956
1957         state = modest_mail_operation_clone_state (self);
1958         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1959         g_slice_free (ModestMailOperationState, state);
1960 }
1961
1962
1963 static void
1964 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1965 {
1966         XFerMsgAsyncHelper *helper;
1967         ModestMailOperation *self;
1968         ModestMailOperationPrivate *priv;
1969
1970         helper = (XFerMsgAsyncHelper *) user_data;
1971         self = helper->mail_op;
1972
1973         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1974
1975         if (*err) {
1976                 priv->error = g_error_copy (*err);
1977                 priv->done = 0;
1978                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1979         } else if (cancelled) {
1980                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1981                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1982                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1983                              _("Error trying to refresh the contents of %s"),
1984                              tny_folder_get_name (folder));
1985         } else {
1986                 priv->done = 1;
1987                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1988         }
1989
1990         /* If user defined callback function was defined, call it */
1991         if (helper->user_callback) {
1992                 gdk_threads_enter ();
1993                 helper->user_callback (priv->source, helper->user_data);
1994                 gdk_threads_leave ();
1995         }
1996
1997         /* Free */
1998         g_object_unref (helper->headers);
1999         g_object_unref (helper->dest_folder);
2000         g_object_unref (helper->mail_op);
2001         g_slice_free   (XFerMsgAsyncHelper, helper);
2002         g_object_unref (folder);
2003
2004         /* Notify about operation end */
2005         modest_mail_operation_notify_end (self, TRUE);
2006 }
2007
2008 void
2009 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2010                                  TnyList *headers, 
2011                                  TnyFolder *folder, 
2012                                  gboolean delete_original,
2013                                  XferMsgsAsynUserCallback user_callback,
2014                                  gpointer user_data)
2015 {
2016         ModestMailOperationPrivate *priv;
2017         TnyIterator *iter;
2018         TnyFolder *src_folder;
2019         XFerMsgAsyncHelper *helper;
2020         TnyHeader *header;
2021         ModestTnyFolderRules rules;
2022
2023         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2024         g_return_if_fail (TNY_IS_LIST (headers));
2025         g_return_if_fail (TNY_IS_FOLDER (folder));
2026
2027         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2028         priv->total = 1;
2029         priv->done = 0;
2030         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2031
2032         /* Apply folder rules */
2033         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2034         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2035                 /* Set status failed and set an error */
2036                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2037                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2038                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2039                              _("FIXME: folder does not accept msgs"));
2040                 /* Notify the queue */
2041                 modest_mail_operation_notify_end (self, FALSE);
2042                 return;
2043         }
2044
2045         /* Create the helper */
2046         helper = g_slice_new0 (XFerMsgAsyncHelper);
2047         helper->mail_op = g_object_ref(self);
2048         helper->dest_folder = g_object_ref(folder);
2049         helper->headers = g_object_ref(headers);
2050         helper->user_callback = user_callback;
2051         helper->user_data = user_data;
2052
2053         /* Get source folder */
2054         iter = tny_list_create_iterator (headers);
2055         header = TNY_HEADER (tny_iterator_get_current (iter));
2056         src_folder = tny_header_get_folder (header);
2057         g_object_unref (header);
2058         g_object_unref (iter);
2059
2060         /* Get account and set it into mail_operation */
2061         priv->account = modest_tny_folder_get_account (src_folder);
2062
2063         /* Transfer messages */
2064         tny_folder_transfer_msgs_async (src_folder, 
2065                                         headers, 
2066                                         folder, 
2067                                         delete_original, 
2068                                         transfer_msgs_cb, 
2069                                         transfer_msgs_status_cb,
2070                                         helper);
2071 }
2072
2073
2074 static void
2075 on_refresh_folder (TnyFolder   *folder, 
2076                    gboolean     cancelled, 
2077                    GError     **error,
2078                    gpointer     user_data)
2079 {
2080         RefreshAsyncHelper *helper = NULL;
2081         ModestMailOperation *self = NULL;
2082         ModestMailOperationPrivate *priv = NULL;
2083
2084         helper = (RefreshAsyncHelper *) user_data;
2085         self = helper->mail_op;
2086         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2087
2088         if (*error) {
2089                 priv->error = g_error_copy (*error);
2090                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2091                 goto out;
2092         }
2093
2094         if (cancelled) {
2095                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2096                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2097                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2098                              _("Error trying to refresh the contents of %s"),
2099                              tny_folder_get_name (folder));
2100                 goto out;
2101         }
2102
2103         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2104
2105  out:
2106         /* Call user defined callback, if it exists */
2107         if (helper->user_callback) {
2108                 gdk_threads_enter ();
2109                 helper->user_callback (priv->source, folder, helper->user_data);
2110                 gdk_threads_leave ();
2111         }
2112
2113         /* Free */
2114         g_object_unref (helper->mail_op);
2115         g_slice_free   (RefreshAsyncHelper, helper);
2116         g_object_unref (folder);
2117
2118         /* Notify about operation end */
2119         modest_mail_operation_notify_end (self, TRUE);
2120 }
2121
2122 static void
2123 on_refresh_folder_status_update (GObject *obj,
2124                                  TnyStatus *status,
2125                                  gpointer user_data)
2126 {
2127         RefreshAsyncHelper *helper = NULL;
2128         ModestMailOperation *self = NULL;
2129         ModestMailOperationPrivate *priv = NULL;
2130         ModestMailOperationState *state;
2131
2132         g_return_if_fail (user_data != NULL);
2133         g_return_if_fail (status != NULL);
2134         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2135
2136         helper = (RefreshAsyncHelper *) user_data;
2137         self = helper->mail_op;
2138         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2139
2140         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2141
2142         priv->done = status->position;
2143         priv->total = status->of_total;
2144
2145         state = modest_mail_operation_clone_state (self);
2146         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2147         g_slice_free (ModestMailOperationState, state);
2148 }
2149
2150 void 
2151 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2152                                        TnyFolder *folder,
2153                                        RefreshAsyncUserCallback user_callback,
2154                                        gpointer user_data)
2155 {
2156         ModestMailOperationPrivate *priv = NULL;
2157         RefreshAsyncHelper *helper = NULL;
2158
2159         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2160
2161         /* Pick a reference */
2162         g_object_ref (folder);
2163
2164         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2165
2166         /* Get account and set it into mail_operation */
2167         priv->account = modest_tny_folder_get_account  (folder);
2168
2169         /* Create the helper */
2170         helper = g_slice_new0 (RefreshAsyncHelper);
2171         helper->mail_op = g_object_ref(self);
2172         helper->user_callback = user_callback;
2173         helper->user_data = user_data;
2174
2175         /* Refresh the folder. TODO: tinymail could issue a status
2176            updates before the callback call then this could happen. We
2177            must review the design */
2178         tny_folder_refresh_async (folder,
2179                                   on_refresh_folder,
2180                                   on_refresh_folder_status_update,
2181                                   helper);
2182 }
2183
2184 /**
2185  *
2186  * It's used by the mail operation queue to notify the observers
2187  * attached to that signal that the operation finished. We need to use
2188  * that because tinymail does not give us the progress of a given
2189  * operation when it finishes (it directly calls the operation
2190  * callback).
2191  */
2192 static void
2193 modest_mail_operation_notify_end (ModestMailOperation *self,
2194                                   gboolean need_lock)
2195 {
2196         ModestMailOperationState *state;
2197         ModestMailOperationPrivate *priv = NULL;
2198
2199         g_return_if_fail (self);
2200
2201         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2202
2203         if (!priv) {
2204                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2205                 return;
2206         }
2207         
2208         /* Set the account back to not busy */
2209         if (priv->account_name) {
2210                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2211                                                      priv->account_name, FALSE);
2212                 g_free(priv->account_name);
2213                 priv->account_name = NULL;
2214         }
2215         
2216         /* Notify the observers about the mail opertation end */
2217         state = modest_mail_operation_clone_state (self);
2218         if (need_lock)
2219                 gdk_threads_enter ();
2220         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2221         if (need_lock)
2222                 gdk_threads_leave ();
2223         g_slice_free (ModestMailOperationState, state);
2224 }