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