* Added a callback to the update_account operation that notifies about the new messa...
[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                 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                 if (new_headers->len > info->retrieve_limit) {
1011                         /* TODO: Ask the user, instead of just
1012                          * failing, showing
1013                          * mail_nc_msg_count_limit_exceeded, with 'Get
1014                          * 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         if (info->callback) 
1090                 info->callback (info->mail_op, 
1091                                 (new_headers) ? new_headers->len : 0, 
1092                                 info->user_data);
1093         
1094         /* Frees */
1095         g_object_unref (query);
1096         g_object_unref (all_folders);
1097         g_object_unref (info->account);
1098         g_object_unref (info->transport_account);
1099         g_free (info->retrieve_type);
1100         g_slice_free (UpdateAccountInfo, info);
1101
1102         first_time = FALSE;
1103
1104         return NULL;
1105 }
1106
1107 gboolean
1108 modest_mail_operation_update_account (ModestMailOperation *self,
1109                                       const gchar *account_name,
1110                                       UpdateAccountCallback callback,
1111                                       gpointer user_data)
1112 {
1113         GThread *thread;
1114         UpdateAccountInfo *info;
1115         ModestMailOperationPrivate *priv;
1116         ModestAccountMgr *mgr;
1117         TnyStoreAccount *modest_account;
1118         TnyTransportAccount *transport_account;
1119
1120         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1121         g_return_val_if_fail (account_name, 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         /* Make sure that we have a connection, and request one 
1132          * if necessary:
1133          * TODO: Is there some way to trigger this for every attempt to 
1134          * use the network? */
1135         if (!modest_platform_connect_and_wait(NULL))
1136                 goto error;
1137
1138         /* Get the Modest account */
1139         modest_account = (TnyStoreAccount *)
1140                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1141                                                                      account_name,
1142                                                                      TNY_ACCOUNT_TYPE_STORE);
1143
1144         if (!modest_account) {
1145                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1146                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1147                              "cannot get tny store account for %s\n", account_name);
1148                 goto error;
1149         }
1150
1151         
1152         /* Get the transport account, we can not do it in the thread
1153            due to some problems with dbus */
1154         transport_account = (TnyTransportAccount *)
1155                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1156                                                                                     account_name);
1157         if (!transport_account) {
1158                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1159                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1160                              "cannot get tny transport account for %s\n", account_name);
1161                 goto error;
1162         }
1163
1164         /* Create the helper object */
1165         info = g_slice_new (UpdateAccountInfo);
1166         info->mail_op = self;
1167         info->account = modest_account;
1168         info->transport_account = transport_account;
1169         info->callback = callback;
1170         info->user_data = user_data;
1171
1172         /* Get the message size limit */
1173         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1174                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1175         if (info->max_size == 0)
1176                 info->max_size = G_MAXINT;
1177         else
1178                 info->max_size = info->max_size * KB;
1179
1180         /* Get per-account retrieval type */
1181         mgr = modest_runtime_get_account_mgr ();
1182         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1183                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1184
1185         /* Get per-account message amount retrieval limit */
1186         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1187                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1188         if (info->retrieve_limit == 0)
1189                 info->retrieve_limit = G_MAXINT;
1190                 
1191         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1192
1193         /* Set account busy */
1194         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1195         priv->account_name = g_strdup(account_name);
1196         
1197         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1198
1199         return TRUE;
1200
1201  error:
1202         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1203         if (callback) 
1204                 callback (self, 0, user_data);
1205         modest_mail_operation_notify_end (self);
1206         return FALSE;
1207 }
1208
1209 /* ******************************************************************* */
1210 /* ************************** STORE  ACTIONS ************************* */
1211 /* ******************************************************************* */
1212
1213
1214 TnyFolder *
1215 modest_mail_operation_create_folder (ModestMailOperation *self,
1216                                      TnyFolderStore *parent,
1217                                      const gchar *name)
1218 {
1219         ModestMailOperationPrivate *priv;
1220         TnyFolder *new_folder = NULL;
1221
1222         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1223         g_return_val_if_fail (name, NULL);
1224         
1225         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1226
1227         /* Check parent */
1228         if (TNY_IS_FOLDER (parent)) {
1229                 /* Check folder rules */
1230                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1231                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1232                         /* Set status failed and set an error */
1233                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1234                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1235                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1236                                      _("mail_in_ui_folder_create_error"));
1237                 }
1238         }
1239
1240         if (!priv->error) {
1241                 /* Create the folder */
1242                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1243                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1244                 if (!priv->error)
1245                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1246         }
1247
1248         /* Notify about operation end */
1249         modest_mail_operation_notify_end (self);
1250
1251         return new_folder;
1252 }
1253
1254 void
1255 modest_mail_operation_remove_folder (ModestMailOperation *self,
1256                                      TnyFolder           *folder,
1257                                      gboolean             remove_to_trash)
1258 {
1259         TnyAccount *account;
1260         ModestMailOperationPrivate *priv;
1261         ModestTnyFolderRules rules;
1262
1263         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1264         g_return_if_fail (TNY_IS_FOLDER (folder));
1265         
1266         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1267         
1268         /* Check folder rules */
1269         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1270         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1271                 /* Set status failed and set an error */
1272                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1273                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1274                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1275                              _("mail_in_ui_folder_delete_error"));
1276                 goto end;
1277         }
1278
1279         /* Get the account */
1280         account = modest_tny_folder_get_account (folder);
1281         priv->account = g_object_ref(account);
1282
1283         /* Delete folder or move to trash */
1284         if (remove_to_trash) {
1285                 TnyFolder *trash_folder = NULL;
1286                 trash_folder = modest_tny_account_get_special_folder (account,
1287                                                                       TNY_FOLDER_TYPE_TRASH);
1288                 /* TODO: error_handling */
1289                  modest_mail_operation_xfer_folder (self, folder,
1290                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1291         } else {
1292                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1293
1294                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1295                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1296
1297                 if (parent)
1298                         g_object_unref (G_OBJECT (parent));
1299         }
1300         g_object_unref (G_OBJECT (account));
1301
1302  end:
1303         /* Notify about operation end */
1304         modest_mail_operation_notify_end (self);
1305 }
1306
1307 static void
1308 transfer_folder_status_cb (GObject *obj,
1309                            TnyStatus *status,
1310                            gpointer user_data)
1311 {
1312         ModestMailOperation *self;
1313         ModestMailOperationPrivate *priv;
1314         ModestMailOperationState *state;
1315
1316         g_return_if_fail (status != NULL);
1317         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1318
1319         self = MODEST_MAIL_OPERATION (user_data);
1320         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1321
1322         if ((status->position == 1) && (status->of_total == 100))
1323                 return;
1324
1325         priv->done = status->position;
1326         priv->total = status->of_total;
1327
1328         state = modest_mail_operation_clone_state (self);
1329         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1330         g_slice_free (ModestMailOperationState, state);
1331 }
1332
1333
1334 static void
1335 transfer_folder_cb (TnyFolder *folder, 
1336                     TnyFolderStore *into, 
1337                     gboolean cancelled, 
1338                     TnyFolder *new_folder, GError **err, 
1339                     gpointer user_data)
1340 {
1341         ModestMailOperation *self = NULL;
1342         ModestMailOperationPrivate *priv = NULL;
1343
1344         self = MODEST_MAIL_OPERATION (user_data);
1345
1346         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1347
1348         if (*err) {
1349                 priv->error = g_error_copy (*err);
1350                 priv->done = 0;
1351                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1352         } else if (cancelled) {
1353                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1354                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1355                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1356                              _("Transference of %s was cancelled."),
1357                              tny_folder_get_name (folder));
1358         } else {
1359                 priv->done = 1;
1360                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1361         }
1362                 
1363         /* Free */
1364         g_object_unref (folder);
1365         g_object_unref (into);
1366         if (new_folder != NULL)
1367                 g_object_unref (new_folder);
1368
1369         /* Notify about operation end */
1370         modest_mail_operation_notify_end (self);
1371 }
1372
1373 void
1374 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1375                                    TnyFolder *folder,
1376                                    TnyFolderStore *parent,
1377                                    gboolean delete_original)
1378 {
1379         ModestMailOperationPrivate *priv = NULL;
1380         ModestTnyFolderRules parent_rules, rules;
1381
1382         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1383         g_return_if_fail (TNY_IS_FOLDER (folder));
1384         g_return_if_fail (TNY_IS_FOLDER (parent));
1385
1386         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1387
1388         /* Get account and set it into mail_operation */
1389         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1390         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1391
1392         /* Get folder rules */
1393         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1394         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1395
1396         if (!TNY_IS_FOLDER_STORE (parent)) {
1397                 
1398         }
1399         
1400         /* The moveable restriction is applied also to copy operation */
1401         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1402                 /* Set status failed and set an error */
1403                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1404                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1405                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1406                              _("mail_in_ui_folder_move_target_error"));
1407
1408                 /* Notify the queue */
1409                 modest_mail_operation_notify_end (self);
1410         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1411                 /* Set status failed and set an error */
1412                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1413                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1414                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1415                              _("FIXME: parent folder does not accept new folders"));
1416
1417                 /* Notify the queue */
1418                 modest_mail_operation_notify_end (self);
1419         } else {
1420                 /* Pick references for async calls */
1421                 g_object_ref (folder);
1422                 g_object_ref (parent);
1423
1424                 /* Move/Copy folder */          
1425                 tny_folder_copy_async (folder,
1426                                        parent,
1427                                        tny_folder_get_name (folder),
1428                                        delete_original,
1429                                        transfer_folder_cb,
1430                                        transfer_folder_status_cb,
1431                                        self);
1432         }
1433 }
1434
1435 void
1436 modest_mail_operation_rename_folder (ModestMailOperation *self,
1437                                      TnyFolder *folder,
1438                                      const gchar *name)
1439 {
1440         ModestMailOperationPrivate *priv;
1441         ModestTnyFolderRules rules;
1442
1443         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1444         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1445         g_return_if_fail (name);
1446         
1447         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1448
1449         /* Get account and set it into mail_operation */
1450         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1451
1452         /* Check folder rules */
1453         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1454         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1455                 /* Set status failed and set an error */
1456                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1457                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1458                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1459                              _("FIXME: unable to rename"));
1460
1461                 /* Notify about operation end */
1462                 modest_mail_operation_notify_end (self);
1463         } else {
1464                 /* Rename. Camel handles folder subscription/unsubscription */
1465                 TnyFolderStore *into;
1466
1467                 into = tny_folder_get_folder_store (folder);
1468                 tny_folder_copy_async (folder, into, name, TRUE,
1469                                  transfer_folder_cb,
1470                                  transfer_folder_status_cb,
1471                                  self);
1472                 if (into)
1473                         g_object_unref (into);
1474                 
1475         }
1476  }
1477
1478 /* ******************************************************************* */
1479 /* **************************  MSG  ACTIONS  ************************* */
1480 /* ******************************************************************* */
1481
1482 void modest_mail_operation_get_msg (ModestMailOperation *self,
1483                                     TnyHeader *header,
1484                                     GetMsgAsyncUserCallback user_callback,
1485                                     gpointer user_data)
1486 {
1487         GetMsgAsyncHelper *helper = NULL;
1488         TnyFolder *folder;
1489         ModestMailOperationPrivate *priv;
1490         
1491         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1492         g_return_if_fail (TNY_IS_HEADER (header));
1493         
1494         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1495         folder = tny_header_get_folder (header);
1496
1497         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1498
1499         /* Get message from folder */
1500         if (folder) {
1501                 /* Get account and set it into mail_operation */
1502                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1503
1504                 helper = g_slice_new0 (GetMsgAsyncHelper);
1505                 helper->mail_op = self;
1506                 helper->user_callback = user_callback;
1507                 helper->user_data = user_data;
1508                 helper->header = g_object_ref (header);
1509
1510                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1511
1512                 g_object_unref (G_OBJECT (folder));
1513         } else {
1514                 /* Set status failed and set an error */
1515                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1516                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1517                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1518                              _("Error trying to get a message. No folder found for header"));
1519
1520                 /* Notify the queue */
1521                 modest_mail_operation_notify_end (self);
1522         }
1523 }
1524
1525 static void
1526 get_msg_cb (TnyFolder *folder, 
1527             gboolean cancelled, 
1528             TnyMsg *msg, 
1529             GError **error, 
1530             gpointer user_data)
1531 {
1532         GetMsgAsyncHelper *helper = NULL;
1533         ModestMailOperation *self = NULL;
1534         ModestMailOperationPrivate *priv = NULL;
1535
1536         helper = (GetMsgAsyncHelper *) user_data;
1537         g_return_if_fail (helper != NULL);       
1538         self = helper->mail_op;
1539         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1540         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1541
1542         /* Check errors and cancel */
1543         if (*error) {
1544                 priv->error = g_error_copy (*error);
1545                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1546                 goto out;
1547         }
1548         if (cancelled) {
1549                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1550                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1551                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1552                              _("Error trying to refresh the contents of %s"),
1553                              tny_folder_get_name (folder));
1554                 goto out;
1555         }
1556
1557         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1558
1559         /* If user defined callback function was defined, call it */
1560         if (helper->user_callback) {
1561                 helper->user_callback (self, helper->header, msg, helper->user_data);
1562         }
1563
1564  out:
1565         /* Free */
1566         g_object_unref (helper->header);
1567         g_slice_free (GetMsgAsyncHelper, helper);
1568                 
1569         /* Notify about operation end */
1570         modest_mail_operation_notify_end (self);        
1571 }
1572
1573 static void     
1574 get_msg_status_cb (GObject *obj,
1575                    TnyStatus *status,  
1576                    gpointer user_data)
1577 {
1578         GetMsgAsyncHelper *helper = NULL;
1579         ModestMailOperation *self;
1580         ModestMailOperationPrivate *priv;
1581         ModestMailOperationState *state;
1582
1583         g_return_if_fail (status != NULL);
1584         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1585
1586         helper = (GetMsgAsyncHelper *) user_data;
1587         g_return_if_fail (helper != NULL);       
1588
1589         self = helper->mail_op;
1590         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1591
1592         if ((status->position == 1) && (status->of_total == 100))
1593                 return;
1594
1595         priv->done = 1;
1596         priv->total = 1;
1597
1598         state = modest_mail_operation_clone_state (self);
1599         state->bytes_done = status->position;
1600         state->bytes_total = status->of_total;
1601         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1602         g_slice_free (ModestMailOperationState, state);
1603 }
1604
1605 /****************************************************/
1606 typedef struct {
1607         ModestMailOperation *mail_op;
1608         TnyList *headers;
1609         GetMsgAsyncUserCallback user_callback;
1610         gpointer user_data;
1611         GDestroyNotify notify;
1612 } GetFullMsgsInfo;
1613
1614 typedef struct {
1615         GetMsgAsyncUserCallback user_callback;
1616         TnyHeader *header;
1617         TnyMsg *msg;
1618         gpointer user_data;
1619         ModestMailOperation *mail_op;
1620 } NotifyGetMsgsInfo;
1621
1622
1623 /* 
1624  * Used by get_msgs_full_thread to call the user_callback for each
1625  * message that has been read
1626  */
1627 static gboolean
1628 notify_get_msgs_full (gpointer data)
1629 {
1630         NotifyGetMsgsInfo *info;
1631
1632         info = (NotifyGetMsgsInfo *) data;      
1633
1634         /* Call the user callback */
1635         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1636
1637         g_slice_free (NotifyGetMsgsInfo, info);
1638
1639         return FALSE;
1640 }
1641
1642 /* 
1643  * Used by get_msgs_full_thread to free al the thread resources and to
1644  * call the destroy function for the passed user_data
1645  */
1646 static gboolean
1647 get_msgs_full_destroyer (gpointer data)
1648 {
1649         GetFullMsgsInfo *info;
1650
1651         info = (GetFullMsgsInfo *) data;
1652
1653         if (info->notify)
1654                 info->notify (info->user_data);
1655
1656         /* free */
1657         g_object_unref (info->headers);
1658         g_slice_free (GetFullMsgsInfo, info);
1659
1660         return FALSE;
1661 }
1662
1663 static gpointer
1664 get_msgs_full_thread (gpointer thr_user_data)
1665 {
1666         GetFullMsgsInfo *info;
1667         ModestMailOperationPrivate *priv = NULL;
1668         TnyIterator *iter = NULL;
1669         
1670         info = (GetFullMsgsInfo *) thr_user_data;       
1671         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1672
1673         iter = tny_list_create_iterator (info->headers);
1674         while (!tny_iterator_is_done (iter)) { 
1675                 TnyHeader *header;
1676                 TnyFolder *folder;
1677                 
1678                 header = TNY_HEADER (tny_iterator_get_current (iter));
1679                 folder = tny_header_get_folder (header);
1680                                 
1681                 /* Get message from folder */
1682                 if (folder) {
1683                         TnyMsg *msg;
1684                         /* The callback will call it per each header */
1685                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1686
1687                         if (msg) {
1688                                 ModestMailOperationState *state;
1689                                 ModestPair *pair;
1690
1691                                 priv->done++;
1692
1693                                 /* notify progress */
1694                                 state = modest_mail_operation_clone_state (info->mail_op);
1695                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1696                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1697                                                  pair, (GDestroyNotify) modest_pair_free);
1698
1699                                 /* The callback is the responsible for
1700                                    freeing the message */
1701                                 if (info->user_callback) {
1702                                         NotifyGetMsgsInfo *info_notify;
1703                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1704                                         info_notify->user_callback = info->user_callback;
1705                                         info_notify->mail_op = info->mail_op;
1706                                         info_notify->header = g_object_ref (header);
1707                                         info_notify->msg = g_object_ref (msg);
1708                                         info_notify->user_data = info->user_data;
1709                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1710                                                          notify_get_msgs_full, 
1711                                                          info_notify, NULL);
1712                                 }
1713                                 g_object_unref (msg);
1714                         }
1715                 } else {
1716                         /* Set status failed and set an error */
1717                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1718                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1719                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1720                                      "Error trying to get a message. No folder found for header");
1721                 }
1722                 g_object_unref (header);                
1723                 tny_iterator_next (iter);
1724         }
1725
1726         /* Set operation status */
1727         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1728                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1729
1730         /* Notify about operation end */
1731         g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1732
1733         /* Free thread resources. Will be called after all previous idles */
1734         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1735
1736         return NULL;
1737 }
1738
1739 void 
1740 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1741                                      TnyList *header_list, 
1742                                      GetMsgAsyncUserCallback user_callback,
1743                                      gpointer user_data,
1744                                      GDestroyNotify notify)
1745 {
1746         TnyHeader *header = NULL;
1747         TnyFolder *folder = NULL;
1748         GThread *thread;
1749         ModestMailOperationPrivate *priv = NULL;
1750         GetFullMsgsInfo *info = NULL;
1751         gboolean size_ok = TRUE;
1752         gint max_size;
1753         TnyIterator *iter = NULL;
1754         
1755         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1756         
1757         /* Init mail operation */
1758         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1759         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1760         priv->done = 0;
1761         priv->total = tny_list_get_length(header_list);
1762
1763         /* Get account and set it into mail_operation */
1764         if (tny_list_get_length (header_list) >= 1) {
1765                 iter = tny_list_create_iterator (header_list);
1766                 header = TNY_HEADER (tny_iterator_get_current (iter));
1767                 folder = tny_header_get_folder (header);                
1768                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1769                 g_object_unref (header);
1770                 g_object_unref (folder);
1771
1772                 if (tny_list_get_length (header_list) == 1) {
1773                         g_object_unref (iter);
1774                         iter = NULL;
1775                 }
1776         }
1777
1778         /* Get msg size limit */
1779         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1780                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1781                                          &(priv->error));
1782         if (priv->error) {
1783                 g_clear_error (&(priv->error));
1784                 max_size = G_MAXINT;
1785         } else {
1786                 max_size = max_size * KB;
1787         }
1788
1789         /* Check message size limits. If there is only one message
1790            always retrieve it */
1791         if (iter != NULL) {
1792                 while (!tny_iterator_is_done (iter) && size_ok) {
1793                         header = TNY_HEADER (tny_iterator_get_current (iter));
1794                         if (tny_header_get_message_size (header) >= max_size)
1795                                 size_ok = FALSE;
1796                         g_object_unref (header);
1797                         tny_iterator_next (iter);
1798                 }
1799                 g_object_unref (iter);
1800         }
1801
1802         if (size_ok) {
1803                 /* Create the info */
1804                 info = g_slice_new0 (GetFullMsgsInfo);
1805                 info->mail_op = self;
1806                 info->user_callback = user_callback;
1807                 info->user_data = user_data;
1808                 info->headers = g_object_ref (header_list);
1809                 info->notify = notify;
1810
1811                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1812         } else {
1813                 /* Set status failed and set an error */
1814                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1815                 /* FIXME: the error msg is different for pop */
1816                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1817                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1818                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1819                 /* Remove from queue and free resources */
1820                 modest_mail_operation_notify_end (self);
1821                 if (notify)
1822                         notify (user_data);
1823         }
1824 }
1825
1826
1827 void 
1828 modest_mail_operation_remove_msg (ModestMailOperation *self,
1829                                   TnyHeader *header,
1830                                   gboolean remove_to_trash)
1831 {
1832         TnyFolder *folder;
1833         ModestMailOperationPrivate *priv;
1834
1835         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1836         g_return_if_fail (TNY_IS_HEADER (header));
1837
1838         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1839         folder = tny_header_get_folder (header);
1840
1841         /* Get account and set it into mail_operation */
1842         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1843
1844         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1845
1846         /* Delete or move to trash */
1847         if (remove_to_trash) {
1848                 TnyFolder *trash_folder;
1849                 TnyStoreAccount *store_account;
1850
1851                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1852                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1853                                                                       TNY_FOLDER_TYPE_TRASH);
1854                 if (trash_folder) {
1855                         TnyList *headers;
1856
1857                         /* Create list */
1858                         headers = tny_simple_list_new ();
1859                         tny_list_append (headers, G_OBJECT (header));
1860                         g_object_unref (header);
1861
1862                         /* Move to trash */
1863                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1864                         g_object_unref (headers);
1865 /*                      g_object_unref (trash_folder); */
1866                 } else {
1867                         ModestMailOperationPrivate *priv;
1868
1869                         /* Set status failed and set an error */
1870                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1871                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1872                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1873                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1874                                      _("Error trying to delete a message. Trash folder not found"));
1875                 }
1876
1877                 g_object_unref (G_OBJECT (store_account));
1878         } else {
1879                 tny_folder_remove_msg (folder, header, &(priv->error));
1880                 if (!priv->error)
1881                         tny_folder_sync(folder, TRUE, &(priv->error));
1882         }
1883
1884         /* Set status */
1885         if (!priv->error)
1886                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1887         else
1888                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1889
1890         /* Free */
1891         g_object_unref (G_OBJECT (folder));
1892
1893         /* Notify about operation end */
1894         modest_mail_operation_notify_end (self);
1895 }
1896
1897 static void
1898 transfer_msgs_status_cb (GObject *obj,
1899                          TnyStatus *status,  
1900                          gpointer user_data)
1901 {
1902         XFerMsgAsyncHelper *helper = NULL;
1903         ModestMailOperation *self;
1904         ModestMailOperationPrivate *priv;
1905         ModestMailOperationState *state;
1906
1907
1908         g_return_if_fail (status != NULL);
1909         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1910
1911         helper = (XFerMsgAsyncHelper *) user_data;
1912         g_return_if_fail (helper != NULL);       
1913
1914         self = helper->mail_op;
1915         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1916
1917         if ((status->position == 1) && (status->of_total == 100))
1918                 return;
1919
1920         priv->done = status->position;
1921         priv->total = status->of_total;
1922
1923         state = modest_mail_operation_clone_state (self);
1924         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1925         g_slice_free (ModestMailOperationState, state);
1926 }
1927
1928
1929 static void
1930 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1931 {
1932         XFerMsgAsyncHelper *helper;
1933         ModestMailOperation *self;
1934         ModestMailOperationPrivate *priv;
1935
1936         helper = (XFerMsgAsyncHelper *) user_data;
1937         self = helper->mail_op;
1938
1939         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1940
1941         if (*err) {
1942                 priv->error = g_error_copy (*err);
1943                 priv->done = 0;
1944                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1945         } else if (cancelled) {
1946                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1947                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1948                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1949                              _("Error trying to refresh the contents of %s"),
1950                              tny_folder_get_name (folder));
1951         } else {
1952                 priv->done = 1;
1953                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1954         }
1955
1956         /* If user defined callback function was defined, call it */
1957         if (helper->user_callback) {
1958                 helper->user_callback (priv->source, helper->user_data);
1959         }
1960
1961         /* Free */
1962         g_object_unref (helper->headers);
1963         g_object_unref (helper->dest_folder);
1964         g_object_unref (helper->mail_op);
1965         g_slice_free   (XFerMsgAsyncHelper, helper);
1966         g_object_unref (folder);
1967
1968         /* Notify about operation end */
1969         modest_mail_operation_notify_end (self);
1970 }
1971
1972 void
1973 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1974                                  TnyList *headers, 
1975                                  TnyFolder *folder, 
1976                                  gboolean delete_original,
1977                                  XferMsgsAsynUserCallback user_callback,
1978                                  gpointer user_data)
1979 {
1980         ModestMailOperationPrivate *priv;
1981         TnyIterator *iter;
1982         TnyFolder *src_folder;
1983         XFerMsgAsyncHelper *helper;
1984         TnyHeader *header;
1985         ModestTnyFolderRules rules;
1986
1987         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1988         g_return_if_fail (TNY_IS_LIST (headers));
1989         g_return_if_fail (TNY_IS_FOLDER (folder));
1990
1991         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1992         priv->total = 1;
1993         priv->done = 0;
1994         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1995
1996         /* Apply folder rules */
1997         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1998
1999         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2000                 /* Set status failed and set an error */
2001                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2002                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2003                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2004                              _("FIXME: folder does not accept msgs"));
2005                 /* Notify the queue */
2006                 modest_mail_operation_notify_end (self);
2007                 return;
2008         }
2009
2010         /* Create the helper */
2011         helper = g_slice_new0 (XFerMsgAsyncHelper);
2012         helper->mail_op = g_object_ref(self);
2013         helper->dest_folder = g_object_ref(folder);
2014         helper->headers = g_object_ref(headers);
2015         helper->user_callback = user_callback;
2016         helper->user_data = user_data;
2017
2018         /* Get source folder */
2019         iter = tny_list_create_iterator (headers);
2020         header = TNY_HEADER (tny_iterator_get_current (iter));
2021         src_folder = tny_header_get_folder (header);
2022         g_object_unref (header);
2023         g_object_unref (iter);
2024
2025         /* Get account and set it into mail_operation */
2026         priv->account = modest_tny_folder_get_account (src_folder);
2027
2028         /* Transfer messages */
2029         tny_folder_transfer_msgs_async (src_folder, 
2030                                         headers, 
2031                                         folder, 
2032                                         delete_original, 
2033                                         transfer_msgs_cb, 
2034                                         transfer_msgs_status_cb,
2035                                         helper);
2036 }
2037
2038
2039 static void
2040 on_refresh_folder (TnyFolder   *folder, 
2041                    gboolean     cancelled, 
2042                    GError     **error,
2043                    gpointer     user_data)
2044 {
2045         RefreshAsyncHelper *helper = NULL;
2046         ModestMailOperation *self = NULL;
2047         ModestMailOperationPrivate *priv = NULL;
2048
2049         helper = (RefreshAsyncHelper *) user_data;
2050         self = helper->mail_op;
2051         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2052
2053         if (*error) {
2054                 priv->error = g_error_copy (*error);
2055                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2056                 goto out;
2057         }
2058
2059         if (cancelled) {
2060                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2061                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2062                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2063                              _("Error trying to refresh the contents of %s"),
2064                              tny_folder_get_name (folder));
2065                 goto out;
2066         }
2067
2068         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2069
2070  out:
2071         /* Call user defined callback, if it exists */
2072         if (helper->user_callback)
2073                 helper->user_callback (priv->source, folder, helper->user_data);
2074
2075         /* Free */
2076         g_object_unref (helper->mail_op);
2077         g_slice_free   (RefreshAsyncHelper, helper);
2078         g_object_unref (folder);
2079
2080         /* Notify about operation end */
2081         modest_mail_operation_notify_end (self);
2082 }
2083
2084 static void
2085 on_refresh_folder_status_update (GObject *obj,
2086                                  TnyStatus *status,
2087                                  gpointer user_data)
2088 {
2089         RefreshAsyncHelper *helper = NULL;
2090         ModestMailOperation *self = NULL;
2091         ModestMailOperationPrivate *priv = NULL;
2092         ModestMailOperationState *state;
2093
2094         g_return_if_fail (user_data != NULL);
2095         g_return_if_fail (status != NULL);
2096         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2097
2098         helper = (RefreshAsyncHelper *) user_data;
2099         self = helper->mail_op;
2100         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2101
2102         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2103
2104         priv->done = status->position;
2105         priv->total = status->of_total;
2106
2107         state = modest_mail_operation_clone_state (self);
2108         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2109         g_slice_free (ModestMailOperationState, state);
2110 }
2111
2112 void 
2113 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2114                                        TnyFolder *folder,
2115                                        RefreshAsyncUserCallback user_callback,
2116                                        gpointer user_data)
2117 {
2118         ModestMailOperationPrivate *priv = NULL;
2119         RefreshAsyncHelper *helper = NULL;
2120
2121         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2122
2123         /* Pick a reference */
2124         g_object_ref (folder);
2125
2126         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2127
2128         /* Get account and set it into mail_operation */
2129         priv->account = modest_tny_folder_get_account  (folder);
2130
2131         /* Create the helper */
2132         helper = g_slice_new0 (RefreshAsyncHelper);
2133         helper->mail_op = g_object_ref(self);
2134         helper->user_callback = user_callback;
2135         helper->user_data = user_data;
2136
2137         /* Refresh the folder. TODO: tinymail could issue a status
2138            updates before the callback call then this could happen. We
2139            must review the design */
2140         tny_folder_refresh_async (folder,
2141                                   on_refresh_folder,
2142                                   on_refresh_folder_status_update,
2143                                   helper);
2144 }
2145
2146 /**
2147  *
2148  * It's used by the mail operation queue to notify the observers
2149  * attached to that signal that the operation finished. We need to use
2150  * that because tinymail does not give us the progress of a given
2151  * operation when it finishes (it directly calls the operation
2152  * callback).
2153  */
2154 static void
2155 modest_mail_operation_notify_end (ModestMailOperation *self)
2156 {
2157         ModestMailOperationState *state;
2158         ModestMailOperationPrivate *priv = NULL;
2159
2160         g_return_if_fail (self);
2161
2162         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2163
2164         if (!priv) {
2165                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2166                 return;
2167         }
2168         
2169         /* Set the account back to not busy */
2170         if (priv->account_name) {
2171                 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2172                                                                                                                                                                 FALSE);
2173                 g_free(priv->account_name);
2174                 priv->account_name = NULL;
2175         }
2176         
2177         /* Notify the observers about the mail opertation end */
2178         state = modest_mail_operation_clone_state (self);
2179         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2180         g_slice_free (ModestMailOperationState, state);
2181 }