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