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