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