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