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