* Do not block signal emissions into idles callbacks, because signal handlers are...
[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         /* Make sure that we have a connection, and request one 
1151          * if necessary:
1152          * TODO: Is there some way to trigger this for every attempt to 
1153          * use the network? */
1154         if (!modest_platform_connect_and_wait(NULL))
1155                 goto error;
1156
1157         /* Get the Modest account */
1158         modest_account = (TnyStoreAccount *)
1159                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1160                                                                      account_name,
1161                                                                      TNY_ACCOUNT_TYPE_STORE);
1162
1163         if (!modest_account) {
1164                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1165                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1166                              "cannot get tny store account for %s\n", account_name);
1167                 goto error;
1168         }
1169
1170         
1171         /* Get the transport account, we can not do it in the thread
1172            due to some problems with dbus */
1173         transport_account = (TnyTransportAccount *)
1174                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1175                                                                                     account_name);
1176         if (!transport_account) {
1177                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1178                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1179                              "cannot get tny transport account for %s\n", account_name);
1180                 goto error;
1181         }
1182
1183         /* Create the helper object */
1184         info = g_slice_new (UpdateAccountInfo);
1185         info->mail_op = self;
1186         info->account = modest_account;
1187         info->transport_account = transport_account;
1188         info->callback = callback;
1189         info->user_data = user_data;
1190
1191         /* Get the message size limit */
1192         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1193                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1194         if (info->max_size == 0)
1195                 info->max_size = G_MAXINT;
1196         else
1197                 info->max_size = info->max_size * KB;
1198
1199         /* Get per-account retrieval type */
1200         mgr = modest_runtime_get_account_mgr ();
1201         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1202                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1203
1204         /* Get per-account message amount retrieval limit */
1205         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1206                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1207         if (info->retrieve_limit == 0)
1208                 info->retrieve_limit = G_MAXINT;
1209                 
1210         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1211
1212         /* Set account busy */
1213         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1214         priv->account_name = g_strdup(account_name);
1215         
1216         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1217
1218         return TRUE;
1219
1220  error:
1221         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1222         if (callback) 
1223                 callback (self, 0, user_data);
1224         modest_mail_operation_notify_end (self, FALSE);
1225         return FALSE;
1226 }
1227
1228 /* ******************************************************************* */
1229 /* ************************** STORE  ACTIONS ************************* */
1230 /* ******************************************************************* */
1231
1232
1233 TnyFolder *
1234 modest_mail_operation_create_folder (ModestMailOperation *self,
1235                                      TnyFolderStore *parent,
1236                                      const gchar *name)
1237 {
1238         ModestMailOperationPrivate *priv;
1239         TnyFolder *new_folder = NULL;
1240
1241         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1242         g_return_val_if_fail (name, NULL);
1243         
1244         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1245
1246         /* Check parent */
1247         if (TNY_IS_FOLDER (parent)) {
1248                 /* Check folder rules */
1249                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1250                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1251                         /* Set status failed and set an error */
1252                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1253                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1254                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1255                                      _("mail_in_ui_folder_create_error"));
1256                 }
1257         }
1258
1259         if (!strcmp (name, " ") || strchr (name, '/')) {
1260                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1261                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1262                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1263                              _("mail_in_ui_folder_create_error"));
1264         }
1265
1266         if (!priv->error) {
1267                 /* Create the folder */
1268                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1269                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1270                 if (!priv->error)
1271                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1272         }
1273
1274         /* Notify about operation end */
1275         modest_mail_operation_notify_end (self, FALSE);
1276
1277         return new_folder;
1278 }
1279
1280 void
1281 modest_mail_operation_remove_folder (ModestMailOperation *self,
1282                                      TnyFolder           *folder,
1283                                      gboolean             remove_to_trash)
1284 {
1285         TnyAccount *account;
1286         ModestMailOperationPrivate *priv;
1287         ModestTnyFolderRules rules;
1288
1289         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1290         g_return_if_fail (TNY_IS_FOLDER (folder));
1291         
1292         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1293         
1294         /* Check folder rules */
1295         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1296         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1297                 /* Set status failed and set an error */
1298                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1299                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1300                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1301                              _("mail_in_ui_folder_delete_error"));
1302                 goto end;
1303         }
1304
1305         /* Get the account */
1306         account = modest_tny_folder_get_account (folder);
1307         priv->account = g_object_ref(account);
1308
1309         /* Delete folder or move to trash */
1310         if (remove_to_trash) {
1311                 TnyFolder *trash_folder = NULL;
1312                 trash_folder = modest_tny_account_get_special_folder (account,
1313                                                                       TNY_FOLDER_TYPE_TRASH);
1314                 /* TODO: error_handling */
1315                  modest_mail_operation_xfer_folder (self, folder,
1316                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1317         } else {
1318                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1319
1320                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1321                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1322
1323                 if (parent)
1324                         g_object_unref (G_OBJECT (parent));
1325         }
1326         g_object_unref (G_OBJECT (account));
1327
1328  end:
1329         /* Notify about operation end */
1330         modest_mail_operation_notify_end (self, FALSE);
1331 }
1332
1333 static void
1334 transfer_folder_status_cb (GObject *obj,
1335                            TnyStatus *status,
1336                            gpointer user_data)
1337 {
1338         ModestMailOperation *self;
1339         ModestMailOperationPrivate *priv;
1340         ModestMailOperationState *state;
1341
1342         g_return_if_fail (status != NULL);
1343         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1344
1345         self = MODEST_MAIL_OPERATION (user_data);
1346         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1347
1348         if ((status->position == 1) && (status->of_total == 100))
1349                 return;
1350
1351         priv->done = status->position;
1352         priv->total = status->of_total;
1353
1354         state = modest_mail_operation_clone_state (self);
1355         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1356         g_slice_free (ModestMailOperationState, state);
1357 }
1358
1359
1360 static void
1361 transfer_folder_cb (TnyFolder *folder, 
1362                     TnyFolderStore *into, 
1363                     gboolean cancelled, 
1364                     TnyFolder *new_folder, 
1365                     GError **err, 
1366                     gpointer user_data)
1367 {
1368         ModestMailOperation *self = NULL;
1369         ModestMailOperationPrivate *priv = NULL;
1370
1371         self = MODEST_MAIL_OPERATION (user_data);
1372
1373         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1374
1375         if (*err) {
1376                 priv->error = g_error_copy (*err);
1377                 priv->done = 0;
1378                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1379         } else if (cancelled) {
1380                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1381                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1382                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1383                              _("Transference of %s was cancelled."),
1384                              tny_folder_get_name (folder));
1385         } else {
1386                 priv->done = 1;
1387                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1388         }
1389                 
1390         /* Free */
1391         g_object_unref (folder);
1392         g_object_unref (into);
1393
1394         /* Notify about operation end */
1395         modest_mail_operation_notify_end (self, TRUE);
1396 }
1397
1398 void
1399 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1400                                    TnyFolder *folder,
1401                                    TnyFolderStore *parent,
1402                                    gboolean delete_original)
1403 {
1404         ModestMailOperationPrivate *priv = NULL;
1405         ModestTnyFolderRules parent_rules = 0, rules = 0;
1406
1407         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1408         g_return_if_fail (TNY_IS_FOLDER (folder));
1409
1410         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1411
1412         /* Get account and set it into mail_operation */
1413         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1414         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1415
1416         /* Get folder rules */
1417         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1418         if (TNY_IS_FOLDER (parent))
1419                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1420         
1421         /* The moveable restriction is applied also to copy operation */
1422         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1423                 /* Set status failed and set an error */
1424                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1425                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1426                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1427                              _("mail_in_ui_folder_move_target_error"));
1428
1429                 /* Notify the queue */
1430                 modest_mail_operation_notify_end (self, FALSE);
1431         } else if (TNY_IS_FOLDER (parent) && 
1432                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1433                 /* Set status failed and set an error */
1434                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1435                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1436                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1437                              _("FIXME: parent folder does not accept new folders"));
1438
1439                 /* Notify the queue */
1440                 modest_mail_operation_notify_end (self, FALSE);
1441         } else {
1442                 /* Pick references for async calls */
1443                 g_object_ref (folder);
1444                 g_object_ref (parent);
1445
1446                 /* Move/Copy folder */          
1447                 tny_folder_copy_async (folder,
1448                                        parent,
1449                                        tny_folder_get_name (folder),
1450                                        delete_original,
1451                                        transfer_folder_cb,
1452                                        transfer_folder_status_cb,
1453                                        self);
1454         }
1455 }
1456
1457 void
1458 modest_mail_operation_rename_folder (ModestMailOperation *self,
1459                                      TnyFolder *folder,
1460                                      const gchar *name)
1461 {
1462         ModestMailOperationPrivate *priv;
1463         ModestTnyFolderRules rules;
1464
1465         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1466         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1467         g_return_if_fail (name);
1468         
1469         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1470
1471         /* Get account and set it into mail_operation */
1472         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1473
1474         /* Check folder rules */
1475         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1476         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1477                 /* Set status failed and set an error */
1478                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1479                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1480                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1481                              _("FIXME: unable to rename"));
1482
1483                 /* Notify about operation end */
1484                 modest_mail_operation_notify_end (self, FALSE);
1485         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1486                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1487                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1488                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1489                              _("FIXME: unable to rename"));
1490                 /* Notify about operation end */
1491                 modest_mail_operation_notify_end (self, FALSE);
1492         } else {
1493                 TnyFolderStore *into;
1494
1495                 /* Rename. Camel handles folder subscription/unsubscription */
1496                 into = tny_folder_get_folder_store (folder);
1497                 tny_folder_copy_async (folder, into, name, TRUE,
1498                                  transfer_folder_cb,
1499                                  transfer_folder_status_cb,
1500                                  self);
1501                 if (into)
1502                         g_object_unref (into);          
1503         }
1504  }
1505
1506 /* ******************************************************************* */
1507 /* **************************  MSG  ACTIONS  ************************* */
1508 /* ******************************************************************* */
1509
1510 void modest_mail_operation_get_msg (ModestMailOperation *self,
1511                                     TnyHeader *header,
1512                                     GetMsgAsyncUserCallback user_callback,
1513                                     gpointer user_data)
1514 {
1515         GetMsgAsyncHelper *helper = NULL;
1516         TnyFolder *folder;
1517         ModestMailOperationPrivate *priv;
1518         
1519         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1520         g_return_if_fail (TNY_IS_HEADER (header));
1521         
1522         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1523         folder = tny_header_get_folder (header);
1524
1525         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1526
1527         /* Get message from folder */
1528         if (folder) {
1529                 /* Get account and set it into mail_operation */
1530                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1531
1532                 helper = g_slice_new0 (GetMsgAsyncHelper);
1533                 helper->mail_op = self;
1534                 helper->user_callback = user_callback;
1535                 helper->user_data = user_data;
1536                 helper->header = g_object_ref (header);
1537
1538                 // The callback's reference so that the mail op is not
1539                 // finalized until the async operation is completed even if
1540                 // the user canceled the request meanwhile.
1541                 g_object_ref (G_OBJECT (helper->mail_op));
1542
1543                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1544
1545                 g_object_unref (G_OBJECT (folder));
1546         } else {
1547                 /* Set status failed and set an error */
1548                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1549                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1550                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1551                              _("Error trying to get a message. No folder found for header"));
1552
1553                 /* Notify the queue */
1554                 modest_mail_operation_notify_end (self, FALSE);
1555         }
1556 }
1557
1558 static void
1559 get_msg_cb (TnyFolder *folder, 
1560             gboolean cancelled, 
1561             TnyMsg *msg, 
1562             GError **error, 
1563             gpointer user_data)
1564 {
1565         GetMsgAsyncHelper *helper = NULL;
1566         ModestMailOperation *self = NULL;
1567         ModestMailOperationPrivate *priv = NULL;
1568
1569         helper = (GetMsgAsyncHelper *) user_data;
1570         g_return_if_fail (helper != NULL);       
1571         self = helper->mail_op;
1572         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1573         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1574
1575         /* Check errors and cancel */
1576         if (*error) {
1577                 priv->error = g_error_copy (*error);
1578                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1579                 goto out;
1580         }
1581         if (cancelled) {
1582                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1583                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1584                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1585                              _("Error trying to refresh the contents of %s"),
1586                              tny_folder_get_name (folder));
1587                 goto out;
1588         }
1589
1590         /* The mail operation might have been canceled in which case we do not
1591            want to notify anyone anymore. */
1592         if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1593                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1594
1595                 /* If user defined callback function was defined, call it */
1596                 if (helper->user_callback) {
1597                         /* This callback is called into an iddle by tinymail,
1598                            and idles are not in the main lock */
1599                         gdk_threads_enter ();
1600                         helper->user_callback (self, helper->header, msg, helper->user_data);
1601                         gdk_threads_leave ();
1602                 }
1603         }
1604
1605  out:
1606         /* Free */
1607         g_object_unref (helper->header);
1608         g_slice_free (GetMsgAsyncHelper, helper);
1609                 
1610         /* Notify about operation end */
1611         if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED)
1612                 modest_mail_operation_notify_end (self, TRUE);
1613
1614         g_object_unref (G_OBJECT (self));
1615 }
1616
1617 static void     
1618 get_msg_status_cb (GObject *obj,
1619                    TnyStatus *status,  
1620                    gpointer user_data)
1621 {
1622         GetMsgAsyncHelper *helper = NULL;
1623         ModestMailOperation *self;
1624         ModestMailOperationPrivate *priv;
1625         ModestMailOperationState *state;
1626
1627         g_return_if_fail (status != NULL);
1628         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1629
1630         helper = (GetMsgAsyncHelper *) user_data;
1631         g_return_if_fail (helper != NULL);       
1632
1633         self = helper->mail_op;
1634         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1635
1636         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1637                 return;
1638
1639         if ((status->position == 1) && (status->of_total == 100))
1640                 return;
1641
1642         priv->done = 1;
1643         priv->total = 1;
1644
1645         state = modest_mail_operation_clone_state (self);
1646         state->bytes_done = status->position;
1647         state->bytes_total = status->of_total;
1648         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1649         g_slice_free (ModestMailOperationState, state);
1650 }
1651
1652 /****************************************************/
1653 typedef struct {
1654         ModestMailOperation *mail_op;
1655         TnyList *headers;
1656         GetMsgAsyncUserCallback user_callback;
1657         gpointer user_data;
1658         GDestroyNotify notify;
1659 } GetFullMsgsInfo;
1660
1661 typedef struct {
1662         GetMsgAsyncUserCallback user_callback;
1663         TnyHeader *header;
1664         TnyMsg *msg;
1665         gpointer user_data;
1666         ModestMailOperation *mail_op;
1667 } NotifyGetMsgsInfo;
1668
1669
1670 /* 
1671  * Used by get_msgs_full_thread to call the user_callback for each
1672  * message that has been read
1673  */
1674 static gboolean
1675 notify_get_msgs_full (gpointer data)
1676 {
1677         NotifyGetMsgsInfo *info;
1678
1679         info = (NotifyGetMsgsInfo *) data;      
1680
1681         /* Call the user callback. Idles are not in the main lock, so
1682            lock it */
1683         gdk_threads_enter ();
1684         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1685         gdk_threads_leave ();
1686
1687         g_slice_free (NotifyGetMsgsInfo, info);
1688
1689         return FALSE;
1690 }
1691
1692 /* 
1693  * Used by get_msgs_full_thread to free al the thread resources and to
1694  * call the destroy function for the passed user_data
1695  */
1696 static gboolean
1697 get_msgs_full_destroyer (gpointer data)
1698 {
1699         GetFullMsgsInfo *info;
1700
1701         info = (GetFullMsgsInfo *) data;
1702
1703         if (info->notify) {
1704                 gdk_threads_enter ();   
1705                 info->notify (info->user_data);
1706                 gdk_threads_leave ();
1707         }
1708
1709         /* free */
1710         g_object_unref (info->headers);
1711         g_slice_free (GetFullMsgsInfo, info);
1712
1713         return FALSE;
1714 }
1715
1716 static gpointer
1717 get_msgs_full_thread (gpointer thr_user_data)
1718 {
1719         GetFullMsgsInfo *info;
1720         ModestMailOperationPrivate *priv = NULL;
1721         TnyIterator *iter = NULL;
1722         
1723         info = (GetFullMsgsInfo *) thr_user_data;       
1724         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1725
1726         iter = tny_list_create_iterator (info->headers);
1727         while (!tny_iterator_is_done (iter)) { 
1728                 TnyHeader *header;
1729                 TnyFolder *folder;
1730                 
1731                 header = TNY_HEADER (tny_iterator_get_current (iter));
1732                 folder = tny_header_get_folder (header);
1733                                 
1734                 /* Get message from folder */
1735                 if (folder) {
1736                         TnyMsg *msg;
1737                         /* The callback will call it per each header */
1738                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1739
1740                         if (msg) {
1741                                 ModestMailOperationState *state;
1742                                 ModestPair *pair;
1743
1744                                 priv->done++;
1745
1746                                 /* notify progress */
1747                                 state = modest_mail_operation_clone_state (info->mail_op);
1748                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1749                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1750                                                  pair, (GDestroyNotify) modest_pair_free);
1751
1752                                 /* The callback is the responsible for
1753                                    freeing the message */
1754                                 if (info->user_callback) {
1755                                         NotifyGetMsgsInfo *info_notify;
1756                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1757                                         info_notify->user_callback = info->user_callback;
1758                                         info_notify->mail_op = info->mail_op;
1759                                         info_notify->header = g_object_ref (header);
1760                                         info_notify->msg = g_object_ref (msg);
1761                                         info_notify->user_data = info->user_data;
1762                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1763                                                          notify_get_msgs_full, 
1764                                                          info_notify, NULL);
1765                                 }
1766                                 g_object_unref (msg);
1767                         }
1768                 } else {
1769                         /* Set status failed and set an error */
1770                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1771                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1772                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1773                                      "Error trying to get a message. No folder found for header");
1774                 }
1775                 g_object_unref (header);                
1776                 tny_iterator_next (iter);
1777         }
1778
1779         /* Set operation status */
1780         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1781                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1782
1783         /* Notify about operation end */
1784         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1785
1786         /* Free thread resources. Will be called after all previous idles */
1787         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1788
1789         return NULL;
1790 }
1791
1792 void 
1793 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1794                                      TnyList *header_list, 
1795                                      GetMsgAsyncUserCallback user_callback,
1796                                      gpointer user_data,
1797                                      GDestroyNotify notify)
1798 {
1799         TnyHeader *header = NULL;
1800         TnyFolder *folder = NULL;
1801         GThread *thread;
1802         ModestMailOperationPrivate *priv = NULL;
1803         GetFullMsgsInfo *info = NULL;
1804         gboolean size_ok = TRUE;
1805         gint max_size;
1806         TnyIterator *iter = NULL;
1807         
1808         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1809         
1810         /* Init mail operation */
1811         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1812         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1813         priv->done = 0;
1814         priv->total = tny_list_get_length(header_list);
1815
1816         /* Get account and set it into mail_operation */
1817         if (tny_list_get_length (header_list) >= 1) {
1818                 iter = tny_list_create_iterator (header_list);
1819                 header = TNY_HEADER (tny_iterator_get_current (iter));
1820                 folder = tny_header_get_folder (header);                
1821                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1822                 g_object_unref (header);
1823                 g_object_unref (folder);
1824
1825                 if (tny_list_get_length (header_list) == 1) {
1826                         g_object_unref (iter);
1827                         iter = NULL;
1828                 }
1829         }
1830
1831         /* Get msg size limit */
1832         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1833                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1834                                          &(priv->error));
1835         if (priv->error) {
1836                 g_clear_error (&(priv->error));
1837                 max_size = G_MAXINT;
1838         } else {
1839                 max_size = max_size * KB;
1840         }
1841
1842         /* Check message size limits. If there is only one message
1843            always retrieve it */
1844         if (iter != NULL) {
1845                 while (!tny_iterator_is_done (iter) && size_ok) {
1846                         header = TNY_HEADER (tny_iterator_get_current (iter));
1847                         if (tny_header_get_message_size (header) >= max_size)
1848                                 size_ok = FALSE;
1849                         g_object_unref (header);
1850                         tny_iterator_next (iter);
1851                 }
1852                 g_object_unref (iter);
1853         }
1854
1855         if (size_ok) {
1856                 /* Create the info */
1857                 info = g_slice_new0 (GetFullMsgsInfo);
1858                 info->mail_op = self;
1859                 info->user_callback = user_callback;
1860                 info->user_data = user_data;
1861                 info->headers = g_object_ref (header_list);
1862                 info->notify = notify;
1863
1864                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1865         } else {
1866                 /* Set status failed and set an error */
1867                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1868                 /* FIXME: the error msg is different for pop */
1869                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1870                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1871                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1872                 /* Remove from queue and free resources */
1873                 modest_mail_operation_notify_end (self, FALSE);
1874                 if (notify)
1875                         notify (user_data);
1876         }
1877 }
1878
1879
1880 void 
1881 modest_mail_operation_remove_msg (ModestMailOperation *self,
1882                                   TnyHeader *header,
1883                                   gboolean remove_to_trash)
1884 {
1885         TnyFolder *folder;
1886         ModestMailOperationPrivate *priv;
1887
1888         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1889         g_return_if_fail (TNY_IS_HEADER (header));
1890
1891         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1892         folder = tny_header_get_folder (header);
1893
1894         /* Get account and set it into mail_operation */
1895         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1896
1897         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1898
1899         /* Delete or move to trash */
1900         if (remove_to_trash) {
1901                 TnyFolder *trash_folder;
1902                 TnyStoreAccount *store_account;
1903
1904                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1905                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1906                                                                       TNY_FOLDER_TYPE_TRASH);
1907                 if (trash_folder) {
1908                         TnyList *headers;
1909
1910                         /* Create list */
1911                         headers = tny_simple_list_new ();
1912                         tny_list_append (headers, G_OBJECT (header));
1913                         g_object_unref (header);
1914
1915                         /* Move to trash */
1916                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1917                         g_object_unref (headers);
1918 /*                      g_object_unref (trash_folder); */
1919                 } else {
1920                         ModestMailOperationPrivate *priv;
1921
1922                         /* Set status failed and set an error */
1923                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1924                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1925                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1926                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1927                                      _("Error trying to delete a message. Trash folder not found"));
1928                 }
1929
1930                 g_object_unref (G_OBJECT (store_account));
1931         } else {
1932                 tny_folder_remove_msg (folder, header, &(priv->error));
1933                 if (!priv->error) {
1934                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1935                         tny_folder_sync(folder, TRUE, &(priv->error));
1936                 }
1937         }
1938
1939         /* Set status */
1940         if (!priv->error)
1941                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1942         else
1943                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1944
1945         /* Free */
1946         g_object_unref (G_OBJECT (folder));
1947
1948         /* Notify about operation end */
1949         modest_mail_operation_notify_end (self, FALSE);
1950 }
1951
1952 static void
1953 transfer_msgs_status_cb (GObject *obj,
1954                          TnyStatus *status,  
1955                          gpointer user_data)
1956 {
1957         XFerMsgAsyncHelper *helper = NULL;
1958         ModestMailOperation *self;
1959         ModestMailOperationPrivate *priv;
1960         ModestMailOperationState *state;
1961
1962
1963         g_return_if_fail (status != NULL);
1964         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1965
1966         helper = (XFerMsgAsyncHelper *) user_data;
1967         g_return_if_fail (helper != NULL);       
1968
1969         self = helper->mail_op;
1970         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1971
1972         if ((status->position == 1) && (status->of_total == 100))
1973                 return;
1974
1975         priv->done = status->position;
1976         priv->total = status->of_total;
1977
1978         state = modest_mail_operation_clone_state (self);
1979         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1980         g_slice_free (ModestMailOperationState, state);
1981 }
1982
1983
1984 static void
1985 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1986 {
1987         XFerMsgAsyncHelper *helper;
1988         ModestMailOperation *self;
1989         ModestMailOperationPrivate *priv;
1990
1991         helper = (XFerMsgAsyncHelper *) user_data;
1992         self = helper->mail_op;
1993
1994         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1995
1996         if (*err) {
1997                 priv->error = g_error_copy (*err);
1998                 priv->done = 0;
1999                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2000         } else if (cancelled) {
2001                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2002                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2003                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2004                              _("Error trying to refresh the contents of %s"),
2005                              tny_folder_get_name (folder));
2006         } else {
2007                 priv->done = 1;
2008                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2009         }
2010
2011         /* Notify about operation end */
2012         modest_mail_operation_notify_end (self, TRUE);
2013
2014         /* If user defined callback function was defined, call it */
2015         if (helper->user_callback) {
2016                 gdk_threads_enter ();
2017                 helper->user_callback (priv->source, helper->user_data);
2018                 gdk_threads_leave ();
2019         }
2020
2021         /* Free */
2022         g_object_unref (helper->headers);
2023         g_object_unref (helper->dest_folder);
2024         g_object_unref (helper->mail_op);
2025         g_slice_free   (XFerMsgAsyncHelper, helper);
2026         g_object_unref (folder);
2027
2028 }
2029
2030 void
2031 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2032                                  TnyList *headers, 
2033                                  TnyFolder *folder, 
2034                                  gboolean delete_original,
2035                                  XferMsgsAsynUserCallback user_callback,
2036                                  gpointer user_data)
2037 {
2038         ModestMailOperationPrivate *priv;
2039         TnyIterator *iter;
2040         TnyFolder *src_folder;
2041         XFerMsgAsyncHelper *helper;
2042         TnyHeader *header;
2043         ModestTnyFolderRules rules;
2044
2045         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2046         g_return_if_fail (TNY_IS_LIST (headers));
2047         g_return_if_fail (TNY_IS_FOLDER (folder));
2048
2049         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2050         priv->total = 1;
2051         priv->done = 0;
2052         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2053
2054         /* Apply folder rules */
2055         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2056         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2057                 /* Set status failed and set an error */
2058                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2059                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2060                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2061                              _("FIXME: folder does not accept msgs"));
2062                 /* Notify the queue */
2063                 modest_mail_operation_notify_end (self, FALSE);
2064                 return;
2065         }
2066
2067         /* Create the helper */
2068         helper = g_slice_new0 (XFerMsgAsyncHelper);
2069         helper->mail_op = g_object_ref(self);
2070         helper->dest_folder = g_object_ref(folder);
2071         helper->headers = g_object_ref(headers);
2072         helper->user_callback = user_callback;
2073         helper->user_data = user_data;
2074
2075         /* Get source folder */
2076         iter = tny_list_create_iterator (headers);
2077         header = TNY_HEADER (tny_iterator_get_current (iter));
2078         src_folder = tny_header_get_folder (header);
2079         g_object_unref (header);
2080         g_object_unref (iter);
2081
2082         /* Get account and set it into mail_operation */
2083         priv->account = modest_tny_folder_get_account (src_folder);
2084
2085         /* Transfer messages */
2086         tny_folder_transfer_msgs_async (src_folder, 
2087                                         headers, 
2088                                         folder, 
2089                                         delete_original, 
2090                                         transfer_msgs_cb, 
2091                                         transfer_msgs_status_cb,
2092                                         helper);
2093 }
2094
2095
2096 static void
2097 on_refresh_folder (TnyFolder   *folder, 
2098                    gboolean     cancelled, 
2099                    GError     **error,
2100                    gpointer     user_data)
2101 {
2102         RefreshAsyncHelper *helper = NULL;
2103         ModestMailOperation *self = NULL;
2104         ModestMailOperationPrivate *priv = NULL;
2105
2106         helper = (RefreshAsyncHelper *) user_data;
2107         self = helper->mail_op;
2108         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2109
2110         if (*error) {
2111                 priv->error = g_error_copy (*error);
2112                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2113                 goto out;
2114         }
2115
2116         if (cancelled) {
2117                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2118                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2119                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2120                              _("Error trying to refresh the contents of %s"),
2121                              tny_folder_get_name (folder));
2122                 goto out;
2123         }
2124
2125         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2126
2127  out:
2128         /* Call user defined callback, if it exists */
2129         if (helper->user_callback) {
2130                 gdk_threads_enter ();
2131                 helper->user_callback (priv->source, folder, helper->user_data);
2132                 gdk_threads_leave ();
2133         }
2134
2135         /* Free */
2136         g_object_unref (helper->mail_op);
2137         g_slice_free   (RefreshAsyncHelper, helper);
2138         g_object_unref (folder);
2139
2140         /* Notify about operation end */
2141         modest_mail_operation_notify_end (self, TRUE);
2142 }
2143
2144 static void
2145 on_refresh_folder_status_update (GObject *obj,
2146                                  TnyStatus *status,
2147                                  gpointer user_data)
2148 {
2149         RefreshAsyncHelper *helper = NULL;
2150         ModestMailOperation *self = NULL;
2151         ModestMailOperationPrivate *priv = NULL;
2152         ModestMailOperationState *state;
2153
2154         g_return_if_fail (user_data != NULL);
2155         g_return_if_fail (status != NULL);
2156         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2157
2158         helper = (RefreshAsyncHelper *) user_data;
2159         self = helper->mail_op;
2160         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2161
2162         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2163
2164         priv->done = status->position;
2165         priv->total = status->of_total;
2166
2167         state = modest_mail_operation_clone_state (self);
2168         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2169         g_slice_free (ModestMailOperationState, state);
2170 }
2171
2172 void 
2173 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2174                                        TnyFolder *folder,
2175                                        RefreshAsyncUserCallback user_callback,
2176                                        gpointer user_data)
2177 {
2178         ModestMailOperationPrivate *priv = NULL;
2179         RefreshAsyncHelper *helper = NULL;
2180
2181         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2182
2183         /* Pick a reference */
2184         g_object_ref (folder);
2185
2186         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2187
2188         /* Get account and set it into mail_operation */
2189         priv->account = modest_tny_folder_get_account  (folder);
2190
2191         /* Create the helper */
2192         helper = g_slice_new0 (RefreshAsyncHelper);
2193         helper->mail_op = g_object_ref(self);
2194         helper->user_callback = user_callback;
2195         helper->user_data = user_data;
2196
2197         /* Refresh the folder. TODO: tinymail could issue a status
2198            updates before the callback call then this could happen. We
2199            must review the design */
2200         tny_folder_refresh_async (folder,
2201                                   on_refresh_folder,
2202                                   on_refresh_folder_status_update,
2203                                   helper);
2204 }
2205
2206 /**
2207  *
2208  * It's used by the mail operation queue to notify the observers
2209  * attached to that signal that the operation finished. We need to use
2210  * that because tinymail does not give us the progress of a given
2211  * operation when it finishes (it directly calls the operation
2212  * callback).
2213  */
2214 static void
2215 modest_mail_operation_notify_end (ModestMailOperation *self,
2216                                   gboolean need_lock)
2217 {
2218         ModestMailOperationState *state;
2219         ModestMailOperationPrivate *priv = NULL;
2220
2221         g_return_if_fail (self);
2222
2223         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2224
2225         if (!priv) {
2226                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2227                 return;
2228         }
2229         
2230         /* Set the account back to not busy */
2231         if (priv->account_name) {
2232                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2233                                                      priv->account_name, FALSE);
2234                 g_free(priv->account_name);
2235                 priv->account_name = NULL;
2236         }
2237         
2238         /* Notify the observers about the mail opertation end */
2239         state = modest_mail_operation_clone_state (self);
2240         if (need_lock)
2241                 gdk_threads_enter ();
2242         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2243         if (need_lock)
2244                 gdk_threads_leave ();
2245         g_slice_free (ModestMailOperationState, state);
2246 }