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