* Fix bug on create_account wizard.
[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 "modest-mail-operation.h"
31 /* include other impl specific header files */
32 #include <string.h>
33 #include <stdarg.h>
34 #include <tny-mime-part.h>
35 #include <tny-store-account.h>
36 #include <tny-folder-store.h>
37 #include <tny-folder-store-query.h>
38 #include <tny-camel-stream.h>
39 #include <tny-simple-list.h>
40 #include <tny-send-queue.h>
41 #include <tny-status.h>
42 #include <tny-folder-observer.h>
43 #include <camel/camel-stream-mem.h>
44 #include <glib/gi18n.h>
45 #include "modest-platform.h"
46 #include <modest-tny-account.h>
47 #include <modest-tny-send-queue.h>
48 #include <modest-runtime.h>
49 #include "modest-text-utils.h"
50 #include "modest-tny-msg.h"
51 #include "modest-tny-folder.h"
52 #include "modest-tny-platform-factory.h"
53 #include "modest-marshal.h"
54 #include "modest-error.h"
55
56 #define KB 1024
57
58 /* 'private'/'protected' functions */
59 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
60 static void modest_mail_operation_init       (ModestMailOperation *obj);
61 static void modest_mail_operation_finalize   (GObject *obj);
62
63 static void     get_msg_cb (TnyFolder *folder, 
64                             gboolean cancelled, 
65                             TnyMsg *msg, 
66                             GError **err, 
67                             gpointer user_data);
68
69 static void     get_msg_status_cb (GObject *obj,
70                                    TnyStatus *status,  
71                                    gpointer user_data);
72
73 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
74
75 enum _ModestMailOperationSignals 
76 {
77         PROGRESS_CHANGED_SIGNAL,
78
79         NUM_SIGNALS
80 };
81
82 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
83 struct _ModestMailOperationPrivate {
84         guint                      id;
85         guint                      done;
86         guint                      total;
87         GObject                   *source;
88         GError                    *error;
89         ErrorCheckingUserCallback  error_checking;
90         ModestMailOperationStatus  status;      
91         ModestMailOperationTypeOperation op_type;
92 };
93
94 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
95                                                    MODEST_TYPE_MAIL_OPERATION, \
96                                                    ModestMailOperationPrivate))
97
98 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
99                                                    priv->status = new_status;\
100                                                }
101
102 typedef struct _GetMsgAsyncHelper {     
103         ModestMailOperation *mail_op;
104         GetMsgAsyncUserCallback user_callback;  
105         guint pending_ops;
106         gpointer user_data;
107 } GetMsgAsyncHelper;
108
109 typedef struct _XFerMsgAsyncHelper
110 {
111         ModestMailOperation *mail_op;
112         TnyList *headers;
113         TnyFolder *dest_folder;
114         XferMsgsAsynUserCallback user_callback; 
115         gpointer user_data;
116 } XFerMsgAsyncHelper;
117
118 /* globals */
119 static GObjectClass *parent_class = NULL;
120
121 static guint signals[NUM_SIGNALS] = {0};
122
123 GType
124 modest_mail_operation_get_type (void)
125 {
126         static GType my_type = 0;
127         if (!my_type) {
128                 static const GTypeInfo my_info = {
129                         sizeof(ModestMailOperationClass),
130                         NULL,           /* base init */
131                         NULL,           /* base finalize */
132                         (GClassInitFunc) modest_mail_operation_class_init,
133                         NULL,           /* class finalize */
134                         NULL,           /* class data */
135                         sizeof(ModestMailOperation),
136                         1,              /* n_preallocs */
137                         (GInstanceInitFunc) modest_mail_operation_init,
138                         NULL
139                 };
140                 my_type = g_type_register_static (G_TYPE_OBJECT,
141                                                   "ModestMailOperation",
142                                                   &my_info, 0);
143         }
144         return my_type;
145 }
146
147 static void
148 modest_mail_operation_class_init (ModestMailOperationClass *klass)
149 {
150         GObjectClass *gobject_class;
151         gobject_class = (GObjectClass*) klass;
152
153         parent_class            = g_type_class_peek_parent (klass);
154         gobject_class->finalize = modest_mail_operation_finalize;
155
156         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
157
158         /**
159          * ModestMailOperation::progress-changed
160          * @self: the #MailOperation that emits the signal
161          * @user_data: user data set when the signal handler was connected
162          *
163          * Emitted when the progress of a mail operation changes
164          */
165         signals[PROGRESS_CHANGED_SIGNAL] = 
166                 g_signal_new ("progress-changed",
167                               G_TYPE_FROM_CLASS (gobject_class),
168                               G_SIGNAL_RUN_FIRST,
169                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
170                               NULL, NULL,
171                               g_cclosure_marshal_VOID__POINTER,
172                               G_TYPE_NONE, 1, G_TYPE_POINTER);
173 }
174
175 static void
176 modest_mail_operation_init (ModestMailOperation *obj)
177 {
178         ModestMailOperationPrivate *priv;
179
180         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
181
182         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
183         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
184         priv->error          = NULL;
185         priv->error_checking = NULL;
186         priv->id             = 0;
187         priv->done           = 0;
188         priv->total          = 0;
189         priv->source         = NULL;
190 }
191
192 static void
193 modest_mail_operation_finalize (GObject *obj)
194 {
195         ModestMailOperationPrivate *priv;
196
197         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
198
199         if (priv->error) {
200                 g_error_free (priv->error);
201                 priv->error = NULL;
202         }
203         if (priv->source) {
204                 g_object_unref (priv->source);
205                 priv->source = NULL;
206         }
207
208         G_OBJECT_CLASS(parent_class)->finalize (obj);
209 }
210
211 ModestMailOperation*
212 modest_mail_operation_new (ModestMailOperationTypeOperation op_type, 
213                            GObject *source)
214 {
215         ModestMailOperation *obj;
216         ModestMailOperationPrivate *priv;
217                 
218         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
219         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
220
221         priv->op_type = op_type;
222         if (source != NULL)
223                 priv->source = g_object_ref(source);
224
225         return obj;
226 }
227
228 ModestMailOperation*
229 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
230                                                GObject *source,
231                                                ErrorCheckingUserCallback error_handler)
232 {
233         ModestMailOperation *obj;
234         ModestMailOperationPrivate *priv;
235                 
236         obj = modest_mail_operation_new (op_type, source);
237         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
238         
239         g_return_val_if_fail (error_handler != NULL, obj);
240         priv->error_checking = error_handler;
241
242         return obj;
243 }
244
245 void
246 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
247 {
248         ModestMailOperationPrivate *priv;
249         
250         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
251         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
252
253         if (priv->error_checking == NULL) 
254                 return; 
255         priv->error_checking (priv->source, self);
256 }
257
258
259 ModestMailOperationTypeOperation
260 modest_mail_operation_get_type_operation (ModestMailOperation *self)
261 {
262         ModestMailOperationPrivate *priv;
263
264         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
265         
266         return priv->op_type;
267 }
268
269 gboolean 
270 modest_mail_operation_is_mine (ModestMailOperation *self, 
271                                GObject *me)
272 {
273         ModestMailOperationPrivate *priv;
274
275         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
276         if (priv->source == NULL) return FALSE;
277
278         return priv->source == me;
279 }
280
281 GObject *
282 modest_mail_operation_get_source (ModestMailOperation *self)
283 {
284         ModestMailOperationPrivate *priv;
285
286         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
287
288         return g_object_ref (priv->source);
289 }
290
291 ModestMailOperationStatus
292 modest_mail_operation_get_status (ModestMailOperation *self)
293 {
294         ModestMailOperationPrivate *priv;
295
296         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
297         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
298                               MODEST_MAIL_OPERATION_STATUS_INVALID);
299
300         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
301         return priv->status;
302 }
303
304 const GError *
305 modest_mail_operation_get_error (ModestMailOperation *self)
306 {
307         ModestMailOperationPrivate *priv;
308
309         g_return_val_if_fail (self, NULL);
310         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
311
312         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
313         return priv->error;
314 }
315
316 gboolean 
317 modest_mail_operation_cancel (ModestMailOperation *self)
318 {
319         ModestMailOperationPrivate *priv;
320
321         if (!MODEST_IS_MAIL_OPERATION (self)) {
322                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
323                 return FALSE;
324         }
325
326         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
327
328         /* TODO: Tinymail does not support cancel operation  */
329 /*      tny_account_cancel (); */
330
331         /* Set new status */
332         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
333
334         /* Notify about operation end */
335         modest_mail_operation_notify_end (self);
336
337         return TRUE;
338 }
339
340 guint 
341 modest_mail_operation_get_task_done (ModestMailOperation *self)
342 {
343         ModestMailOperationPrivate *priv;
344
345         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
346
347         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
348         return priv->done;
349 }
350
351 guint 
352 modest_mail_operation_get_task_total (ModestMailOperation *self)
353 {
354         ModestMailOperationPrivate *priv;
355
356         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
357
358         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
359         return priv->total;
360 }
361
362 gboolean
363 modest_mail_operation_is_finished (ModestMailOperation *self)
364 {
365         ModestMailOperationPrivate *priv;
366         gboolean retval = FALSE;
367
368         if (!MODEST_IS_MAIL_OPERATION (self)) {
369                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
370                 return retval;
371         }
372
373         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
374
375         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
376             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
377             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
378             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
379                 retval = TRUE;
380         } else {
381                 retval = FALSE;
382         }
383
384         return retval;
385 }
386
387 guint
388 modest_mail_operation_get_id (ModestMailOperation *self)
389 {
390         ModestMailOperationPrivate *priv;
391
392         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
393
394         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
395         return priv->done;
396 }
397
398 guint 
399 modest_mail_operation_set_id (ModestMailOperation *self,
400                               guint id)
401 {
402         ModestMailOperationPrivate *priv;
403
404         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
405
406         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
407         return priv->done;
408 }
409
410 /*
411  * Creates an image of the current state of a mail operation, the
412  * caller must free it
413  */
414 static ModestMailOperationState *
415 modest_mail_operation_clone_state (ModestMailOperation *self)
416 {
417         ModestMailOperationState *state;
418         ModestMailOperationPrivate *priv;
419
420         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
421
422         state = g_slice_new (ModestMailOperationState);
423
424         state->status = priv->status;
425         state->op_type = priv->op_type;
426         state->done = priv->done;
427         state->total = priv->total;
428         state->finished = modest_mail_operation_is_finished (self);
429
430         return state;
431 }
432
433 /* ******************************************************************* */
434 /* ************************** SEND   ACTIONS ************************* */
435 /* ******************************************************************* */
436
437 void
438 modest_mail_operation_send_mail (ModestMailOperation *self,
439                                  TnyTransportAccount *transport_account,
440                                  TnyMsg* msg)
441 {
442         TnySendQueue *send_queue;
443         
444         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
445         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
446         g_return_if_fail (TNY_IS_MSG (msg));
447         
448         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
449         if (!TNY_IS_SEND_QUEUE(send_queue))
450                 g_printerr ("modest: could not find send queue for account\n");
451         else {
452                 GError *err = NULL;
453                 tny_send_queue_add (send_queue, msg, &err);
454                 if (err) {
455                         g_printerr ("modest: error adding msg to send queue: %s\n",
456                                     err->message);
457                         g_error_free (err);
458                 } else {
459                         /* g_message ("modest: message added to send queue"); */
460                 }
461         }
462
463         /* Notify about operation end */
464         modest_mail_operation_notify_end (self);
465 }
466
467 void
468 modest_mail_operation_send_new_mail (ModestMailOperation *self,
469                                      TnyTransportAccount *transport_account,
470                                      const gchar *from,  const gchar *to,
471                                      const gchar *cc,  const gchar *bcc,
472                                      const gchar *subject, const gchar *plain_body,
473                                      const gchar *html_body,
474                                      const GList *attachments_list,
475                                      TnyHeaderFlags priority_flags)
476 {
477         TnyMsg *new_msg;
478         ModestMailOperationPrivate *priv = NULL;
479         /* GList *node = NULL; */
480
481         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
482         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
483
484         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
485
486         /* Check parametters */
487         if (to == NULL) {
488                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
489                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
490                              _("Error trying to send a mail. You need to set at least one recipient"));
491                 return;
492         }
493
494         if (html_body == NULL) {
495                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
496         } else {
497                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
498         }
499         if (!new_msg) {
500                 g_printerr ("modest: failed to create a new msg\n");
501                 return;
502         }
503
504         /* TODO: add priority handling. It's received in the priority_flags operator, and
505            it should have effect in the sending operation */
506
507         /* Call mail operation */
508         modest_mail_operation_send_mail (self, transport_account, new_msg);
509
510         /* Free */
511         g_object_unref (G_OBJECT (new_msg));
512 }
513
514 void
515 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
516                                       TnyTransportAccount *transport_account,
517                                       const gchar *from,  const gchar *to,
518                                       const gchar *cc,  const gchar *bcc,
519                                       const gchar *subject, const gchar *plain_body,
520                                       const gchar *html_body,
521                                       const GList *attachments_list,
522                                       TnyHeaderFlags priority_flags)
523 {
524         TnyMsg *msg = NULL;
525         TnyFolder *folder = NULL;
526         ModestMailOperationPrivate *priv = NULL;
527         GError *err = NULL;
528
529         /* GList *node = NULL; */
530
531         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
532         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
533
534         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
535
536         if (html_body == NULL) {
537                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
538         } else {
539                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
540         }
541         if (!msg) {
542                 g_printerr ("modest: failed to create a new msg\n");
543                 goto cleanup;
544         }
545
546         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
547         if (!folder) {
548                 g_printerr ("modest: failed to find Drafts folder\n");
549                 goto cleanup;
550         }
551         
552         tny_folder_add_msg (folder, msg, &err);
553         if (err) {
554                 g_printerr ("modest: error adding msg to Drafts folder: %s",
555                             err->message);
556                 g_error_free (err);
557                 goto cleanup;
558         }
559
560         modest_mail_operation_notify_end (self);
561
562         /* Free */
563 cleanup:
564         if (msg)
565                 g_object_unref (G_OBJECT(msg));
566         if (folder)
567                 g_object_unref (G_OBJECT(folder));
568 }
569
570 typedef struct 
571 {
572         ModestMailOperation *mail_op;
573         TnyStoreAccount *account;
574         TnyTransportAccount *transport_account;
575         gint max_size;
576         gint retrieve_limit;
577         gchar *retrieve_type;
578 } UpdateAccountInfo;
579
580 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
581 /* We use this folder observer to track the headers that have been
582  * added to a folder */
583 typedef struct {
584         GObject parent;
585         TnyList *new_headers;
586 } InternalFolderObserver;
587
588 typedef struct {
589         GObjectClass parent;
590 } InternalFolderObserverClass;
591
592 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
593
594 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
595                          internal_folder_observer,
596                          G_TYPE_OBJECT,
597                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
598
599
600 static void
601 foreach_add_item (gpointer header, gpointer user_data)
602 {
603         tny_list_prepend (TNY_LIST (user_data), 
604                           g_object_ref (G_OBJECT (header)));
605 }
606
607 /* This is the method that looks for new messages in a folder */
608 static void
609 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
610 {
611         TnyFolderChangeChanged changed;
612
613         changed = tny_folder_change_get_changed (change);
614
615         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
616                 TnyList *list;
617
618                 /* Get added headers */
619                 list = tny_simple_list_new ();
620                 tny_folder_change_get_added_headers (change, list);
621
622                 /* Add them to the folder observer */
623                 tny_list_foreach (list, foreach_add_item, 
624                                   ((InternalFolderObserver *)self)->new_headers);
625
626                 g_object_unref (G_OBJECT (list));
627         }
628 }
629
630 static void
631 internal_folder_observer_init (InternalFolderObserver *self) 
632 {
633         self->new_headers = tny_simple_list_new ();
634 }
635 static void
636 internal_folder_observer_finalize (GObject *object) 
637 {
638         InternalFolderObserver *self;
639
640         self = (InternalFolderObserver *) object;
641         g_object_unref (self->new_headers);
642
643         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
644 }
645 static void
646 tny_folder_observer_init (TnyFolderObserverIface *iface) 
647 {
648         iface->update_func = internal_folder_observer_update;
649 }
650 static void
651 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
652 {
653         GObjectClass *object_class;
654
655         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
656         object_class = (GObjectClass*) klass;
657         object_class->finalize = internal_folder_observer_finalize;
658 }
659
660 /*****************/
661
662 static void
663 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
664 {
665         TnyIterator *iter;
666         TnyList *folders = tny_simple_list_new ();
667
668         tny_folder_store_get_folders (store, folders, query, NULL);
669         iter = tny_list_create_iterator (folders);
670
671         while (!tny_iterator_is_done (iter)) {
672
673                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
674
675                 tny_list_prepend (all_folders, G_OBJECT (folder));
676                 recurse_folders (folder, query, all_folders);    
677                 g_object_unref (G_OBJECT (folder));
678
679                 tny_iterator_next (iter);
680         }
681          g_object_unref (G_OBJECT (iter));
682          g_object_unref (G_OBJECT (folders));
683 }
684
685 /* 
686  * Issues the "progress-changed" signal. The timer won't be removed,
687  * so you must call g_source_remove to stop the signal emission
688  */
689 static gboolean
690 idle_notify_progress (gpointer data)
691 {
692         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
693         ModestMailOperationState *state;
694
695         state = modest_mail_operation_clone_state (mail_op);
696         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
697         g_slice_free (ModestMailOperationState, state);
698
699         return TRUE;
700 }
701
702 /* 
703  * Issues the "progress-changed" signal and removes the timer. It uses
704  * a lock to ensure that the progress information of the mail
705  * operation is not modified while there are notifications pending
706  */
707 static gboolean
708 idle_notify_progress_once (gpointer data)
709 {
710         ModestPair *pair;
711
712         pair = (ModestPair *) data;
713
714         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
715
716         /* Free the state and the reference to the mail operation */
717         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
718         g_object_unref (pair->first);
719
720         return FALSE;
721 }
722
723 /* 
724  * Used by update_account_thread to notify the queue from the main
725  * loop. We call it inside an idle call to achieve that
726  */
727 static gboolean
728 notify_update_account_queue (gpointer data)
729 {
730         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
731
732         modest_mail_operation_notify_end (mail_op);
733         g_object_unref (mail_op);
734
735         return FALSE;
736 }
737
738 static int
739 compare_headers_by_date (gconstpointer a, 
740                          gconstpointer b)
741 {
742         TnyHeader **header1, **header2;
743         time_t sent1, sent2;
744
745         header1 = (TnyHeader **) a;
746         header2 = (TnyHeader **) b;
747
748         sent1 = tny_header_get_date_sent (*header1);
749         sent2 = tny_header_get_date_sent (*header2);
750
751         /* We want the most recent ones (greater time_t) at the
752            beginning */
753         if (sent1 < sent2)
754                 return 1;
755         else
756                 return -1;
757 }
758
759 static gpointer
760 update_account_thread (gpointer thr_user_data)
761 {
762         UpdateAccountInfo *info;
763         TnyList *all_folders = NULL;
764         GPtrArray *new_headers;
765         TnyIterator *iter = NULL;
766         TnyFolderStoreQuery *query = NULL;
767         ModestMailOperationPrivate *priv;
768
769         info = (UpdateAccountInfo *) thr_user_data;
770         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
771
772         /* Get all the folders We can do it synchronously because
773            we're already running in a different thread than the UI */
774         all_folders = tny_simple_list_new ();
775         query = tny_folder_store_query_new ();
776         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
777         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
778                                       all_folders,
779                                       query,
780                                       &(priv->error));
781         if (priv->error) {
782                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
783                 goto out;
784         }
785
786         iter = tny_list_create_iterator (all_folders);
787         while (!tny_iterator_is_done (iter)) {
788                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
789
790                 recurse_folders (folder, query, all_folders);
791                 tny_iterator_next (iter);
792         }
793         g_object_unref (G_OBJECT (iter));
794
795         /* Update status and notify. We need to call the notification
796            with a source functopm in order to call it from the main
797            loop. We need that in order not to get into trouble with
798            Gtk+. We use a timeout in order to provide more status
799            information, because the sync tinymail call does not
800            provide it for the moment */
801         gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
802
803         /* Refresh folders */
804         new_headers = g_ptr_array_new ();
805         iter = tny_list_create_iterator (all_folders);
806         while (!tny_iterator_is_done (iter) && !priv->error) {
807
808                 InternalFolderObserver *observer;
809                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
810
811                 /* Refresh the folder */
812                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
813                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
814                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
815
816                 /* If the retrieve type is headers only do nothing more */
817                 if (!strcmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
818                     !strcmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
819                         TnyIterator *iter;
820
821                         iter = tny_list_create_iterator (observer->new_headers);
822                         while (!tny_iterator_is_done (iter)) {
823                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
824                                 /* Apply per-message size limits */
825                                 if (tny_header_get_message_size (header) < info->max_size)
826                                         g_ptr_array_add (new_headers, g_object_ref (header));
827
828                                 g_object_unref (header);
829                                 tny_iterator_next (iter);
830                         }
831                         g_object_unref (iter);
832                 }
833                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
834                 g_object_unref (observer);
835
836                 if (priv->error)
837                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
838
839                 g_object_unref (G_OBJECT (folder));
840                 tny_iterator_next (iter);
841         }
842         g_object_unref (G_OBJECT (iter));
843         g_source_remove (timeout);
844
845         if (new_headers->len > 0) {
846                 gint msg_num = 0;
847
848                 /* Order by date */
849                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
850
851                 /* Apply message count limit */
852                 /* TODO if the number of messages exceeds the maximum, ask the
853                    user to download them all */
854                 priv->done = 0;
855                 priv->total = MIN (new_headers->len, info->retrieve_limit);
856                 while ((msg_num < info->retrieve_limit)) {
857
858                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
859                         TnyFolder *folder = tny_header_get_folder (header);
860                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
861                         ModestMailOperationState *state;
862                         ModestPair* pair;
863
864                         priv->done++;
865                         /* We can not just use the mail operation because the
866                            values of done and total could change before the
867                            idle is called */
868                         state = modest_mail_operation_clone_state (info->mail_op);
869                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
870                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
871                                          pair, (GDestroyNotify) modest_pair_free);
872
873                         g_object_unref (msg);
874                         g_object_unref (folder);
875
876                         msg_num++;
877                 }
878                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
879                 g_ptr_array_free (new_headers, FALSE);
880         }
881
882         /* Perform send */
883         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
884         priv->done = 0;
885         priv->total = 0;
886
887         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue
888                 (info->transport_account);
889
890         timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
891         /* TODO: Is this meant to block? */
892         modest_tny_send_queue_try_to_send (send_queue);
893         g_source_remove (timeout);
894
895         g_object_unref (G_OBJECT(send_queue));
896         
897         /* Check if the operation was a success */
898         if (!priv->error) {
899                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
900
901                 /* Update the last updated key */
902                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
903                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
904                                             MODEST_ACCOUNT_LAST_UPDATED, 
905                                             time(NULL), 
906                                             TRUE);
907         }
908
909  out:
910         /* Notify about operation end. Note that the info could be
911            freed before this idle happens, but the mail operation will
912            be still alive */
913         g_idle_add (notify_update_account_queue, info->mail_op);
914
915         /* Frees */
916         g_object_unref (query);
917         g_object_unref (all_folders);
918         g_object_unref (info->account);
919         g_object_unref (info->transport_account);
920         g_free (info->retrieve_type);
921         g_slice_free (UpdateAccountInfo, info);
922
923         return NULL;
924 }
925
926 gboolean
927 modest_mail_operation_update_account (ModestMailOperation *self,
928                                       const gchar *account_name)
929 {
930         GThread *thread;
931         UpdateAccountInfo *info;
932         ModestMailOperationPrivate *priv;
933         ModestAccountMgr *mgr;
934         TnyStoreAccount *modest_account;
935         TnyTransportAccount *transport_account;
936
937         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
938         g_return_val_if_fail (account_name, FALSE);
939
940         /* Init mail operation. Set total and done to 0, and do not
941            update them, this way the progress objects will know that
942            we have no clue about the number of the objects */
943         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
944         priv->total = 0;
945         priv->done  = 0;
946         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
947         
948         /* Get the Modest account */
949         modest_account = (TnyStoreAccount *)
950                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
951                                                                      account_name,
952                                                                      TNY_ACCOUNT_TYPE_STORE);
953
954         if (!modest_account) {
955                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
956                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
957                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
958                              "cannot get tny store account for %s\n", account_name);
959                 modest_mail_operation_notify_end (self);
960                 return FALSE;
961         }
962
963         /* Get the transport account, we can not do it in the thread
964            due to some problems with dbus */
965         transport_account = (TnyTransportAccount *)
966                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
967                                                                                     account_name);
968         if (!transport_account) {
969                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
970                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
971                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
972                              "cannot get tny transport account for %s\n", account_name);
973                 modest_mail_operation_notify_end (self);
974                 return FALSE;
975         }
976
977         /* Create the helper object */
978         info = g_slice_new (UpdateAccountInfo);
979         info->mail_op = self;
980         info->account = modest_account;
981         info->transport_account = transport_account;
982
983         /* Get the message size limit */
984         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
985                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
986         if (info->max_size == 0)
987                 info->max_size = G_MAXINT;
988         else
989                 info->max_size = info->max_size * KB;
990
991         /* Get per-account retrieval type */
992         mgr = modest_runtime_get_account_mgr ();
993         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
994                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
995
996         /* Get per-account message amount retrieval limit */
997         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
998                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
999         if (info->retrieve_limit == 0)
1000                 info->retrieve_limit = G_MAXINT;
1001
1002         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1003
1004         return TRUE;
1005 }
1006
1007 /* ******************************************************************* */
1008 /* ************************** STORE  ACTIONS ************************* */
1009 /* ******************************************************************* */
1010
1011
1012 TnyFolder *
1013 modest_mail_operation_create_folder (ModestMailOperation *self,
1014                                      TnyFolderStore *parent,
1015                                      const gchar *name)
1016 {
1017         ModestTnyFolderRules rules;
1018         ModestMailOperationPrivate *priv;
1019         TnyFolder *new_folder = NULL;
1020         gboolean can_create = FALSE;
1021
1022         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1023         g_return_val_if_fail (name, NULL);
1024         
1025         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1026
1027         /* Check parent */
1028         if (!TNY_IS_FOLDER (parent)) {
1029                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1030                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1031                              _("mail_in_ui_folder_create_error"));
1032         } else {
1033                 /* Check folder rules */
1034                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1035                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)
1036                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1037                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1038                                      _("mail_in_ui_folder_create_error"));
1039                 else
1040                         can_create = TRUE;              
1041         }
1042
1043         if (can_create) {
1044                 /* Create the folder */
1045                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1046                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1047         }
1048
1049         /* Notify about operation end */
1050         modest_mail_operation_notify_end (self);
1051
1052         return new_folder;
1053 }
1054
1055 void
1056 modest_mail_operation_remove_folder (ModestMailOperation *self,
1057                                      TnyFolder           *folder,
1058                                      gboolean             remove_to_trash)
1059 {
1060         TnyAccount *account;
1061         ModestMailOperationPrivate *priv;
1062         ModestTnyFolderRules rules;
1063
1064         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1065         g_return_if_fail (TNY_IS_FOLDER (folder));
1066         
1067         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1068         
1069         /* Check folder rules */
1070         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1071         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1072                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1073                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1074                              _("mail_in_ui_folder_delete_error"));
1075                 goto end;
1076         }
1077
1078         /* Get the account */
1079         account = tny_folder_get_account (folder);
1080
1081         /* Delete folder or move to trash */
1082         if (remove_to_trash) {
1083                 TnyFolder *trash_folder = NULL;
1084                 trash_folder = modest_tny_account_get_special_folder (account,
1085                                                                       TNY_FOLDER_TYPE_TRASH);
1086                 /* TODO: error_handling */
1087                  modest_mail_operation_xfer_folder (self, folder,
1088                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1089         } else {
1090                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1091
1092                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1093                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1094
1095                 if (parent)
1096                         g_object_unref (G_OBJECT (parent));
1097         }
1098         g_object_unref (G_OBJECT (account));
1099
1100  end:
1101         /* Notify about operation end */
1102         modest_mail_operation_notify_end (self);
1103 }
1104
1105 void
1106 modest_mail_operation_rename_folder (ModestMailOperation *self,
1107                                      TnyFolder *folder,
1108                                      const gchar *name)
1109 {
1110         ModestMailOperationPrivate *priv;
1111         ModestTnyFolderRules rules;
1112
1113         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1114         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1115         g_return_if_fail (name);
1116         
1117         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1118
1119         /* Check folder rules */
1120         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1121         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1122                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1123                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1124                              _("FIXME: unable to rename"));
1125         } else {
1126                 /* Rename. Camel handles folder subscription/unsubscription */
1127                 TnyFolderStore *into;
1128                 TnyFolder *nfol;
1129
1130                 into = tny_folder_get_folder_store (folder);
1131                 nfol = tny_folder_copy (folder, into, name, TRUE, &(priv->error));
1132                 if (into)
1133                         g_object_unref (into);
1134                 if (nfol)
1135                         g_object_unref (nfol);
1136
1137                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1138                 
1139         }
1140
1141         /* Notify about operation end */
1142         modest_mail_operation_notify_end (self);
1143  }
1144
1145 static void
1146 transfer_folder_status_cb (GObject *obj,
1147                            TnyStatus *status,
1148                            gpointer user_data)
1149 {
1150         ModestMailOperation *self;
1151         ModestMailOperationPrivate *priv;
1152         ModestMailOperationState *state;
1153
1154         g_return_if_fail (status != NULL);
1155         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1156
1157         self = MODEST_MAIL_OPERATION (user_data);
1158         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1159
1160         if ((status->position == 1) && (status->of_total == 100))
1161                 return;
1162
1163         priv->done = status->position;
1164         priv->total = status->of_total;
1165
1166         state = modest_mail_operation_clone_state (self);
1167         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1168         g_slice_free (ModestMailOperationState, state);
1169 }
1170
1171
1172 static void
1173 transfer_folder_cb (TnyFolder *folder, 
1174                     TnyFolderStore *into, 
1175                     gboolean cancelled, 
1176                     TnyFolder *new_folder, GError **err, 
1177                     gpointer user_data)
1178 {
1179         ModestMailOperation *self = NULL;
1180         ModestMailOperationPrivate *priv = NULL;
1181
1182         self = MODEST_MAIL_OPERATION (user_data);
1183
1184         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1185
1186         if (*err) {
1187                 priv->error = g_error_copy (*err);
1188                 priv->done = 0;
1189                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1190         } else if (cancelled) {
1191                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1192                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1193                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1194                              _("Transference of %s was cancelled."),
1195                              tny_folder_get_name (folder));
1196         } else {
1197                 priv->done = 1;
1198                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1199         }
1200                 
1201         /* Free */
1202         g_object_unref (folder);
1203         g_object_unref (into);
1204         if (new_folder != NULL)
1205                 g_object_unref (new_folder);
1206
1207         /* Notify about operation end */
1208         modest_mail_operation_notify_end (self);
1209 }
1210
1211 void
1212 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1213                                    TnyFolder *folder,
1214                                    TnyFolderStore *parent,
1215                                    gboolean delete_original)
1216 {
1217         ModestMailOperationPrivate *priv = NULL;
1218         ModestTnyFolderRules parent_rules, rules;
1219
1220         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1221         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1222         g_return_if_fail (TNY_IS_FOLDER (folder));
1223
1224         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1225
1226         /* Get folder rules */
1227         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1228         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1229
1230         /* The moveable restriction is applied also to copy operation */
1231         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
1232                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1233                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1234                              _("mail_in_ui_folder_move_target_error"));
1235
1236                 /* Notify the queue */
1237                 modest_mail_operation_notify_end (self);
1238         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1239                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1240                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1241                              _("FIXME: parent folder does not accept new folders"));
1242
1243                 /* Notify the queue */
1244                 modest_mail_operation_notify_end (self);
1245         } else {
1246                 /* Pick references for async calls */
1247                 g_object_ref (folder);
1248                 g_object_ref (parent);
1249
1250                 /* Move/Copy folder */          
1251                 tny_folder_copy_async (folder,
1252                                        parent,
1253                                        tny_folder_get_name (folder),
1254                                        delete_original,
1255                                        transfer_folder_cb,
1256                                        transfer_folder_status_cb,
1257                                        self);
1258         }
1259 }
1260
1261
1262 /* ******************************************************************* */
1263 /* **************************  MSG  ACTIONS  ************************* */
1264 /* ******************************************************************* */
1265
1266 void modest_mail_operation_get_msg (ModestMailOperation *self,
1267                                     TnyHeader *header,
1268                                     GetMsgAsyncUserCallback user_callback,
1269                                     gpointer user_data)
1270 {
1271         GetMsgAsyncHelper *helper = NULL;
1272         TnyFolder *folder;
1273         ModestMailOperationPrivate *priv;
1274         
1275         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1276         g_return_if_fail (TNY_IS_HEADER (header));
1277         
1278         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1279         folder = tny_header_get_folder (header);
1280
1281         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1282
1283         /* Get message from folder */
1284         if (folder) {
1285                 helper = g_slice_new0 (GetMsgAsyncHelper);
1286                 helper->mail_op = self;
1287                 helper->user_callback = user_callback;
1288                 helper->pending_ops = 1;
1289                 helper->user_data = user_data;
1290
1291                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1292
1293                 g_object_unref (G_OBJECT (folder));
1294         } else {
1295                 /* Set status failed and set an error */
1296                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1297                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1298                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1299                              _("Error trying to get a message. No folder found for header"));
1300         }
1301 }
1302
1303 static void
1304 get_msg_cb (TnyFolder *folder, 
1305             gboolean cancelled, 
1306             TnyMsg *msg, 
1307             GError **error, 
1308             gpointer user_data)
1309 {
1310         GetMsgAsyncHelper *helper = NULL;
1311         ModestMailOperation *self = NULL;
1312         ModestMailOperationPrivate *priv = NULL;
1313
1314         helper = (GetMsgAsyncHelper *) user_data;
1315         g_return_if_fail (helper != NULL);       
1316         self = helper->mail_op;
1317         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1318         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1319         
1320         helper->pending_ops--;
1321
1322         /* Check errors and cancel */
1323         if (*error) {
1324                 priv->error = g_error_copy (*error);
1325                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1326                 goto out;
1327         }
1328         if (cancelled) {
1329                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1330                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1331                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1332                              _("Error trying to refresh the contents of %s"),
1333                              tny_folder_get_name (folder));
1334                 goto out;
1335         }
1336
1337         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1338
1339         /* If user defined callback function was defined, call it */
1340         if (helper->user_callback) {
1341                 helper->user_callback (self, NULL, msg, helper->user_data);
1342         }
1343
1344         /* Free */
1345  out:
1346         if (helper->pending_ops == 0) {
1347                 g_slice_free (GetMsgAsyncHelper, helper);
1348                 
1349                 /* Notify about operation end */
1350                 modest_mail_operation_notify_end (self);        
1351         }
1352 }
1353
1354 static void     
1355 get_msg_status_cb (GObject *obj,
1356                    TnyStatus *status,  
1357                    gpointer user_data)
1358 {
1359         GetMsgAsyncHelper *helper = NULL;
1360         ModestMailOperation *self;
1361         ModestMailOperationPrivate *priv;
1362         ModestMailOperationState *state;
1363
1364         g_return_if_fail (status != NULL);
1365         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1366
1367         helper = (GetMsgAsyncHelper *) user_data;
1368         g_return_if_fail (helper != NULL);       
1369
1370         self = helper->mail_op;
1371         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1372
1373         if ((status->position == 1) && (status->of_total == 100))
1374                 return;
1375
1376         priv->done = 1;
1377         priv->total = 1;
1378
1379         state = modest_mail_operation_clone_state (self);
1380         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1381         g_slice_free (ModestMailOperationState, state);
1382 }
1383
1384 /****************************************************/
1385 typedef struct {
1386         ModestMailOperation *mail_op;
1387         TnyList *headers;
1388         GetMsgAsyncUserCallback user_callback;
1389         gpointer user_data;
1390         GDestroyNotify notify;
1391 } GetFullMsgsInfo;
1392
1393 typedef struct {
1394         GetMsgAsyncUserCallback user_callback;
1395         TnyHeader *header;
1396         TnyMsg *msg;
1397         gpointer user_data;
1398         ModestMailOperation *mail_op;
1399 } NotifyGetMsgsInfo;
1400
1401
1402 /* 
1403  * Used by get_msgs_full_thread to call the user_callback for each
1404  * message that has been read
1405  */
1406 static gboolean
1407 notify_get_msgs_full (gpointer data)
1408 {
1409         NotifyGetMsgsInfo *info;
1410
1411         info = (NotifyGetMsgsInfo *) data;      
1412
1413         /* Call the user callback */
1414         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1415
1416         g_slice_free (NotifyGetMsgsInfo, info);
1417
1418         return FALSE;
1419 }
1420
1421 /* 
1422  * Used by get_msgs_full_thread to free al the thread resources and to
1423  * call the destroy function for the passed user_data
1424  */
1425 static gboolean
1426 get_msgs_full_destroyer (gpointer data)
1427 {
1428         GetFullMsgsInfo *info;
1429
1430         info = (GetFullMsgsInfo *) data;
1431
1432         if (info->notify)
1433                 info->notify (info->user_data);
1434
1435         /* free */
1436         g_object_unref (info->headers);
1437         g_slice_free (GetFullMsgsInfo, info);
1438
1439         return FALSE;
1440 }
1441
1442 static gpointer
1443 get_msgs_full_thread (gpointer thr_user_data)
1444 {
1445         GetFullMsgsInfo *info;
1446         ModestMailOperationPrivate *priv = NULL;
1447         TnyIterator *iter = NULL;
1448         
1449         info = (GetFullMsgsInfo *) thr_user_data;       
1450         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1451
1452         iter = tny_list_create_iterator (info->headers);
1453         while (!tny_iterator_is_done (iter)) { 
1454                 TnyHeader *header;
1455                 TnyFolder *folder;
1456                 
1457                 header = TNY_HEADER (tny_iterator_get_current (iter));
1458                 folder = tny_header_get_folder (header);
1459                                 
1460                 /* Get message from folder */
1461                 if (folder) {
1462                         TnyMsg *msg;
1463                         /* The callback will call it per each header */
1464                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1465
1466                         if (msg) {
1467                                 ModestMailOperationState *state;
1468                                 ModestPair *pair;
1469
1470                                 priv->done++;
1471
1472                                 /* notify progress */
1473                                 state = modest_mail_operation_clone_state (info->mail_op);
1474                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1475                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1476                                                  pair, (GDestroyNotify) modest_pair_free);
1477
1478                                 /* The callback is the responsible for
1479                                    freeing the message */
1480                                 if (info->user_callback) {
1481                                         NotifyGetMsgsInfo *info_notify;
1482                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1483                                         info_notify->user_callback = info->user_callback;
1484                                         info_notify->mail_op = info->mail_op;
1485                                         info_notify->header = g_object_ref (header);
1486                                         info_notify->msg = g_object_ref (msg);
1487                                         info_notify->user_data = info->user_data;
1488                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1489                                                          notify_get_msgs_full, 
1490                                                          info_notify, NULL);
1491                                 }
1492                                 g_object_unref (msg);
1493                         }
1494                 } else {
1495                         /* Set status failed and set an error */
1496                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1497                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1498                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1499                                      "Error trying to get a message. No folder found for header");
1500                 }
1501                 g_object_unref (header);                
1502                 tny_iterator_next (iter);
1503         }
1504
1505         /* Notify about operation end */
1506         g_idle_add (notify_update_account_queue, info->mail_op);
1507
1508         /* Free thread resources. Will be called after all previous idles */
1509         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1510
1511         return NULL;
1512 }
1513
1514 void 
1515 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1516                                      TnyList *header_list, 
1517                                      GetMsgAsyncUserCallback user_callback,
1518                                      gpointer user_data,
1519                                      GDestroyNotify notify)
1520 {
1521         GThread *thread;
1522         ModestMailOperationPrivate *priv = NULL;
1523         GetFullMsgsInfo *info = NULL;
1524         gboolean size_ok = TRUE;
1525         gint max_size;
1526         GError *error = NULL;
1527         
1528         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1529         
1530         /* Init mail operation */
1531         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1532         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1533         priv->done = 0;
1534         priv->total = tny_list_get_length(header_list);
1535
1536         /* Get msg size limit */
1537         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1538                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1539                                          &error);
1540         if (error) {
1541                 g_clear_error (&error);
1542                 max_size = G_MAXINT;
1543         } else {
1544                 max_size = max_size * KB;
1545         }
1546
1547         /* Check message size limits. If there is only one message
1548            always retrieve it */
1549         if (tny_list_get_length (header_list) > 1) {
1550                 TnyIterator *iter;
1551
1552                 iter = tny_list_create_iterator (header_list);
1553                 while (!tny_iterator_is_done (iter) && size_ok) {
1554                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1555                         if (tny_header_get_message_size (header) >= max_size)
1556                                 size_ok = FALSE;
1557                         g_object_unref (header);
1558                         tny_iterator_next (iter);
1559                 }
1560                 g_object_unref (iter);
1561         }
1562
1563         if (size_ok) {
1564                 /* Create the info */
1565                 info = g_slice_new0 (GetFullMsgsInfo);
1566                 info->mail_op = self;
1567                 info->user_callback = user_callback;
1568                 info->user_data = user_data;
1569                 info->headers = g_object_ref (header_list);
1570                 info->notify = notify;
1571
1572                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1573         } else {
1574                 /* FIXME: the error msg is different for pop */
1575                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1576                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1577                              _("emev_ni_ui_imap_msg_sizelimit_error"));
1578                 /* Remove from queue and free resources */
1579                 modest_mail_operation_notify_end (self);
1580                 if (notify)
1581                         notify (user_data);
1582         }
1583 }
1584
1585
1586 void 
1587 modest_mail_operation_remove_msg (ModestMailOperation *self,
1588                                   TnyHeader *header,
1589                                   gboolean remove_to_trash)
1590 {
1591         TnyFolder *folder;
1592         ModestMailOperationPrivate *priv;
1593
1594         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1595         g_return_if_fail (TNY_IS_HEADER (header));
1596
1597         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1598         folder = tny_header_get_folder (header);
1599
1600         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1601
1602         /* Delete or move to trash */
1603         if (remove_to_trash) {
1604                 TnyFolder *trash_folder;
1605                 TnyStoreAccount *store_account;
1606
1607                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1608                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1609                                                                       TNY_FOLDER_TYPE_TRASH);
1610                 if (trash_folder) {
1611                         TnyList *headers;
1612
1613                         /* Create list */
1614                         headers = tny_simple_list_new ();
1615                         tny_list_append (headers, G_OBJECT (header));
1616                         g_object_unref (header);
1617
1618                         /* Move to trash */
1619                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1620                         g_object_unref (headers);
1621 /*                      g_object_unref (trash_folder); */
1622                 } else {
1623                         ModestMailOperationPrivate *priv;
1624
1625                         /* Set status failed and set an error */
1626                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1627                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1628                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1629                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1630                                      _("Error trying to delete a message. Trash folder not found"));
1631                 }
1632
1633                 g_object_unref (G_OBJECT (store_account));
1634         } else {
1635                 tny_folder_remove_msg (folder, header, &(priv->error));
1636                 if (!priv->error)
1637                         tny_folder_sync(folder, TRUE, &(priv->error));
1638         }
1639
1640         /* Set status */
1641         if (!priv->error)
1642                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1643         else
1644                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1645
1646         /* Free */
1647         g_object_unref (G_OBJECT (folder));
1648
1649         /* Notify about operation end */
1650         modest_mail_operation_notify_end (self);
1651 }
1652
1653 static void
1654 transfer_msgs_status_cb (GObject *obj,
1655                          TnyStatus *status,  
1656                          gpointer user_data)
1657 {
1658         XFerMsgAsyncHelper *helper = NULL;
1659         ModestMailOperation *self;
1660         ModestMailOperationPrivate *priv;
1661         ModestMailOperationState *state;
1662
1663
1664         g_return_if_fail (status != NULL);
1665         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1666
1667         helper = (XFerMsgAsyncHelper *) user_data;
1668         g_return_if_fail (helper != NULL);       
1669
1670         self = helper->mail_op;
1671         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1672
1673         if ((status->position == 1) && (status->of_total == 100))
1674                 return;
1675
1676         priv->done = status->position;
1677         priv->total = status->of_total;
1678
1679         state = modest_mail_operation_clone_state (self);
1680         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1681         g_slice_free (ModestMailOperationState, state);
1682 }
1683
1684
1685 static void
1686 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1687 {
1688         XFerMsgAsyncHelper *helper;
1689         ModestMailOperation *self;
1690         ModestMailOperationPrivate *priv;
1691
1692         helper = (XFerMsgAsyncHelper *) user_data;
1693         self = helper->mail_op;
1694
1695         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1696
1697         if (*err) {
1698                 priv->error = g_error_copy (*err);
1699                 priv->done = 0;
1700                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1701         } else if (cancelled) {
1702                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1703                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1704                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1705                              _("Error trying to refresh the contents of %s"),
1706                              tny_folder_get_name (folder));
1707         } else {
1708                 priv->done = 1;
1709                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1710         }
1711
1712         /* If user defined callback function was defined, call it */
1713         if (helper->user_callback) {
1714                 helper->user_callback (priv->source, helper->user_data);
1715         }
1716
1717         /* Free */
1718         g_object_unref (helper->headers);
1719         g_object_unref (helper->dest_folder);
1720         g_object_unref (helper->mail_op);
1721         g_slice_free   (XFerMsgAsyncHelper, helper);
1722         g_object_unref (folder);
1723
1724         /* Notify about operation end */
1725         modest_mail_operation_notify_end (self);
1726 }
1727
1728 void
1729 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1730                                  TnyList *headers, 
1731                                  TnyFolder *folder, 
1732                                  gboolean delete_original,
1733                                  XferMsgsAsynUserCallback user_callback,
1734                                  gpointer user_data)
1735 {
1736         ModestMailOperationPrivate *priv;
1737         TnyIterator *iter;
1738         TnyFolder *src_folder;
1739         XFerMsgAsyncHelper *helper;
1740         TnyHeader *header;
1741         ModestTnyFolderRules rules;
1742
1743         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1744         g_return_if_fail (TNY_IS_LIST (headers));
1745         g_return_if_fail (TNY_IS_FOLDER (folder));
1746
1747         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1748         priv->total = 1;
1749         priv->done = 0;
1750         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1751
1752         /* Apply folder rules */
1753         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1754
1755         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1756                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1757                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1758                              _("FIXME: folder does not accept msgs"));
1759                 /* Notify the queue */
1760                 modest_mail_operation_notify_end (self);
1761                 return;
1762         }
1763
1764         /* Create the helper */
1765         helper = g_slice_new0 (XFerMsgAsyncHelper);
1766         helper->mail_op = g_object_ref(self);
1767         helper->dest_folder = g_object_ref(folder);
1768         helper->headers = g_object_ref(headers);
1769         helper->user_callback = user_callback;
1770         helper->user_data = user_data;
1771
1772         /* Get source folder */
1773         iter = tny_list_create_iterator (headers);
1774         header = TNY_HEADER (tny_iterator_get_current (iter));
1775         src_folder = tny_header_get_folder (header);
1776         g_object_unref (header);
1777         g_object_unref (iter);
1778
1779         /* Transfer messages */
1780         tny_folder_transfer_msgs_async (src_folder, 
1781                                         headers, 
1782                                         folder, 
1783                                         delete_original, 
1784                                         transfer_msgs_cb, 
1785                                         transfer_msgs_status_cb,
1786                                         helper);
1787 }
1788
1789
1790 static void
1791 on_refresh_folder (TnyFolder   *folder, 
1792                    gboolean     cancelled, 
1793                    GError     **error,
1794                    gpointer     user_data)
1795 {
1796         ModestMailOperation *self;
1797         ModestMailOperationPrivate *priv;
1798
1799         self = MODEST_MAIL_OPERATION (user_data);
1800         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1801
1802         if (*error) {
1803                 priv->error = g_error_copy (*error);
1804                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1805                 goto out;
1806         }
1807
1808         if (cancelled) {
1809                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1810                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1811                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1812                              _("Error trying to refresh the contents of %s"),
1813                              tny_folder_get_name (folder));
1814                 goto out;
1815         }
1816
1817         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1818
1819  out:
1820         /* Free */
1821         g_object_unref (folder);
1822
1823         /* Notify about operation end */
1824         modest_mail_operation_notify_end (self);
1825 }
1826
1827 static void
1828 on_refresh_folder_status_update (GObject *obj,
1829                                  TnyStatus *status,
1830                                  gpointer user_data)
1831 {
1832         ModestMailOperation *self;
1833         ModestMailOperationPrivate *priv;
1834         ModestMailOperationState *state;
1835
1836         g_return_if_fail (status != NULL);
1837         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1838
1839         self = MODEST_MAIL_OPERATION (user_data);
1840         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1841
1842         priv->done = status->position;
1843         priv->total = status->of_total;
1844
1845         state = modest_mail_operation_clone_state (self);
1846         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1847         g_slice_free (ModestMailOperationState, state);
1848 }
1849
1850 void 
1851 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1852                                        TnyFolder *folder)
1853 {
1854         ModestMailOperationPrivate *priv;
1855
1856         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1857
1858         /* Pick a reference */
1859         g_object_ref (folder);
1860
1861         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1862
1863         /* Refresh the folder. TODO: tinymail could issue a status
1864            updates before the callback call then this could happen. We
1865            must review the design */
1866         tny_folder_refresh_async (folder,
1867                                   on_refresh_folder,
1868                                   on_refresh_folder_status_update,
1869                                   self);
1870 }
1871
1872 /**
1873  *
1874  * It's used by the mail operation queue to notify the observers
1875  * attached to that signal that the operation finished. We need to use
1876  * that because tinymail does not give us the progress of a given
1877  * operation when it finishes (it directly calls the operation
1878  * callback).
1879  */
1880 static void
1881 modest_mail_operation_notify_end (ModestMailOperation *self)
1882 {
1883         ModestMailOperationState *state;
1884
1885         /* Notify the observers about the mail opertation end */
1886         state = modest_mail_operation_clone_state (self);
1887         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1888         g_slice_free (ModestMailOperationState, state);
1889
1890         /* Notify the queue */
1891         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1892 }