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