* Fixed bug in empty_view of headers treeview when selected folder is empty.
[modest] / src / modest-mail-operation.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include <stdarg.h>
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-send-queue.h>
40 #include <tny-status.h>
41 #include <tny-folder-observer.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include "modest-platform.h"
45 #include <modest-tny-account.h>
46 #include <modest-tny-send-queue.h>
47 #include <modest-runtime.h>
48 #include "modest-text-utils.h"
49 #include "modest-tny-msg.h"
50 #include "modest-tny-folder.h"
51 #include "modest-tny-platform-factory.h"
52 #include "modest-marshal.h"
53 #include "modest-error.h"
54 #include "modest-mail-operation.h"
55
56 #define KB 1024
57
58 /* 'private'/'protected' functions */
59 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
60 static void modest_mail_operation_init       (ModestMailOperation *obj);
61 static void modest_mail_operation_finalize   (GObject *obj);
62
63 static void     get_msg_cb (TnyFolder *folder, 
64                             gboolean cancelled, 
65                             TnyMsg *msg, 
66                             GError **err, 
67                             gpointer user_data);
68
69 static void     get_msg_status_cb (GObject *obj,
70                                    TnyStatus *status,  
71                                    gpointer user_data);
72
73 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
74
75 static gboolean did_a_cancel = FALSE;
76
77 enum _ModestMailOperationSignals 
78 {
79         PROGRESS_CHANGED_SIGNAL,
80
81         NUM_SIGNALS
82 };
83
84 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
85 struct _ModestMailOperationPrivate {
86         TnyAccount                 *account;
87         guint                      done;
88         guint                      total;
89         GObject                   *source;
90         GError                    *error;
91         ErrorCheckingUserCallback  error_checking;
92         gpointer                   error_checking_user_data;
93         ModestMailOperationStatus  status;      
94         ModestMailOperationTypeOperation op_type;
95 };
96
97 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
98                                                    MODEST_TYPE_MAIL_OPERATION, \
99                                                    ModestMailOperationPrivate))
100
101 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
102                                                    priv->status = new_status;\
103                                                }
104
105 typedef struct _GetMsgAsyncHelper {     
106         ModestMailOperation *mail_op;
107         GetMsgAsyncUserCallback user_callback;  
108         guint pending_ops;
109         gpointer user_data;
110 } GetMsgAsyncHelper;
111
112 typedef struct _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         tny_header_set_flags (header, priority_flags);
532
533         /* Call mail operation */
534         modest_mail_operation_send_mail (self, transport_account, new_msg);
535
536         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
537         if (folder) {
538                 if (draft_msg != NULL) {
539                         header = tny_msg_get_header (draft_msg);
540                         /* Note: This can fail (with a warning) if the message is not really already in a folder,
541                          * because this function requires it to have a UID. */
542                         tny_folder_remove_msg (folder, header, NULL);
543                         g_object_unref (header);
544                 }
545         }
546
547         /* Free */
548         g_object_unref (G_OBJECT (new_msg));
549 }
550
551 void
552 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
553                                       TnyTransportAccount *transport_account,
554                                       TnyMsg *draft_msg,
555                                       const gchar *from,  const gchar *to,
556                                       const gchar *cc,  const gchar *bcc,
557                                       const gchar *subject, const gchar *plain_body,
558                                       const gchar *html_body,
559                                       const GList *attachments_list,
560                                       TnyHeaderFlags priority_flags)
561 {
562         TnyMsg *msg = NULL;
563         TnyFolder *folder = NULL;
564         TnyHeader *header = NULL;
565         ModestMailOperationPrivate *priv = NULL;
566
567         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
568         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
569
570         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
571
572         /* Get account and set it into mail_operation */
573         priv->account = g_object_ref (transport_account);
574
575         if (html_body == NULL) {
576                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
577         } else {
578                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
579         }
580         if (!msg) {
581                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
582                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
583                              "modest: failed to create a new msg\n");
584                 goto end;
585         }
586
587         /* add priority flags */
588         header = tny_msg_get_header (msg);
589         tny_header_set_flags (header, priority_flags);
590
591         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
592         if (!folder) {
593                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
594                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
595                              "modest: failed to create a new msg\n");
596                 goto end;
597         }
598
599         if (draft_msg != NULL) {
600                 header = tny_msg_get_header (draft_msg);
601                 tny_folder_remove_msg (folder, header, NULL);
602                 g_object_unref (header);
603         }
604         
605         tny_folder_add_msg (folder, msg, &(priv->error));
606         if (priv->error)
607                 goto end;
608
609 end:
610         if (msg)
611                 g_object_unref (G_OBJECT(msg));
612         if (folder)
613                 g_object_unref (G_OBJECT(folder));
614
615         modest_mail_operation_notify_end (self);
616 }
617
618 typedef struct 
619 {
620         ModestMailOperation *mail_op;
621         TnyStoreAccount *account;
622         TnyTransportAccount *transport_account;
623         gint max_size;
624         gint retrieve_limit;
625         gchar *retrieve_type;
626 } UpdateAccountInfo;
627
628 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
629 /* We use this folder observer to track the headers that have been
630  * added to a folder */
631 typedef struct {
632         GObject parent;
633         TnyList *new_headers;
634 } InternalFolderObserver;
635
636 typedef struct {
637         GObjectClass parent;
638 } InternalFolderObserverClass;
639
640 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
641
642 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
643                          internal_folder_observer,
644                          G_TYPE_OBJECT,
645                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
646
647
648 static void
649 foreach_add_item (gpointer header, gpointer user_data)
650 {
651         /* printf("DEBUG: %s: header subject=%s\n", 
652          * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
653          */
654         tny_list_prepend (TNY_LIST (user_data), 
655                           g_object_ref (G_OBJECT (header)));
656 }
657
658 /* This is the method that looks for new messages in a folder */
659 static void
660 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
661 {
662         InternalFolderObserver *derived = (InternalFolderObserver *)self;
663         
664         TnyFolderChangeChanged changed;
665
666         changed = tny_folder_change_get_changed (change);
667
668         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
669                 TnyList *list;
670
671                 /* Get added headers */
672                 list = tny_simple_list_new ();
673                 tny_folder_change_get_added_headers (change, list);
674
675                 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n", 
676                  *      __FUNCTION__, tny_list_get_length(list));
677                  */
678                  
679                 /* Add them to the folder observer */
680                 tny_list_foreach (list, foreach_add_item, 
681                                   derived->new_headers);
682
683                 g_object_unref (G_OBJECT (list));
684         }
685 }
686
687 static void
688 internal_folder_observer_init (InternalFolderObserver *self) 
689 {
690         self->new_headers = tny_simple_list_new ();
691 }
692 static void
693 internal_folder_observer_finalize (GObject *object) 
694 {
695         InternalFolderObserver *self;
696
697         self = (InternalFolderObserver *) object;
698         g_object_unref (self->new_headers);
699
700         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
701 }
702 static void
703 tny_folder_observer_init (TnyFolderObserverIface *iface) 
704 {
705         iface->update_func = internal_folder_observer_update;
706 }
707 static void
708 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
709 {
710         GObjectClass *object_class;
711
712         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
713         object_class = (GObjectClass*) klass;
714         object_class->finalize = internal_folder_observer_finalize;
715 }
716
717 /*****************/
718
719 static void
720 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
721 {
722         TnyIterator *iter;
723         TnyList *folders = tny_simple_list_new ();
724
725         tny_folder_store_get_folders (store, folders, query, NULL);
726         iter = tny_list_create_iterator (folders);
727
728         while (!tny_iterator_is_done (iter)) {
729
730                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
731
732                 tny_list_prepend (all_folders, G_OBJECT (folder));
733                 recurse_folders (folder, query, all_folders);    
734                 g_object_unref (G_OBJECT (folder));
735
736                 tny_iterator_next (iter);
737         }
738          g_object_unref (G_OBJECT (iter));
739          g_object_unref (G_OBJECT (folders));
740 }
741
742 /* 
743  * Issues the "progress-changed" signal. The timer won't be removed,
744  * so you must call g_source_remove to stop the signal emission
745  */
746 static gboolean
747 idle_notify_progress (gpointer data)
748 {
749         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
750         ModestMailOperationState *state;
751
752         state = modest_mail_operation_clone_state (mail_op);
753         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
754         g_slice_free (ModestMailOperationState, state);
755
756         return TRUE;
757 }
758
759 /* 
760  * Issues the "progress-changed" signal and removes the timer. It uses
761  * a lock to ensure that the progress information of the mail
762  * operation is not modified while there are notifications pending
763  */
764 static gboolean
765 idle_notify_progress_once (gpointer data)
766 {
767         ModestPair *pair;
768
769         pair = (ModestPair *) data;
770
771         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
772
773         /* Free the state and the reference to the mail operation */
774         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
775         g_object_unref (pair->first);
776
777         return FALSE;
778 }
779
780 /* 
781  * Used by update_account_thread to notify the queue from the main
782  * loop. We call it inside an idle call to achieve that
783  */
784 static gboolean
785 notify_update_account_queue (gpointer data)
786 {
787         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
788         ModestMailOperationPrivate *priv = NULL;
789
790         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
791
792         modest_mail_operation_notify_end (mail_op);
793         g_object_unref (mail_op);
794
795         return FALSE;
796 }
797
798 static int
799 compare_headers_by_date (gconstpointer a, 
800                          gconstpointer b)
801 {
802         TnyHeader **header1, **header2;
803         time_t sent1, sent2;
804
805         header1 = (TnyHeader **) a;
806         header2 = (TnyHeader **) b;
807
808         sent1 = tny_header_get_date_sent (*header1);
809         sent2 = tny_header_get_date_sent (*header2);
810
811         /* We want the most recent ones (greater time_t) at the
812            beginning */
813         if (sent1 < sent2)
814                 return 1;
815         else
816                 return -1;
817 }
818
819 static gboolean 
820 set_last_updated_idle (gpointer data)
821 {
822         /* It does not matter if the time is not exactly the same than
823            the time when this idle was called, it's just an
824            approximation and it won't be very different */
825         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
826                                     (gchar *) data, 
827                                     MODEST_ACCOUNT_LAST_UPDATED, 
828                                     time(NULL), 
829                                     TRUE);
830
831         return FALSE;
832 }
833
834 static gpointer
835 update_account_thread (gpointer thr_user_data)
836 {
837         UpdateAccountInfo *info;
838         TnyList *all_folders = NULL;
839         GPtrArray *new_headers;
840         TnyIterator *iter = NULL;
841         TnyFolderStoreQuery *query = NULL;
842         ModestMailOperationPrivate *priv;
843         ModestTnySendQueue *send_queue;
844
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, 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->pending_ops = 1;
1447                 helper->user_data = user_data;
1448
1449                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1450
1451                 g_object_unref (G_OBJECT (folder));
1452         } else {
1453                 /* Set status failed and set an error */
1454                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1455                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1456                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1457                              _("Error trying to get a message. No folder found for header"));
1458
1459                 /* Notify the queue */
1460                 modest_mail_operation_notify_end (self);
1461         }
1462 }
1463
1464 static void
1465 get_msg_cb (TnyFolder *folder, 
1466             gboolean cancelled, 
1467             TnyMsg *msg, 
1468             GError **error, 
1469             gpointer user_data)
1470 {
1471         GetMsgAsyncHelper *helper = NULL;
1472         ModestMailOperation *self = NULL;
1473         ModestMailOperationPrivate *priv = NULL;
1474
1475         helper = (GetMsgAsyncHelper *) user_data;
1476         g_return_if_fail (helper != NULL);       
1477         self = helper->mail_op;
1478         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1479         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1480         
1481         helper->pending_ops--;
1482
1483         /* Check errors and cancel */
1484         if (*error) {
1485                 priv->error = g_error_copy (*error);
1486                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1487                 goto out;
1488         }
1489         if (cancelled) {
1490                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1491                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1492                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1493                              _("Error trying to refresh the contents of %s"),
1494                              tny_folder_get_name (folder));
1495                 goto out;
1496         }
1497
1498         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1499
1500         /* If user defined callback function was defined, call it */
1501         if (helper->user_callback) {
1502                 helper->user_callback (self, NULL, msg, helper->user_data);
1503         }
1504
1505         /* Free */
1506  out:
1507         if (helper->pending_ops == 0) {
1508                 g_slice_free (GetMsgAsyncHelper, helper);
1509                 
1510                 /* Notify about operation end */
1511                 modest_mail_operation_notify_end (self);        
1512         }
1513 }
1514
1515 static void     
1516 get_msg_status_cb (GObject *obj,
1517                    TnyStatus *status,  
1518                    gpointer user_data)
1519 {
1520         GetMsgAsyncHelper *helper = NULL;
1521         ModestMailOperation *self;
1522         ModestMailOperationPrivate *priv;
1523         ModestMailOperationState *state;
1524
1525         g_return_if_fail (status != NULL);
1526         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1527
1528         helper = (GetMsgAsyncHelper *) user_data;
1529         g_return_if_fail (helper != NULL);       
1530
1531         self = helper->mail_op;
1532         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1533
1534         if ((status->position == 1) && (status->of_total == 100))
1535                 return;
1536
1537         priv->done = 1;
1538         priv->total = 1;
1539
1540         state = modest_mail_operation_clone_state (self);
1541         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1542         g_slice_free (ModestMailOperationState, state);
1543 }
1544
1545 /****************************************************/
1546 typedef struct {
1547         ModestMailOperation *mail_op;
1548         TnyList *headers;
1549         GetMsgAsyncUserCallback user_callback;
1550         gpointer user_data;
1551         GDestroyNotify notify;
1552 } GetFullMsgsInfo;
1553
1554 typedef struct {
1555         GetMsgAsyncUserCallback user_callback;
1556         TnyHeader *header;
1557         TnyMsg *msg;
1558         gpointer user_data;
1559         ModestMailOperation *mail_op;
1560 } NotifyGetMsgsInfo;
1561
1562
1563 /* 
1564  * Used by get_msgs_full_thread to call the user_callback for each
1565  * message that has been read
1566  */
1567 static gboolean
1568 notify_get_msgs_full (gpointer data)
1569 {
1570         NotifyGetMsgsInfo *info;
1571
1572         info = (NotifyGetMsgsInfo *) data;      
1573
1574         /* Call the user callback */
1575         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1576
1577         g_slice_free (NotifyGetMsgsInfo, info);
1578
1579         return FALSE;
1580 }
1581
1582 /* 
1583  * Used by get_msgs_full_thread to free al the thread resources and to
1584  * call the destroy function for the passed user_data
1585  */
1586 static gboolean
1587 get_msgs_full_destroyer (gpointer data)
1588 {
1589         GetFullMsgsInfo *info;
1590
1591         info = (GetFullMsgsInfo *) data;
1592
1593         if (info->notify)
1594                 info->notify (info->user_data);
1595
1596         /* free */
1597         g_object_unref (info->headers);
1598         g_slice_free (GetFullMsgsInfo, info);
1599
1600         return FALSE;
1601 }
1602
1603 static gpointer
1604 get_msgs_full_thread (gpointer thr_user_data)
1605 {
1606         GetFullMsgsInfo *info;
1607         ModestMailOperationPrivate *priv = NULL;
1608         TnyIterator *iter = NULL;
1609         
1610         info = (GetFullMsgsInfo *) thr_user_data;       
1611         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1612
1613         iter = tny_list_create_iterator (info->headers);
1614         while (!tny_iterator_is_done (iter)) { 
1615                 TnyHeader *header;
1616                 TnyFolder *folder;
1617                 
1618                 header = TNY_HEADER (tny_iterator_get_current (iter));
1619                 folder = tny_header_get_folder (header);
1620                                 
1621                 /* Get message from folder */
1622                 if (folder) {
1623                         TnyMsg *msg;
1624                         /* The callback will call it per each header */
1625                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1626
1627                         if (msg) {
1628                                 ModestMailOperationState *state;
1629                                 ModestPair *pair;
1630
1631                                 priv->done++;
1632
1633                                 /* notify progress */
1634                                 state = modest_mail_operation_clone_state (info->mail_op);
1635                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1636                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1637                                                  pair, (GDestroyNotify) modest_pair_free);
1638
1639                                 /* The callback is the responsible for
1640                                    freeing the message */
1641                                 if (info->user_callback) {
1642                                         NotifyGetMsgsInfo *info_notify;
1643                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1644                                         info_notify->user_callback = info->user_callback;
1645                                         info_notify->mail_op = info->mail_op;
1646                                         info_notify->header = g_object_ref (header);
1647                                         info_notify->msg = g_object_ref (msg);
1648                                         info_notify->user_data = info->user_data;
1649                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1650                                                          notify_get_msgs_full, 
1651                                                          info_notify, NULL);
1652                                 }
1653                                 g_object_unref (msg);
1654                         }
1655                 } else {
1656                         /* Set status failed and set an error */
1657                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1658                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1659                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1660                                      "Error trying to get a message. No folder found for header");
1661                 }
1662                 g_object_unref (header);                
1663                 tny_iterator_next (iter);
1664         }
1665
1666         /* Set operation status */
1667         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1668                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1669
1670         /* Notify about operation end */
1671         g_idle_add (notify_update_account_queue, info->mail_op);
1672
1673         /* Free thread resources. Will be called after all previous idles */
1674         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1675
1676         return NULL;
1677 }
1678
1679 void 
1680 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1681                                      TnyList *header_list, 
1682                                      GetMsgAsyncUserCallback user_callback,
1683                                      gpointer user_data,
1684                                      GDestroyNotify notify)
1685 {
1686         TnyHeader *header = NULL;
1687         TnyFolder *folder = NULL;
1688         GThread *thread;
1689         ModestMailOperationPrivate *priv = NULL;
1690         GetFullMsgsInfo *info = NULL;
1691         gboolean size_ok = TRUE;
1692         gint max_size;
1693         TnyIterator *iter = NULL;
1694         
1695         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1696         
1697         /* Init mail operation */
1698         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1699         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1700         priv->done = 0;
1701         priv->total = tny_list_get_length(header_list);
1702
1703         /* Get account and set it into mail_operation */
1704         if (tny_list_get_length (header_list) >= 1) {
1705                 iter = tny_list_create_iterator (header_list);
1706                 header = TNY_HEADER (tny_iterator_get_current (iter));
1707                 folder = tny_header_get_folder (header);                
1708                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1709                 g_object_unref (header);
1710                 g_object_unref (folder);
1711
1712                 if (tny_list_get_length (header_list) == 1) {
1713                         g_object_unref (iter);
1714                         iter = NULL;
1715                 }
1716         }
1717
1718         /* Get msg size limit */
1719         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1720                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1721                                          &(priv->error));
1722         if (priv->error) {
1723                 g_clear_error (&(priv->error));
1724                 max_size = G_MAXINT;
1725         } else {
1726                 max_size = max_size * KB;
1727         }
1728
1729         /* Check message size limits. If there is only one message
1730            always retrieve it */
1731         if (iter != NULL) {
1732                 while (!tny_iterator_is_done (iter) && size_ok) {
1733                         header = TNY_HEADER (tny_iterator_get_current (iter));
1734                         if (tny_header_get_message_size (header) >= max_size)
1735                                 size_ok = FALSE;
1736                         g_object_unref (header);
1737                         tny_iterator_next (iter);
1738                 }
1739                 g_object_unref (iter);
1740         }
1741
1742         if (size_ok) {
1743                 /* Create the info */
1744                 info = g_slice_new0 (GetFullMsgsInfo);
1745                 info->mail_op = self;
1746                 info->user_callback = user_callback;
1747                 info->user_data = user_data;
1748                 info->headers = g_object_ref (header_list);
1749                 info->notify = notify;
1750
1751                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1752         } else {
1753                 /* Set status failed and set an error */
1754                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1755                 /* FIXME: the error msg is different for pop */
1756                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1757                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1758                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1759                 /* Remove from queue and free resources */
1760                 modest_mail_operation_notify_end (self);
1761                 if (notify)
1762                         notify (user_data);
1763         }
1764 }
1765
1766
1767 void 
1768 modest_mail_operation_remove_msg (ModestMailOperation *self,
1769                                   TnyHeader *header,
1770                                   gboolean remove_to_trash)
1771 {
1772         TnyFolder *folder;
1773         ModestMailOperationPrivate *priv;
1774
1775         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1776         g_return_if_fail (TNY_IS_HEADER (header));
1777
1778         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1779         folder = tny_header_get_folder (header);
1780
1781         /* Get account and set it into mail_operation */
1782         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1783
1784         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1785
1786         /* Delete or move to trash */
1787         if (remove_to_trash) {
1788                 TnyFolder *trash_folder;
1789                 TnyStoreAccount *store_account;
1790
1791                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1792                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1793                                                                       TNY_FOLDER_TYPE_TRASH);
1794                 if (trash_folder) {
1795                         TnyList *headers;
1796
1797                         /* Create list */
1798                         headers = tny_simple_list_new ();
1799                         tny_list_append (headers, G_OBJECT (header));
1800                         g_object_unref (header);
1801
1802                         /* Move to trash */
1803                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1804                         g_object_unref (headers);
1805 /*                      g_object_unref (trash_folder); */
1806                 } else {
1807                         ModestMailOperationPrivate *priv;
1808
1809                         /* Set status failed and set an error */
1810                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1811                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1812                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1813                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1814                                      _("Error trying to delete a message. Trash folder not found"));
1815                 }
1816
1817                 g_object_unref (G_OBJECT (store_account));
1818         } else {
1819                 tny_folder_remove_msg (folder, header, &(priv->error));
1820                 if (!priv->error)
1821                         tny_folder_sync(folder, TRUE, &(priv->error));
1822         }
1823
1824         /* Set status */
1825         if (!priv->error)
1826                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1827         else
1828                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1829
1830         /* Free */
1831         g_object_unref (G_OBJECT (folder));
1832
1833         /* Notify about operation end */
1834         modest_mail_operation_notify_end (self);
1835 }
1836
1837 static void
1838 transfer_msgs_status_cb (GObject *obj,
1839                          TnyStatus *status,  
1840                          gpointer user_data)
1841 {
1842         XFerMsgAsyncHelper *helper = NULL;
1843         ModestMailOperation *self;
1844         ModestMailOperationPrivate *priv;
1845         ModestMailOperationState *state;
1846
1847
1848         g_return_if_fail (status != NULL);
1849         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1850
1851         helper = (XFerMsgAsyncHelper *) user_data;
1852         g_return_if_fail (helper != NULL);       
1853
1854         self = helper->mail_op;
1855         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1856
1857         if ((status->position == 1) && (status->of_total == 100))
1858                 return;
1859
1860         priv->done = status->position;
1861         priv->total = status->of_total;
1862
1863         state = modest_mail_operation_clone_state (self);
1864         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1865         g_slice_free (ModestMailOperationState, state);
1866 }
1867
1868
1869 static void
1870 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1871 {
1872         XFerMsgAsyncHelper *helper;
1873         ModestMailOperation *self;
1874         ModestMailOperationPrivate *priv;
1875
1876         helper = (XFerMsgAsyncHelper *) user_data;
1877         self = helper->mail_op;
1878
1879         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1880
1881         if (*err) {
1882                 priv->error = g_error_copy (*err);
1883                 priv->done = 0;
1884                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1885         } else if (cancelled) {
1886                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1887                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1888                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1889                              _("Error trying to refresh the contents of %s"),
1890                              tny_folder_get_name (folder));
1891         } else {
1892                 priv->done = 1;
1893                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1894         }
1895
1896         /* If user defined callback function was defined, call it */
1897         if (helper->user_callback) {
1898                 helper->user_callback (priv->source, helper->user_data);
1899         }
1900
1901         /* Free */
1902         g_object_unref (helper->headers);
1903         g_object_unref (helper->dest_folder);
1904         g_object_unref (helper->mail_op);
1905         g_slice_free   (XFerMsgAsyncHelper, helper);
1906         g_object_unref (folder);
1907
1908         /* Notify about operation end */
1909         modest_mail_operation_notify_end (self);
1910 }
1911
1912 void
1913 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1914                                  TnyList *headers, 
1915                                  TnyFolder *folder, 
1916                                  gboolean delete_original,
1917                                  XferMsgsAsynUserCallback user_callback,
1918                                  gpointer user_data)
1919 {
1920         ModestMailOperationPrivate *priv;
1921         TnyIterator *iter;
1922         TnyFolder *src_folder;
1923         XFerMsgAsyncHelper *helper;
1924         TnyHeader *header;
1925         ModestTnyFolderRules rules;
1926
1927         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1928         g_return_if_fail (TNY_IS_LIST (headers));
1929         g_return_if_fail (TNY_IS_FOLDER (folder));
1930
1931         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1932         priv->total = 1;
1933         priv->done = 0;
1934         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1935
1936         /* Apply folder rules */
1937         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1938
1939         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1940                 /* Set status failed and set an error */
1941                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1942                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1943                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1944                              _("FIXME: folder does not accept msgs"));
1945                 /* Notify the queue */
1946                 modest_mail_operation_notify_end (self);
1947                 return;
1948         }
1949
1950         /* Create the helper */
1951         helper = g_slice_new0 (XFerMsgAsyncHelper);
1952         helper->mail_op = g_object_ref(self);
1953         helper->dest_folder = g_object_ref(folder);
1954         helper->headers = g_object_ref(headers);
1955         helper->user_callback = user_callback;
1956         helper->user_data = user_data;
1957
1958         /* Get source folder */
1959         iter = tny_list_create_iterator (headers);
1960         header = TNY_HEADER (tny_iterator_get_current (iter));
1961         src_folder = tny_header_get_folder (header);
1962         g_object_unref (header);
1963         g_object_unref (iter);
1964
1965         /* Get account and set it into mail_operation */
1966         priv->account = modest_tny_folder_get_account (src_folder);
1967
1968         /* Transfer messages */
1969         tny_folder_transfer_msgs_async (src_folder, 
1970                                         headers, 
1971                                         folder, 
1972                                         delete_original, 
1973                                         transfer_msgs_cb, 
1974                                         transfer_msgs_status_cb,
1975                                         helper);
1976 }
1977
1978
1979 static void
1980 on_refresh_folder (TnyFolder   *folder, 
1981                    gboolean     cancelled, 
1982                    GError     **error,
1983                    gpointer     user_data)
1984 {
1985         RefreshAsyncHelper *helper = NULL;
1986         ModestMailOperation *self = NULL;
1987         ModestMailOperationPrivate *priv = NULL;
1988
1989         helper = (RefreshAsyncHelper *) user_data;
1990         self = helper->mail_op;
1991         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1992
1993         if (*error) {
1994                 priv->error = g_error_copy (*error);
1995                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1996                 goto out;
1997         }
1998
1999         if (cancelled) {
2000                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2001                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2002                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2003                              _("Error trying to refresh the contents of %s"),
2004                              tny_folder_get_name (folder));
2005                 goto out;
2006         }
2007
2008         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2009
2010         /* Call user defined callback, if it exists */
2011         if (helper->user_callback)
2012                 helper->user_callback (priv->source, folder, helper->user_data);
2013
2014  out:
2015         /* Free */
2016         g_object_unref (helper->mail_op);
2017         g_slice_free   (RefreshAsyncHelper, helper);
2018         g_object_unref (folder);
2019
2020         /* Notify about operation end */
2021         modest_mail_operation_notify_end (self);
2022 }
2023
2024 static void
2025 on_refresh_folder_status_update (GObject *obj,
2026                                  TnyStatus *status,
2027                                  gpointer user_data)
2028 {
2029         RefreshAsyncHelper *helper = NULL;
2030         ModestMailOperation *self = NULL;
2031         ModestMailOperationPrivate *priv = NULL;
2032         ModestMailOperationState *state;
2033
2034         g_return_if_fail (user_data != NULL);
2035         g_return_if_fail (status != NULL);
2036         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2037
2038         helper = (RefreshAsyncHelper *) user_data;
2039         self = helper->mail_op;
2040         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2041
2042         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2043
2044         priv->done = status->position;
2045         priv->total = status->of_total;
2046
2047         state = modest_mail_operation_clone_state (self);
2048         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2049         g_slice_free (ModestMailOperationState, state);
2050 }
2051
2052 void 
2053 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2054                                        TnyFolder *folder,
2055                                        RefreshAsyncUserCallback user_callback,
2056                                        gpointer user_data)
2057 {
2058         ModestMailOperationPrivate *priv = NULL;
2059         RefreshAsyncHelper *helper = NULL;
2060
2061         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2062
2063         /* Pick a reference */
2064         g_object_ref (folder);
2065
2066         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2067
2068         /* Get account and set it into mail_operation */
2069         priv->account = modest_tny_folder_get_account  (folder);
2070
2071         /* Create the helper */
2072         helper = g_slice_new0 (RefreshAsyncHelper);
2073         helper->mail_op = g_object_ref(self);
2074         helper->user_callback = user_callback;
2075         helper->user_data = user_data;
2076
2077         /* Refresh the folder. TODO: tinymail could issue a status
2078            updates before the callback call then this could happen. We
2079            must review the design */
2080         tny_folder_refresh_async (folder,
2081                                   on_refresh_folder,
2082                                   on_refresh_folder_status_update,
2083                                   helper);
2084 }
2085
2086 /**
2087  *
2088  * It's used by the mail operation queue to notify the observers
2089  * attached to that signal that the operation finished. We need to use
2090  * that because tinymail does not give us the progress of a given
2091  * operation when it finishes (it directly calls the operation
2092  * callback).
2093  */
2094 static void
2095 modest_mail_operation_notify_end (ModestMailOperation *self)
2096 {
2097         ModestMailOperationState *state;
2098
2099         /* Notify the observers about the mail opertation end */
2100         state = modest_mail_operation_clone_state (self);
2101         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2102         g_slice_free (ModestMailOperationState, state);
2103 }