56f6783482e29fff8b3a01ad3ea7a7ae58e88a2f
[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         gdk_threads_enter ();
803
804         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
805         ModestMailOperationState *state;
806
807         state = modest_mail_operation_clone_state (mail_op);
808         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
809         g_slice_free (ModestMailOperationState, state);
810         
811         gdk_threads_leave ();
812
813         return TRUE;
814 }
815
816 /* 
817  * Issues the "progress-changed" signal and removes the timer. It uses
818  * a lock to ensure that the progress information of the mail
819  * operation is not modified while there are notifications pending
820  */
821 static gboolean
822 idle_notify_progress_once (gpointer data)
823 {
824         gdk_threads_enter ();
825
826         ModestPair *pair;
827
828         pair = (ModestPair *) data;
829
830         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
831
832         /* Free the state and the reference to the mail operation */
833         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
834         g_object_unref (pair->first);
835
836         gdk_threads_leave ();
837
838         return FALSE;
839 }
840
841 /* 
842  * Used by update_account_thread to notify the queue from the main
843  * loop. We call it inside an idle call to achieve that
844  */
845 static gboolean
846 idle_notify_update_account_queue (gpointer data)
847 {
848         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
849         ModestMailOperationPrivate *priv = NULL;
850
851         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
852
853         /* Do not need to block, the notify end will do it for us */    
854         modest_mail_operation_notify_end (mail_op, TRUE);
855         g_object_unref (mail_op);
856
857         return FALSE;
858 }
859
860 static int
861 compare_headers_by_date (gconstpointer a, 
862                          gconstpointer b)
863 {
864         TnyHeader **header1, **header2;
865         time_t sent1, sent2;
866
867         header1 = (TnyHeader **) a;
868         header2 = (TnyHeader **) b;
869
870         sent1 = tny_header_get_date_sent (*header1);
871         sent2 = tny_header_get_date_sent (*header2);
872
873         /* We want the most recent ones (greater time_t) at the
874            beginning */
875         if (sent1 < sent2)
876                 return 1;
877         else
878                 return -1;
879 }
880
881 static gboolean 
882 set_last_updated_idle (gpointer data)
883 {
884         gdk_threads_enter ();
885
886         /* It does not matter if the time is not exactly the same than
887            the time when this idle was called, it's just an
888            approximation and it won't be very different */
889         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
890                                     (gchar *) data, 
891                                     MODEST_ACCOUNT_LAST_UPDATED, 
892                                     time(NULL), 
893                                     TRUE);
894
895         gdk_threads_leave ();
896
897         return FALSE;
898 }
899
900 static gpointer
901 update_account_thread (gpointer thr_user_data)
902 {
903         static gboolean first_time = TRUE;
904         UpdateAccountInfo *info;
905         TnyList *all_folders = NULL;
906         GPtrArray *new_headers = NULL;
907         TnyIterator *iter = NULL;
908         TnyFolderStoreQuery *query = NULL;
909         ModestMailOperationPrivate *priv = NULL;
910         ModestTnySendQueue *send_queue = NULL;
911
912         info = (UpdateAccountInfo *) thr_user_data;
913         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
914
915         /* Get account and set it into mail_operation */
916         priv->account = g_object_ref (info->account);
917
918         /*
919          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
920          * show any updates unless we do that
921          */
922         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
923                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
924
925         /* Get all the folders. We can do it synchronously because
926            we're already running in a different thread than the UI */
927         all_folders = tny_simple_list_new ();
928         query = tny_folder_store_query_new ();
929         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
930         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
931                                       all_folders,
932                                       query,
933                                       &(priv->error));
934         if (priv->error) {
935                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
936                 goto out;
937         }
938
939         iter = tny_list_create_iterator (all_folders);
940         while (!tny_iterator_is_done (iter)) {
941                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
942
943                 recurse_folders (folder, query, all_folders);
944                 tny_iterator_next (iter);
945         }
946         g_object_unref (G_OBJECT (iter));
947
948         /* Update status and notify. We need to call the notification
949            with a source function in order to call it from the main
950            loop. We need that in order not to get into trouble with
951            Gtk+. We use a timeout in order to provide more status
952            information, because the sync tinymail call does not
953            provide it for the moment */
954         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
955
956         /* Refresh folders */
957         new_headers = g_ptr_array_new ();
958         iter = tny_list_create_iterator (all_folders);
959
960         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
961
962                 InternalFolderObserver *observer;
963                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
964
965                 /* Refresh the folder */
966                 /* Our observer receives notification of new emails during folder refreshes,
967                  * so we can use observer->new_headers.
968                  */
969                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
970                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
971                 
972                 /* This gets the status information (headers) from the server.
973                  * We use the blocking version, because we are already in a separate 
974                  * thread.
975                  */
976
977                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
978                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
979                         TnyIterator *iter;
980
981                         /* If the retrieve type is full messages, refresh and get the messages */
982                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
983
984                         iter = tny_list_create_iterator (observer->new_headers);
985                         while (!tny_iterator_is_done (iter)) {
986                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
987                                  
988                                 /* Apply per-message size limits */
989                                 if (tny_header_get_message_size (header) < info->max_size)
990                                         g_ptr_array_add (new_headers, g_object_ref (header));
991
992                                 g_object_unref (header);
993                                 tny_iterator_next (iter);
994                         }
995                         g_object_unref (iter);
996                 } else {
997                         /* We do not need to do it the first time
998                            because it's automatically done by the tree
999                            model */
1000                         if (G_UNLIKELY (!first_time))
1001                                 tny_folder_poke_status (TNY_FOLDER (folder));
1002                 }
1003                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1004                 g_object_unref (observer);
1005                 observer = NULL;                        
1006
1007                 g_object_unref (G_OBJECT (folder));
1008                 if (priv->error)
1009                 {
1010                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1011                         goto out;
1012                 }
1013                 
1014                 tny_iterator_next (iter);
1015         }
1016
1017         did_a_cancel = FALSE;
1018
1019         g_object_unref (G_OBJECT (iter));
1020         g_source_remove (timeout);
1021
1022         if (new_headers->len > 0) {
1023                 gint msg_num = 0;
1024
1025                 /* Order by date */
1026                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1027
1028                 /* Apply message count limit */
1029                 /* If the number of messages exceeds the maximum, ask the
1030                  * user to download them all,
1031                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1032                  */
1033                 if (new_headers->len > info->retrieve_limit) {
1034                         /* TODO: Ask the user, instead of just
1035                          * failing, showing
1036                          * mail_nc_msg_count_limit_exceeded, with 'Get
1037                          * all' and 'Newest only' buttons. */
1038                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1039                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1040                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1041                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1042                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1043                         goto out;
1044                 }
1045                 
1046                 priv->done = 0;
1047                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1048                 while (msg_num < priv->total) {
1049
1050                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1051                         TnyFolder *folder = tny_header_get_folder (header);
1052                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1053                         ModestMailOperationState *state;
1054                         ModestPair* pair;
1055
1056                         priv->done++;
1057                         /* We can not just use the mail operation because the
1058                            values of done and total could change before the
1059                            idle is called */
1060                         state = modest_mail_operation_clone_state (info->mail_op);
1061                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1062                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1063                                          pair, (GDestroyNotify) modest_pair_free);
1064
1065                         g_object_unref (msg);
1066                         g_object_unref (folder);
1067
1068                         msg_num++;
1069                 }
1070                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1071                 g_ptr_array_free (new_headers, FALSE);
1072         }
1073         
1074         /* Perform send */
1075         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1076         priv->done = 0;
1077         priv->total = 0;
1078         if (priv->account != NULL) 
1079                 g_object_unref (priv->account);
1080         priv->account = g_object_ref (info->transport_account);
1081         
1082         send_queue = modest_runtime_get_send_queue (info->transport_account);
1083         if (send_queue) {
1084                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1085                 modest_tny_send_queue_try_to_send (send_queue);
1086                 g_source_remove (timeout);
1087         } else {
1088                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1089                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1090                              "cannot create a send queue for %s\n", 
1091                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1092                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1093         }
1094         
1095         /* Check if the operation was a success */
1096         if (!priv->error) {
1097                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1098
1099                 /* Update the last updated key */
1100                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1101                                  set_last_updated_idle, 
1102                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1103                                  (GDestroyNotify) g_free);
1104         }
1105
1106  out:
1107         /* Notify about operation end. Note that the info could be
1108            freed before this idle happens, but the mail operation will
1109            be still alive */
1110         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1111
1112         if (info->callback) {
1113                 /* This thread is not in the main lock */
1114                 gdk_threads_enter ();
1115                 info->callback (info->mail_op, 
1116                                 (new_headers) ? new_headers->len : 0, 
1117                                 info->user_data);
1118                 gdk_threads_leave ();
1119         }
1120         
1121         /* Frees */
1122         g_object_unref (query);
1123         g_object_unref (all_folders);
1124         g_object_unref (info->account);
1125         g_object_unref (info->transport_account);
1126         g_free (info->retrieve_type);
1127         g_slice_free (UpdateAccountInfo, info);
1128
1129         first_time = FALSE;
1130
1131         return NULL;
1132 }
1133
1134 gboolean
1135 modest_mail_operation_update_account (ModestMailOperation *self,
1136                                       const gchar *account_name,
1137                                       UpdateAccountCallback callback,
1138                                       gpointer user_data)
1139 {
1140         GThread *thread;
1141         UpdateAccountInfo *info;
1142         ModestMailOperationPrivate *priv;
1143         ModestAccountMgr *mgr;
1144         TnyStoreAccount *modest_account;
1145         TnyTransportAccount *transport_account;
1146
1147         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1148         g_return_val_if_fail (account_name, FALSE);
1149
1150         /* Init mail operation. Set total and done to 0, and do not
1151            update them, this way the progress objects will know that
1152            we have no clue about the number of the objects */
1153         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1154         priv->total = 0;
1155         priv->done  = 0;
1156         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1157
1158         /* Make sure that we have a connection, and request one 
1159          * if necessary:
1160          * TODO: Is there some way to trigger this for every attempt to 
1161          * use the network? */
1162         if (!modest_platform_connect_and_wait(NULL))
1163                 goto error;
1164
1165         /* Get the Modest account */
1166         modest_account = (TnyStoreAccount *)
1167                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1168                                                                      account_name,
1169                                                                      TNY_ACCOUNT_TYPE_STORE);
1170
1171         if (!modest_account) {
1172                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1173                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1174                              "cannot get tny store account for %s\n", account_name);
1175                 goto error;
1176         }
1177
1178         
1179         /* Get the transport account, we can not do it in the thread
1180            due to some problems with dbus */
1181         transport_account = (TnyTransportAccount *)
1182                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1183                                                                                     account_name);
1184         if (!transport_account) {
1185                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1186                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1187                              "cannot get tny transport account for %s\n", account_name);
1188                 goto error;
1189         }
1190
1191         /* Create the helper object */
1192         info = g_slice_new (UpdateAccountInfo);
1193         info->mail_op = self;
1194         info->account = modest_account;
1195         info->transport_account = transport_account;
1196         info->callback = callback;
1197         info->user_data = user_data;
1198
1199         /* Get the message size limit */
1200         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1201                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1202         if (info->max_size == 0)
1203                 info->max_size = G_MAXINT;
1204         else
1205                 info->max_size = info->max_size * KB;
1206
1207         /* Get per-account retrieval type */
1208         mgr = modest_runtime_get_account_mgr ();
1209         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1210                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1211
1212         /* Get per-account message amount retrieval limit */
1213         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1214                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1215         if (info->retrieve_limit == 0)
1216                 info->retrieve_limit = G_MAXINT;
1217                 
1218         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1219
1220         /* Set account busy */
1221         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1222         priv->account_name = g_strdup(account_name);
1223         
1224         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1225
1226         return TRUE;
1227
1228  error:
1229         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1230         if (callback) 
1231                 callback (self, 0, user_data);
1232         modest_mail_operation_notify_end (self, FALSE);
1233         return FALSE;
1234 }
1235
1236 /* ******************************************************************* */
1237 /* ************************** STORE  ACTIONS ************************* */
1238 /* ******************************************************************* */
1239
1240
1241 TnyFolder *
1242 modest_mail_operation_create_folder (ModestMailOperation *self,
1243                                      TnyFolderStore *parent,
1244                                      const gchar *name)
1245 {
1246         ModestMailOperationPrivate *priv;
1247         TnyFolder *new_folder = NULL;
1248
1249         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1250         g_return_val_if_fail (name, NULL);
1251         
1252         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1253
1254         /* Check parent */
1255         if (TNY_IS_FOLDER (parent)) {
1256                 /* Check folder rules */
1257                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1258                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1259                         /* Set status failed and set an error */
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
1267         if (!strcmp (name, " ") || strchr (name, '/')) {
1268                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1269                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1270                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1271                              _("mail_in_ui_folder_create_error"));
1272         }
1273
1274         if (!priv->error) {
1275                 /* Create the folder */
1276                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1277                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1278                 if (!priv->error)
1279                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1280         }
1281
1282         /* Notify about operation end */
1283         modest_mail_operation_notify_end (self, FALSE);
1284
1285         return new_folder;
1286 }
1287
1288 void
1289 modest_mail_operation_remove_folder (ModestMailOperation *self,
1290                                      TnyFolder           *folder,
1291                                      gboolean             remove_to_trash)
1292 {
1293         TnyAccount *account;
1294         ModestMailOperationPrivate *priv;
1295         ModestTnyFolderRules rules;
1296
1297         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1298         g_return_if_fail (TNY_IS_FOLDER (folder));
1299         
1300         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1301         
1302         /* Check folder rules */
1303         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1304         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1305                 /* Set status failed and set an error */
1306                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1307                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1308                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1309                              _("mail_in_ui_folder_delete_error"));
1310                 goto end;
1311         }
1312
1313         /* Get the account */
1314         account = modest_tny_folder_get_account (folder);
1315         priv->account = g_object_ref(account);
1316
1317         /* Delete folder or move to trash */
1318         if (remove_to_trash) {
1319                 TnyFolder *trash_folder = NULL;
1320                 trash_folder = modest_tny_account_get_special_folder (account,
1321                                                                       TNY_FOLDER_TYPE_TRASH);
1322                 /* TODO: error_handling */
1323                  modest_mail_operation_xfer_folder (self, folder,
1324                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1325         } else {
1326                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1327
1328                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1329                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1330
1331                 if (parent)
1332                         g_object_unref (G_OBJECT (parent));
1333         }
1334         g_object_unref (G_OBJECT (account));
1335
1336  end:
1337         /* Notify about operation end */
1338         modest_mail_operation_notify_end (self, FALSE);
1339 }
1340
1341 static void
1342 transfer_folder_status_cb (GObject *obj,
1343                            TnyStatus *status,
1344                            gpointer user_data)
1345 {
1346         ModestMailOperation *self;
1347         ModestMailOperationPrivate *priv;
1348         ModestMailOperationState *state;
1349
1350         g_return_if_fail (status != NULL);
1351         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1352
1353         self = MODEST_MAIL_OPERATION (user_data);
1354         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1355
1356         if ((status->position == 1) && (status->of_total == 100))
1357                 return;
1358
1359         priv->done = status->position;
1360         priv->total = status->of_total;
1361
1362         state = modest_mail_operation_clone_state (self);
1363         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1364         g_slice_free (ModestMailOperationState, state);
1365 }
1366
1367
1368 static void
1369 transfer_folder_cb (TnyFolder *folder, 
1370                     TnyFolderStore *into, 
1371                     gboolean cancelled, 
1372                     TnyFolder *new_folder, 
1373                     GError **err, 
1374                     gpointer user_data)
1375 {
1376         ModestMailOperation *self = NULL;
1377         ModestMailOperationPrivate *priv = NULL;
1378
1379         self = MODEST_MAIL_OPERATION (user_data);
1380
1381         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1382
1383         if (*err) {
1384                 priv->error = g_error_copy (*err);
1385                 priv->done = 0;
1386                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1387         } else if (cancelled) {
1388                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1389                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1390                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1391                              _("Transference of %s was cancelled."),
1392                              tny_folder_get_name (folder));
1393         } else {
1394                 priv->done = 1;
1395                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1396         }
1397                 
1398         /* Free */
1399         g_object_unref (folder);
1400         g_object_unref (into);
1401
1402         /* Notify about operation end */
1403         modest_mail_operation_notify_end (self, TRUE);
1404 }
1405
1406 void
1407 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1408                                    TnyFolder *folder,
1409                                    TnyFolderStore *parent,
1410                                    gboolean delete_original)
1411 {
1412         ModestMailOperationPrivate *priv = NULL;
1413         ModestTnyFolderRules parent_rules, rules;
1414
1415         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1416         g_return_if_fail (TNY_IS_FOLDER (folder));
1417         g_return_if_fail (TNY_IS_FOLDER (parent));
1418
1419         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1420
1421         /* Get account and set it into mail_operation */
1422         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1423         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1424
1425         /* Get folder rules */
1426         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1427         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1428
1429         if (!TNY_IS_FOLDER_STORE (parent)) {
1430                 
1431         }
1432         
1433         /* The moveable restriction is applied also to copy operation */
1434         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1435                 /* Set status failed and set an error */
1436                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1437                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1438                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1439                              _("mail_in_ui_folder_move_target_error"));
1440
1441                 /* Notify the queue */
1442                 modest_mail_operation_notify_end (self, FALSE);
1443         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1444                 /* Set status failed and set an error */
1445                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1446                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1447                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1448                              _("FIXME: parent folder does not accept new folders"));
1449
1450                 /* Notify the queue */
1451                 modest_mail_operation_notify_end (self, FALSE);
1452         } else {
1453                 /* Pick references for async calls */
1454                 g_object_ref (folder);
1455                 g_object_ref (parent);
1456
1457                 /* Move/Copy folder */          
1458                 tny_folder_copy_async (folder,
1459                                        parent,
1460                                        tny_folder_get_name (folder),
1461                                        delete_original,
1462                                        transfer_folder_cb,
1463                                        transfer_folder_status_cb,
1464                                        self);
1465         }
1466 }
1467
1468 void
1469 modest_mail_operation_rename_folder (ModestMailOperation *self,
1470                                      TnyFolder *folder,
1471                                      const gchar *name)
1472 {
1473         ModestMailOperationPrivate *priv;
1474         ModestTnyFolderRules rules;
1475
1476         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1477         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1478         g_return_if_fail (name);
1479         
1480         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1481
1482         /* Get account and set it into mail_operation */
1483         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1484
1485         /* Check folder rules */
1486         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1487         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1488                 /* Set status failed and set an error */
1489                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1490                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1491                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1492                              _("FIXME: unable to rename"));
1493
1494                 /* Notify about operation end */
1495                 modest_mail_operation_notify_end (self, FALSE);
1496         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1497                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1498                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1499                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1500                              _("FIXME: unable to rename"));
1501                 /* Notify about operation end */
1502                 modest_mail_operation_notify_end (self, FALSE);
1503         } else {
1504                 TnyFolderStore *into;
1505
1506                 /* Rename. Camel handles folder subscription/unsubscription */
1507                 into = tny_folder_get_folder_store (folder);
1508                 tny_folder_copy_async (folder, into, name, TRUE,
1509                                  transfer_folder_cb,
1510                                  transfer_folder_status_cb,
1511                                  self);
1512                 if (into)
1513                         g_object_unref (into);          
1514         }
1515  }
1516
1517 /* ******************************************************************* */
1518 /* **************************  MSG  ACTIONS  ************************* */
1519 /* ******************************************************************* */
1520
1521 void modest_mail_operation_get_msg (ModestMailOperation *self,
1522                                     TnyHeader *header,
1523                                     GetMsgAsyncUserCallback user_callback,
1524                                     gpointer user_data)
1525 {
1526         GetMsgAsyncHelper *helper = NULL;
1527         TnyFolder *folder;
1528         ModestMailOperationPrivate *priv;
1529         
1530         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1531         g_return_if_fail (TNY_IS_HEADER (header));
1532         
1533         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1534         folder = tny_header_get_folder (header);
1535
1536         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1537
1538         /* Get message from folder */
1539         if (folder) {
1540                 /* Get account and set it into mail_operation */
1541                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1542
1543                 helper = g_slice_new0 (GetMsgAsyncHelper);
1544                 helper->mail_op = self;
1545                 helper->user_callback = user_callback;
1546                 helper->user_data = user_data;
1547                 helper->header = g_object_ref (header);
1548
1549                 // The callback's reference so that the mail op is not
1550                 // finalized until the async operation is completed even if
1551                 // the user canceled the request meanwhile.
1552                 g_object_ref (G_OBJECT (helper->mail_op));
1553
1554                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1555
1556                 g_object_unref (G_OBJECT (folder));
1557         } else {
1558                 /* Set status failed and set an error */
1559                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1560                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1561                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1562                              _("Error trying to get a message. No folder found for header"));
1563
1564                 /* Notify the queue */
1565                 modest_mail_operation_notify_end (self, FALSE);
1566         }
1567 }
1568
1569 static void
1570 get_msg_cb (TnyFolder *folder, 
1571             gboolean cancelled, 
1572             TnyMsg *msg, 
1573             GError **error, 
1574             gpointer user_data)
1575 {
1576         GetMsgAsyncHelper *helper = NULL;
1577         ModestMailOperation *self = NULL;
1578         ModestMailOperationPrivate *priv = NULL;
1579
1580         helper = (GetMsgAsyncHelper *) user_data;
1581         g_return_if_fail (helper != NULL);       
1582         self = helper->mail_op;
1583         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1584         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1585
1586         /* Check errors and cancel */
1587         if (*error) {
1588                 priv->error = g_error_copy (*error);
1589                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1590                 goto out;
1591         }
1592         if (cancelled) {
1593                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1594                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1595                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1596                              _("Error trying to refresh the contents of %s"),
1597                              tny_folder_get_name (folder));
1598                 goto out;
1599         }
1600
1601         /* The mail operation might have been canceled in which case we do not
1602            want to notify anyone anymore. */
1603         if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1604                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1605
1606                 /* If user defined callback function was defined, call it */
1607                 if (helper->user_callback) {
1608                         /* This callback is called into an iddle by tinymail,
1609                            and idles are not in the main lock */
1610                         gdk_threads_enter ();
1611                         helper->user_callback (self, helper->header, msg, helper->user_data);
1612                         gdk_threads_leave ();
1613                 }
1614         }
1615
1616  out:
1617         /* Free */
1618         g_object_unref (helper->header);
1619         g_slice_free (GetMsgAsyncHelper, helper);
1620                 
1621         /* Notify about operation end */
1622         if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED)
1623                 modest_mail_operation_notify_end (self, TRUE);
1624
1625         g_object_unref (G_OBJECT (self));
1626 }
1627
1628 static void     
1629 get_msg_status_cb (GObject *obj,
1630                    TnyStatus *status,  
1631                    gpointer user_data)
1632 {
1633         GetMsgAsyncHelper *helper = NULL;
1634         ModestMailOperation *self;
1635         ModestMailOperationPrivate *priv;
1636         ModestMailOperationState *state;
1637
1638         g_return_if_fail (status != NULL);
1639         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1640
1641         helper = (GetMsgAsyncHelper *) user_data;
1642         g_return_if_fail (helper != NULL);       
1643
1644         self = helper->mail_op;
1645         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1646
1647         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1648                 return;
1649
1650         if ((status->position == 1) && (status->of_total == 100))
1651                 return;
1652
1653         priv->done = 1;
1654         priv->total = 1;
1655
1656         state = modest_mail_operation_clone_state (self);
1657         state->bytes_done = status->position;
1658         state->bytes_total = status->of_total;
1659         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1660         g_slice_free (ModestMailOperationState, state);
1661 }
1662
1663 /****************************************************/
1664 typedef struct {
1665         ModestMailOperation *mail_op;
1666         TnyList *headers;
1667         GetMsgAsyncUserCallback user_callback;
1668         gpointer user_data;
1669         GDestroyNotify notify;
1670 } GetFullMsgsInfo;
1671
1672 typedef struct {
1673         GetMsgAsyncUserCallback user_callback;
1674         TnyHeader *header;
1675         TnyMsg *msg;
1676         gpointer user_data;
1677         ModestMailOperation *mail_op;
1678 } NotifyGetMsgsInfo;
1679
1680
1681 /* 
1682  * Used by get_msgs_full_thread to call the user_callback for each
1683  * message that has been read
1684  */
1685 static gboolean
1686 notify_get_msgs_full (gpointer data)
1687 {
1688         NotifyGetMsgsInfo *info;
1689
1690         info = (NotifyGetMsgsInfo *) data;      
1691
1692         /* Call the user callback. Idles are not in the main lock, so
1693            lock it */
1694         gdk_threads_enter ();
1695         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1696         gdk_threads_leave ();
1697
1698         g_slice_free (NotifyGetMsgsInfo, info);
1699
1700         return FALSE;
1701 }
1702
1703 /* 
1704  * Used by get_msgs_full_thread to free al the thread resources and to
1705  * call the destroy function for the passed user_data
1706  */
1707 static gboolean
1708 get_msgs_full_destroyer (gpointer data)
1709 {
1710         GetFullMsgsInfo *info;
1711
1712         info = (GetFullMsgsInfo *) data;
1713
1714         if (info->notify) {
1715                 gdk_threads_enter ();   
1716                 info->notify (info->user_data);
1717                 gdk_threads_leave ();
1718         }
1719
1720         /* free */
1721         g_object_unref (info->headers);
1722         g_slice_free (GetFullMsgsInfo, info);
1723
1724         return FALSE;
1725 }
1726
1727 static gpointer
1728 get_msgs_full_thread (gpointer thr_user_data)
1729 {
1730         GetFullMsgsInfo *info;
1731         ModestMailOperationPrivate *priv = NULL;
1732         TnyIterator *iter = NULL;
1733         
1734         info = (GetFullMsgsInfo *) thr_user_data;       
1735         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1736
1737         iter = tny_list_create_iterator (info->headers);
1738         while (!tny_iterator_is_done (iter)) { 
1739                 TnyHeader *header;
1740                 TnyFolder *folder;
1741                 
1742                 header = TNY_HEADER (tny_iterator_get_current (iter));
1743                 folder = tny_header_get_folder (header);
1744                                 
1745                 /* Get message from folder */
1746                 if (folder) {
1747                         TnyMsg *msg;
1748                         /* The callback will call it per each header */
1749                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1750
1751                         if (msg) {
1752                                 ModestMailOperationState *state;
1753                                 ModestPair *pair;
1754
1755                                 priv->done++;
1756
1757                                 /* notify progress */
1758                                 state = modest_mail_operation_clone_state (info->mail_op);
1759                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1760                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1761                                                  pair, (GDestroyNotify) modest_pair_free);
1762
1763                                 /* The callback is the responsible for
1764                                    freeing the message */
1765                                 if (info->user_callback) {
1766                                         NotifyGetMsgsInfo *info_notify;
1767                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1768                                         info_notify->user_callback = info->user_callback;
1769                                         info_notify->mail_op = info->mail_op;
1770                                         info_notify->header = g_object_ref (header);
1771                                         info_notify->msg = g_object_ref (msg);
1772                                         info_notify->user_data = info->user_data;
1773                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1774                                                          notify_get_msgs_full, 
1775                                                          info_notify, NULL);
1776                                 }
1777                                 g_object_unref (msg);
1778                         }
1779                 } else {
1780                         /* Set status failed and set an error */
1781                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1782                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1783                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1784                                      "Error trying to get a message. No folder found for header");
1785                 }
1786                 g_object_unref (header);                
1787                 tny_iterator_next (iter);
1788         }
1789
1790         /* Set operation status */
1791         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1792                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1793
1794         /* Notify about operation end */
1795         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1796
1797         /* Free thread resources. Will be called after all previous idles */
1798         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1799
1800         return NULL;
1801 }
1802
1803 void 
1804 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1805                                      TnyList *header_list, 
1806                                      GetMsgAsyncUserCallback user_callback,
1807                                      gpointer user_data,
1808                                      GDestroyNotify notify)
1809 {
1810         TnyHeader *header = NULL;
1811         TnyFolder *folder = NULL;
1812         GThread *thread;
1813         ModestMailOperationPrivate *priv = NULL;
1814         GetFullMsgsInfo *info = NULL;
1815         gboolean size_ok = TRUE;
1816         gint max_size;
1817         TnyIterator *iter = NULL;
1818         
1819         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1820         
1821         /* Init mail operation */
1822         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1823         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1824         priv->done = 0;
1825         priv->total = tny_list_get_length(header_list);
1826
1827         /* Get account and set it into mail_operation */
1828         if (tny_list_get_length (header_list) >= 1) {
1829                 iter = tny_list_create_iterator (header_list);
1830                 header = TNY_HEADER (tny_iterator_get_current (iter));
1831                 folder = tny_header_get_folder (header);                
1832                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1833                 g_object_unref (header);
1834                 g_object_unref (folder);
1835
1836                 if (tny_list_get_length (header_list) == 1) {
1837                         g_object_unref (iter);
1838                         iter = NULL;
1839                 }
1840         }
1841
1842         /* Get msg size limit */
1843         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1844                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1845                                          &(priv->error));
1846         if (priv->error) {
1847                 g_clear_error (&(priv->error));
1848                 max_size = G_MAXINT;
1849         } else {
1850                 max_size = max_size * KB;
1851         }
1852
1853         /* Check message size limits. If there is only one message
1854            always retrieve it */
1855         if (iter != NULL) {
1856                 while (!tny_iterator_is_done (iter) && size_ok) {
1857                         header = TNY_HEADER (tny_iterator_get_current (iter));
1858                         if (tny_header_get_message_size (header) >= max_size)
1859                                 size_ok = FALSE;
1860                         g_object_unref (header);
1861                         tny_iterator_next (iter);
1862                 }
1863                 g_object_unref (iter);
1864         }
1865
1866         if (size_ok) {
1867                 /* Create the info */
1868                 info = g_slice_new0 (GetFullMsgsInfo);
1869                 info->mail_op = self;
1870                 info->user_callback = user_callback;
1871                 info->user_data = user_data;
1872                 info->headers = g_object_ref (header_list);
1873                 info->notify = notify;
1874
1875                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1876         } else {
1877                 /* Set status failed and set an error */
1878                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1879                 /* FIXME: the error msg is different for pop */
1880                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1881                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1882                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1883                 /* Remove from queue and free resources */
1884                 modest_mail_operation_notify_end (self, FALSE);
1885                 if (notify)
1886                         notify (user_data);
1887         }
1888 }
1889
1890
1891 void 
1892 modest_mail_operation_remove_msg (ModestMailOperation *self,
1893                                   TnyHeader *header,
1894                                   gboolean remove_to_trash)
1895 {
1896         TnyFolder *folder;
1897         ModestMailOperationPrivate *priv;
1898
1899         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1900         g_return_if_fail (TNY_IS_HEADER (header));
1901
1902         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1903         folder = tny_header_get_folder (header);
1904
1905         /* Get account and set it into mail_operation */
1906         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1907
1908         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1909
1910         /* Delete or move to trash */
1911         if (remove_to_trash) {
1912                 TnyFolder *trash_folder;
1913                 TnyStoreAccount *store_account;
1914
1915                 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1916                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1917                                                                       TNY_FOLDER_TYPE_TRASH);
1918                 if (trash_folder) {
1919                         TnyList *headers;
1920
1921                         /* Create list */
1922                         headers = tny_simple_list_new ();
1923                         tny_list_append (headers, G_OBJECT (header));
1924                         g_object_unref (header);
1925
1926                         /* Move to trash */
1927                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1928                         g_object_unref (headers);
1929 /*                      g_object_unref (trash_folder); */
1930                 } else {
1931                         ModestMailOperationPrivate *priv;
1932
1933                         /* Set status failed and set an error */
1934                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1935                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1936                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1937                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1938                                      _("Error trying to delete a message. Trash folder not found"));
1939                 }
1940
1941                 g_object_unref (G_OBJECT (store_account));
1942         } else {
1943                 tny_folder_remove_msg (folder, header, &(priv->error));
1944                 if (!priv->error) {
1945                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1946                         tny_folder_sync(folder, TRUE, &(priv->error));
1947                 }
1948         }
1949
1950         /* Set status */
1951         if (!priv->error)
1952                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1953         else
1954                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1955
1956         /* Free */
1957         g_object_unref (G_OBJECT (folder));
1958
1959         /* Notify about operation end */
1960         modest_mail_operation_notify_end (self, FALSE);
1961 }
1962
1963 static void
1964 transfer_msgs_status_cb (GObject *obj,
1965                          TnyStatus *status,  
1966                          gpointer user_data)
1967 {
1968         XFerMsgAsyncHelper *helper = NULL;
1969         ModestMailOperation *self;
1970         ModestMailOperationPrivate *priv;
1971         ModestMailOperationState *state;
1972
1973
1974         g_return_if_fail (status != NULL);
1975         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1976
1977         helper = (XFerMsgAsyncHelper *) user_data;
1978         g_return_if_fail (helper != NULL);       
1979
1980         self = helper->mail_op;
1981         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1982
1983         if ((status->position == 1) && (status->of_total == 100))
1984                 return;
1985
1986         priv->done = status->position;
1987         priv->total = status->of_total;
1988
1989         state = modest_mail_operation_clone_state (self);
1990         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1991         g_slice_free (ModestMailOperationState, state);
1992 }
1993
1994
1995 static void
1996 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1997 {
1998         XFerMsgAsyncHelper *helper;
1999         ModestMailOperation *self;
2000         ModestMailOperationPrivate *priv;
2001
2002         helper = (XFerMsgAsyncHelper *) user_data;
2003         self = helper->mail_op;
2004
2005         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2006
2007         if (*err) {
2008                 priv->error = g_error_copy (*err);
2009                 priv->done = 0;
2010                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2011         } else if (cancelled) {
2012                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2013                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2014                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2015                              _("Error trying to refresh the contents of %s"),
2016                              tny_folder_get_name (folder));
2017         } else {
2018                 priv->done = 1;
2019                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2020         }
2021
2022         /* If user defined callback function was defined, call it */
2023         if (helper->user_callback) {
2024                 gdk_threads_enter ();
2025                 helper->user_callback (priv->source, helper->user_data);
2026                 gdk_threads_leave ();
2027         }
2028
2029         /* Free */
2030         g_object_unref (helper->headers);
2031         g_object_unref (helper->dest_folder);
2032         g_object_unref (helper->mail_op);
2033         g_slice_free   (XFerMsgAsyncHelper, helper);
2034         g_object_unref (folder);
2035
2036         /* Notify about operation end */
2037         modest_mail_operation_notify_end (self, TRUE);
2038 }
2039
2040 void
2041 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2042                                  TnyList *headers, 
2043                                  TnyFolder *folder, 
2044                                  gboolean delete_original,
2045                                  XferMsgsAsynUserCallback user_callback,
2046                                  gpointer user_data)
2047 {
2048         ModestMailOperationPrivate *priv;
2049         TnyIterator *iter;
2050         TnyFolder *src_folder;
2051         XFerMsgAsyncHelper *helper;
2052         TnyHeader *header;
2053         ModestTnyFolderRules rules;
2054
2055         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2056         g_return_if_fail (TNY_IS_LIST (headers));
2057         g_return_if_fail (TNY_IS_FOLDER (folder));
2058
2059         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2060         priv->total = 1;
2061         priv->done = 0;
2062         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2063
2064         /* Apply folder rules */
2065         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2066         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2067                 /* Set status failed and set an error */
2068                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2069                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2070                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2071                              _("FIXME: folder does not accept msgs"));
2072                 /* Notify the queue */
2073                 modest_mail_operation_notify_end (self, FALSE);
2074                 return;
2075         }
2076
2077         /* Create the helper */
2078         helper = g_slice_new0 (XFerMsgAsyncHelper);
2079         helper->mail_op = g_object_ref(self);
2080         helper->dest_folder = g_object_ref(folder);
2081         helper->headers = g_object_ref(headers);
2082         helper->user_callback = user_callback;
2083         helper->user_data = user_data;
2084
2085         /* Get source folder */
2086         iter = tny_list_create_iterator (headers);
2087         header = TNY_HEADER (tny_iterator_get_current (iter));
2088         src_folder = tny_header_get_folder (header);
2089         g_object_unref (header);
2090         g_object_unref (iter);
2091
2092         /* Get account and set it into mail_operation */
2093         priv->account = modest_tny_folder_get_account (src_folder);
2094
2095         /* Transfer messages */
2096         tny_folder_transfer_msgs_async (src_folder, 
2097                                         headers, 
2098                                         folder, 
2099                                         delete_original, 
2100                                         transfer_msgs_cb, 
2101                                         transfer_msgs_status_cb,
2102                                         helper);
2103 }
2104
2105
2106 static void
2107 on_refresh_folder (TnyFolder   *folder, 
2108                    gboolean     cancelled, 
2109                    GError     **error,
2110                    gpointer     user_data)
2111 {
2112         RefreshAsyncHelper *helper = NULL;
2113         ModestMailOperation *self = NULL;
2114         ModestMailOperationPrivate *priv = NULL;
2115
2116         helper = (RefreshAsyncHelper *) user_data;
2117         self = helper->mail_op;
2118         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2119
2120         if (*error) {
2121                 priv->error = g_error_copy (*error);
2122                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2123                 goto out;
2124         }
2125
2126         if (cancelled) {
2127                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2128                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2129                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2130                              _("Error trying to refresh the contents of %s"),
2131                              tny_folder_get_name (folder));
2132                 goto out;
2133         }
2134
2135         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2136
2137  out:
2138         /* Call user defined callback, if it exists */
2139         if (helper->user_callback) {
2140                 gdk_threads_enter ();
2141                 helper->user_callback (priv->source, folder, helper->user_data);
2142                 gdk_threads_leave ();
2143         }
2144
2145         /* Free */
2146         g_object_unref (helper->mail_op);
2147         g_slice_free   (RefreshAsyncHelper, helper);
2148         g_object_unref (folder);
2149
2150         /* Notify about operation end */
2151         modest_mail_operation_notify_end (self, TRUE);
2152 }
2153
2154 static void
2155 on_refresh_folder_status_update (GObject *obj,
2156                                  TnyStatus *status,
2157                                  gpointer user_data)
2158 {
2159         RefreshAsyncHelper *helper = NULL;
2160         ModestMailOperation *self = NULL;
2161         ModestMailOperationPrivate *priv = NULL;
2162         ModestMailOperationState *state;
2163
2164         g_return_if_fail (user_data != NULL);
2165         g_return_if_fail (status != NULL);
2166         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2167
2168         helper = (RefreshAsyncHelper *) user_data;
2169         self = helper->mail_op;
2170         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2171
2172         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2173
2174         priv->done = status->position;
2175         priv->total = status->of_total;
2176
2177         state = modest_mail_operation_clone_state (self);
2178         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2179         g_slice_free (ModestMailOperationState, state);
2180 }
2181
2182 void 
2183 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2184                                        TnyFolder *folder,
2185                                        RefreshAsyncUserCallback user_callback,
2186                                        gpointer user_data)
2187 {
2188         ModestMailOperationPrivate *priv = NULL;
2189         RefreshAsyncHelper *helper = NULL;
2190
2191         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2192
2193         /* Pick a reference */
2194         g_object_ref (folder);
2195
2196         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2197
2198         /* Get account and set it into mail_operation */
2199         priv->account = modest_tny_folder_get_account  (folder);
2200
2201         /* Create the helper */
2202         helper = g_slice_new0 (RefreshAsyncHelper);
2203         helper->mail_op = g_object_ref(self);
2204         helper->user_callback = user_callback;
2205         helper->user_data = user_data;
2206
2207         /* Refresh the folder. TODO: tinymail could issue a status
2208            updates before the callback call then this could happen. We
2209            must review the design */
2210         tny_folder_refresh_async (folder,
2211                                   on_refresh_folder,
2212                                   on_refresh_folder_status_update,
2213                                   helper);
2214 }
2215
2216 /**
2217  *
2218  * It's used by the mail operation queue to notify the observers
2219  * attached to that signal that the operation finished. We need to use
2220  * that because tinymail does not give us the progress of a given
2221  * operation when it finishes (it directly calls the operation
2222  * callback).
2223  */
2224 static void
2225 modest_mail_operation_notify_end (ModestMailOperation *self,
2226                                   gboolean need_lock)
2227 {
2228         ModestMailOperationState *state;
2229         ModestMailOperationPrivate *priv = NULL;
2230
2231         g_return_if_fail (self);
2232
2233         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2234
2235         if (!priv) {
2236                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2237                 return;
2238         }
2239         
2240         /* Set the account back to not busy */
2241         if (priv->account_name) {
2242                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2243                                                      priv->account_name, FALSE);
2244                 g_free(priv->account_name);
2245                 priv->account_name = NULL;
2246         }
2247         
2248         /* Notify the observers about the mail opertation end */
2249         state = modest_mail_operation_clone_state (self);
2250         if (need_lock)
2251                 gdk_threads_enter ();
2252         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2253         if (need_lock)
2254                 gdk_threads_leave ();
2255         g_slice_free (ModestMailOperationState, state);
2256 }