* Added ending notification to the send_mail 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         priv->done = 1;
508         priv->total = 1;
509
510         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
511         if (!TNY_IS_SEND_QUEUE(send_queue)) {
512                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
513                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
514                              "modest: could not find send queue for account\n");
515         } else {
516                 /* TODO: connect to the msg-sent in order to know when
517                    the mail operation is finished */
518                 tny_send_queue_add (send_queue, msg, &(priv->error));
519                 /* TODO: we're setting always success, do the check in
520                    the handler */
521                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
522         }
523
524         /* TODO: do this in the handler of the "msg-sent"
525            signal.Notify about operation end */
526         modest_mail_operation_notify_end (self);
527 }
528
529 void
530 modest_mail_operation_send_new_mail (ModestMailOperation *self,
531                                      TnyTransportAccount *transport_account,
532                                      TnyMsg *draft_msg,
533                                      const gchar *from,  const gchar *to,
534                                      const gchar *cc,  const gchar *bcc,
535                                      const gchar *subject, const gchar *plain_body,
536                                      const gchar *html_body,
537                                      const GList *attachments_list,
538                                      TnyHeaderFlags priority_flags)
539 {
540         TnyMsg *new_msg = NULL;
541         TnyFolder *folder = NULL;
542         TnyHeader *header = NULL;
543         ModestMailOperationPrivate *priv = NULL;
544
545         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
546         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
547
548         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
549
550         /* Check parametters */
551         if (to == NULL) {
552                 /* Set status failed and set an error */
553                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
554                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
555                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
556                              _("Error trying to send a mail. You need to set at least one recipient"));
557                 return;
558         }
559
560         if (html_body == NULL) {
561                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
562         } else {
563                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
564         }
565         if (!new_msg) {
566                 g_printerr ("modest: failed to create a new msg\n");
567                 return;
568         }
569
570         /* Set priority flags in message */
571         header = tny_msg_get_header (new_msg);
572         if (priority_flags != 0)
573                 tny_header_set_flags (header, priority_flags);
574
575         /* Call mail operation */
576         modest_mail_operation_send_mail (self, transport_account, new_msg);
577
578         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
579         if (folder) {
580                 if (draft_msg != NULL) {
581                         header = tny_msg_get_header (draft_msg);
582                         /* Note: This can fail (with a warning) if the message is not really already in a folder,
583                          * because this function requires it to have a UID. */
584                         tny_folder_remove_msg (folder, header, NULL);
585                         g_object_unref (header);
586                 }
587         }
588
589         /* Free */
590         g_object_unref (G_OBJECT (new_msg));
591 }
592
593 void
594 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
595                                       TnyTransportAccount *transport_account,
596                                       TnyMsg *draft_msg,
597                                       const gchar *from,  const gchar *to,
598                                       const gchar *cc,  const gchar *bcc,
599                                       const gchar *subject, const gchar *plain_body,
600                                       const gchar *html_body,
601                                       const GList *attachments_list,
602                                       TnyHeaderFlags priority_flags)
603 {
604         TnyMsg *msg = NULL;
605         TnyFolder *folder = NULL;
606         TnyHeader *header = NULL;
607         ModestMailOperationPrivate *priv = NULL;
608
609         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
610         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
611
612         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
613
614         /* Get account and set it into mail_operation */
615         priv->account = g_object_ref (transport_account);
616
617         if (html_body == NULL) {
618                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
619         } else {
620                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
621         }
622         if (!msg) {
623                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
624                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
625                              "modest: failed to create a new msg\n");
626                 goto end;
627         }
628
629         /* add priority flags */
630         header = tny_msg_get_header (msg);
631         tny_header_set_flags (header, priority_flags);
632
633         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
634         if (!folder) {
635                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
636                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
637                              "modest: failed to create a new msg\n");
638                 goto end;
639         }
640
641         if (draft_msg != NULL) {
642                 header = tny_msg_get_header (draft_msg);
643                 /* Remove the old draft expunging it */
644                 tny_folder_remove_msg (folder, header, NULL);
645                 tny_folder_sync (folder, TRUE, NULL);
646                 g_object_unref (header);
647         }
648         
649         tny_folder_add_msg (folder, msg, &(priv->error));
650         if (priv->error)
651                 goto end;
652
653 end:
654         if (msg)
655                 g_object_unref (G_OBJECT(msg));
656         if (folder)
657                 g_object_unref (G_OBJECT(folder));
658
659         modest_mail_operation_notify_end (self);
660 }
661
662 typedef struct 
663 {
664         ModestMailOperation *mail_op;
665         TnyStoreAccount *account;
666         TnyTransportAccount *transport_account;
667         gint max_size;
668         gint retrieve_limit;
669         gchar *retrieve_type;
670         gchar *account_name;
671 } UpdateAccountInfo;
672
673 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
674 /* We use this folder observer to track the headers that have been
675  * added to a folder */
676 typedef struct {
677         GObject parent;
678         TnyList *new_headers;
679 } InternalFolderObserver;
680
681 typedef struct {
682         GObjectClass parent;
683 } InternalFolderObserverClass;
684
685 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
686
687 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
688                          internal_folder_observer,
689                          G_TYPE_OBJECT,
690                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
691
692
693 static void
694 foreach_add_item (gpointer header, gpointer user_data)
695 {
696         /* printf("DEBUG: %s: header subject=%s\n", 
697          * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
698          */
699         tny_list_prepend (TNY_LIST (user_data), 
700                           g_object_ref (G_OBJECT (header)));
701 }
702
703 /* This is the method that looks for new messages in a folder */
704 static void
705 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
706 {
707         InternalFolderObserver *derived = (InternalFolderObserver *)self;
708         
709         TnyFolderChangeChanged changed;
710
711         changed = tny_folder_change_get_changed (change);
712
713         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
714                 TnyList *list;
715
716                 /* Get added headers */
717                 list = tny_simple_list_new ();
718                 tny_folder_change_get_added_headers (change, list);
719
720                 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n", 
721                  *      __FUNCTION__, tny_list_get_length(list));
722                  */
723                  
724                 /* Add them to the folder observer */
725                 tny_list_foreach (list, foreach_add_item, 
726                                   derived->new_headers);
727
728                 g_object_unref (G_OBJECT (list));
729         }
730 }
731
732 static void
733 internal_folder_observer_init (InternalFolderObserver *self) 
734 {
735         self->new_headers = tny_simple_list_new ();
736 }
737 static void
738 internal_folder_observer_finalize (GObject *object) 
739 {
740         InternalFolderObserver *self;
741
742         self = (InternalFolderObserver *) object;
743         g_object_unref (self->new_headers);
744
745         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
746 }
747 static void
748 tny_folder_observer_init (TnyFolderObserverIface *iface) 
749 {
750         iface->update_func = internal_folder_observer_update;
751 }
752 static void
753 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
754 {
755         GObjectClass *object_class;
756
757         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
758         object_class = (GObjectClass*) klass;
759         object_class->finalize = internal_folder_observer_finalize;
760 }
761
762 /*****************/
763
764 static void
765 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
766 {
767         TnyIterator *iter;
768         TnyList *folders = tny_simple_list_new ();
769
770         tny_folder_store_get_folders (store, folders, query, NULL);
771         iter = tny_list_create_iterator (folders);
772
773         while (!tny_iterator_is_done (iter)) {
774
775                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
776
777                 tny_list_prepend (all_folders, G_OBJECT (folder));
778                 recurse_folders (folder, query, all_folders);    
779                 g_object_unref (G_OBJECT (folder));
780
781                 tny_iterator_next (iter);
782         }
783          g_object_unref (G_OBJECT (iter));
784          g_object_unref (G_OBJECT (folders));
785 }
786
787 /* 
788  * Issues the "progress-changed" signal. The timer won't be removed,
789  * so you must call g_source_remove to stop the signal emission
790  */
791 static gboolean
792 idle_notify_progress (gpointer data)
793 {
794         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
795         ModestMailOperationState *state;
796
797         state = modest_mail_operation_clone_state (mail_op);
798         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
799         g_slice_free (ModestMailOperationState, state);
800         
801         return TRUE;
802 }
803
804 /* 
805  * Issues the "progress-changed" signal and removes the timer. It uses
806  * a lock to ensure that the progress information of the mail
807  * operation is not modified while there are notifications pending
808  */
809 static gboolean
810 idle_notify_progress_once (gpointer data)
811 {
812         ModestPair *pair;
813
814         pair = (ModestPair *) data;
815
816         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
817
818         /* Free the state and the reference to the mail operation */
819         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
820         g_object_unref (pair->first);
821
822         return FALSE;
823 }
824
825 /* 
826  * Used by update_account_thread to notify the queue from the main
827  * loop. We call it inside an idle call to achieve that
828  */
829 static gboolean
830 notify_update_account_queue (gpointer data)
831 {
832         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
833         ModestMailOperationPrivate *priv = NULL;
834
835         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
836         
837         modest_mail_operation_notify_end (mail_op);
838         g_object_unref (mail_op);
839
840         return FALSE;
841 }
842
843 static int
844 compare_headers_by_date (gconstpointer a, 
845                          gconstpointer b)
846 {
847         TnyHeader **header1, **header2;
848         time_t sent1, sent2;
849
850         header1 = (TnyHeader **) a;
851         header2 = (TnyHeader **) b;
852
853         sent1 = tny_header_get_date_sent (*header1);
854         sent2 = tny_header_get_date_sent (*header2);
855
856         /* We want the most recent ones (greater time_t) at the
857            beginning */
858         if (sent1 < sent2)
859                 return 1;
860         else
861                 return -1;
862 }
863
864 static gboolean 
865 set_last_updated_idle (gpointer data)
866 {
867         /* It does not matter if the time is not exactly the same than
868            the time when this idle was called, it's just an
869            approximation and it won't be very different */
870         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
871                                     (gchar *) data, 
872                                     MODEST_ACCOUNT_LAST_UPDATED, 
873                                     time(NULL), 
874                                     TRUE);
875
876         return FALSE;
877 }
878
879 static gpointer
880 update_account_thread (gpointer thr_user_data)
881 {
882         static gboolean first_time = TRUE;
883         UpdateAccountInfo *info;
884         TnyList *all_folders = NULL;
885         GPtrArray *new_headers;
886         TnyIterator *iter = NULL;
887         TnyFolderStoreQuery *query = NULL;
888         ModestMailOperationPrivate *priv;
889         ModestTnySendQueue *send_queue;
890
891         info = (UpdateAccountInfo *) thr_user_data;
892         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
893
894         /* Get account and set it into mail_operation */
895         priv->account = g_object_ref (info->account);
896
897         /*
898          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
899          * show any updates unless we do that
900          */
901         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
902                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
903
904         /* Get all the folders. We can do it synchronously because
905            we're already running in a different thread than the UI */
906         all_folders = tny_simple_list_new ();
907         query = tny_folder_store_query_new ();
908         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
909         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
910                                       all_folders,
911                                       query,
912                                       &(priv->error));
913         if (priv->error) {
914                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
915                 goto out;
916         }
917
918         iter = tny_list_create_iterator (all_folders);
919         while (!tny_iterator_is_done (iter)) {
920                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
921
922                 recurse_folders (folder, query, all_folders);
923                 tny_iterator_next (iter);
924         }
925         g_object_unref (G_OBJECT (iter));
926
927         /* Update status and notify. We need to call the notification
928            with a source function in order to call it from the main
929            loop. We need that in order not to get into trouble with
930            Gtk+. We use a timeout in order to provide more status
931            information, because the sync tinymail call does not
932            provide it for the moment */
933         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
934
935         /* Refresh folders */
936         new_headers = g_ptr_array_new ();
937         iter = tny_list_create_iterator (all_folders);
938
939         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
940
941                 InternalFolderObserver *observer;
942                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
943
944                 /* Refresh the folder */
945                 /* Our observer receives notification of new emails during folder refreshes,
946                  * so we can use observer->new_headers.
947                  * TODO: This does not seem to be providing accurate numbers.
948                  * Possibly the observer is notified asynchronously.
949                  */
950                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
951                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
952                 
953                 /* This gets the status information (headers) from the server.
954                  * We use the blocking version, because we are already in a separate 
955                  * thread.
956                  */
957
958                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
959                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
960                         TnyIterator *iter;
961
962                         /* If the retrieve type is full messages, refresh and get the messages */
963                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
964
965                         iter = tny_list_create_iterator (observer->new_headers);
966                         while (!tny_iterator_is_done (iter)) {
967                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
968                                  
969                                 /* Apply per-message size limits */
970                                 if (tny_header_get_message_size (header) < info->max_size)
971                                         g_ptr_array_add (new_headers, g_object_ref (header));
972
973                                 g_object_unref (header);
974                                 tny_iterator_next (iter);
975                         }
976                         g_object_unref (iter);
977                 } else {
978                         /* We do not need to do it the first time
979                            because it's automatically done by the tree
980                            model */
981                         if (G_UNLIKELY (!first_time))
982                                 tny_folder_poke_status (TNY_FOLDER (folder));
983                 }
984                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
985                 g_object_unref (observer);
986                 observer = NULL;
987
988                 if (priv->error)
989                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
990
991                 g_object_unref (G_OBJECT (folder));
992                 tny_iterator_next (iter);
993         }
994
995         did_a_cancel = FALSE;
996
997         g_object_unref (G_OBJECT (iter));
998         g_source_remove (timeout);
999
1000         if (new_headers->len > 0) {
1001                 gint msg_num = 0;
1002
1003                 /* Order by date */
1004                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1005
1006                 /* Apply message count limit */
1007                 /* If the number of messages exceeds the maximum, ask the
1008                  * user to download them all,
1009                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1010                  */
1011                 printf ("************************** DEBUG: %s: account=%s, len=%d, retrieve_limit = %d\n", __FUNCTION__, 
1012                         tny_account_get_id (priv->account), new_headers->len, info->retrieve_limit);
1013                 if (new_headers->len > info->retrieve_limit) {
1014                         /* TODO: Ask the user, instead of just failing, showing mail_nc_msg_count_limit_exceeded, 
1015                          * with 'Get all' and 'Newest only' buttons. */
1016                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1017                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1018                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1019                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1020                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1021                         goto out;
1022                 }
1023                 
1024                 priv->done = 0;
1025                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1026                 while (msg_num < priv->total) {
1027
1028                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1029                         TnyFolder *folder = tny_header_get_folder (header);
1030                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1031                         ModestMailOperationState *state;
1032                         ModestPair* pair;
1033
1034                         priv->done++;
1035                         /* We can not just use the mail operation because the
1036                            values of done and total could change before the
1037                            idle is called */
1038                         state = modest_mail_operation_clone_state (info->mail_op);
1039                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1040                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1041                                          pair, (GDestroyNotify) modest_pair_free);
1042
1043                         g_object_unref (msg);
1044                         g_object_unref (folder);
1045
1046                         msg_num++;
1047                 }
1048                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1049                 g_ptr_array_free (new_headers, FALSE);
1050         }
1051
1052         /* Perform send */
1053         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1054         priv->done = 0;
1055         priv->total = 0;
1056         if (priv->account != NULL) 
1057                 g_object_unref (priv->account);
1058         priv->account = g_object_ref (info->transport_account);
1059         
1060         send_queue = modest_runtime_get_send_queue (info->transport_account);
1061         if (send_queue) {
1062                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1063                 modest_tny_send_queue_try_to_send (send_queue);
1064                 g_source_remove (timeout);
1065         } else {
1066                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1067                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1068                              "cannot create a send queue for %s\n", 
1069                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1070                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1071         }
1072         
1073         /* Check if the operation was a success */
1074         if (!priv->error) {
1075                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1076
1077                 /* Update the last updated key */
1078                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1079                                  set_last_updated_idle, 
1080                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1081                                  (GDestroyNotify) g_free);
1082         }
1083
1084  out:
1085         /* Notify about operation end. Note that the info could be
1086            freed before this idle happens, but the mail operation will
1087            be still alive */
1088         g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1089         
1090         /* Frees */
1091         g_object_unref (query);
1092         g_object_unref (all_folders);
1093         g_object_unref (info->account);
1094         g_object_unref (info->transport_account);
1095         g_free (info->retrieve_type);
1096         g_slice_free (UpdateAccountInfo, info);
1097
1098         first_time = FALSE;
1099
1100         return NULL;
1101 }
1102
1103 gboolean
1104 modest_mail_operation_update_account (ModestMailOperation *self,
1105                                       const gchar *account_name)
1106 {
1107         GThread *thread;
1108         UpdateAccountInfo *info;
1109         ModestMailOperationPrivate *priv;
1110         ModestAccountMgr *mgr;
1111         TnyStoreAccount *modest_account;
1112         TnyTransportAccount *transport_account;
1113
1114         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1115         g_return_val_if_fail (account_name, FALSE);
1116
1117         /* Make sure that we have a connection, and request one 
1118          * if necessary:
1119          * TODO: Is there some way to trigger this for every attempt to 
1120          * use the network? */
1121         if (!modest_platform_connect_and_wait(NULL))
1122                 return FALSE;
1123         
1124         /* Init mail operation. Set total and done to 0, and do not
1125            update them, this way the progress objects will know that
1126            we have no clue about the number of the objects */
1127         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1128         priv->total = 0;
1129         priv->done  = 0;
1130         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1131         
1132         /* Get the Modest account */
1133         modest_account = (TnyStoreAccount *)
1134                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1135                                                                      account_name,
1136                                                                      TNY_ACCOUNT_TYPE_STORE);
1137
1138         if (!modest_account) {
1139                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1140                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1141                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1142                              "cannot get tny store account for %s\n", account_name);
1143                 modest_mail_operation_notify_end (self);
1144
1145                 return FALSE;
1146         }
1147
1148         
1149         /* Get the transport account, we can not do it in the thread
1150            due to some problems with dbus */
1151         transport_account = (TnyTransportAccount *)
1152                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1153                                                                                     account_name);
1154         if (!transport_account) {
1155                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1156                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1157                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1158                              "cannot get tny transport account for %s\n", account_name);
1159                 modest_mail_operation_notify_end (self);
1160
1161                 return FALSE;
1162         }
1163
1164         /* Create the helper object */
1165         info = g_slice_new (UpdateAccountInfo);
1166         info->mail_op = self;
1167         info->account = modest_account;
1168         info->transport_account = transport_account;
1169
1170         /* Get the message size limit */
1171         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1172                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1173         if (info->max_size == 0)
1174                 info->max_size = G_MAXINT;
1175         else
1176                 info->max_size = info->max_size * KB;
1177
1178         /* Get per-account retrieval type */
1179         mgr = modest_runtime_get_account_mgr ();
1180         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1181                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1182
1183         /* Get per-account message amount retrieval limit */
1184         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1185                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1186         if (info->retrieve_limit == 0)
1187                 info->retrieve_limit = G_MAXINT;
1188                 
1189         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1190
1191         /* Set account busy */
1192         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1193         priv->account_name = g_strdup(account_name);
1194         
1195         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1196
1197         return TRUE;
1198 }
1199
1200 /* ******************************************************************* */
1201 /* ************************** STORE  ACTIONS ************************* */
1202 /* ******************************************************************* */
1203
1204
1205 TnyFolder *
1206 modest_mail_operation_create_folder (ModestMailOperation *self,
1207                                      TnyFolderStore *parent,
1208                                      const gchar *name)
1209 {
1210         ModestMailOperationPrivate *priv;
1211         TnyFolder *new_folder = NULL;
1212
1213         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1214         g_return_val_if_fail (name, NULL);
1215         
1216         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1217
1218         /* Check parent */
1219         if (TNY_IS_FOLDER (parent)) {
1220                 /* Check folder rules */
1221                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1222                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1223                         /* Set status failed and set an error */
1224                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1225                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1226                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1227                                      _("mail_in_ui_folder_create_error"));
1228                 }
1229         }
1230
1231         if (!priv->error) {
1232                 /* Create the folder */
1233                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1234                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1235                 if (!priv->error)
1236                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1237         }
1238
1239         /* Notify about operation end */
1240         modest_mail_operation_notify_end (self);
1241
1242         return new_folder;
1243 }
1244
1245 void
1246 modest_mail_operation_remove_folder (ModestMailOperation *self,
1247                                      TnyFolder           *folder,
1248                                      gboolean             remove_to_trash)
1249 {
1250         TnyAccount *account;
1251         ModestMailOperationPrivate *priv;
1252         ModestTnyFolderRules rules;
1253
1254         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1255         g_return_if_fail (TNY_IS_FOLDER (folder));
1256         
1257         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1258         
1259         /* Check folder rules */
1260         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1261         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1262                 /* Set status failed and set an error */
1263                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1264                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1265                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1266                              _("mail_in_ui_folder_delete_error"));
1267                 goto end;
1268         }
1269
1270         /* Get the account */
1271         account = modest_tny_folder_get_account (folder);
1272         priv->account = g_object_ref(account);
1273
1274         /* Delete folder or move to trash */
1275         if (remove_to_trash) {
1276                 TnyFolder *trash_folder = NULL;
1277                 trash_folder = modest_tny_account_get_special_folder (account,
1278                                                                       TNY_FOLDER_TYPE_TRASH);
1279                 /* TODO: error_handling */
1280                  modest_mail_operation_xfer_folder (self, folder,
1281                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1282         } else {
1283                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1284
1285                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1286                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1287
1288                 if (parent)
1289                         g_object_unref (G_OBJECT (parent));
1290         }
1291         g_object_unref (G_OBJECT (account));
1292
1293  end:
1294         /* Notify about operation end */
1295         modest_mail_operation_notify_end (self);
1296 }
1297
1298 static void
1299 transfer_folder_status_cb (GObject *obj,
1300                            TnyStatus *status,
1301                            gpointer user_data)
1302 {
1303         ModestMailOperation *self;
1304         ModestMailOperationPrivate *priv;
1305         ModestMailOperationState *state;
1306
1307         g_return_if_fail (status != NULL);
1308         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1309
1310         self = MODEST_MAIL_OPERATION (user_data);
1311         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1312
1313         if ((status->position == 1) && (status->of_total == 100))
1314                 return;
1315
1316         priv->done = status->position;
1317         priv->total = status->of_total;
1318
1319         state = modest_mail_operation_clone_state (self);
1320         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1321         g_slice_free (ModestMailOperationState, state);
1322 }
1323
1324
1325 static void
1326 transfer_folder_cb (TnyFolder *folder, 
1327                     TnyFolderStore *into, 
1328                     gboolean cancelled, 
1329                     TnyFolder *new_folder, GError **err, 
1330                     gpointer user_data)
1331 {
1332         ModestMailOperation *self = NULL;
1333         ModestMailOperationPrivate *priv = NULL;
1334
1335         self = MODEST_MAIL_OPERATION (user_data);
1336
1337         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1338
1339         if (*err) {
1340                 priv->error = g_error_copy (*err);
1341                 priv->done = 0;
1342                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1343         } else if (cancelled) {
1344                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1345                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1346                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1347                              _("Transference of %s was cancelled."),
1348                              tny_folder_get_name (folder));
1349         } else {
1350                 priv->done = 1;
1351                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1352         }
1353                 
1354         /* Free */
1355         g_object_unref (folder);
1356         g_object_unref (into);
1357         if (new_folder != NULL)
1358                 g_object_unref (new_folder);
1359
1360         /* Notify about operation end */
1361         modest_mail_operation_notify_end (self);
1362 }
1363
1364 void
1365 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1366                                    TnyFolder *folder,
1367                                    TnyFolderStore *parent,
1368                                    gboolean delete_original)
1369 {
1370         ModestMailOperationPrivate *priv = NULL;
1371         ModestTnyFolderRules parent_rules, rules;
1372
1373         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1374         g_return_if_fail (TNY_IS_FOLDER (folder));
1375         g_return_if_fail (TNY_IS_FOLDER (parent));
1376
1377         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1378
1379         /* Get account and set it into mail_operation */
1380         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1381         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1382
1383         /* Get folder rules */
1384         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1385         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1386
1387         if (!TNY_IS_FOLDER_STORE (parent)) {
1388                 
1389         }
1390         
1391         /* The moveable restriction is applied also to copy operation */
1392         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1393                 /* Set status failed and set an error */
1394                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1395                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1396                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1397                              _("mail_in_ui_folder_move_target_error"));
1398
1399                 /* Notify the queue */
1400                 modest_mail_operation_notify_end (self);
1401         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1402                 /* Set status failed and set an error */
1403                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1404                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1405                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1406                              _("FIXME: parent folder does not accept new folders"));
1407
1408                 /* Notify the queue */
1409                 modest_mail_operation_notify_end (self);
1410         } else {
1411                 /* Pick references for async calls */
1412                 g_object_ref (folder);
1413                 g_object_ref (parent);
1414
1415                 /* Move/Copy folder */          
1416                 tny_folder_copy_async (folder,
1417                                        parent,
1418                                        tny_folder_get_name (folder),
1419                                        delete_original,
1420                                        transfer_folder_cb,
1421                                        transfer_folder_status_cb,
1422                                        self);
1423         }
1424 }
1425
1426 void
1427 modest_mail_operation_rename_folder (ModestMailOperation *self,
1428                                      TnyFolder *folder,
1429                                      const gchar *name)
1430 {
1431         ModestMailOperationPrivate *priv;
1432         ModestTnyFolderRules rules;
1433
1434         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1435         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1436         g_return_if_fail (name);
1437         
1438         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1439
1440         /* Get account and set it into mail_operation */
1441         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1442
1443         /* Check folder rules */
1444         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1445         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1446                 /* Set status failed and set an error */
1447                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1448                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1449                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1450                              _("FIXME: unable to rename"));
1451
1452                 /* Notify about operation end */
1453                 modest_mail_operation_notify_end (self);
1454         } else {
1455                 /* Rename. Camel handles folder subscription/unsubscription */
1456                 TnyFolderStore *into;
1457
1458                 into = tny_folder_get_folder_store (folder);
1459                 tny_folder_copy_async (folder, into, name, TRUE,
1460                                  transfer_folder_cb,
1461                                  transfer_folder_status_cb,
1462                                  self);
1463                 if (into)
1464                         g_object_unref (into);
1465                 
1466         }
1467  }
1468
1469 /* ******************************************************************* */
1470 /* **************************  MSG  ACTIONS  ************************* */
1471 /* ******************************************************************* */
1472
1473 void modest_mail_operation_get_msg (ModestMailOperation *self,
1474                                     TnyHeader *header,
1475                                     GetMsgAsyncUserCallback user_callback,
1476                                     gpointer user_data)
1477 {
1478         GetMsgAsyncHelper *helper = NULL;
1479         TnyFolder *folder;
1480         ModestMailOperationPrivate *priv;
1481         
1482         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1483         g_return_if_fail (TNY_IS_HEADER (header));
1484         
1485         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1486         folder = tny_header_get_folder (header);
1487
1488         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1489
1490         /* Get message from folder */
1491         if (folder) {
1492                 /* Get account and set it into mail_operation */
1493                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1494
1495                 helper = g_slice_new0 (GetMsgAsyncHelper);
1496                 helper->mail_op = self;
1497                 helper->user_callback = user_callback;
1498                 helper->user_data = user_data;
1499                 helper->header = g_object_ref (header);
1500
1501                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1502
1503                 g_object_unref (G_OBJECT (folder));
1504         } else {
1505                 /* Set status failed and set an error */
1506                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1507                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1508                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1509                              _("Error trying to get a message. No folder found for header"));
1510
1511                 /* Notify the queue */
1512                 modest_mail_operation_notify_end (self);
1513         }
1514 }
1515
1516 static void
1517 get_msg_cb (TnyFolder *folder, 
1518             gboolean cancelled, 
1519             TnyMsg *msg, 
1520             GError **error, 
1521             gpointer user_data)
1522 {
1523         GetMsgAsyncHelper *helper = NULL;
1524         ModestMailOperation *self = NULL;
1525         ModestMailOperationPrivate *priv = NULL;
1526
1527         helper = (GetMsgAsyncHelper *) user_data;
1528         g_return_if_fail (helper != NULL);       
1529         self = helper->mail_op;
1530         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1531         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1532
1533         /* Check errors and cancel */
1534         if (*error) {
1535                 priv->error = g_error_copy (*error);
1536                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1537                 goto out;
1538         }
1539         if (cancelled) {
1540                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1541                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1542                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1543                              _("Error trying to refresh the contents of %s"),
1544                              tny_folder_get_name (folder));
1545                 goto out;
1546         }
1547
1548         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1549
1550         /* If user defined callback function was defined, call it */
1551         if (helper->user_callback) {
1552                 helper->user_callback (self, helper->header, msg, helper->user_data);
1553         }
1554
1555  out:
1556         /* Free */
1557         g_object_unref (helper->header);
1558         g_slice_free (GetMsgAsyncHelper, helper);
1559                 
1560         /* Notify about operation end */
1561         modest_mail_operation_notify_end (self);        
1562 }
1563
1564 static void     
1565 get_msg_status_cb (GObject *obj,
1566                    TnyStatus *status,  
1567                    gpointer user_data)
1568 {
1569         GetMsgAsyncHelper *helper = NULL;
1570         ModestMailOperation *self;
1571         ModestMailOperationPrivate *priv;
1572         ModestMailOperationState *state;
1573
1574         g_return_if_fail (status != NULL);
1575         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1576
1577         helper = (GetMsgAsyncHelper *) user_data;
1578         g_return_if_fail (helper != NULL);       
1579
1580         self = helper->mail_op;
1581         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1582
1583         if ((status->position == 1) && (status->of_total == 100))
1584                 return;
1585
1586         priv->done = 1;
1587         priv->total = 1;
1588
1589         state = modest_mail_operation_clone_state (self);
1590         state->bytes_done = status->position;
1591         state->bytes_total = status->of_total;
1592         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1593         g_slice_free (ModestMailOperationState, state);
1594 }
1595
1596 /****************************************************/
1597 typedef struct {
1598         ModestMailOperation *mail_op;
1599         TnyList *headers;
1600         GetMsgAsyncUserCallback user_callback;
1601         gpointer user_data;
1602         GDestroyNotify notify;
1603 } GetFullMsgsInfo;
1604
1605 typedef struct {
1606         GetMsgAsyncUserCallback user_callback;
1607         TnyHeader *header;
1608         TnyMsg *msg;
1609         gpointer user_data;
1610         ModestMailOperation *mail_op;
1611 } NotifyGetMsgsInfo;
1612
1613
1614 /* 
1615  * Used by get_msgs_full_thread to call the user_callback for each
1616  * message that has been read
1617  */
1618 static gboolean
1619 notify_get_msgs_full (gpointer data)
1620 {
1621         NotifyGetMsgsInfo *info;
1622
1623         info = (NotifyGetMsgsInfo *) data;      
1624
1625         /* Call the user callback */
1626         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1627
1628         g_slice_free (NotifyGetMsgsInfo, info);
1629
1630         return FALSE;
1631 }
1632
1633 /* 
1634  * Used by get_msgs_full_thread to free al the thread resources and to
1635  * call the destroy function for the passed user_data
1636  */
1637 static gboolean
1638 get_msgs_full_destroyer (gpointer data)
1639 {
1640         GetFullMsgsInfo *info;
1641
1642         info = (GetFullMsgsInfo *) data;
1643
1644         if (info->notify)
1645                 info->notify (info->user_data);
1646
1647         /* free */
1648         g_object_unref (info->headers);
1649         g_slice_free (GetFullMsgsInfo, info);
1650
1651         return FALSE;
1652 }
1653
1654 static gpointer
1655 get_msgs_full_thread (gpointer thr_user_data)
1656 {
1657         GetFullMsgsInfo *info;
1658         ModestMailOperationPrivate *priv = NULL;
1659         TnyIterator *iter = NULL;
1660         
1661         info = (GetFullMsgsInfo *) thr_user_data;       
1662         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1663
1664         iter = tny_list_create_iterator (info->headers);
1665         while (!tny_iterator_is_done (iter)) { 
1666                 TnyHeader *header;
1667                 TnyFolder *folder;
1668                 
1669                 header = TNY_HEADER (tny_iterator_get_current (iter));
1670                 folder = tny_header_get_folder (header);
1671                                 
1672                 /* Get message from folder */
1673                 if (folder) {
1674                         TnyMsg *msg;
1675                         /* The callback will call it per each header */
1676                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1677
1678                         if (msg) {
1679                                 ModestMailOperationState *state;
1680                                 ModestPair *pair;
1681
1682                                 priv->done++;
1683
1684                                 /* notify progress */
1685                                 state = modest_mail_operation_clone_state (info->mail_op);
1686                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1687                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1688                                                  pair, (GDestroyNotify) modest_pair_free);
1689
1690                                 /* The callback is the responsible for
1691                                    freeing the message */
1692                                 if (info->user_callback) {
1693                                         NotifyGetMsgsInfo *info_notify;
1694                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1695                                         info_notify->user_callback = info->user_callback;
1696                                         info_notify->mail_op = info->mail_op;
1697                                         info_notify->header = g_object_ref (header);
1698                                         info_notify->msg = g_object_ref (msg);
1699                                         info_notify->user_data = info->user_data;
1700                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1701                                                          notify_get_msgs_full, 
1702                                                          info_notify, NULL);
1703                                 }
1704                                 g_object_unref (msg);
1705                         }
1706                 } else {
1707                         /* Set status failed and set an error */
1708                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1709                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1710                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1711                                      "Error trying to get a message. No folder found for header");
1712                 }
1713                 g_object_unref (header);                
1714                 tny_iterator_next (iter);
1715         }
1716
1717         /* Set operation status */
1718         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1719                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1720
1721         /* Notify about operation end */
1722         g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1723
1724         /* Free thread resources. Will be called after all previous idles */
1725         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1726
1727         return NULL;
1728 }
1729
1730 void 
1731 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1732                                      TnyList *header_list, 
1733                                      GetMsgAsyncUserCallback user_callback,
1734                                      gpointer user_data,
1735                                      GDestroyNotify notify)
1736 {
1737         TnyHeader *header = NULL;
1738         TnyFolder *folder = NULL;
1739         GThread *thread;
1740         ModestMailOperationPrivate *priv = NULL;
1741         GetFullMsgsInfo *info = NULL;
1742         gboolean size_ok = TRUE;
1743         gint max_size;
1744         TnyIterator *iter = NULL;
1745         
1746         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1747         
1748         /* Init mail operation */
1749         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1750         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1751         priv->done = 0;
1752         priv->total = tny_list_get_length(header_list);
1753
1754         /* Get account and set it into mail_operation */
1755         if (tny_list_get_length (header_list) >= 1) {
1756                 iter = tny_list_create_iterator (header_list);
1757                 header = TNY_HEADER (tny_iterator_get_current (iter));
1758                 folder = tny_header_get_folder (header);                
1759                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1760                 g_object_unref (header);
1761                 g_object_unref (folder);
1762
1763                 if (tny_list_get_length (header_list) == 1) {
1764                         g_object_unref (iter);
1765                         iter = NULL;
1766                 }
1767         }
1768
1769         /* Get msg size limit */
1770         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1771                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1772                                          &(priv->error));
1773         if (priv->error) {
1774                 g_clear_error (&(priv->error));
1775                 max_size = G_MAXINT;
1776         } else {
1777                 max_size = max_size * KB;
1778         }
1779
1780         /* Check message size limits. If there is only one message
1781            always retrieve it */
1782         if (iter != NULL) {
1783                 while (!tny_iterator_is_done (iter) && size_ok) {
1784                         header = TNY_HEADER (tny_iterator_get_current (iter));
1785                         if (tny_header_get_message_size (header) >= max_size)
1786                                 size_ok = FALSE;
1787                         g_object_unref (header);
1788                         tny_iterator_next (iter);
1789                 }
1790                 g_object_unref (iter);
1791         }
1792
1793         if (size_ok) {
1794                 /* Create the info */
1795                 info = g_slice_new0 (GetFullMsgsInfo);
1796                 info->mail_op = self;
1797                 info->user_callback = user_callback;
1798                 info->user_data = user_data;
1799                 info->headers = g_object_ref (header_list);
1800                 info->notify = notify;
1801
1802                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1803         } else {
1804                 /* Set status failed and set an error */
1805                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1806                 /* FIXME: the error msg is different for pop */
1807                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1808                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1809                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1810                 /* Remove from queue and free resources */
1811                 modest_mail_operation_notify_end (self);
1812                 if (notify)
1813                         notify (user_data);
1814         }
1815 }
1816
1817
1818 void 
1819 modest_mail_operation_remove_msg (ModestMailOperation *self,
1820                                   TnyHeader *header,
1821                                   gboolean remove_to_trash)
1822 {
1823         TnyFolder *folder;
1824         ModestMailOperationPrivate *priv;
1825
1826         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1827         g_return_if_fail (TNY_IS_HEADER (header));
1828
1829         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1830         folder = tny_header_get_folder (header);
1831
1832         /* Get account and set it into mail_operation */
1833         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1834
1835         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1836
1837         /* Delete or move to trash */
1838         if (remove_to_trash) {
1839                 TnyFolder *trash_folder;
1840                 TnyStoreAccount *store_account;
1841
1842                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1843                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1844                                                                       TNY_FOLDER_TYPE_TRASH);
1845                 if (trash_folder) {
1846                         TnyList *headers;
1847
1848                         /* Create list */
1849                         headers = tny_simple_list_new ();
1850                         tny_list_append (headers, G_OBJECT (header));
1851                         g_object_unref (header);
1852
1853                         /* Move to trash */
1854                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1855                         g_object_unref (headers);
1856 /*                      g_object_unref (trash_folder); */
1857                 } else {
1858                         ModestMailOperationPrivate *priv;
1859
1860                         /* Set status failed and set an error */
1861                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1862                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1863                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1864                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1865                                      _("Error trying to delete a message. Trash folder not found"));
1866                 }
1867
1868                 g_object_unref (G_OBJECT (store_account));
1869         } else {
1870                 tny_folder_remove_msg (folder, header, &(priv->error));
1871                 if (!priv->error)
1872                         tny_folder_sync(folder, TRUE, &(priv->error));
1873         }
1874
1875         /* Set status */
1876         if (!priv->error)
1877                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1878         else
1879                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1880
1881         /* Free */
1882         g_object_unref (G_OBJECT (folder));
1883
1884         /* Notify about operation end */
1885         modest_mail_operation_notify_end (self);
1886 }
1887
1888 static void
1889 transfer_msgs_status_cb (GObject *obj,
1890                          TnyStatus *status,  
1891                          gpointer user_data)
1892 {
1893         XFerMsgAsyncHelper *helper = NULL;
1894         ModestMailOperation *self;
1895         ModestMailOperationPrivate *priv;
1896         ModestMailOperationState *state;
1897
1898
1899         g_return_if_fail (status != NULL);
1900         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1901
1902         helper = (XFerMsgAsyncHelper *) user_data;
1903         g_return_if_fail (helper != NULL);       
1904
1905         self = helper->mail_op;
1906         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1907
1908         if ((status->position == 1) && (status->of_total == 100))
1909                 return;
1910
1911         priv->done = status->position;
1912         priv->total = status->of_total;
1913
1914         state = modest_mail_operation_clone_state (self);
1915         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1916         g_slice_free (ModestMailOperationState, state);
1917 }
1918
1919
1920 static void
1921 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1922 {
1923         XFerMsgAsyncHelper *helper;
1924         ModestMailOperation *self;
1925         ModestMailOperationPrivate *priv;
1926
1927         helper = (XFerMsgAsyncHelper *) user_data;
1928         self = helper->mail_op;
1929
1930         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1931
1932         if (*err) {
1933                 priv->error = g_error_copy (*err);
1934                 priv->done = 0;
1935                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1936         } else if (cancelled) {
1937                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1938                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1939                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1940                              _("Error trying to refresh the contents of %s"),
1941                              tny_folder_get_name (folder));
1942         } else {
1943                 priv->done = 1;
1944                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1945         }
1946
1947         /* If user defined callback function was defined, call it */
1948         if (helper->user_callback) {
1949                 helper->user_callback (priv->source, helper->user_data);
1950         }
1951
1952         /* Free */
1953         g_object_unref (helper->headers);
1954         g_object_unref (helper->dest_folder);
1955         g_object_unref (helper->mail_op);
1956         g_slice_free   (XFerMsgAsyncHelper, helper);
1957         g_object_unref (folder);
1958
1959         /* Notify about operation end */
1960         modest_mail_operation_notify_end (self);
1961 }
1962
1963 void
1964 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1965                                  TnyList *headers, 
1966                                  TnyFolder *folder, 
1967                                  gboolean delete_original,
1968                                  XferMsgsAsynUserCallback user_callback,
1969                                  gpointer user_data)
1970 {
1971         ModestMailOperationPrivate *priv;
1972         TnyIterator *iter;
1973         TnyFolder *src_folder;
1974         XFerMsgAsyncHelper *helper;
1975         TnyHeader *header;
1976         ModestTnyFolderRules rules;
1977
1978         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1979         g_return_if_fail (TNY_IS_LIST (headers));
1980         g_return_if_fail (TNY_IS_FOLDER (folder));
1981
1982         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1983         priv->total = 1;
1984         priv->done = 0;
1985         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1986
1987         /* Apply folder rules */
1988         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1989
1990         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1991                 /* Set status failed and set an error */
1992                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1993                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1994                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1995                              _("FIXME: folder does not accept msgs"));
1996                 /* Notify the queue */
1997                 modest_mail_operation_notify_end (self);
1998                 return;
1999         }
2000
2001         /* Create the helper */
2002         helper = g_slice_new0 (XFerMsgAsyncHelper);
2003         helper->mail_op = g_object_ref(self);
2004         helper->dest_folder = g_object_ref(folder);
2005         helper->headers = g_object_ref(headers);
2006         helper->user_callback = user_callback;
2007         helper->user_data = user_data;
2008
2009         /* Get source folder */
2010         iter = tny_list_create_iterator (headers);
2011         header = TNY_HEADER (tny_iterator_get_current (iter));
2012         src_folder = tny_header_get_folder (header);
2013         g_object_unref (header);
2014         g_object_unref (iter);
2015
2016         /* Get account and set it into mail_operation */
2017         priv->account = modest_tny_folder_get_account (src_folder);
2018
2019         /* Transfer messages */
2020         tny_folder_transfer_msgs_async (src_folder, 
2021                                         headers, 
2022                                         folder, 
2023                                         delete_original, 
2024                                         transfer_msgs_cb, 
2025                                         transfer_msgs_status_cb,
2026                                         helper);
2027 }
2028
2029
2030 static void
2031 on_refresh_folder (TnyFolder   *folder, 
2032                    gboolean     cancelled, 
2033                    GError     **error,
2034                    gpointer     user_data)
2035 {
2036         RefreshAsyncHelper *helper = NULL;
2037         ModestMailOperation *self = NULL;
2038         ModestMailOperationPrivate *priv = NULL;
2039
2040         helper = (RefreshAsyncHelper *) user_data;
2041         self = helper->mail_op;
2042         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2043
2044         if (*error) {
2045                 priv->error = g_error_copy (*error);
2046                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2047                 goto out;
2048         }
2049
2050         if (cancelled) {
2051                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2052                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2053                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2054                              _("Error trying to refresh the contents of %s"),
2055                              tny_folder_get_name (folder));
2056                 goto out;
2057         }
2058
2059         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2060
2061  out:
2062         /* Call user defined callback, if it exists */
2063         if (helper->user_callback)
2064                 helper->user_callback (priv->source, folder, helper->user_data);
2065
2066         /* Free */
2067         g_object_unref (helper->mail_op);
2068         g_slice_free   (RefreshAsyncHelper, helper);
2069         g_object_unref (folder);
2070
2071         /* Notify about operation end */
2072         modest_mail_operation_notify_end (self);
2073 }
2074
2075 static void
2076 on_refresh_folder_status_update (GObject *obj,
2077                                  TnyStatus *status,
2078                                  gpointer user_data)
2079 {
2080         RefreshAsyncHelper *helper = NULL;
2081         ModestMailOperation *self = NULL;
2082         ModestMailOperationPrivate *priv = NULL;
2083         ModestMailOperationState *state;
2084
2085         g_return_if_fail (user_data != NULL);
2086         g_return_if_fail (status != NULL);
2087         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2088
2089         helper = (RefreshAsyncHelper *) user_data;
2090         self = helper->mail_op;
2091         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2092
2093         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2094
2095         priv->done = status->position;
2096         priv->total = status->of_total;
2097
2098         state = modest_mail_operation_clone_state (self);
2099         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2100         g_slice_free (ModestMailOperationState, state);
2101 }
2102
2103 void 
2104 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2105                                        TnyFolder *folder,
2106                                        RefreshAsyncUserCallback user_callback,
2107                                        gpointer user_data)
2108 {
2109         ModestMailOperationPrivate *priv = NULL;
2110         RefreshAsyncHelper *helper = NULL;
2111
2112         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2113
2114         /* Pick a reference */
2115         g_object_ref (folder);
2116
2117         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2118
2119         /* Get account and set it into mail_operation */
2120         priv->account = modest_tny_folder_get_account  (folder);
2121
2122         /* Create the helper */
2123         helper = g_slice_new0 (RefreshAsyncHelper);
2124         helper->mail_op = g_object_ref(self);
2125         helper->user_callback = user_callback;
2126         helper->user_data = user_data;
2127
2128         /* Refresh the folder. TODO: tinymail could issue a status
2129            updates before the callback call then this could happen. We
2130            must review the design */
2131         tny_folder_refresh_async (folder,
2132                                   on_refresh_folder,
2133                                   on_refresh_folder_status_update,
2134                                   helper);
2135 }
2136
2137 /**
2138  *
2139  * It's used by the mail operation queue to notify the observers
2140  * attached to that signal that the operation finished. We need to use
2141  * that because tinymail does not give us the progress of a given
2142  * operation when it finishes (it directly calls the operation
2143  * callback).
2144  */
2145 static void
2146 modest_mail_operation_notify_end (ModestMailOperation *self)
2147 {
2148         ModestMailOperationState *state;
2149         ModestMailOperationPrivate *priv = NULL;
2150
2151         g_return_if_fail (self);
2152
2153         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2154
2155         if (!priv) {
2156                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2157                 return;
2158         }
2159         
2160         /* Set the account back to not busy */
2161         if (priv->account_name) {
2162                 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2163                                                                                                                                                                 FALSE);
2164                 g_free(priv->account_name);
2165                 priv->account_name = NULL;
2166         }
2167         
2168         /* Notify the observers about the mail opertation end */
2169         state = modest_mail_operation_clone_state (self);
2170         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2171         g_slice_free (ModestMailOperationState, state);
2172 }