12001e1568fdec889c7312c07b8e1915d1d6d5db
[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 < priv->total)) {
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         modest_tny_send_queue_try_to_send (send_queue);
892         g_source_remove (timeout);
893
894         g_object_unref (G_OBJECT(send_queue));
895         
896         /* Check if the operation was a success */
897         if (!priv->error) {
898                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
899
900                 /* Update the last updated key */
901                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
902                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
903                                             MODEST_ACCOUNT_LAST_UPDATED, 
904                                             time(NULL), 
905                                             TRUE);
906         }
907
908  out:
909         /* Notify about operation end. Note that the info could be
910            freed before this idle happens, but the mail operation will
911            be still alive */
912         g_idle_add (notify_update_account_queue, info->mail_op);
913
914         /* Frees */
915         g_object_unref (query);
916         g_object_unref (all_folders);
917         g_object_unref (info->account);
918         g_object_unref (info->transport_account);
919         g_free (info->retrieve_type);
920         g_slice_free (UpdateAccountInfo, info);
921
922         return NULL;
923 }
924
925 gboolean
926 modest_mail_operation_update_account (ModestMailOperation *self,
927                                       const gchar *account_name)
928 {
929         GThread *thread;
930         UpdateAccountInfo *info;
931         ModestMailOperationPrivate *priv;
932         ModestAccountMgr *mgr;
933         TnyStoreAccount *modest_account;
934         TnyTransportAccount *transport_account;
935
936         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
937         g_return_val_if_fail (account_name, FALSE);
938
939         /* Init mail operation. Set total and done to 0, and do not
940            update them, this way the progress objects will know that
941            we have no clue about the number of the objects */
942         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
943         priv->total = 0;
944         priv->done  = 0;
945         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
946         
947         /* Get the Modest account */
948         modest_account = (TnyStoreAccount *)
949                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
950                                                                      account_name,
951                                                                      TNY_ACCOUNT_TYPE_STORE);
952
953         if (!modest_account) {
954                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
955                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
956                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
957                              "cannot get tny store account for %s\n", account_name);
958                 modest_mail_operation_notify_end (self);
959                 return FALSE;
960         }
961
962         /* Get the transport account, we can not do it in the thread
963            due to some problems with dbus */
964         transport_account = (TnyTransportAccount *)
965                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
966                                                                                     account_name);
967         if (!transport_account) {
968                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
969                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
970                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
971                              "cannot get tny transport account for %s\n", account_name);
972                 modest_mail_operation_notify_end (self);
973                 return FALSE;
974         }
975
976         /* Create the helper object */
977         info = g_slice_new (UpdateAccountInfo);
978         info->mail_op = self;
979         info->account = modest_account;
980         info->transport_account = transport_account;
981
982         /* Get the message size limit */
983         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
984                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
985         if (info->max_size == 0)
986                 info->max_size = G_MAXINT;
987         else
988                 info->max_size = info->max_size * KB;
989
990         /* Get per-account retrieval type */
991         mgr = modest_runtime_get_account_mgr ();
992         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
993                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
994
995         /* Get per-account message amount retrieval limit */
996         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
997                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
998         if (info->retrieve_limit == 0)
999                 info->retrieve_limit = G_MAXINT;
1000
1001         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1002
1003         return TRUE;
1004 }
1005
1006 /* ******************************************************************* */
1007 /* ************************** STORE  ACTIONS ************************* */
1008 /* ******************************************************************* */
1009
1010
1011 TnyFolder *
1012 modest_mail_operation_create_folder (ModestMailOperation *self,
1013                                      TnyFolderStore *parent,
1014                                      const gchar *name)
1015 {
1016         ModestTnyFolderRules rules;
1017         ModestMailOperationPrivate *priv;
1018         TnyFolder *new_folder = NULL;
1019         gboolean can_create = FALSE;
1020
1021         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1022         g_return_val_if_fail (name, NULL);
1023         
1024         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1025
1026         /* Check parent */
1027         if (!TNY_IS_FOLDER (parent)) {
1028                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1029                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1030                              _("mail_in_ui_folder_create_error"));
1031         } else {
1032                 /* Check folder rules */
1033                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1034                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)
1035                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1036                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1037                                      _("mail_in_ui_folder_create_error"));
1038                 else
1039                         can_create = TRUE;              
1040         }
1041
1042         if (can_create) {
1043                 /* Create the folder */
1044                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1045                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1046         }
1047
1048         /* Notify about operation end */
1049         modest_mail_operation_notify_end (self);
1050
1051         return new_folder;
1052 }
1053
1054 void
1055 modest_mail_operation_remove_folder (ModestMailOperation *self,
1056                                      TnyFolder           *folder,
1057                                      gboolean             remove_to_trash)
1058 {
1059         TnyAccount *account;
1060         ModestMailOperationPrivate *priv;
1061         ModestTnyFolderRules rules;
1062
1063         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1064         g_return_if_fail (TNY_IS_FOLDER (folder));
1065         
1066         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1067         
1068         /* Check folder rules */
1069         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1070         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1071                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1072                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1073                              _("mail_in_ui_folder_delete_error"));
1074                 goto end;
1075         }
1076
1077         /* Get the account */
1078         account = tny_folder_get_account (folder);
1079
1080         /* Delete folder or move to trash */
1081         if (remove_to_trash) {
1082                 TnyFolder *trash_folder = NULL;
1083                 trash_folder = modest_tny_account_get_special_folder (account,
1084                                                                       TNY_FOLDER_TYPE_TRASH);
1085                 /* TODO: error_handling */
1086                  modest_mail_operation_xfer_folder (self, folder,
1087                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
1088         } else {
1089                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1090
1091                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1092                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1093
1094                 if (parent)
1095                         g_object_unref (G_OBJECT (parent));
1096         }
1097         g_object_unref (G_OBJECT (account));
1098
1099  end:
1100         /* Notify about operation end */
1101         modest_mail_operation_notify_end (self);
1102 }
1103
1104 void
1105 modest_mail_operation_rename_folder (ModestMailOperation *self,
1106                                      TnyFolder *folder,
1107                                      const gchar *name)
1108 {
1109         ModestMailOperationPrivate *priv;
1110         ModestTnyFolderRules rules;
1111
1112         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1113         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1114         g_return_if_fail (name);
1115         
1116         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1117
1118         /* Check folder rules */
1119         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1120         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1121                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1122                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1123                              _("FIXME: unable to rename"));
1124         } else {
1125                 /* Rename. Camel handles folder subscription/unsubscription */
1126                 TnyFolderStore *into;
1127                 TnyFolder *nfol;
1128
1129                 into = tny_folder_get_folder_store (folder);
1130                 nfol = tny_folder_copy (folder, into, name, TRUE, &(priv->error));
1131                 if (into)
1132                         g_object_unref (into);
1133                 if (nfol)
1134                         g_object_unref (nfol);
1135
1136                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1137                 
1138         }
1139
1140         /* Notify about operation end */
1141         modest_mail_operation_notify_end (self);
1142  }
1143
1144 static void
1145 transfer_folder_status_cb (GObject *obj,
1146                            TnyStatus *status,
1147                            gpointer user_data)
1148 {
1149         ModestMailOperation *self;
1150         ModestMailOperationPrivate *priv;
1151         ModestMailOperationState *state;
1152
1153         g_return_if_fail (status != NULL);
1154         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1155
1156         self = MODEST_MAIL_OPERATION (user_data);
1157         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1158
1159         if ((status->position == 1) && (status->of_total == 100))
1160                 return;
1161
1162         priv->done = status->position;
1163         priv->total = status->of_total;
1164
1165         state = modest_mail_operation_clone_state (self);
1166         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1167         g_slice_free (ModestMailOperationState, state);
1168 }
1169
1170
1171 static void
1172 transfer_folder_cb (TnyFolder *folder, 
1173                     TnyFolderStore *into, 
1174                     gboolean cancelled, 
1175                     TnyFolder *new_folder, GError **err, 
1176                     gpointer user_data)
1177 {
1178         ModestMailOperation *self = NULL;
1179         ModestMailOperationPrivate *priv = NULL;
1180
1181         self = MODEST_MAIL_OPERATION (user_data);
1182
1183         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1184
1185         if (*err) {
1186                 priv->error = g_error_copy (*err);
1187                 priv->done = 0;
1188                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1189         } else if (cancelled) {
1190                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1191                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1192                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1193                              _("Transference of %s was cancelled."),
1194                              tny_folder_get_name (folder));
1195         } else {
1196                 priv->done = 1;
1197                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1198         }
1199                 
1200         /* Free */
1201         g_object_unref (folder);
1202         g_object_unref (into);
1203         if (new_folder != NULL)
1204                 g_object_unref (new_folder);
1205
1206         /* Notify about operation end */
1207         modest_mail_operation_notify_end (self);
1208 }
1209
1210 void
1211 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1212                                    TnyFolder *folder,
1213                                    TnyFolderStore *parent,
1214                                    gboolean delete_original)
1215 {
1216         ModestMailOperationPrivate *priv = NULL;
1217         ModestTnyFolderRules parent_rules, rules;
1218
1219         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1220         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1221         g_return_if_fail (TNY_IS_FOLDER (folder));
1222
1223         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1224
1225         /* Get folder rules */
1226         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1227         parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1228
1229         /* The moveable restriction is applied also to copy operation */
1230         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
1231                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1232                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1233                              _("mail_in_ui_folder_move_target_error"));
1234
1235                 /* Notify the queue */
1236                 modest_mail_operation_notify_end (self);
1237         } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1238                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1239                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1240                              _("FIXME: parent folder does not accept new folders"));
1241
1242                 /* Notify the queue */
1243                 modest_mail_operation_notify_end (self);
1244         } else {
1245                 /* Pick references for async calls */
1246                 g_object_ref (folder);
1247                 g_object_ref (parent);
1248
1249                 /* Move/Copy folder */          
1250                 tny_folder_copy_async (folder,
1251                                        parent,
1252                                        tny_folder_get_name (folder),
1253                                        delete_original,
1254                                        transfer_folder_cb,
1255                                        transfer_folder_status_cb,
1256                                        self);
1257         }
1258 }
1259
1260
1261 /* ******************************************************************* */
1262 /* **************************  MSG  ACTIONS  ************************* */
1263 /* ******************************************************************* */
1264
1265 void modest_mail_operation_get_msg (ModestMailOperation *self,
1266                                     TnyHeader *header,
1267                                     GetMsgAsyncUserCallback user_callback,
1268                                     gpointer user_data)
1269 {
1270         GetMsgAsyncHelper *helper = NULL;
1271         TnyFolder *folder;
1272         ModestMailOperationPrivate *priv;
1273         
1274         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1275         g_return_if_fail (TNY_IS_HEADER (header));
1276         
1277         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1278         folder = tny_header_get_folder (header);
1279
1280         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1281
1282         /* Get message from folder */
1283         if (folder) {
1284                 helper = g_slice_new0 (GetMsgAsyncHelper);
1285                 helper->mail_op = self;
1286                 helper->user_callback = user_callback;
1287                 helper->pending_ops = 1;
1288                 helper->user_data = user_data;
1289
1290                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1291
1292                 g_object_unref (G_OBJECT (folder));
1293         } else {
1294                 /* Set status failed and set an error */
1295                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1296                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1297                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1298                              _("Error trying to get a message. No folder found for header"));
1299         }
1300 }
1301
1302 static void
1303 get_msg_cb (TnyFolder *folder, 
1304             gboolean cancelled, 
1305             TnyMsg *msg, 
1306             GError **error, 
1307             gpointer user_data)
1308 {
1309         GetMsgAsyncHelper *helper = NULL;
1310         ModestMailOperation *self = NULL;
1311         ModestMailOperationPrivate *priv = NULL;
1312
1313         helper = (GetMsgAsyncHelper *) user_data;
1314         g_return_if_fail (helper != NULL);       
1315         self = helper->mail_op;
1316         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1317         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1318         
1319         helper->pending_ops--;
1320
1321         /* Check errors and cancel */
1322         if (*error) {
1323                 priv->error = g_error_copy (*error);
1324                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1325                 goto out;
1326         }
1327         if (cancelled) {
1328                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1329                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1330                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1331                              _("Error trying to refresh the contents of %s"),
1332                              tny_folder_get_name (folder));
1333                 goto out;
1334         }
1335
1336         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1337
1338         /* If user defined callback function was defined, call it */
1339         if (helper->user_callback) {
1340                 helper->user_callback (self, NULL, msg, helper->user_data);
1341         }
1342
1343         /* Free */
1344  out:
1345         if (helper->pending_ops == 0) {
1346                 g_slice_free (GetMsgAsyncHelper, helper);
1347                 
1348                 /* Notify about operation end */
1349                 modest_mail_operation_notify_end (self);        
1350         }
1351 }
1352
1353 static void     
1354 get_msg_status_cb (GObject *obj,
1355                    TnyStatus *status,  
1356                    gpointer user_data)
1357 {
1358         GetMsgAsyncHelper *helper = NULL;
1359         ModestMailOperation *self;
1360         ModestMailOperationPrivate *priv;
1361         ModestMailOperationState *state;
1362
1363         g_return_if_fail (status != NULL);
1364         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1365
1366         helper = (GetMsgAsyncHelper *) user_data;
1367         g_return_if_fail (helper != NULL);       
1368
1369         self = helper->mail_op;
1370         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1371
1372         if ((status->position == 1) && (status->of_total == 100))
1373                 return;
1374
1375         priv->done = 1;
1376         priv->total = 1;
1377
1378         state = modest_mail_operation_clone_state (self);
1379         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1380         g_slice_free (ModestMailOperationState, state);
1381 }
1382
1383 /****************************************************/
1384 typedef struct {
1385         ModestMailOperation *mail_op;
1386         TnyList *headers;
1387         GetMsgAsyncUserCallback user_callback;
1388         gpointer user_data;
1389         GDestroyNotify notify;
1390 } GetFullMsgsInfo;
1391
1392 typedef struct {
1393         GetMsgAsyncUserCallback user_callback;
1394         TnyHeader *header;
1395         TnyMsg *msg;
1396         gpointer user_data;
1397         ModestMailOperation *mail_op;
1398 } NotifyGetMsgsInfo;
1399
1400
1401 /* 
1402  * Used by get_msgs_full_thread to call the user_callback for each
1403  * message that has been read
1404  */
1405 static gboolean
1406 notify_get_msgs_full (gpointer data)
1407 {
1408         NotifyGetMsgsInfo *info;
1409
1410         info = (NotifyGetMsgsInfo *) data;      
1411
1412         /* Call the user callback */
1413         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1414
1415         g_slice_free (NotifyGetMsgsInfo, info);
1416
1417         return FALSE;
1418 }
1419
1420 /* 
1421  * Used by get_msgs_full_thread to free al the thread resources and to
1422  * call the destroy function for the passed user_data
1423  */
1424 static gboolean
1425 get_msgs_full_destroyer (gpointer data)
1426 {
1427         GetFullMsgsInfo *info;
1428
1429         info = (GetFullMsgsInfo *) data;
1430
1431         if (info->notify)
1432                 info->notify (info->user_data);
1433
1434         /* free */
1435         g_object_unref (info->headers);
1436         g_slice_free (GetFullMsgsInfo, info);
1437
1438         return FALSE;
1439 }
1440
1441 static gpointer
1442 get_msgs_full_thread (gpointer thr_user_data)
1443 {
1444         GetFullMsgsInfo *info;
1445         ModestMailOperationPrivate *priv = NULL;
1446         TnyIterator *iter = NULL;
1447         
1448         info = (GetFullMsgsInfo *) thr_user_data;       
1449         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1450
1451         iter = tny_list_create_iterator (info->headers);
1452         while (!tny_iterator_is_done (iter)) { 
1453                 TnyHeader *header;
1454                 TnyFolder *folder;
1455                 
1456                 header = TNY_HEADER (tny_iterator_get_current (iter));
1457                 folder = tny_header_get_folder (header);
1458                                 
1459                 /* Get message from folder */
1460                 if (folder) {
1461                         TnyMsg *msg;
1462                         /* The callback will call it per each header */
1463                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1464
1465                         if (msg) {
1466                                 ModestMailOperationState *state;
1467                                 ModestPair *pair;
1468
1469                                 priv->done++;
1470
1471                                 /* notify progress */
1472                                 state = modest_mail_operation_clone_state (info->mail_op);
1473                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1474                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1475                                                  pair, (GDestroyNotify) modest_pair_free);
1476
1477                                 /* The callback is the responsible for
1478                                    freeing the message */
1479                                 if (info->user_callback) {
1480                                         NotifyGetMsgsInfo *info_notify;
1481                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1482                                         info_notify->user_callback = info->user_callback;
1483                                         info_notify->mail_op = info->mail_op;
1484                                         info_notify->header = g_object_ref (header);
1485                                         info_notify->msg = g_object_ref (msg);
1486                                         info_notify->user_data = info->user_data;
1487                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1488                                                          notify_get_msgs_full, 
1489                                                          info_notify, NULL);
1490                                 }
1491                                 g_object_unref (msg);
1492                         }
1493                 } else {
1494                         /* Set status failed and set an error */
1495                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1496                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1497                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1498                                      "Error trying to get a message. No folder found for header");
1499                 }
1500                 g_object_unref (header);                
1501                 tny_iterator_next (iter);
1502         }
1503
1504         /* Notify about operation end */
1505         g_idle_add (notify_update_account_queue, info->mail_op);
1506
1507         /* Free thread resources. Will be called after all previous idles */
1508         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1509
1510         return NULL;
1511 }
1512
1513 void 
1514 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1515                                      TnyList *header_list, 
1516                                      GetMsgAsyncUserCallback user_callback,
1517                                      gpointer user_data,
1518                                      GDestroyNotify notify)
1519 {
1520         GThread *thread;
1521         ModestMailOperationPrivate *priv = NULL;
1522         GetFullMsgsInfo *info = NULL;
1523         gboolean size_ok = TRUE;
1524         gint max_size;
1525         GError *error = NULL;
1526         
1527         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1528         
1529         /* Init mail operation */
1530         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1531         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1532         priv->done = 0;
1533         priv->total = tny_list_get_length(header_list);
1534
1535         /* Get msg size limit */
1536         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1537                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1538                                          &error);
1539         if (error) {
1540                 g_clear_error (&error);
1541                 max_size = G_MAXINT;
1542         } else {
1543                 max_size = max_size * KB;
1544         }
1545
1546         /* Check message size limits. If there is only one message
1547            always retrieve it */
1548         if (tny_list_get_length (header_list) > 1) {
1549                 TnyIterator *iter;
1550
1551                 iter = tny_list_create_iterator (header_list);
1552                 while (!tny_iterator_is_done (iter) && size_ok) {
1553                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1554                         if (tny_header_get_message_size (header) >= max_size)
1555                                 size_ok = FALSE;
1556                         g_object_unref (header);
1557                         tny_iterator_next (iter);
1558                 }
1559                 g_object_unref (iter);
1560         }
1561
1562         if (size_ok) {
1563                 /* Create the info */
1564                 info = g_slice_new0 (GetFullMsgsInfo);
1565                 info->mail_op = self;
1566                 info->user_callback = user_callback;
1567                 info->user_data = user_data;
1568                 info->headers = g_object_ref (header_list);
1569                 info->notify = notify;
1570
1571                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1572         } else {
1573                 /* FIXME: the error msg is different for pop */
1574                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1575                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1576                              _("emev_ni_ui_imap_msg_sizelimit_error"));
1577                 /* Remove from queue and free resources */
1578                 modest_mail_operation_notify_end (self);
1579                 if (notify)
1580                         notify (user_data);
1581         }
1582 }
1583
1584
1585 void 
1586 modest_mail_operation_remove_msg (ModestMailOperation *self,
1587                                   TnyHeader *header,
1588                                   gboolean remove_to_trash)
1589 {
1590         TnyFolder *folder;
1591         ModestMailOperationPrivate *priv;
1592
1593         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1594         g_return_if_fail (TNY_IS_HEADER (header));
1595
1596         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1597         folder = tny_header_get_folder (header);
1598
1599         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1600
1601         /* Delete or move to trash */
1602         if (remove_to_trash) {
1603                 TnyFolder *trash_folder;
1604                 TnyStoreAccount *store_account;
1605
1606                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1607                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1608                                                                       TNY_FOLDER_TYPE_TRASH);
1609                 if (trash_folder) {
1610                         TnyList *headers;
1611
1612                         /* Create list */
1613                         headers = tny_simple_list_new ();
1614                         tny_list_append (headers, G_OBJECT (header));
1615                         g_object_unref (header);
1616
1617                         /* Move to trash */
1618                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1619                         g_object_unref (headers);
1620 /*                      g_object_unref (trash_folder); */
1621                 } else {
1622                         ModestMailOperationPrivate *priv;
1623
1624                         /* Set status failed and set an error */
1625                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1626                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1627                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1628                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1629                                      _("Error trying to delete a message. Trash folder not found"));
1630                 }
1631
1632                 g_object_unref (G_OBJECT (store_account));
1633         } else {
1634                 tny_folder_remove_msg (folder, header, &(priv->error));
1635                 if (!priv->error)
1636                         tny_folder_sync(folder, TRUE, &(priv->error));
1637         }
1638
1639         /* Set status */
1640         if (!priv->error)
1641                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1642         else
1643                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1644
1645         /* Free */
1646         g_object_unref (G_OBJECT (folder));
1647
1648         /* Notify about operation end */
1649         modest_mail_operation_notify_end (self);
1650 }
1651
1652 static void
1653 transfer_msgs_status_cb (GObject *obj,
1654                          TnyStatus *status,  
1655                          gpointer user_data)
1656 {
1657         XFerMsgAsyncHelper *helper = NULL;
1658         ModestMailOperation *self;
1659         ModestMailOperationPrivate *priv;
1660         ModestMailOperationState *state;
1661
1662
1663         g_return_if_fail (status != NULL);
1664         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1665
1666         helper = (XFerMsgAsyncHelper *) user_data;
1667         g_return_if_fail (helper != NULL);       
1668
1669         self = helper->mail_op;
1670         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1671
1672         if ((status->position == 1) && (status->of_total == 100))
1673                 return;
1674
1675         priv->done = status->position;
1676         priv->total = status->of_total;
1677
1678         state = modest_mail_operation_clone_state (self);
1679         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1680         g_slice_free (ModestMailOperationState, state);
1681 }
1682
1683
1684 static void
1685 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1686 {
1687         XFerMsgAsyncHelper *helper;
1688         ModestMailOperation *self;
1689         ModestMailOperationPrivate *priv;
1690
1691         helper = (XFerMsgAsyncHelper *) user_data;
1692         self = helper->mail_op;
1693
1694         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1695
1696         if (*err) {
1697                 priv->error = g_error_copy (*err);
1698                 priv->done = 0;
1699                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1700         } else if (cancelled) {
1701                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1702                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1703                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1704                              _("Error trying to refresh the contents of %s"),
1705                              tny_folder_get_name (folder));
1706         } else {
1707                 priv->done = 1;
1708                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1709         }
1710
1711         /* If user defined callback function was defined, call it */
1712         if (helper->user_callback) {
1713                 helper->user_callback (priv->source, helper->user_data);
1714         }
1715
1716         /* Free */
1717         g_object_unref (helper->headers);
1718         g_object_unref (helper->dest_folder);
1719         g_object_unref (helper->mail_op);
1720         g_slice_free   (XFerMsgAsyncHelper, helper);
1721         g_object_unref (folder);
1722
1723         /* Notify about operation end */
1724         modest_mail_operation_notify_end (self);
1725 }
1726
1727 void
1728 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1729                                  TnyList *headers, 
1730                                  TnyFolder *folder, 
1731                                  gboolean delete_original,
1732                                  XferMsgsAsynUserCallback user_callback,
1733                                  gpointer user_data)
1734 {
1735         ModestMailOperationPrivate *priv;
1736         TnyIterator *iter;
1737         TnyFolder *src_folder;
1738         XFerMsgAsyncHelper *helper;
1739         TnyHeader *header;
1740         ModestTnyFolderRules rules;
1741
1742         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1743         g_return_if_fail (TNY_IS_LIST (headers));
1744         g_return_if_fail (TNY_IS_FOLDER (folder));
1745
1746         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1747         priv->total = 1;
1748         priv->done = 0;
1749         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1750
1751         /* Apply folder rules */
1752         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1753
1754         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1755                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1756                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1757                              _("FIXME: folder does not accept msgs"));
1758                 /* Notify the queue */
1759                 modest_mail_operation_notify_end (self);
1760                 return;
1761         }
1762
1763         /* Create the helper */
1764         helper = g_slice_new0 (XFerMsgAsyncHelper);
1765         helper->mail_op = g_object_ref(self);
1766         helper->dest_folder = g_object_ref(folder);
1767         helper->headers = g_object_ref(headers);
1768         helper->user_callback = user_callback;
1769         helper->user_data = user_data;
1770
1771         /* Get source folder */
1772         iter = tny_list_create_iterator (headers);
1773         header = TNY_HEADER (tny_iterator_get_current (iter));
1774         src_folder = tny_header_get_folder (header);
1775         g_object_unref (header);
1776         g_object_unref (iter);
1777
1778         /* Transfer messages */
1779         tny_folder_transfer_msgs_async (src_folder, 
1780                                         headers, 
1781                                         folder, 
1782                                         delete_original, 
1783                                         transfer_msgs_cb, 
1784                                         transfer_msgs_status_cb,
1785                                         helper);
1786 }
1787
1788
1789 static void
1790 on_refresh_folder (TnyFolder   *folder, 
1791                    gboolean     cancelled, 
1792                    GError     **error,
1793                    gpointer     user_data)
1794 {
1795         ModestMailOperation *self;
1796         ModestMailOperationPrivate *priv;
1797
1798         self = MODEST_MAIL_OPERATION (user_data);
1799         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1800
1801         if (*error) {
1802                 priv->error = g_error_copy (*error);
1803                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1804                 goto out;
1805         }
1806
1807         if (cancelled) {
1808                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1809                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1810                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1811                              _("Error trying to refresh the contents of %s"),
1812                              tny_folder_get_name (folder));
1813                 goto out;
1814         }
1815
1816         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1817
1818  out:
1819         /* Free */
1820         g_object_unref (folder);
1821
1822         /* Notify about operation end */
1823         modest_mail_operation_notify_end (self);
1824 }
1825
1826 static void
1827 on_refresh_folder_status_update (GObject *obj,
1828                                  TnyStatus *status,
1829                                  gpointer user_data)
1830 {
1831         ModestMailOperation *self;
1832         ModestMailOperationPrivate *priv;
1833         ModestMailOperationState *state;
1834
1835         g_return_if_fail (status != NULL);
1836         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1837
1838         self = MODEST_MAIL_OPERATION (user_data);
1839         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1840
1841         priv->done = status->position;
1842         priv->total = status->of_total;
1843
1844         state = modest_mail_operation_clone_state (self);
1845         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1846         g_slice_free (ModestMailOperationState, state);
1847 }
1848
1849 void 
1850 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1851                                        TnyFolder *folder)
1852 {
1853         ModestMailOperationPrivate *priv;
1854
1855         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1856
1857         /* Pick a reference */
1858         g_object_ref (folder);
1859
1860         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1861
1862         /* Refresh the folder. TODO: tinymail could issue a status
1863            updates before the callback call then this could happen. We
1864            must review the design */
1865         tny_folder_refresh_async (folder,
1866                                   on_refresh_folder,
1867                                   on_refresh_folder_status_update,
1868                                   self);
1869 }
1870
1871 /**
1872  *
1873  * It's used by the mail operation queue to notify the observers
1874  * attached to that signal that the operation finished. We need to use
1875  * that because tinymail does not give us the progress of a given
1876  * operation when it finishes (it directly calls the operation
1877  * callback).
1878  */
1879 static void
1880 modest_mail_operation_notify_end (ModestMailOperation *self)
1881 {
1882         ModestMailOperationState *state;
1883
1884         /* Notify the observers about the mail opertation end */
1885         state = modest_mail_operation_clone_state (self);
1886         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1887         g_slice_free (ModestMailOperationState, state);
1888
1889         /* Notify the queue */
1890         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1891 }