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