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