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