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