2007-06-29 Murray Cumming <murrayc@murrayc.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, &(priv->error));
648                 g_object_unref (header);
649         }
650         
651         if (!priv->error)
652                 tny_folder_add_msg (folder, msg, &(priv->error));
653
654         if (!priv->error)
655                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
656         else
657                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
658
659 end:
660         if (msg)
661                 g_object_unref (G_OBJECT(msg));
662         if (folder)
663                 g_object_unref (G_OBJECT(folder));
664
665         modest_mail_operation_notify_end (self, FALSE);
666 }
667
668 typedef struct 
669 {
670         ModestMailOperation *mail_op;
671         TnyStoreAccount *account;
672         TnyTransportAccount *transport_account;
673         gint max_size;
674         gint retrieve_limit;
675         gchar *retrieve_type;
676         gchar *account_name;
677         UpdateAccountCallback callback;
678         gpointer user_data;
679 } UpdateAccountInfo;
680
681 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
682 /* We use this folder observer to track the headers that have been
683  * added to a folder */
684 typedef struct {
685         GObject parent;
686         TnyList *new_headers;
687 } InternalFolderObserver;
688
689 typedef struct {
690         GObjectClass parent;
691 } InternalFolderObserverClass;
692
693 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
694
695 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
696                          internal_folder_observer,
697                          G_TYPE_OBJECT,
698                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
699
700
701 static void
702 foreach_add_item (gpointer header, gpointer user_data)
703 {
704         /* printf("DEBUG: %s: header subject=%s\n", 
705          * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
706          */
707         tny_list_prepend (TNY_LIST (user_data), 
708                           g_object_ref (G_OBJECT (header)));
709 }
710
711 /* This is the method that looks for new messages in a folder */
712 static void
713 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
714 {
715         InternalFolderObserver *derived = (InternalFolderObserver *)self;
716         
717         TnyFolderChangeChanged changed;
718
719         changed = tny_folder_change_get_changed (change);
720
721         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
722                 TnyList *list;
723
724                 /* Get added headers */
725                 list = tny_simple_list_new ();
726                 tny_folder_change_get_added_headers (change, list);
727
728                 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n", 
729                  *      __FUNCTION__, tny_list_get_length(list));
730                  */
731                  
732                 /* Add them to the folder observer */
733                 tny_list_foreach (list, foreach_add_item, 
734                                   derived->new_headers);
735
736                 g_object_unref (G_OBJECT (list));
737         }
738 }
739
740 static void
741 internal_folder_observer_init (InternalFolderObserver *self) 
742 {
743         self->new_headers = tny_simple_list_new ();
744 }
745 static void
746 internal_folder_observer_finalize (GObject *object) 
747 {
748         InternalFolderObserver *self;
749
750         self = (InternalFolderObserver *) object;
751         g_object_unref (self->new_headers);
752
753         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
754 }
755 static void
756 tny_folder_observer_init (TnyFolderObserverIface *iface) 
757 {
758         iface->update_func = internal_folder_observer_update;
759 }
760 static void
761 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
762 {
763         GObjectClass *object_class;
764
765         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
766         object_class = (GObjectClass*) klass;
767         object_class->finalize = internal_folder_observer_finalize;
768 }
769
770 /*****************/
771
772 static void
773 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
774 {
775         TnyIterator *iter;
776         TnyList *folders = tny_simple_list_new ();
777
778         tny_folder_store_get_folders (store, folders, query, NULL);
779         iter = tny_list_create_iterator (folders);
780
781         while (!tny_iterator_is_done (iter)) {
782
783                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
784
785                 tny_list_prepend (all_folders, G_OBJECT (folder));
786                 recurse_folders (folder, query, all_folders);    
787                 g_object_unref (G_OBJECT (folder));
788
789                 tny_iterator_next (iter);
790         }
791          g_object_unref (G_OBJECT (iter));
792          g_object_unref (G_OBJECT (folders));
793 }
794
795 /* 
796  * Issues the "progress-changed" signal. The timer won't be removed,
797  * so you must call g_source_remove to stop the signal emission
798  */
799 static gboolean
800 idle_notify_progress (gpointer data)
801 {
802         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
803         ModestMailOperationState *state;
804
805         state = modest_mail_operation_clone_state (mail_op);
806         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
807         g_slice_free (ModestMailOperationState, state);
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         ModestPair *pair;
821
822         pair = (ModestPair *) data;
823
824         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
825
826         /* Free the state and the reference to the mail operation */
827         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
828         g_object_unref (pair->first);
829
830         return FALSE;
831 }
832
833 /* 
834  * Used by update_account_thread to notify the queue from the main
835  * loop. We call it inside an idle call to achieve that
836  */
837 static gboolean
838 idle_notify_update_account_queue (gpointer data)
839 {
840         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
841         ModestMailOperationPrivate *priv = NULL;
842
843         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
844
845         /* Do not need to block, the notify end will do it for us */    
846         modest_mail_operation_notify_end (mail_op, TRUE);
847         g_object_unref (mail_op);
848
849         return FALSE;
850 }
851
852 static int
853 compare_headers_by_date (gconstpointer a, 
854                          gconstpointer b)
855 {
856         TnyHeader **header1, **header2;
857         time_t sent1, sent2;
858
859         header1 = (TnyHeader **) a;
860         header2 = (TnyHeader **) b;
861
862         sent1 = tny_header_get_date_sent (*header1);
863         sent2 = tny_header_get_date_sent (*header2);
864
865         /* We want the most recent ones (greater time_t) at the
866            beginning */
867         if (sent1 < sent2)
868                 return 1;
869         else
870                 return -1;
871 }
872
873 static gboolean 
874 set_last_updated_idle (gpointer data)
875 {
876         gdk_threads_enter ();
877
878         /* It does not matter if the time is not exactly the same than
879            the time when this idle was called, it's just an
880            approximation and it won't be very different */
881         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
882                                     (gchar *) data, 
883                                     MODEST_ACCOUNT_LAST_UPDATED, 
884                                     time(NULL), 
885                                     TRUE);
886
887         gdk_threads_leave ();
888
889         return FALSE;
890 }
891
892 static gpointer
893 update_account_thread (gpointer thr_user_data)
894 {
895         static gboolean first_time = TRUE;
896         UpdateAccountInfo *info;
897         TnyList *all_folders = NULL;
898         GPtrArray *new_headers = NULL;
899         TnyIterator *iter = NULL;
900         TnyFolderStoreQuery *query = NULL;
901         ModestMailOperationPrivate *priv = NULL;
902         ModestTnySendQueue *send_queue = NULL;
903
904         info = (UpdateAccountInfo *) thr_user_data;
905         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
906
907         /* Get account and set it into mail_operation */
908         priv->account = g_object_ref (info->account);
909
910         /*
911          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
912          * show any updates unless we do that
913          */
914         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
915                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
916
917         /* Get all the folders. We can do it synchronously because
918            we're already running in a different thread than the UI */
919         all_folders = tny_simple_list_new ();
920         query = tny_folder_store_query_new ();
921         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
922         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
923                                       all_folders,
924                                       query,
925                                       &(priv->error));
926         if (priv->error) {
927                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
928                 goto out;
929         }
930
931         iter = tny_list_create_iterator (all_folders);
932         while (!tny_iterator_is_done (iter)) {
933                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
934
935                 recurse_folders (folder, query, all_folders);
936                 tny_iterator_next (iter);
937         }
938         g_object_unref (G_OBJECT (iter));
939
940         /* Update status and notify. We need to call the notification
941            with a source function in order to call it from the main
942            loop. We need that in order not to get into trouble with
943            Gtk+. We use a timeout in order to provide more status
944            information, because the sync tinymail call does not
945            provide it for the moment */
946         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
947
948         /* Refresh folders */
949         new_headers = g_ptr_array_new ();
950         iter = tny_list_create_iterator (all_folders);
951
952         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
953
954                 InternalFolderObserver *observer;
955                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
956
957                 /* Refresh the folder */
958                 /* Our observer receives notification of new emails during folder refreshes,
959                  * so we can use observer->new_headers.
960                  */
961                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
962                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
963                 
964                 /* This gets the status information (headers) from the server.
965                  * We use the blocking version, because we are already in a separate 
966                  * thread.
967                  */
968
969                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
970                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
971                         TnyIterator *iter;
972
973                         /* If the retrieve type is full messages, refresh and get the messages */
974                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
975
976                         iter = tny_list_create_iterator (observer->new_headers);
977                         while (!tny_iterator_is_done (iter)) {
978                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
979                                  
980                                 /* Apply per-message size limits */
981                                 if (tny_header_get_message_size (header) < info->max_size)
982                                         g_ptr_array_add (new_headers, g_object_ref (header));
983
984                                 g_object_unref (header);
985                                 tny_iterator_next (iter);
986                         }
987                         g_object_unref (iter);
988                 } else {
989                         /* We do not need to do it the first time
990                            because it's automatically done by the tree
991                            model */
992                         if (G_UNLIKELY (!first_time))
993                                 tny_folder_poke_status (TNY_FOLDER (folder));
994                 }
995                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
996                 g_object_unref (observer);
997                 observer = NULL;                        
998
999                 g_object_unref (G_OBJECT (folder));
1000                 if (priv->error)
1001                 {
1002                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1003                         goto out;
1004                 }
1005                 
1006                 tny_iterator_next (iter);
1007         }
1008
1009         did_a_cancel = FALSE;
1010
1011         g_object_unref (G_OBJECT (iter));
1012         g_source_remove (timeout);
1013
1014         if (new_headers->len > 0) {
1015                 gint msg_num = 0;
1016
1017                 /* Order by date */
1018                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1019
1020                 /* Apply message count limit */
1021                 /* If the number of messages exceeds the maximum, ask the
1022                  * user to download them all,
1023                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1024                  */
1025                 if (new_headers->len > info->retrieve_limit) {
1026                         /* TODO: Ask the user, instead of just
1027                          * failing, showing
1028                          * mail_nc_msg_count_limit_exceeded, with 'Get
1029                          * all' and 'Newest only' buttons. */
1030                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1031                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1032                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1033                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1034                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1035                         goto out;
1036                 }
1037                 
1038                 priv->done = 0;
1039                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1040                 while (msg_num < priv->total) {
1041
1042                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1043                         TnyFolder *folder = tny_header_get_folder (header);
1044                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1045                         ModestMailOperationState *state;
1046                         ModestPair* pair;
1047
1048                         priv->done++;
1049                         /* We can not just use the mail operation because the
1050                            values of done and total could change before the
1051                            idle is called */
1052                         state = modest_mail_operation_clone_state (info->mail_op);
1053                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1054                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1055                                          pair, (GDestroyNotify) modest_pair_free);
1056
1057                         g_object_unref (msg);
1058                         g_object_unref (folder);
1059
1060                         msg_num++;
1061                 }
1062                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1063                 g_ptr_array_free (new_headers, FALSE);
1064         }
1065         
1066         /* Perform send */
1067         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1068         priv->done = 0;
1069         priv->total = 0;
1070         if (priv->account != NULL) 
1071                 g_object_unref (priv->account);
1072         priv->account = g_object_ref (info->transport_account);
1073         
1074         send_queue = modest_runtime_get_send_queue (info->transport_account);
1075         if (send_queue) {
1076                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1077                 modest_tny_send_queue_try_to_send (send_queue);
1078                 g_source_remove (timeout);
1079         } else {
1080                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1081                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1082                              "cannot create a send queue for %s\n", 
1083                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1084                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1085         }
1086         
1087         /* Check if the operation was a success */
1088         if (!priv->error) {
1089                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1090
1091                 /* Update the last updated key */
1092                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1093                                  set_last_updated_idle, 
1094                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1095                                  (GDestroyNotify) g_free);
1096         }
1097
1098  out:
1099         /* Notify about operation end. Note that the info could be
1100            freed before this idle happens, but the mail operation will
1101            be still alive */
1102         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1103
1104         if (info->callback) {
1105                 /* This thread is not in the main lock */
1106                 gdk_threads_enter ();
1107                 info->callback (info->mail_op, 
1108                                 (new_headers) ? new_headers->len : 0, 
1109                                 info->user_data);
1110                 gdk_threads_leave ();
1111         }
1112         
1113         /* Frees */
1114         g_object_unref (query);
1115         g_object_unref (all_folders);
1116         g_object_unref (info->account);
1117         g_object_unref (info->transport_account);
1118         g_free (info->retrieve_type);
1119         g_slice_free (UpdateAccountInfo, info);
1120
1121         first_time = FALSE;
1122
1123         return NULL;
1124 }
1125
1126 gboolean
1127 modest_mail_operation_update_account (ModestMailOperation *self,
1128                                       const gchar *account_name,
1129                                       UpdateAccountCallback callback,
1130                                       gpointer user_data)
1131 {
1132         GThread *thread;
1133         UpdateAccountInfo *info;
1134         ModestMailOperationPrivate *priv;
1135         ModestAccountMgr *mgr;
1136         TnyStoreAccount *modest_account;
1137         TnyTransportAccount *transport_account;
1138
1139         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1140         g_return_val_if_fail (account_name, FALSE);
1141
1142         /* Init mail operation. Set total and done to 0, and do not
1143            update them, this way the progress objects will know that
1144            we have no clue about the number of the objects */
1145         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1146         priv->total = 0;
1147         priv->done  = 0;
1148         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1149
1150         /* Get the Modest account */
1151         modest_account = (TnyStoreAccount *)
1152                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1153                                                                      account_name,
1154                                                                      TNY_ACCOUNT_TYPE_STORE);
1155
1156         if (!modest_account) {
1157                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1158                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1159                              "cannot get tny store account for %s\n", account_name);
1160                 goto error;
1161         }
1162
1163         
1164         /* Get the transport account, we can not do it in the thread
1165            due to some problems with dbus */
1166         transport_account = (TnyTransportAccount *)
1167                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1168                                                                                     account_name);
1169         if (!transport_account) {
1170                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1171                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1172                              "cannot get tny transport account for %s\n", account_name);
1173                 goto error;
1174         }
1175
1176         /* Create the helper object */
1177         info = g_slice_new (UpdateAccountInfo);
1178         info->mail_op = self;
1179         info->account = modest_account;
1180         info->transport_account = transport_account;
1181         info->callback = callback;
1182         info->user_data = user_data;
1183
1184         /* Get the message size limit */
1185         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1186                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1187         if (info->max_size == 0)
1188                 info->max_size = G_MAXINT;
1189         else
1190                 info->max_size = info->max_size * KB;
1191
1192         /* Get per-account retrieval type */
1193         mgr = modest_runtime_get_account_mgr ();
1194         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1195                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1196
1197         /* Get per-account message amount retrieval limit */
1198         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1199                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1200         if (info->retrieve_limit == 0)
1201                 info->retrieve_limit = G_MAXINT;
1202                 
1203         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1204
1205         /* Set account busy */
1206         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1207         priv->account_name = g_strdup(account_name);
1208         
1209         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1210
1211         return TRUE;
1212
1213  error:
1214         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1215         if (callback) 
1216                 callback (self, 0, user_data);
1217         modest_mail_operation_notify_end (self, FALSE);
1218         return FALSE;
1219 }
1220
1221 /* ******************************************************************* */
1222 /* ************************** STORE  ACTIONS ************************* */
1223 /* ******************************************************************* */
1224
1225
1226 TnyFolder *
1227 modest_mail_operation_create_folder (ModestMailOperation *self,
1228                                      TnyFolderStore *parent,
1229                                      const gchar *name)
1230 {
1231         ModestMailOperationPrivate *priv;
1232         TnyFolder *new_folder = NULL;
1233
1234         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1235         g_return_val_if_fail (name, NULL);
1236         
1237         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1238
1239         /* Check parent */
1240         if (TNY_IS_FOLDER (parent)) {
1241                 /* Check folder rules */
1242                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1243                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1244                         /* Set status failed and set an error */
1245                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1246                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1247                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1248                                      _("mail_in_ui_folder_create_error"));
1249                 }
1250         }
1251
1252         if (!strcmp (name, " ") || strchr (name, '/')) {
1253                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1254                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1255                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1256                              _("mail_in_ui_folder_create_error"));
1257         }
1258
1259         if (!priv->error) {
1260                 /* Create the folder */
1261                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1262                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1263                 if (!priv->error)
1264                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1265         }
1266
1267         /* Notify about operation end */
1268         modest_mail_operation_notify_end (self, FALSE);
1269
1270         return new_folder;
1271 }
1272
1273 void
1274 modest_mail_operation_remove_folder (ModestMailOperation *self,
1275                                      TnyFolder           *folder,
1276                                      gboolean             remove_to_trash)
1277 {
1278         TnyAccount *account;
1279         ModestMailOperationPrivate *priv;
1280         ModestTnyFolderRules rules;
1281
1282         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1283         g_return_if_fail (TNY_IS_FOLDER (folder));
1284         
1285         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1286         
1287         /* Check folder rules */
1288         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1289         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1290                 /* Set status failed and set an error */
1291                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1292                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1293                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1294                              _("mail_in_ui_folder_delete_error"));
1295                 goto end;
1296         }
1297
1298         /* Get the account */
1299         account = modest_tny_folder_get_account (folder);
1300         priv->account = g_object_ref(account);
1301
1302         /* Delete folder or move to trash */
1303         if (remove_to_trash) {
1304                 TnyFolder *trash_folder = NULL;
1305                 trash_folder = modest_tny_account_get_special_folder (account,
1306                                                                       TNY_FOLDER_TYPE_TRASH);
1307                 /* TODO: error_handling */
1308                  modest_mail_operation_xfer_folder (self, folder,
1309                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1310         } else {
1311                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1312
1313                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1314                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1315
1316                 if (parent)
1317                         g_object_unref (G_OBJECT (parent));
1318         }
1319         g_object_unref (G_OBJECT (account));
1320
1321  end:
1322         /* Notify about operation end */
1323         modest_mail_operation_notify_end (self, FALSE);
1324 }
1325
1326 static void
1327 transfer_folder_status_cb (GObject *obj,
1328                            TnyStatus *status,
1329                            gpointer user_data)
1330 {
1331         ModestMailOperation *self;
1332         ModestMailOperationPrivate *priv;
1333         ModestMailOperationState *state;
1334
1335         g_return_if_fail (status != NULL);
1336         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1337
1338         self = MODEST_MAIL_OPERATION (user_data);
1339         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1340
1341         if ((status->position == 1) && (status->of_total == 100))
1342                 return;
1343
1344         priv->done = status->position;
1345         priv->total = status->of_total;
1346
1347         state = modest_mail_operation_clone_state (self);
1348         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1349         g_slice_free (ModestMailOperationState, state);
1350 }
1351
1352
1353 static void
1354 transfer_folder_cb (TnyFolder *folder, 
1355                     TnyFolderStore *into, 
1356                     gboolean cancelled, 
1357                     TnyFolder *new_folder, 
1358                     GError **err, 
1359                     gpointer user_data)
1360 {
1361         ModestMailOperation *self = NULL;
1362         ModestMailOperationPrivate *priv = NULL;
1363
1364         self = MODEST_MAIL_OPERATION (user_data);
1365
1366         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1367
1368         if (*err) {
1369                 priv->error = g_error_copy (*err);
1370                 priv->done = 0;
1371                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1372         } else if (cancelled) {
1373                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1374                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1375                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1376                              _("Transference of %s was cancelled."),
1377                              tny_folder_get_name (folder));
1378         } else {
1379                 priv->done = 1;
1380                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1381         }
1382                 
1383         /* Free */
1384         g_object_unref (folder);
1385         g_object_unref (into);
1386
1387         /* Notify about operation end */
1388         modest_mail_operation_notify_end (self, TRUE);
1389 }
1390
1391 void
1392 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1393                                    TnyFolder *folder,
1394                                    TnyFolderStore *parent,
1395                                    gboolean delete_original)
1396 {
1397         ModestMailOperationPrivate *priv = NULL;
1398         ModestTnyFolderRules parent_rules = 0, rules; 
1399
1400         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1401         g_return_if_fail (TNY_IS_FOLDER (folder));
1402
1403         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1404
1405         /* Get account and set it into mail_operation */
1406         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1407         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1408
1409         /* Get folder rules */
1410         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1411         if (TNY_IS_FOLDER (parent))
1412                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1413         
1414         /* The moveable restriction is applied also to copy operation */
1415         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1416                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1417                 /* Set status failed and set an error */
1418                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1419                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1420                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1421                              _("mail_in_ui_folder_move_target_error"));
1422
1423                 /* Notify the queue */
1424                 modest_mail_operation_notify_end (self, FALSE);
1425         } else if (TNY_IS_FOLDER (parent) && 
1426                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1427                 /* Set status failed and set an error */
1428                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1429                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1430                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1431                              _("FIXME: parent folder does not accept new folders"));
1432
1433                 /* Notify the queue */
1434                 modest_mail_operation_notify_end (self, FALSE);
1435         } else {
1436                 /* Pick references for async calls */
1437                 g_object_ref (folder);
1438                 g_object_ref (parent);
1439
1440                 /* Move/Copy folder */          
1441                 tny_folder_copy_async (folder,
1442                                        parent,
1443                                        tny_folder_get_name (folder),
1444                                        delete_original,
1445                                        transfer_folder_cb,
1446                                        transfer_folder_status_cb,
1447                                        self);
1448         }
1449 }
1450
1451 void
1452 modest_mail_operation_rename_folder (ModestMailOperation *self,
1453                                      TnyFolder *folder,
1454                                      const gchar *name)
1455 {
1456         ModestMailOperationPrivate *priv;
1457         ModestTnyFolderRules rules;
1458
1459         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1460         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1461         g_return_if_fail (name);
1462         
1463         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1464
1465         /* Get account and set it into mail_operation */
1466         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1467
1468         /* Check folder rules */
1469         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1470         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1471                 /* Set status failed and set an error */
1472                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1473                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1474                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1475                              _("FIXME: unable to rename"));
1476
1477                 /* Notify about operation end */
1478                 modest_mail_operation_notify_end (self, FALSE);
1479         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1480                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1481                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1482                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1483                              _("FIXME: unable to rename"));
1484                 /* Notify about operation end */
1485                 modest_mail_operation_notify_end (self, FALSE);
1486         } else {
1487                 TnyFolderStore *into;
1488
1489                 /* Rename. Camel handles folder subscription/unsubscription */
1490                 into = tny_folder_get_folder_store (folder);
1491                 tny_folder_copy_async (folder, into, name, TRUE,
1492                                  transfer_folder_cb,
1493                                  transfer_folder_status_cb,
1494                                  self);
1495                 if (into)
1496                         g_object_unref (into);          
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_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1929                         tny_folder_sync(folder, TRUE, &(priv->error));
1930                 }
1931         }
1932
1933         /* Set status */
1934         if (!priv->error)
1935                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1936         else
1937                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1938
1939         /* Free */
1940         g_object_unref (G_OBJECT (folder));
1941
1942         /* Notify about operation end */
1943         modest_mail_operation_notify_end (self, FALSE);
1944 }
1945
1946 static void
1947 transfer_msgs_status_cb (GObject *obj,
1948                          TnyStatus *status,  
1949                          gpointer user_data)
1950 {
1951         XFerMsgAsyncHelper *helper = NULL;
1952         ModestMailOperation *self;
1953         ModestMailOperationPrivate *priv;
1954         ModestMailOperationState *state;
1955
1956
1957         g_return_if_fail (status != NULL);
1958         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1959
1960         helper = (XFerMsgAsyncHelper *) user_data;
1961         g_return_if_fail (helper != NULL);       
1962
1963         self = helper->mail_op;
1964         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1965
1966         if ((status->position == 1) && (status->of_total == 100))
1967                 return;
1968
1969         priv->done = status->position;
1970         priv->total = status->of_total;
1971
1972         state = modest_mail_operation_clone_state (self);
1973         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1974         g_slice_free (ModestMailOperationState, state);
1975 }
1976
1977
1978 static void
1979 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1980 {
1981         XFerMsgAsyncHelper *helper;
1982         ModestMailOperation *self;
1983         ModestMailOperationPrivate *priv;
1984
1985         helper = (XFerMsgAsyncHelper *) user_data;
1986         self = helper->mail_op;
1987
1988         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1989
1990         if (*err) {
1991                 priv->error = g_error_copy (*err);
1992                 priv->done = 0;
1993                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1994         } else if (cancelled) {
1995                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1996                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1997                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1998                              _("Error trying to refresh the contents of %s"),
1999                              tny_folder_get_name (folder));
2000         } else {
2001                 priv->done = 1;
2002                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2003         }
2004
2005         /* Notify about operation end */
2006         modest_mail_operation_notify_end (self, TRUE);
2007
2008         /* If user defined callback function was defined, call it */
2009         if (helper->user_callback) {
2010                 gdk_threads_enter ();
2011                 helper->user_callback (priv->source, helper->user_data);
2012                 gdk_threads_leave ();
2013         }
2014
2015         /* Free */
2016         g_object_unref (helper->headers);
2017         g_object_unref (helper->dest_folder);
2018         g_object_unref (helper->mail_op);
2019         g_slice_free   (XFerMsgAsyncHelper, helper);
2020         g_object_unref (folder);
2021
2022 }
2023
2024 void
2025 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2026                                  TnyList *headers, 
2027                                  TnyFolder *folder, 
2028                                  gboolean delete_original,
2029                                  XferMsgsAsynUserCallback user_callback,
2030                                  gpointer user_data)
2031 {
2032         ModestMailOperationPrivate *priv;
2033         TnyIterator *iter;
2034         TnyFolder *src_folder;
2035         XFerMsgAsyncHelper *helper;
2036         TnyHeader *header;
2037         ModestTnyFolderRules rules;
2038
2039         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2040         g_return_if_fail (TNY_IS_LIST (headers));
2041         g_return_if_fail (TNY_IS_FOLDER (folder));
2042
2043         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2044         priv->total = 1;
2045         priv->done = 0;
2046         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2047
2048         /* Apply folder rules */
2049         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2050         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2051                 /* Set status failed and set an error */
2052                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2053                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2054                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2055                              _("FIXME: folder does not accept msgs"));
2056                 /* Notify the queue */
2057                 modest_mail_operation_notify_end (self, FALSE);
2058                 return;
2059         }
2060
2061         /* Create the helper */
2062         helper = g_slice_new0 (XFerMsgAsyncHelper);
2063         helper->mail_op = g_object_ref(self);
2064         helper->dest_folder = g_object_ref(folder);
2065         helper->headers = g_object_ref(headers);
2066         helper->user_callback = user_callback;
2067         helper->user_data = user_data;
2068
2069         /* Get source folder */
2070         iter = tny_list_create_iterator (headers);
2071         header = TNY_HEADER (tny_iterator_get_current (iter));
2072         src_folder = tny_header_get_folder (header);
2073         g_object_unref (header);
2074         g_object_unref (iter);
2075
2076         /* Get account and set it into mail_operation */
2077         priv->account = modest_tny_folder_get_account (src_folder);
2078
2079         /* Transfer messages */
2080         tny_folder_transfer_msgs_async (src_folder, 
2081                                         headers, 
2082                                         folder, 
2083                                         delete_original, 
2084                                         transfer_msgs_cb, 
2085                                         transfer_msgs_status_cb,
2086                                         helper);
2087 }
2088
2089
2090 static void
2091 on_refresh_folder (TnyFolder   *folder, 
2092                    gboolean     cancelled, 
2093                    GError     **error,
2094                    gpointer     user_data)
2095 {
2096         RefreshAsyncHelper *helper = NULL;
2097         ModestMailOperation *self = NULL;
2098         ModestMailOperationPrivate *priv = NULL;
2099
2100         helper = (RefreshAsyncHelper *) user_data;
2101         self = helper->mail_op;
2102         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2103
2104         if (*error) {
2105                 priv->error = g_error_copy (*error);
2106                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2107                 goto out;
2108         }
2109
2110         if (cancelled) {
2111                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2112                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2113                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2114                              _("Error trying to refresh the contents of %s"),
2115                              tny_folder_get_name (folder));
2116                 goto out;
2117         }
2118
2119         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2120
2121  out:
2122         /* Call user defined callback, if it exists */
2123         if (helper->user_callback) {
2124                 gdk_threads_enter ();
2125                 helper->user_callback (priv->source, folder, helper->user_data);
2126                 gdk_threads_leave ();
2127         }
2128
2129         /* Free */
2130         g_object_unref (helper->mail_op);
2131         g_slice_free   (RefreshAsyncHelper, helper);
2132         g_object_unref (folder);
2133
2134         /* Notify about operation end */
2135         modest_mail_operation_notify_end (self, TRUE);
2136 }
2137
2138 static void
2139 on_refresh_folder_status_update (GObject *obj,
2140                                  TnyStatus *status,
2141                                  gpointer user_data)
2142 {
2143         RefreshAsyncHelper *helper = NULL;
2144         ModestMailOperation *self = NULL;
2145         ModestMailOperationPrivate *priv = NULL;
2146         ModestMailOperationState *state;
2147
2148         g_return_if_fail (user_data != NULL);
2149         g_return_if_fail (status != NULL);
2150         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2151
2152         helper = (RefreshAsyncHelper *) user_data;
2153         self = helper->mail_op;
2154         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2155
2156         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2157
2158         priv->done = status->position;
2159         priv->total = status->of_total;
2160
2161         state = modest_mail_operation_clone_state (self);
2162         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2163         g_slice_free (ModestMailOperationState, state);
2164 }
2165
2166 void 
2167 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2168                                        TnyFolder *folder,
2169                                        RefreshAsyncUserCallback user_callback,
2170                                        gpointer user_data)
2171 {
2172         ModestMailOperationPrivate *priv = NULL;
2173         RefreshAsyncHelper *helper = NULL;
2174
2175         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2176
2177         /* Pick a reference */
2178         g_object_ref (folder);
2179
2180         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2181
2182         /* Get account and set it into mail_operation */
2183         priv->account = modest_tny_folder_get_account  (folder);
2184
2185         /* Create the helper */
2186         helper = g_slice_new0 (RefreshAsyncHelper);
2187         helper->mail_op = g_object_ref(self);
2188         helper->user_callback = user_callback;
2189         helper->user_data = user_data;
2190
2191         /* Refresh the folder. TODO: tinymail could issue a status
2192            updates before the callback call then this could happen. We
2193            must review the design */
2194         tny_folder_refresh_async (folder,
2195                                   on_refresh_folder,
2196                                   on_refresh_folder_status_update,
2197                                   helper);
2198 }
2199
2200 /**
2201  *
2202  * It's used by the mail operation queue to notify the observers
2203  * attached to that signal that the operation finished. We need to use
2204  * that because tinymail does not give us the progress of a given
2205  * operation when it finishes (it directly calls the operation
2206  * callback).
2207  */
2208 static void
2209 modest_mail_operation_notify_end (ModestMailOperation *self,
2210                                   gboolean need_lock)
2211 {
2212         ModestMailOperationState *state;
2213         ModestMailOperationPrivate *priv = NULL;
2214
2215         g_return_if_fail (self);
2216
2217         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2218
2219         if (!priv) {
2220                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2221                 return;
2222         }
2223         
2224         /* Set the account back to not busy */
2225         if (priv->account_name) {
2226                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2227                                                      priv->account_name, FALSE);
2228                 g_free(priv->account_name);
2229                 priv->account_name = NULL;
2230         }
2231         
2232         /* Notify the observers about the mail opertation end */
2233         state = modest_mail_operation_clone_state (self);
2234         if (need_lock)
2235                 gdk_threads_enter ();
2236         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2237         if (need_lock)
2238                 gdk_threads_leave ();
2239         g_slice_free (ModestMailOperationState, state);
2240 }