* Added support to get_folders call in the local folder account to filter by query
[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 <string.h>
31 #include <stdarg.h>
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-camel-pop-folder.h>
39 #include <tny-camel-imap-folder.h>
40 #include <tny-camel-mem-stream.h>
41 #include <tny-simple-list.h>
42 #include <tny-send-queue.h>
43 #include <tny-status.h>
44 #include <tny-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include <modest-tny-account.h>
49 #include <modest-tny-send-queue.h>
50 #include <modest-runtime.h>
51 #include "modest-text-utils.h"
52 #include "modest-tny-msg.h"
53 #include "modest-tny-folder.h"
54 #include "modest-tny-account-store.h"
55 #include "modest-tny-platform-factory.h"
56 #include "modest-marshal.h"
57 #include "modest-error.h"
58 #include "modest-mail-operation.h"
59
60 #define KB 1024
61 #define GET_SIZE_BUFFER_SIZE 128
62
63 /* 'private'/'protected' functions */
64 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
65 static void modest_mail_operation_init       (ModestMailOperation *obj);
66 static void modest_mail_operation_finalize   (GObject *obj);
67
68 static void     get_msg_cb (TnyFolder *folder, 
69                             gboolean cancelled, 
70                             TnyMsg *msg, 
71                             GError **err, 
72                             gpointer user_data);
73
74 static void     get_msg_status_cb (GObject *obj,
75                                    TnyStatus *status,  
76                                    gpointer user_data);
77
78 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
79
80 static gboolean did_a_cancel = FALSE;
81
82 enum _ModestMailOperationSignals 
83 {
84         PROGRESS_CHANGED_SIGNAL,
85
86         NUM_SIGNALS
87 };
88
89 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
90 struct _ModestMailOperationPrivate {
91         TnyAccount                 *account;
92         gchar                                                                            *account_name;
93         guint                      done;
94         guint                      total;
95         GObject                   *source;
96         GError                    *error;
97         ErrorCheckingUserCallback  error_checking;
98         gpointer                   error_checking_user_data;
99         ModestMailOperationStatus  status;      
100         ModestMailOperationTypeOperation op_type;
101 };
102
103 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
104                                                    MODEST_TYPE_MAIL_OPERATION, \
105                                                    ModestMailOperationPrivate))
106
107 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
108                                                    priv->status = new_status;\
109                                                }
110
111 typedef struct _GetMsgAsyncHelper {     
112         ModestMailOperation *mail_op;
113         TnyHeader *header;
114         GetMsgAsyncUserCallback user_callback;  
115         gpointer user_data;
116 } GetMsgAsyncHelper;
117
118 typedef struct _RefreshAsyncHelper {    
119         ModestMailOperation *mail_op;
120         RefreshAsyncUserCallback user_callback; 
121         gpointer user_data;
122 } RefreshAsyncHelper;
123
124 typedef struct _XFerMsgAsyncHelper
125 {
126         ModestMailOperation *mail_op;
127         TnyList *headers;
128         TnyFolder *dest_folder;
129         XferMsgsAsynUserCallback user_callback; 
130         gpointer user_data;
131 } XFerMsgAsyncHelper;
132
133 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
134                                                       TnyMsg *msg,
135                                                       gpointer userdata);
136
137 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
138                                                        const gchar *from, const gchar *to,
139                                                        const gchar *cc, const gchar *bcc,
140                                                        const gchar *subject, const gchar *plain_body,
141                                                        const gchar *html_body, const GList *attachments_list,
142                                                        TnyHeaderFlags priority_flags,
143                                                        ModestMailOperationCreateMsgCallback callback,
144                                                        gpointer userdata);
145
146 static gboolean      idle_notify_queue (gpointer data);
147 typedef struct
148 {
149         ModestMailOperation *mail_op;
150         gchar *from;
151         gchar *to;
152         gchar *cc;
153         gchar *bcc;
154         gchar *subject;
155         gchar *plain_body;
156         gchar *html_body;
157         GList *attachments_list;
158         TnyHeaderFlags priority_flags;
159         ModestMailOperationCreateMsgCallback callback;
160         gpointer userdata;
161 } CreateMsgInfo;
162
163 typedef struct
164 {
165         ModestMailOperation *mail_op;
166         TnyMsg *msg;
167         ModestMailOperationCreateMsgCallback callback;
168         gpointer userdata;
169 } CreateMsgIdleInfo;
170
171 /* globals */
172 static GObjectClass *parent_class = NULL;
173
174 static guint signals[NUM_SIGNALS] = {0};
175
176 GType
177 modest_mail_operation_get_type (void)
178 {
179         static GType my_type = 0;
180         if (!my_type) {
181                 static const GTypeInfo my_info = {
182                         sizeof(ModestMailOperationClass),
183                         NULL,           /* base init */
184                         NULL,           /* base finalize */
185                         (GClassInitFunc) modest_mail_operation_class_init,
186                         NULL,           /* class finalize */
187                         NULL,           /* class data */
188                         sizeof(ModestMailOperation),
189                         1,              /* n_preallocs */
190                         (GInstanceInitFunc) modest_mail_operation_init,
191                         NULL
192                 };
193                 my_type = g_type_register_static (G_TYPE_OBJECT,
194                                                   "ModestMailOperation",
195                                                   &my_info, 0);
196         }
197         return my_type;
198 }
199
200 static void
201 modest_mail_operation_class_init (ModestMailOperationClass *klass)
202 {
203         GObjectClass *gobject_class;
204         gobject_class = (GObjectClass*) klass;
205
206         parent_class            = g_type_class_peek_parent (klass);
207         gobject_class->finalize = modest_mail_operation_finalize;
208
209         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
210
211         /**
212          * ModestMailOperation::progress-changed
213          * @self: the #MailOperation that emits the signal
214          * @user_data: user data set when the signal handler was connected
215          *
216          * Emitted when the progress of a mail operation changes
217          */
218         signals[PROGRESS_CHANGED_SIGNAL] = 
219                 g_signal_new ("progress-changed",
220                               G_TYPE_FROM_CLASS (gobject_class),
221                               G_SIGNAL_RUN_FIRST,
222                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
223                               NULL, NULL,
224                               g_cclosure_marshal_VOID__POINTER,
225                               G_TYPE_NONE, 1, G_TYPE_POINTER);
226
227 }
228
229 static void
230 modest_mail_operation_init (ModestMailOperation *obj)
231 {
232         ModestMailOperationPrivate *priv;
233
234         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
235
236         priv->account        = NULL;
237         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
238         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
239         priv->error          = NULL;
240         priv->done           = 0;
241         priv->total          = 0;
242         priv->source         = NULL;
243         priv->error_checking = NULL;
244         priv->error_checking_user_data = NULL;
245 }
246
247 static void
248 modest_mail_operation_finalize (GObject *obj)
249 {
250         ModestMailOperationPrivate *priv;
251
252         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
253
254         
255         
256         if (priv->error) {
257                 g_error_free (priv->error);
258                 priv->error = NULL;
259         }
260         if (priv->source) {
261                 g_object_unref (priv->source);
262                 priv->source = NULL;
263         }
264         if (priv->account) {
265                 g_object_unref (priv->account);
266                 priv->account = NULL;
267         }
268
269
270         G_OBJECT_CLASS(parent_class)->finalize (obj);
271 }
272
273 ModestMailOperation*
274 modest_mail_operation_new (ModestMailOperationTypeOperation op_type, 
275                            GObject *source)
276 {
277         ModestMailOperation *obj;
278         ModestMailOperationPrivate *priv;
279                 
280         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
281         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
282
283         priv->op_type = op_type;
284         if (source != NULL)
285                 priv->source = g_object_ref(source);
286
287         return obj;
288 }
289
290 ModestMailOperation*
291 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
292                                                GObject *source,
293                                                ErrorCheckingUserCallback error_handler,
294                                                gpointer user_data)
295 {
296         ModestMailOperation *obj;
297         ModestMailOperationPrivate *priv;
298                 
299         obj = modest_mail_operation_new (op_type, source);
300         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
301         
302         g_return_val_if_fail (error_handler != NULL, obj);
303         priv->error_checking = error_handler;
304
305         return obj;
306 }
307
308 void
309 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
310 {
311         ModestMailOperationPrivate *priv;
312         
313         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
314         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
315
316         if (priv->error_checking != NULL)
317                 priv->error_checking (self, priv->error_checking_user_data);
318 }
319
320
321 ModestMailOperationTypeOperation
322 modest_mail_operation_get_type_operation (ModestMailOperation *self)
323 {
324         ModestMailOperationPrivate *priv;
325
326         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
327         
328         return priv->op_type;
329 }
330
331 gboolean 
332 modest_mail_operation_is_mine (ModestMailOperation *self, 
333                                GObject *me)
334 {
335         ModestMailOperationPrivate *priv;
336
337         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
338         if (priv->source == NULL) return FALSE;
339
340         return priv->source == me;
341 }
342
343 GObject *
344 modest_mail_operation_get_source (ModestMailOperation *self)
345 {
346         ModestMailOperationPrivate *priv;
347
348         g_return_val_if_fail (self, NULL);
349         
350         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
351         if (!priv) {
352                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
353                 return NULL;
354         }
355         
356         return g_object_ref (priv->source);
357 }
358
359 ModestMailOperationStatus
360 modest_mail_operation_get_status (ModestMailOperation *self)
361 {
362         ModestMailOperationPrivate *priv;
363
364         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
365         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
366                               MODEST_MAIL_OPERATION_STATUS_INVALID);
367
368         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
369         if (!priv) {
370                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
371                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
372         }
373         
374         return priv->status;
375 }
376
377 const GError *
378 modest_mail_operation_get_error (ModestMailOperation *self)
379 {
380         ModestMailOperationPrivate *priv;
381
382         g_return_val_if_fail (self, NULL);
383         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
384
385         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
386
387         if (!priv) {
388                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
389                 return NULL;
390         }
391
392         return priv->error;
393 }
394
395 gboolean 
396 modest_mail_operation_cancel (ModestMailOperation *self)
397 {
398         ModestMailOperationPrivate *priv;
399
400         if (!MODEST_IS_MAIL_OPERATION (self)) {
401                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
402                 return FALSE;
403         }
404
405         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
406         if (!priv) {
407                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
408                 return FALSE;
409         }
410
411         did_a_cancel = TRUE;
412
413         /* Set new status */
414         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
415
416         /* This emits progress-changed on which the mail operation queue is
417          * listening, so the mail operation is correctly removed from the
418          * queue without further explicit calls. */
419         modest_mail_operation_notify_end (self);
420         
421         return TRUE;
422 }
423
424 guint 
425 modest_mail_operation_get_task_done (ModestMailOperation *self)
426 {
427         ModestMailOperationPrivate *priv;
428
429         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
430
431         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
432         return priv->done;
433 }
434
435 guint 
436 modest_mail_operation_get_task_total (ModestMailOperation *self)
437 {
438         ModestMailOperationPrivate *priv;
439
440         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
441
442         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
443         return priv->total;
444 }
445
446 gboolean
447 modest_mail_operation_is_finished (ModestMailOperation *self)
448 {
449         ModestMailOperationPrivate *priv;
450         gboolean retval = FALSE;
451
452         if (!MODEST_IS_MAIL_OPERATION (self)) {
453                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
454                 return retval;
455         }
456
457         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
458
459         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
460             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
461             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
462             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
463                 retval = TRUE;
464         } else {
465                 retval = FALSE;
466         }
467
468         return retval;
469 }
470
471 guint
472 modest_mail_operation_get_id (ModestMailOperation *self)
473 {
474         ModestMailOperationPrivate *priv;
475
476         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
477
478         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
479         return priv->done;
480 }
481
482 guint 
483 modest_mail_operation_set_id (ModestMailOperation *self,
484                               guint id)
485 {
486         ModestMailOperationPrivate *priv;
487
488         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
489
490         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
491         return priv->done;
492 }
493
494 /*
495  * Creates an image of the current state of a mail operation, the
496  * caller must free it
497  */
498 static ModestMailOperationState *
499 modest_mail_operation_clone_state (ModestMailOperation *self)
500 {
501         ModestMailOperationState *state;
502         ModestMailOperationPrivate *priv;
503
504         /* FIXME: this should be fixed properly
505          * 
506          * in some cases, priv was NULL, so checking here to
507          * make sure.
508          */
509         g_return_val_if_fail (self, NULL);
510         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
511         g_return_val_if_fail (priv, NULL);
512
513         if (!priv)
514                 return NULL;
515
516         state = g_slice_new (ModestMailOperationState);
517
518         state->status = priv->status;
519         state->op_type = priv->op_type;
520         state->done = priv->done;
521         state->total = priv->total;
522         state->finished = modest_mail_operation_is_finished (self);
523         state->bytes_done = 0;
524         state->bytes_total = 0;
525
526         return state;
527 }
528
529 /* ******************************************************************* */
530 /* ************************** SEND   ACTIONS ************************* */
531 /* ******************************************************************* */
532
533 void
534 modest_mail_operation_send_mail (ModestMailOperation *self,
535                                  TnyTransportAccount *transport_account,
536                                  TnyMsg* msg)
537 {
538         TnySendQueue *send_queue = NULL;
539         ModestMailOperationPrivate *priv;
540         
541         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
542         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
543         g_return_if_fail (TNY_IS_MSG (msg));
544         
545         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
546
547         /* Get account and set it into mail_operation */
548         priv->account = g_object_ref (transport_account);
549         priv->done = 1;
550         priv->total = 1;
551
552         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
553         if (!TNY_IS_SEND_QUEUE(send_queue)) {
554                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
555                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
556                              "modest: could not find send queue for account\n");
557         } else {
558                 /* TODO: connect to the msg-sent in order to know when
559                    the mail operation is finished */
560                 tny_send_queue_add (send_queue, msg, &(priv->error));
561                 /* TODO: we're setting always success, do the check in
562                    the handler */
563                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
564         }
565
566         /* TODO: do this in the handler of the "msg-sent"
567            signal.Notify about operation end */
568         modest_mail_operation_notify_end (self);
569 }
570
571 static gboolean
572 idle_create_msg_cb (gpointer idle_data)
573 {
574         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
575
576         gdk_threads_enter ();
577         info->callback (info->mail_op, info->msg, info->userdata);
578         gdk_threads_leave ();
579         g_object_unref (info->mail_op);
580         if (info->msg)
581                 g_object_unref (info->msg);
582         g_slice_free (CreateMsgIdleInfo, info);
583
584         return FALSE;
585 }
586
587 static gpointer 
588 create_msg_thread (gpointer thread_data)
589 {
590         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
591         TnyMsg *new_msg = NULL;
592         ModestMailOperationPrivate *priv;
593
594         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
595         if (info->html_body == NULL) {
596                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
597                                               info->bcc, info->subject, info->plain_body, 
598                                               info->attachments_list); /* FIXME: attachments */
599         } else {
600                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
601                                                          info->bcc, info->subject, info->html_body,
602                                                          info->plain_body, info->attachments_list);
603         }
604
605         if (new_msg) {
606                 TnyHeader *header;
607                 /* Set priority flags in message */
608                 header = tny_msg_get_header (new_msg);
609                 if (info->priority_flags != 0)
610                         tny_header_set_flags (header, info->priority_flags);
611                 if (info->attachments_list != NULL) {
612                         tny_header_set_flags (header, TNY_HEADER_FLAG_ATTACHMENTS);
613                 }
614                 g_object_unref (G_OBJECT(header));
615         } else {
616                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
617                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
618                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
619                              "modest: failed to create a new msg\n");
620         }
621
622
623         g_free (info->to);
624         g_free (info->from);
625         g_free (info->cc);
626         g_free (info->bcc);
627         g_free (info->plain_body);
628         g_free (info->html_body);
629         g_free (info->subject);
630         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
631         g_list_free (info->attachments_list);
632
633         if (info->callback) {
634                 CreateMsgIdleInfo *idle_info;
635                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
636                 idle_info->mail_op = info->mail_op;
637                 g_object_ref (info->mail_op);
638                 idle_info->msg = new_msg;
639                 if (new_msg)
640                         g_object_ref (new_msg);
641                 idle_info->callback = info->callback;
642                 idle_info->userdata = info->userdata;
643                 g_idle_add (idle_create_msg_cb, idle_info);
644         } else {
645                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
646         }
647
648         g_object_unref (info->mail_op);
649         g_slice_free (CreateMsgInfo, info);
650         return NULL;
651 }
652
653 void
654 modest_mail_operation_create_msg (ModestMailOperation *self,
655                                   const gchar *from, const gchar *to,
656                                   const gchar *cc, const gchar *bcc,
657                                   const gchar *subject, const gchar *plain_body,
658                                   const gchar *html_body,
659                                   const GList *attachments_list,
660                                   TnyHeaderFlags priority_flags,
661                                   ModestMailOperationCreateMsgCallback callback,
662                                   gpointer userdata)
663 {
664         CreateMsgInfo *info = NULL;
665
666         info = g_slice_new0 (CreateMsgInfo);
667         info->mail_op = self;
668         g_object_ref (self);
669
670         info->from = g_strdup (from);
671         info->to = g_strdup (to);
672         info->cc = g_strdup (cc);
673         info->subject = g_strdup (subject);
674         info->plain_body = g_strdup (plain_body);
675         info->html_body = g_strdup (html_body);
676         info->attachments_list = g_list_copy ((GList *) attachments_list);
677         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
678         info->priority_flags = priority_flags;
679
680         info->callback = callback;
681         info->userdata = userdata;
682
683         g_thread_create (create_msg_thread, info, FALSE, NULL);
684 }
685
686 typedef struct
687 {
688         TnyTransportAccount *transport_account;
689         TnyMsg *draft_msg;
690 } SendNewMailInfo;
691
692 static void
693 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
694                                         TnyMsg *msg,
695                                         gpointer userdata)
696 {
697         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
698         TnyFolder *folder;
699         TnyHeader *header;
700
701         if (!msg) {
702                 goto end;
703         }
704
705         /* Call mail operation */
706         modest_mail_operation_send_mail (self, info->transport_account, msg);
707
708         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
709         if (folder) {
710                 if (info->draft_msg != NULL) {
711                         header = tny_msg_get_header (info->draft_msg);
712                         /* Note: This can fail (with a warning) if the message is not really already in a folder,
713                          * because this function requires it to have a UID. */
714                         tny_folder_remove_msg (folder, header, NULL);
715                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
716                         tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
717                         g_object_unref (header);
718                 }
719         }
720
721 end:
722         if (info->draft_msg)
723                 g_object_unref (info->draft_msg);
724         if (info->transport_account)
725                 g_object_unref (info->transport_account);
726         g_slice_free (SendNewMailInfo, info);
727         modest_mail_operation_notify_end (self);
728 }
729
730 void
731 modest_mail_operation_send_new_mail (ModestMailOperation *self,
732                                      TnyTransportAccount *transport_account,
733                                      TnyMsg *draft_msg,
734                                      const gchar *from,  const gchar *to,
735                                      const gchar *cc,  const gchar *bcc,
736                                      const gchar *subject, const gchar *plain_body,
737                                      const gchar *html_body,
738                                      const GList *attachments_list,
739                                      TnyHeaderFlags priority_flags)
740 {
741         ModestMailOperationPrivate *priv = NULL;
742         SendNewMailInfo *info;
743
744         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
745         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
746
747         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
748
749         /* Check parametters */
750         if (to == NULL) {
751                 /* Set status failed and set an error */
752                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
753                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
754                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
755                              _("Error trying to send a mail. You need to set at least one recipient"));
756                 return;
757         }
758         info = g_slice_new0 (SendNewMailInfo);
759         info->transport_account = transport_account;
760         if (transport_account)
761                 g_object_ref (transport_account);
762         info->draft_msg = draft_msg;
763         if (draft_msg)
764                 g_object_ref (draft_msg);
765         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
766                                           attachments_list, priority_flags,
767                                           modest_mail_operation_send_new_mail_cb, info);
768
769 }
770
771 typedef struct
772 {
773         TnyTransportAccount *transport_account;
774         TnyMsg *draft_msg;
775         ModestMsgEditWindow *edit_window;
776 } SaveToDraftsInfo;
777
778 static void
779 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
780                                          TnyMsg *msg,
781                                          gpointer userdata)
782 {
783         TnyFolder *folder = NULL;
784         TnyHeader *header = NULL;
785         ModestMailOperationPrivate *priv = NULL;
786         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
787
788         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
789         if (!msg) {
790                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
791                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
792                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
793                              "modest: failed to create a new msg\n");
794                 goto end;
795         }
796
797         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
798         if (!folder) {
799                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
800                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
801                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
802                              "modest: failed to create a new msg\n");
803                 goto end;
804         }
805
806         if (info->draft_msg != NULL) {
807                 header = tny_msg_get_header (info->draft_msg);
808                 /* Remove the old draft expunging it */
809                 tny_folder_remove_msg (folder, header, NULL);
810                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
811                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
812                 tny_folder_sync (folder, FALSE, &(priv->error));  /* FALSE --> don't expunge */
813                 g_object_unref (header);
814         }
815         
816         if (!priv->error)
817                 tny_folder_add_msg (folder, msg, &(priv->error));
818
819         if (!priv->error)
820                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
821         else
822                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
823
824         if (info->edit_window)
825                 modest_msg_edit_window_set_draft (info->edit_window, msg);
826
827
828 end:
829         if (folder)
830                 g_object_unref (G_OBJECT(folder));
831         if (info->edit_window)
832                 g_object_unref (G_OBJECT(info->edit_window));
833         if (info->draft_msg)
834                 g_object_unref (G_OBJECT (info->draft_msg));
835         if (info->transport_account)
836                 g_object_unref (G_OBJECT(info->transport_account));
837         g_slice_free (SaveToDraftsInfo, info);
838
839         modest_mail_operation_notify_end (self);
840 }
841
842 void
843 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
844                                       TnyTransportAccount *transport_account,
845                                       TnyMsg *draft_msg,
846                                       ModestMsgEditWindow *edit_window,
847                                       const gchar *from,  const gchar *to,
848                                       const gchar *cc,  const gchar *bcc,
849                                       const gchar *subject, const gchar *plain_body,
850                                       const gchar *html_body,
851                                       const GList *attachments_list,
852                                       TnyHeaderFlags priority_flags)
853 {
854         ModestMailOperationPrivate *priv = NULL;
855         SaveToDraftsInfo *info = NULL;
856
857         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
858         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
859
860         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
861
862         /* Get account and set it into mail_operation */
863         priv->account = g_object_ref (transport_account);
864
865         info = g_slice_new0 (SaveToDraftsInfo);
866         info->transport_account = g_object_ref (transport_account);
867         info->draft_msg = draft_msg;
868         if (draft_msg)
869                 g_object_ref (draft_msg);
870         info->edit_window = edit_window;
871         if (edit_window)
872                 g_object_ref (edit_window);
873
874         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
875                                           attachments_list, priority_flags,
876                                           modest_mail_operation_save_to_drafts_cb, info);
877
878 }
879
880 typedef struct 
881 {
882         ModestMailOperation *mail_op;
883         TnyStoreAccount *account;
884         TnyTransportAccount *transport_account;
885         gint max_size;
886         gint retrieve_limit;
887         gchar *retrieve_type;
888         gchar *account_name;
889         UpdateAccountCallback callback;
890         gpointer user_data;
891         gint new_headers;
892 } UpdateAccountInfo;
893
894 typedef struct
895 {
896         ModestMailOperation *mail_op;
897         TnyMimePart *mime_part;
898         gssize size;
899         GetMimePartSizeCallback callback;
900         gpointer userdata;
901 } GetMimePartSizeInfo;
902
903 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
904 /* We use this folder observer to track the headers that have been
905  * added to a folder */
906 typedef struct {
907         GObject parent;
908         TnyList *new_headers;
909 } InternalFolderObserver;
910
911 typedef struct {
912         GObjectClass parent;
913 } InternalFolderObserverClass;
914
915 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
916
917 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
918                          internal_folder_observer,
919                          G_TYPE_OBJECT,
920                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
921
922
923 static void
924 foreach_add_item (gpointer header, gpointer user_data)
925 {
926         tny_list_prepend (TNY_LIST (user_data), 
927                           g_object_ref (G_OBJECT (header)));
928 }
929
930 /* This is the method that looks for new messages in a folder */
931 static void
932 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
933 {
934         InternalFolderObserver *derived = (InternalFolderObserver *)self;
935         
936         TnyFolderChangeChanged changed;
937
938         changed = tny_folder_change_get_changed (change);
939
940         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
941                 TnyList *list;
942
943                 /* Get added headers */
944                 list = tny_simple_list_new ();
945                 tny_folder_change_get_added_headers (change, list);
946
947                 /* Add them to the folder observer */
948                 tny_list_foreach (list, foreach_add_item, 
949                                   derived->new_headers);
950
951                 g_object_unref (G_OBJECT (list));
952         }
953 }
954
955 static void
956 internal_folder_observer_init (InternalFolderObserver *self) 
957 {
958         self->new_headers = tny_simple_list_new ();
959 }
960 static void
961 internal_folder_observer_finalize (GObject *object) 
962 {
963         InternalFolderObserver *self;
964
965         self = (InternalFolderObserver *) object;
966         g_object_unref (self->new_headers);
967
968         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
969 }
970 static void
971 tny_folder_observer_init (TnyFolderObserverIface *iface) 
972 {
973         iface->update_func = internal_folder_observer_update;
974 }
975 static void
976 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
977 {
978         GObjectClass *object_class;
979
980         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
981         object_class = (GObjectClass*) klass;
982         object_class->finalize = internal_folder_observer_finalize;
983 }
984
985 /*****************/
986
987 static void
988 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
989 {
990         TnyIterator *iter;
991         TnyList *folders = tny_simple_list_new ();
992
993         tny_folder_store_get_folders (store, folders, query, NULL);
994         iter = tny_list_create_iterator (folders);
995
996         while (!tny_iterator_is_done (iter)) {
997
998                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
999
1000                 tny_list_prepend (all_folders, G_OBJECT (folder));
1001                 recurse_folders (folder, query, all_folders);    
1002                 g_object_unref (G_OBJECT (folder));
1003
1004                 tny_iterator_next (iter);
1005         }
1006          g_object_unref (G_OBJECT (iter));
1007          g_object_unref (G_OBJECT (folders));
1008 }
1009
1010 /* 
1011  * Issues the "progress-changed" signal. The timer won't be removed,
1012  * so you must call g_source_remove to stop the signal emission
1013  */
1014 static gboolean
1015 idle_notify_progress (gpointer data)
1016 {
1017         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1018         ModestMailOperationState *state;
1019
1020         state = modest_mail_operation_clone_state (mail_op);
1021         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1022         g_slice_free (ModestMailOperationState, state);
1023         
1024         return TRUE;
1025 }
1026
1027 /* 
1028  * Issues the "progress-changed" signal and removes the timer. It uses
1029  * a lock to ensure that the progress information of the mail
1030  * operation is not modified while there are notifications pending
1031  */
1032 static gboolean
1033 idle_notify_progress_once (gpointer data)
1034 {
1035         ModestPair *pair;
1036
1037         pair = (ModestPair *) data;
1038
1039         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1040
1041         /* Free the state and the reference to the mail operation */
1042         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1043         g_object_unref (pair->first);
1044
1045         return FALSE;
1046 }
1047
1048 /* 
1049  * Used to notify the queue from the main
1050  * loop. We call it inside an idle call to achieve that
1051  */
1052 static gboolean
1053 idle_notify_queue (gpointer data)
1054 {
1055         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1056
1057         /* Do not need to block, the notify end will do it for us */    
1058         modest_mail_operation_notify_end (mail_op);
1059         g_object_unref (mail_op);
1060
1061         return FALSE;
1062 }
1063
1064 static int
1065 compare_headers_by_date (gconstpointer a, 
1066                          gconstpointer b)
1067 {
1068         TnyHeader **header1, **header2;
1069         time_t sent1, sent2;
1070
1071         header1 = (TnyHeader **) a;
1072         header2 = (TnyHeader **) b;
1073
1074         sent1 = tny_header_get_date_sent (*header1);
1075         sent2 = tny_header_get_date_sent (*header2);
1076
1077         /* We want the most recent ones (greater time_t) at the
1078            beginning */
1079         if (sent1 < sent2)
1080                 return 1;
1081         else
1082                 return -1;
1083 }
1084
1085 static gboolean 
1086 set_last_updated_idle (gpointer data)
1087 {
1088         gdk_threads_enter ();
1089
1090         /* It does not matter if the time is not exactly the same than
1091            the time when this idle was called, it's just an
1092            approximation and it won't be very different */
1093         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
1094                                     (gchar *) data, 
1095                                     MODEST_ACCOUNT_LAST_UPDATED, 
1096                                     time(NULL), 
1097                                     TRUE);
1098
1099         gdk_threads_leave ();
1100
1101         return FALSE;
1102 }
1103
1104 static gboolean
1105 idle_update_account_cb (gpointer data)
1106 {
1107         UpdateAccountInfo *idle_info;
1108
1109         idle_info = (UpdateAccountInfo *) data;
1110
1111         gdk_threads_enter ();
1112         idle_info->callback (idle_info->mail_op,
1113                              idle_info->new_headers,
1114                              idle_info->user_data);
1115         gdk_threads_leave ();
1116
1117         /* Frees */
1118         g_object_unref (idle_info->mail_op);
1119         g_free (idle_info);
1120
1121         return FALSE;
1122 }
1123
1124
1125 static gpointer
1126 update_account_thread (gpointer thr_user_data)
1127 {
1128         static gboolean first_time = TRUE;
1129         UpdateAccountInfo *info;
1130         TnyList *all_folders = NULL;
1131         GPtrArray *new_headers = NULL;
1132         TnyIterator *iter = NULL;
1133         TnyFolderStoreQuery *query = NULL;
1134         ModestMailOperationPrivate *priv = NULL;
1135         ModestTnySendQueue *send_queue = NULL;
1136
1137         info = (UpdateAccountInfo *) thr_user_data;
1138         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1139
1140         /* Get account and set it into mail_operation */
1141         priv->account = g_object_ref (info->account);
1142
1143         /*
1144          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
1145          * show any updates unless we do that
1146          */
1147         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
1148                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1149
1150         /* Get all the folders. We can do it synchronously because
1151            we're already running in a different thread than the UI */
1152         all_folders = tny_simple_list_new ();
1153         query = tny_folder_store_query_new ();
1154         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1155         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1156                                       all_folders,
1157                                       query,
1158                                       &(priv->error));
1159         if (priv->error) {
1160                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1161                 goto out;
1162         }
1163
1164         iter = tny_list_create_iterator (all_folders);
1165         while (!tny_iterator_is_done (iter)) {
1166                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1167
1168                 recurse_folders (folder, query, all_folders);
1169                 tny_iterator_next (iter);
1170         }
1171         g_object_unref (G_OBJECT (iter));
1172
1173         /* Update status and notify. We need to call the notification
1174            with a source function in order to call it from the main
1175            loop. We need that in order not to get into trouble with
1176            Gtk+. We use a timeout in order to provide more status
1177            information, because the sync tinymail call does not
1178            provide it for the moment */
1179         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1180
1181         /* Refresh folders */
1182         new_headers = g_ptr_array_new ();
1183         iter = tny_list_create_iterator (all_folders);
1184
1185         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
1186
1187                 InternalFolderObserver *observer;
1188                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1189
1190                 /* Refresh the folder */
1191                 /* Our observer receives notification of new emails during folder refreshes,
1192                  * so we can use observer->new_headers.
1193                  */
1194                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1195                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1196                 
1197                 /* This gets the status information (headers) from the server.
1198                  * We use the blocking version, because we are already in a separate 
1199                  * thread.
1200                  */
1201
1202                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
1203                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1204                         TnyIterator *iter;
1205
1206                         /* If the retrieve type is full messages, refresh and get the messages */
1207                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1208
1209                         iter = tny_list_create_iterator (observer->new_headers);
1210                         while (!tny_iterator_is_done (iter)) {
1211                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1212                                  
1213                                 /* Apply per-message size limits */
1214                                 if (tny_header_get_message_size (header) < info->max_size)
1215                                         g_ptr_array_add (new_headers, g_object_ref (header));
1216
1217                                 g_object_unref (header);
1218                                 tny_iterator_next (iter);
1219                         }
1220                         g_object_unref (iter);
1221                 } else {
1222                         /* We do not need to do it the first time
1223                            because it's automatically done by the tree
1224                            model */
1225                         if (G_UNLIKELY (!first_time))
1226                                 tny_folder_poke_status (TNY_FOLDER (folder));
1227                 }
1228                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1229                 g_object_unref (observer);
1230                 observer = NULL;                        
1231
1232                 g_object_unref (G_OBJECT (folder));
1233                 if (priv->error)
1234                 {
1235                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1236                         goto out;
1237                 }
1238                 
1239                 tny_iterator_next (iter);
1240         }
1241
1242         did_a_cancel = FALSE;
1243
1244         g_object_unref (G_OBJECT (iter));
1245         g_source_remove (timeout);
1246
1247         if (new_headers->len > 0) {
1248                 gint msg_num = 0;
1249
1250                 /* Order by date */
1251                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1252
1253                 /* Apply message count limit */
1254                 /* If the number of messages exceeds the maximum, ask the
1255                  * user to download them all,
1256                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1257                  */
1258                 if (new_headers->len > info->retrieve_limit) {
1259                         /* TODO: Ask the user, instead of just
1260                          * failing, showing
1261                          * mail_nc_msg_count_limit_exceeded, with 'Get
1262                          * all' and 'Newest only' buttons. */
1263                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1264                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1265                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1266                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1267                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1268                         goto out;
1269                 }
1270                 
1271                 priv->done = 0;
1272                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1273                 while (msg_num < priv->total) {
1274
1275                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1276                         TnyFolder *folder = tny_header_get_folder (header);
1277                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1278                         ModestMailOperationState *state;
1279                         ModestPair* pair;
1280
1281                         priv->done++;
1282                         /* We can not just use the mail operation because the
1283                            values of done and total could change before the
1284                            idle is called */
1285                         state = modest_mail_operation_clone_state (info->mail_op);
1286                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1287                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1288                                          pair, (GDestroyNotify) modest_pair_free);
1289
1290                         g_object_unref (msg);
1291                         g_object_unref (folder);
1292
1293                         msg_num++;
1294                 }
1295                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1296                 g_ptr_array_free (new_headers, FALSE);
1297         }
1298         
1299         /* Perform send (if operation was not cancelled) */
1300         if (did_a_cancel) goto out;             
1301 /*      priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND; */
1302         priv->done = 0;
1303         priv->total = 0;
1304         if (priv->account != NULL) 
1305                 g_object_unref (priv->account);
1306         priv->account = g_object_ref (info->transport_account);
1307         
1308         send_queue = modest_runtime_get_send_queue (info->transport_account);
1309         if (send_queue) {
1310 /*              timeout = g_timeout_add (250, idle_notify_progress, info->mail_op); */
1311                 modest_tny_send_queue_try_to_send (send_queue);
1312 /*              g_source_remove (timeout); */
1313         } else {
1314                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1315                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1316                              "cannot create a send queue for %s\n", 
1317                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1318                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1319         }
1320         
1321         /* Check if the operation was a success */
1322         if (!priv->error) {
1323                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1324
1325                 /* Update the last updated key */
1326                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1327                                  set_last_updated_idle, 
1328                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1329                                  (GDestroyNotify) g_free);
1330         }
1331
1332  out:
1333
1334         if (info->callback) {
1335                 UpdateAccountInfo *idle_info;
1336
1337                 /* This thread is not in the main lock */
1338                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1339                 idle_info->mail_op = g_object_ref (info->mail_op);
1340                 idle_info->new_headers = (new_headers) ? new_headers->len : 0;
1341                 idle_info->callback = info->callback;
1342                 g_idle_add (idle_update_account_cb, idle_info);
1343         }
1344
1345         /* Notify about operation end. Note that the info could be
1346            freed before this idle happens, but the mail operation will
1347            be still alive */
1348         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1349
1350         /* Frees */
1351         g_object_unref (query);
1352         g_object_unref (all_folders);
1353         g_object_unref (info->account);
1354         g_object_unref (info->transport_account);
1355         g_free (info->retrieve_type);
1356         g_slice_free (UpdateAccountInfo, info);
1357
1358         first_time = FALSE;
1359
1360         return NULL;
1361 }
1362
1363 gboolean
1364 modest_mail_operation_update_account (ModestMailOperation *self,
1365                                       const gchar *account_name,
1366                                       UpdateAccountCallback callback,
1367                                       gpointer user_data)
1368 {
1369         GThread *thread = NULL;
1370         UpdateAccountInfo *info = NULL;
1371         ModestMailOperationPrivate *priv = NULL;
1372         ModestAccountMgr *mgr = NULL;
1373         TnyStoreAccount *store_account = NULL;
1374         TnyTransportAccount *transport_account = NULL;
1375
1376         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1377         g_return_val_if_fail (account_name, FALSE);
1378
1379         /* Init mail operation. Set total and done to 0, and do not
1380            update them, this way the progress objects will know that
1381            we have no clue about the number of the objects */
1382         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1383         priv->total = 0;
1384         priv->done  = 0;
1385         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1386
1387         /* Get the Modest account */
1388         store_account = (TnyStoreAccount *)
1389                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1390                                                                      account_name,
1391                                                                      TNY_ACCOUNT_TYPE_STORE);
1392                                                                      
1393         /* Make sure that we have a connection, and request one 
1394          * if necessary:
1395          * TODO: Is there some way to trigger this for every attempt to 
1396          * use the network? */
1397         if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1398                 goto error;
1399
1400         if (!store_account) {
1401                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1402                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1403                              "cannot get tny store account for %s\n", account_name);
1404                 goto error;
1405         }
1406
1407         
1408         /* Get the transport account, we can not do it in the thread
1409            due to some problems with dbus */
1410         transport_account = (TnyTransportAccount *)
1411                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1412                                                                                     account_name);
1413         if (!transport_account) {
1414                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1415                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1416                              "cannot get tny transport account for %s\n", account_name);
1417                 goto error;
1418         }
1419
1420         /* Create the helper object */
1421         info = g_slice_new (UpdateAccountInfo);
1422         info->mail_op = self;
1423         info->account = store_account;
1424         info->transport_account = transport_account;
1425         info->callback = callback;
1426         info->user_data = user_data;
1427
1428         /* Get the message size limit */
1429         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1430                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1431         if (info->max_size == 0)
1432                 info->max_size = G_MAXINT;
1433         else
1434                 info->max_size = info->max_size * KB;
1435
1436         /* Get per-account retrieval type */
1437         mgr = modest_runtime_get_account_mgr ();
1438         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1439                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1440
1441         /* Get per-account message amount retrieval limit */
1442         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1443                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1444         if (info->retrieve_limit == 0)
1445                 info->retrieve_limit = G_MAXINT;
1446                 
1447         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1448
1449         /* Set account busy */
1450         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1451         priv->account_name = g_strdup(account_name);
1452         
1453         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1454
1455         return TRUE;
1456
1457  error:
1458         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1459         if (callback) 
1460                 callback (self, 0, user_data);
1461         modest_mail_operation_notify_end (self);
1462         return FALSE;
1463 }
1464
1465 /* ******************************************************************* */
1466 /* ************************** STORE  ACTIONS ************************* */
1467 /* ******************************************************************* */
1468
1469
1470 TnyFolder *
1471 modest_mail_operation_create_folder (ModestMailOperation *self,
1472                                      TnyFolderStore *parent,
1473                                      const gchar *name)
1474 {
1475         ModestMailOperationPrivate *priv;
1476         TnyFolder *new_folder = NULL;
1477
1478         TnyList *list = tny_simple_list_new ();
1479         TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1480
1481         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1482         g_return_val_if_fail (name, NULL);
1483         
1484         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1485
1486         /* Check for already existing folder */
1487         tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1488         tny_folder_store_get_folders (parent, list, query, NULL);
1489         g_object_unref (G_OBJECT (query));
1490
1491         if (tny_list_get_length (list) > 0) {
1492                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1493                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1494                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1495                              _CS("ckdg_ib_folder_already_exists"));
1496         }
1497
1498         g_object_unref (G_OBJECT (list));
1499
1500         /* Check parent */
1501         if (TNY_IS_FOLDER (parent)) {
1502                 /* Check folder rules */
1503                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1504                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1505                         /* Set status failed and set an error */
1506                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1507                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1508                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1509                                      _("mail_in_ui_folder_create_error"));
1510                 }
1511         }
1512
1513         if (!strcmp (name, " ") || strchr (name, '/')) {
1514                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1515                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1516                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1517                              _("mail_in_ui_folder_create_error"));
1518         }
1519
1520         if (!priv->error) {
1521                 /* Create the folder */
1522                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1523                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1524                 if (!priv->error)
1525                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1526         }
1527
1528         /* Notify about operation end */
1529         modest_mail_operation_notify_end (self);
1530
1531         return new_folder;
1532 }
1533
1534 void
1535 modest_mail_operation_remove_folder (ModestMailOperation *self,
1536                                      TnyFolder           *folder,
1537                                      gboolean             remove_to_trash)
1538 {
1539         TnyAccount *account;
1540         ModestMailOperationPrivate *priv;
1541         ModestTnyFolderRules rules;
1542
1543         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1544         g_return_if_fail (TNY_IS_FOLDER (folder));
1545         
1546         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1547         
1548         /* Check folder rules */
1549         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1550         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1551                 /* Set status failed and set an error */
1552                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1553                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1554                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1555                              _("mail_in_ui_folder_delete_error"));
1556                 goto end;
1557         }
1558
1559         /* Get the account */
1560         account = modest_tny_folder_get_account (folder);
1561         priv->account = g_object_ref(account);
1562
1563         /* Delete folder or move to trash */
1564         if (remove_to_trash) {
1565                 TnyFolder *trash_folder = NULL;
1566                 trash_folder = modest_tny_account_get_special_folder (account,
1567                                                                       TNY_FOLDER_TYPE_TRASH);
1568                 /* TODO: error_handling */
1569                  modest_mail_operation_xfer_folder (self, folder,
1570                                                     TNY_FOLDER_STORE (trash_folder), 
1571                                                     TRUE, NULL, NULL);
1572         } else {
1573                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1574
1575                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1576                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1577
1578                 if (parent)
1579                         g_object_unref (G_OBJECT (parent));
1580         }
1581         g_object_unref (G_OBJECT (account));
1582
1583  end:
1584         /* Notify about operation end */
1585         modest_mail_operation_notify_end (self);
1586 }
1587
1588 static void
1589 transfer_folder_status_cb (GObject *obj,
1590                            TnyStatus *status,
1591                            gpointer user_data)
1592 {
1593         ModestMailOperation *self;
1594         ModestMailOperationPrivate *priv;
1595         ModestMailOperationState *state;
1596         XFerMsgAsyncHelper *helper;
1597
1598         g_return_if_fail (status != NULL);
1599         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1600
1601         helper = (XFerMsgAsyncHelper *) user_data;
1602         g_return_if_fail (helper != NULL);
1603
1604         self = helper->mail_op;
1605         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1606
1607         priv->done = status->position;
1608         priv->total = status->of_total;
1609
1610         state = modest_mail_operation_clone_state (self);
1611         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1612         g_slice_free (ModestMailOperationState, state);
1613 }
1614
1615
1616 static void
1617 transfer_folder_cb (TnyFolder *folder, 
1618                     TnyFolderStore *into, 
1619                     gboolean cancelled, 
1620                     TnyFolder *new_folder, 
1621                     GError **err, 
1622                     gpointer user_data)
1623 {
1624         XFerMsgAsyncHelper *helper;
1625         ModestMailOperation *self = NULL;
1626         ModestMailOperationPrivate *priv = NULL;
1627
1628         helper = (XFerMsgAsyncHelper *) user_data;
1629         g_return_if_fail (helper != NULL);       
1630
1631         self = helper->mail_op;
1632         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1633
1634         if (*err) {
1635                 priv->error = g_error_copy (*err);
1636                 priv->done = 0;
1637                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1638         } else if (cancelled) {
1639                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1640                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1641                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1642                              _("Transference of %s was cancelled."),
1643                              tny_folder_get_name (folder));
1644         } else {
1645                 priv->done = 1;
1646                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1647         }
1648                 
1649         /* Notify about operation end */
1650         modest_mail_operation_notify_end (self);
1651
1652         /* If user defined callback function was defined, call it */
1653         if (helper->user_callback) {
1654                 gdk_threads_enter ();
1655                 helper->user_callback (priv->source, helper->user_data);
1656                 gdk_threads_leave ();
1657         }
1658
1659         /* Free */
1660         g_object_unref (helper->mail_op);
1661         g_slice_free   (XFerMsgAsyncHelper, helper);
1662 }
1663
1664 /**
1665  *
1666  * This function checks if the new name is a valid name for our local
1667  * folders account. The new name could not be the same than then name
1668  * of any of the mandatory local folders
1669  *
1670  * We can not rely on tinymail because tinymail does not check the
1671  * name of the virtual folders that the account could have in the case
1672  * that we're doing a rename (because it directly calls Camel which
1673  * knows nothing about our virtual folders). 
1674  *
1675  * In the case of an actual copy/move (i.e. move/copy a folder between
1676  * accounts) tinymail uses the tny_folder_store_create_account which
1677  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1678  * checks the new name of the folder, so this call in that case
1679  * wouldn't be needed. *But* NOTE that if tinymail changes its
1680  * implementation (if folder transfers within the same account is no
1681  * longer implemented as a rename) this call will allow Modest to work
1682  * perfectly
1683  *
1684  * If the new name is not valid, this function will set the status to
1685  * failed and will set also an error in the mail operation
1686  */
1687 static gboolean
1688 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1689                                  TnyFolderStore *into,
1690                                  const gchar *new_name)
1691 {
1692         if (TNY_IS_ACCOUNT (into) && 
1693             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1694             modest_tny_local_folders_account_extra_folder_exists (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1695                                                                   new_name)) {
1696                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1697                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1698                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1699                              _("FIXME: folder name already in use"));
1700                 return FALSE;
1701         } else
1702                 return TRUE;
1703 }
1704
1705 void
1706 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1707                                    TnyFolder *folder,
1708                                    TnyFolderStore *parent,
1709                                    gboolean delete_original,
1710                                    XferMsgsAsynUserCallback user_callback,
1711                                    gpointer user_data)
1712 {
1713         ModestMailOperationPrivate *priv = NULL;
1714         ModestTnyFolderRules parent_rules = 0, rules; 
1715         XFerMsgAsyncHelper *helper = NULL;
1716
1717         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1718         g_return_if_fail (TNY_IS_FOLDER (folder));
1719
1720         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1721
1722         /* Get account and set it into mail_operation */
1723         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1724         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1725
1726         /* Get folder rules */
1727         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1728         if (TNY_IS_FOLDER (parent))
1729                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1730         
1731         /* The moveable restriction is applied also to copy operation */
1732         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1733                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1734                 /* Set status failed and set an error */
1735                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1736                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1737                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1738                              _("mail_in_ui_folder_move_target_error"));
1739
1740                 /* Notify the queue */
1741                 modest_mail_operation_notify_end (self);
1742         } else if (TNY_IS_FOLDER (parent) && 
1743                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1744                 /* Set status failed and set an error */
1745                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1746                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1747                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1748                              _("FIXME: parent folder does not accept new folders"));
1749
1750                 /* Notify the queue */
1751                 modest_mail_operation_notify_end (self);
1752         } else {
1753
1754
1755                 /* Check that the new folder name is not used by any
1756                    special local folder */
1757                 if (new_name_valid_if_local_account (priv, parent, 
1758                                                      tny_folder_get_name (folder))) {
1759                         /* Create the helper */
1760                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1761                         helper->mail_op = g_object_ref(self);
1762                         helper->dest_folder = NULL;
1763                         helper->headers = NULL;
1764                         helper->user_callback = user_callback;
1765                         helper->user_data = user_data;
1766                         
1767                         /* Move/Copy folder */          
1768                         tny_folder_copy_async (folder,
1769                                                parent,
1770                                                tny_folder_get_name (folder),
1771                                                delete_original,
1772                                                transfer_folder_cb,
1773                                                transfer_folder_status_cb,
1774                                                helper);
1775                 } else {
1776                         modest_mail_operation_notify_end (self);
1777                 }
1778         }
1779 }
1780
1781 void
1782 modest_mail_operation_rename_folder (ModestMailOperation *self,
1783                                      TnyFolder *folder,
1784                                      const gchar *name)
1785 {
1786         ModestMailOperationPrivate *priv;
1787         ModestTnyFolderRules rules;
1788         XFerMsgAsyncHelper *helper;
1789
1790         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1791         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1792         g_return_if_fail (name);
1793         
1794         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1795
1796         /* Get account and set it into mail_operation */
1797         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1798
1799         /* Check folder rules */
1800         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1801         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1802                 /* Set status failed and set an error */
1803                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1804                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1805                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1806                              _("FIXME: unable to rename"));
1807
1808                 /* Notify about operation end */
1809                 modest_mail_operation_notify_end (self);
1810         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1811                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1812                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1813                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1814                              _("FIXME: unable to rename"));
1815                 /* Notify about operation end */
1816                 modest_mail_operation_notify_end (self);
1817         } else {
1818                 TnyFolderStore *into;
1819
1820                 into = tny_folder_get_folder_store (folder);    
1821
1822                 /* Check that the new folder name is not used by any
1823                    special local folder */
1824                 if (new_name_valid_if_local_account (priv, into, name)) {
1825                         /* Create the helper */
1826                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1827                         helper->mail_op = g_object_ref(self);
1828                         helper->dest_folder = NULL;
1829                         helper->headers = NULL;
1830                         helper->user_callback = NULL;
1831                         helper->user_data = NULL;
1832                 
1833                         /* Rename. Camel handles folder subscription/unsubscription */
1834                         tny_folder_copy_async (folder, into, name, TRUE,
1835                                                transfer_folder_cb,
1836                                                transfer_folder_status_cb,
1837                                                helper);
1838                 } else {
1839                         modest_mail_operation_notify_end (self);
1840                 }
1841                 g_object_unref (into);
1842         }
1843 }
1844
1845 /* ******************************************************************* */
1846 /* **************************  MSG  ACTIONS  ************************* */
1847 /* ******************************************************************* */
1848
1849 void modest_mail_operation_get_msg (ModestMailOperation *self,
1850                                     TnyHeader *header,
1851                                     GetMsgAsyncUserCallback user_callback,
1852                                     gpointer user_data)
1853 {
1854         GetMsgAsyncHelper *helper = NULL;
1855         TnyFolder *folder;
1856         ModestMailOperationPrivate *priv;
1857         
1858         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1859         g_return_if_fail (TNY_IS_HEADER (header));
1860         
1861         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1862         folder = tny_header_get_folder (header);
1863
1864         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1865
1866         /* Get message from folder */
1867         if (folder) {
1868                 /* Get account and set it into mail_operation */
1869                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1870
1871                 helper = g_slice_new0 (GetMsgAsyncHelper);
1872                 helper->mail_op = self;
1873                 helper->user_callback = user_callback;
1874                 helper->user_data = user_data;
1875                 helper->header = g_object_ref (header);
1876
1877                 // The callback's reference so that the mail op is not
1878                 // finalized until the async operation is completed even if
1879                 // the user canceled the request meanwhile.
1880                 g_object_ref (G_OBJECT (helper->mail_op));
1881
1882                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1883
1884                 g_object_unref (G_OBJECT (folder));
1885         } else {
1886                 /* Set status failed and set an error */
1887                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1888                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1889                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1890                              _("Error trying to get a message. No folder found for header"));
1891
1892                 /* Notify the queue */
1893                 modest_mail_operation_notify_end (self);
1894         }
1895 }
1896
1897 static gboolean
1898 idle_get_mime_part_size_cb (gpointer userdata)
1899 {
1900         GetMimePartSizeInfo *idle_info;
1901
1902         idle_info = (GetMimePartSizeInfo *) userdata;
1903
1904         gdk_threads_enter ();
1905         idle_info->callback (idle_info->mail_op,
1906                              idle_info->size,
1907                              idle_info->userdata);
1908         gdk_threads_leave ();
1909
1910         g_object_unref (idle_info->mail_op);
1911         g_slice_free (GetMimePartSizeInfo, idle_info);
1912
1913         return FALSE;
1914 }
1915
1916 static gpointer
1917 get_mime_part_size_thread (gpointer thr_user_data)
1918 {
1919         GetMimePartSizeInfo *info;
1920         gchar read_buffer[GET_SIZE_BUFFER_SIZE];
1921         TnyStream *stream;
1922         gssize readed_size;
1923         gssize total = 0;
1924         ModestMailOperationPrivate *priv;
1925
1926         info = (GetMimePartSizeInfo *) thr_user_data;
1927         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1928
1929         stream = tny_camel_mem_stream_new ();
1930         tny_mime_part_decode_to_stream (info->mime_part, stream);
1931         tny_stream_reset (stream);
1932         if (tny_stream_is_eos (stream)) {
1933                 tny_stream_close (stream);
1934                 stream = tny_mime_part_get_stream (info->mime_part);
1935         }
1936         
1937         while (!tny_stream_is_eos (stream)) {
1938                 readed_size = tny_stream_read (stream, read_buffer, GET_SIZE_BUFFER_SIZE);
1939                 total += readed_size;
1940         }
1941
1942         if (info->callback) {
1943                 GetMimePartSizeInfo *idle_info;
1944
1945                 idle_info = g_slice_new0 (GetMimePartSizeInfo);
1946                 idle_info->mail_op = g_object_ref (info->mail_op);
1947                 idle_info->size = total;
1948                 idle_info->callback = info->callback;
1949                 idle_info->userdata = info->userdata;
1950                 g_idle_add (idle_get_mime_part_size_cb, idle_info);
1951         }
1952
1953         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1954
1955         g_object_unref (info->mail_op);
1956         g_object_unref (stream);
1957         g_object_unref (info->mime_part);
1958         g_slice_free  (GetMimePartSizeInfo, info);
1959
1960         return NULL;
1961 }
1962
1963 void          
1964 modest_mail_operation_get_mime_part_size (ModestMailOperation *self,
1965                                           TnyMimePart *part,
1966                                           GetMimePartSizeCallback user_callback,
1967                                           gpointer user_data,
1968                                           GDestroyNotify notify)
1969 {
1970         GetMimePartSizeInfo *info;
1971         ModestMailOperationPrivate *priv;
1972         GThread *thread;
1973         
1974         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1975         g_return_if_fail (TNY_IS_MIME_PART (part));
1976         
1977         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1978
1979         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1980         info = g_slice_new0 (GetMimePartSizeInfo);
1981         info->mail_op = g_object_ref (self);
1982         info->mime_part = g_object_ref (part);
1983         info->callback = user_callback;
1984         info->userdata = user_data;
1985
1986         thread = g_thread_create (get_mime_part_size_thread, info, FALSE, NULL);
1987
1988 }
1989
1990 static void
1991 get_msg_cb (TnyFolder *folder, 
1992             gboolean cancelled, 
1993             TnyMsg *msg, 
1994             GError **error, 
1995             gpointer user_data)
1996 {
1997         GetMsgAsyncHelper *helper = NULL;
1998         ModestMailOperation *self = NULL;
1999         ModestMailOperationPrivate *priv = NULL;
2000
2001         helper = (GetMsgAsyncHelper *) user_data;
2002         g_return_if_fail (helper != NULL);       
2003         self = helper->mail_op;
2004         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2005         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2006
2007         /* Check errors and cancel */
2008         if (*error) {
2009                 priv->error = g_error_copy (*error);
2010                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2011         } else if (cancelled) {
2012                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2013                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2014                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2015                              _("Error trying to refresh the contents of %s"),
2016                              tny_folder_get_name (folder));
2017         } else {
2018                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2019         }
2020
2021         /* If user defined callback function was defined, call it even
2022            if the operation failed*/
2023         if (helper->user_callback) {
2024                 /* This callback is called into an iddle by tinymail,
2025                    and idles are not in the main lock */
2026                 gdk_threads_enter ();
2027                 helper->user_callback (self, helper->header, msg, helper->user_data);
2028                 gdk_threads_leave ();   
2029         }
2030
2031         /* Notify about operation end */
2032         modest_mail_operation_notify_end (self);
2033         /* Free */
2034         g_object_unref (helper->mail_op);
2035         g_object_unref (helper->header);
2036         g_slice_free (GetMsgAsyncHelper, helper);
2037                 
2038 }
2039
2040 static void     
2041 get_msg_status_cb (GObject *obj,
2042                    TnyStatus *status,  
2043                    gpointer user_data)
2044 {
2045         GetMsgAsyncHelper *helper = NULL;
2046         ModestMailOperation *self;
2047         ModestMailOperationPrivate *priv;
2048         ModestMailOperationState *state;
2049
2050         g_return_if_fail (status != NULL);
2051         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2052
2053         helper = (GetMsgAsyncHelper *) user_data;
2054         g_return_if_fail (helper != NULL);       
2055
2056         self = helper->mail_op;
2057         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2058
2059         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2060                 TnyFolder *folder = tny_header_get_folder (helper->header);
2061                 if (folder) {
2062                         TnyAccount *account;
2063                         account = tny_folder_get_account (folder);
2064                         if (account) {
2065                                 tny_account_cancel (account);
2066                                 g_object_unref (account);
2067                         }
2068                         g_object_unref (folder);
2069                 }
2070                
2071                 return;
2072         }
2073
2074         priv->done = 1;
2075         priv->total = 1;
2076
2077         state = modest_mail_operation_clone_state (self);
2078         state->bytes_done = status->position;
2079         state->bytes_total = status->of_total;
2080         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2081         g_slice_free (ModestMailOperationState, state);
2082 }
2083
2084 /****************************************************/
2085 typedef struct {
2086         ModestMailOperation *mail_op;
2087         TnyList *headers;
2088         GetMsgAsyncUserCallback user_callback;
2089         gpointer user_data;
2090         GDestroyNotify notify;
2091 } GetFullMsgsInfo;
2092
2093 typedef struct {
2094         GetMsgAsyncUserCallback user_callback;
2095         TnyHeader *header;
2096         TnyMsg *msg;
2097         gpointer user_data;
2098         ModestMailOperation *mail_op;
2099 } NotifyGetMsgsInfo;
2100
2101
2102 /* 
2103  * Used by get_msgs_full_thread to call the user_callback for each
2104  * message that has been read
2105  */
2106 static gboolean
2107 notify_get_msgs_full (gpointer data)
2108 {
2109         NotifyGetMsgsInfo *info;
2110
2111         info = (NotifyGetMsgsInfo *) data;      
2112
2113         /* Call the user callback. Idles are not in the main lock, so
2114            lock it */
2115         gdk_threads_enter ();
2116         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2117         gdk_threads_leave ();
2118
2119         g_slice_free (NotifyGetMsgsInfo, info);
2120
2121         return FALSE;
2122 }
2123
2124 /* 
2125  * Used by get_msgs_full_thread to free al the thread resources and to
2126  * call the destroy function for the passed user_data
2127  */
2128 static gboolean
2129 get_msgs_full_destroyer (gpointer data)
2130 {
2131         GetFullMsgsInfo *info;
2132
2133         info = (GetFullMsgsInfo *) data;
2134
2135         if (info->notify) {
2136                 gdk_threads_enter ();   
2137                 info->notify (info->user_data);
2138                 gdk_threads_leave ();
2139         }
2140
2141         /* free */
2142         g_object_unref (info->headers);
2143         g_slice_free (GetFullMsgsInfo, info);
2144
2145         return FALSE;
2146 }
2147
2148 static gpointer
2149 get_msgs_full_thread (gpointer thr_user_data)
2150 {
2151         GetFullMsgsInfo *info;
2152         ModestMailOperationPrivate *priv = NULL;
2153         TnyIterator *iter = NULL;
2154         
2155         info = (GetFullMsgsInfo *) thr_user_data;       
2156         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2157
2158         iter = tny_list_create_iterator (info->headers);
2159         while (!tny_iterator_is_done (iter)) { 
2160                 TnyHeader *header;
2161                 TnyFolder *folder;
2162                 
2163                 header = TNY_HEADER (tny_iterator_get_current (iter));
2164                 folder = tny_header_get_folder (header);
2165                                 
2166                 /* Get message from folder */
2167                 if (folder) {
2168                         TnyMsg *msg;
2169                         /* The callback will call it per each header */
2170                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2171
2172                         if (msg) {
2173                                 ModestMailOperationState *state;
2174                                 ModestPair *pair;
2175
2176                                 priv->done++;
2177
2178                                 /* notify progress */
2179                                 state = modest_mail_operation_clone_state (info->mail_op);
2180                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2181                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2182                                                  pair, (GDestroyNotify) modest_pair_free);
2183
2184                                 /* The callback is the responsible for
2185                                    freeing the message */
2186                                 if (info->user_callback) {
2187                                         NotifyGetMsgsInfo *info_notify;
2188                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2189                                         info_notify->user_callback = info->user_callback;
2190                                         info_notify->mail_op = info->mail_op;
2191                                         info_notify->header = g_object_ref (header);
2192                                         info_notify->msg = g_object_ref (msg);
2193                                         info_notify->user_data = info->user_data;
2194                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2195                                                          notify_get_msgs_full, 
2196                                                          info_notify, NULL);
2197                                 }
2198                                 g_object_unref (msg);
2199                         } 
2200                 } else {
2201                         /* Set status failed and set an error */
2202                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2203                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2204                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2205                                      "Error trying to get a message. No folder found for header");
2206                 }
2207                 g_object_unref (header);                
2208                 tny_iterator_next (iter);
2209         }
2210
2211         /* Set operation status */
2212         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2213                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2214
2215         /* Notify about operation end */
2216         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2217
2218         /* Free thread resources. Will be called after all previous idles */
2219         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2220
2221         return NULL;
2222 }
2223
2224 void 
2225 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2226                                      TnyList *header_list, 
2227                                      GetMsgAsyncUserCallback user_callback,
2228                                      gpointer user_data,
2229                                      GDestroyNotify notify)
2230 {
2231         TnyHeader *header = NULL;
2232         TnyFolder *folder = NULL;
2233         GThread *thread;
2234         ModestMailOperationPrivate *priv = NULL;
2235         GetFullMsgsInfo *info = NULL;
2236         gboolean size_ok = TRUE;
2237         gint max_size;
2238         TnyIterator *iter = NULL;
2239         
2240         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2241         
2242         /* Init mail operation */
2243         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2244         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2245         priv->done = 0;
2246         priv->total = tny_list_get_length(header_list);
2247
2248         /* Get account and set it into mail_operation */
2249         if (tny_list_get_length (header_list) >= 1) {
2250                 iter = tny_list_create_iterator (header_list);
2251                 header = TNY_HEADER (tny_iterator_get_current (iter));
2252                 folder = tny_header_get_folder (header);                
2253                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2254                 g_object_unref (header);
2255                 g_object_unref (folder);
2256
2257                 if (tny_list_get_length (header_list) == 1) {
2258                         g_object_unref (iter);
2259                         iter = NULL;
2260                 }
2261         }
2262
2263         /* Get msg size limit */
2264         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2265                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2266                                          &(priv->error));
2267         if (priv->error) {
2268                 g_clear_error (&(priv->error));
2269                 max_size = G_MAXINT;
2270         } else {
2271                 max_size = max_size * KB;
2272         }
2273
2274         /* Check message size limits. If there is only one message
2275            always retrieve it */
2276         if (iter != NULL) {
2277                 while (!tny_iterator_is_done (iter) && size_ok) {
2278                         header = TNY_HEADER (tny_iterator_get_current (iter));
2279                         if (tny_header_get_message_size (header) >= max_size)
2280                                 size_ok = FALSE;
2281                         g_object_unref (header);
2282                         tny_iterator_next (iter);
2283                 }
2284                 g_object_unref (iter);
2285         }
2286
2287         if (size_ok) {
2288                 /* Create the info */
2289                 info = g_slice_new0 (GetFullMsgsInfo);
2290                 info->mail_op = self;
2291                 info->user_callback = user_callback;
2292                 info->user_data = user_data;
2293                 info->headers = g_object_ref (header_list);
2294                 info->notify = notify;
2295
2296                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2297         } else {
2298                 /* Set status failed and set an error */
2299                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2300                 /* FIXME: the error msg is different for pop */
2301                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2302                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2303                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2304                 /* Remove from queue and free resources */
2305                 modest_mail_operation_notify_end (self);
2306                 if (notify)
2307                         notify (user_data);
2308         }
2309 }
2310
2311
2312 void 
2313 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
2314                                   gboolean remove_to_trash /*ignored*/)
2315 {
2316         TnyFolder *folder;
2317         ModestMailOperationPrivate *priv;
2318
2319         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2320         g_return_if_fail (TNY_IS_HEADER (header));
2321
2322         if (remove_to_trash)
2323                 g_warning ("remove to trash is not implemented");
2324
2325         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2326         folder = tny_header_get_folder (header);
2327
2328         /* Get account and set it into mail_operation */
2329         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2330
2331         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2332
2333
2334         tny_folder_remove_msg (folder, header, &(priv->error));
2335         if (!priv->error) {
2336                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2337                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2338
2339                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2340                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2341                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2342                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2343                 else
2344                         /* lcoal folders */
2345                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2346         }
2347         
2348         
2349         /* Set status */
2350         if (!priv->error)
2351                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2352         else
2353                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2354
2355         /* Free */
2356         g_object_unref (G_OBJECT (folder));
2357
2358         /* Notify about operation end */
2359         modest_mail_operation_notify_end (self);
2360 }
2361
2362 static void
2363 transfer_msgs_status_cb (GObject *obj,
2364                          TnyStatus *status,  
2365                          gpointer user_data)
2366 {
2367         XFerMsgAsyncHelper *helper = NULL;
2368         ModestMailOperation *self;
2369         ModestMailOperationPrivate *priv;
2370         ModestMailOperationState *state;
2371
2372
2373         g_return_if_fail (status != NULL);
2374         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2375
2376         helper = (XFerMsgAsyncHelper *) user_data;
2377         g_return_if_fail (helper != NULL);       
2378
2379         self = helper->mail_op;
2380         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2381
2382         priv->done = status->position;
2383         priv->total = status->of_total;
2384
2385         state = modest_mail_operation_clone_state (self);
2386         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2387         g_slice_free (ModestMailOperationState, state);
2388 }
2389
2390
2391 static void
2392 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2393 {
2394         XFerMsgAsyncHelper *helper;
2395         ModestMailOperation *self;
2396         ModestMailOperationPrivate *priv;
2397
2398         helper = (XFerMsgAsyncHelper *) user_data;
2399         self = helper->mail_op;
2400
2401         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2402
2403         if (*err) {
2404                 priv->error = g_error_copy (*err);
2405                 priv->done = 0;
2406                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2407         } else if (cancelled) {
2408                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2409                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2410                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2411                              _("Error trying to refresh the contents of %s"),
2412                              tny_folder_get_name (folder));
2413         } else {
2414                 priv->done = 1;
2415                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2416         }
2417
2418         /* Notify about operation end */
2419         modest_mail_operation_notify_end (self);
2420
2421         /* If user defined callback function was defined, call it */
2422         if (helper->user_callback) {
2423                 gdk_threads_enter ();
2424                 helper->user_callback (priv->source, helper->user_data);
2425                 gdk_threads_leave ();
2426         }
2427
2428         /* Free */
2429         g_object_unref (helper->headers);
2430         g_object_unref (helper->dest_folder);
2431         g_object_unref (helper->mail_op);
2432         g_slice_free   (XFerMsgAsyncHelper, helper);
2433         g_object_unref (folder);
2434
2435 }
2436
2437 void
2438 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2439                                  TnyList *headers, 
2440                                  TnyFolder *folder, 
2441                                  gboolean delete_original,
2442                                  XferMsgsAsynUserCallback user_callback,
2443                                  gpointer user_data)
2444 {
2445         ModestMailOperationPrivate *priv;
2446         TnyIterator *iter;
2447         TnyFolder *src_folder;
2448         XFerMsgAsyncHelper *helper;
2449         TnyHeader *header;
2450         ModestTnyFolderRules rules;
2451         const gchar *id1 = NULL;
2452         const gchar *id2 = NULL;
2453         gboolean same_folder = FALSE;
2454
2455         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2456         g_return_if_fail (TNY_IS_LIST (headers));
2457         g_return_if_fail (TNY_IS_FOLDER (folder));
2458
2459         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2460         priv->total = 1;
2461         priv->done = 0;
2462         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2463
2464         /* Apply folder rules */
2465         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2466         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2467                 /* Set status failed and set an error */
2468                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2469                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2470                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2471                              _CS("ckct_ib_unable_to_paste_here"));
2472                 /* Notify the queue */
2473                 modest_mail_operation_notify_end (self);
2474                 return;
2475         }
2476                 
2477         /* Get source folder */
2478         iter = tny_list_create_iterator (headers);
2479         header = TNY_HEADER (tny_iterator_get_current (iter));
2480         src_folder = tny_header_get_folder (header);
2481         g_object_unref (header);
2482         g_object_unref (iter);
2483
2484         /* Check folder source and destination */
2485         id1 = tny_folder_get_id (src_folder);
2486         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2487         same_folder = !g_ascii_strcasecmp (id1, id2);
2488         if (same_folder) {
2489                 /* Set status failed and set an error */
2490                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2491                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2492                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2493                              _("mcen_ib_unable_to_copy_samefolder"));
2494                 
2495                 /* Notify the queue */
2496                 modest_mail_operation_notify_end (self);
2497                 
2498                 /* Free */
2499                 g_object_unref (src_folder);            
2500                 return;
2501         }
2502
2503         /* Create the helper */
2504         helper = g_slice_new0 (XFerMsgAsyncHelper);
2505         helper->mail_op = g_object_ref(self);
2506         helper->dest_folder = g_object_ref(folder);
2507         helper->headers = g_object_ref(headers);
2508         helper->user_callback = user_callback;
2509         helper->user_data = user_data;
2510
2511         /* Get account and set it into mail_operation */
2512         priv->account = modest_tny_folder_get_account (src_folder);
2513
2514         /* Transfer messages */
2515         tny_folder_transfer_msgs_async (src_folder, 
2516                                         headers, 
2517                                         folder, 
2518                                         delete_original, 
2519                                         transfer_msgs_cb, 
2520                                         transfer_msgs_status_cb,
2521                                         helper);
2522 }
2523
2524
2525 static void
2526 on_refresh_folder (TnyFolder   *folder, 
2527                    gboolean     cancelled, 
2528                    GError     **error,
2529                    gpointer     user_data)
2530 {
2531         RefreshAsyncHelper *helper = NULL;
2532         ModestMailOperation *self = NULL;
2533         ModestMailOperationPrivate *priv = NULL;
2534
2535         helper = (RefreshAsyncHelper *) user_data;
2536         self = helper->mail_op;
2537         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2538
2539         if (*error) {
2540                 priv->error = g_error_copy (*error);
2541                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2542                 goto out;
2543         }
2544
2545         if (cancelled) {
2546                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2547                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2548                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2549                              _("Error trying to refresh the contents of %s"),
2550                              tny_folder_get_name (folder));
2551                 goto out;
2552         }
2553
2554         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2555  out:
2556         /* Call user defined callback, if it exists */
2557         if (helper->user_callback) {
2558                 gdk_threads_enter ();
2559                 helper->user_callback (self, folder, helper->user_data);
2560                 gdk_threads_leave ();
2561         }
2562
2563         /* Free */
2564         g_object_unref (helper->mail_op);
2565         g_slice_free   (RefreshAsyncHelper, helper);
2566
2567         /* Notify about operation end */
2568         modest_mail_operation_notify_end (self);
2569 }
2570
2571 static void
2572 on_refresh_folder_status_update (GObject *obj,
2573                                  TnyStatus *status,
2574                                  gpointer user_data)
2575 {
2576         RefreshAsyncHelper *helper = NULL;
2577         ModestMailOperation *self = NULL;
2578         ModestMailOperationPrivate *priv = NULL;
2579         ModestMailOperationState *state;
2580
2581         g_return_if_fail (user_data != NULL);
2582         g_return_if_fail (status != NULL);
2583         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2584
2585         helper = (RefreshAsyncHelper *) user_data;
2586         self = helper->mail_op;
2587         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2588
2589         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2590
2591         priv->done = status->position;
2592         priv->total = status->of_total;
2593
2594         state = modest_mail_operation_clone_state (self);
2595         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2596         g_slice_free (ModestMailOperationState, state);
2597 }
2598
2599 void 
2600 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2601                                        TnyFolder *folder,
2602                                        RefreshAsyncUserCallback user_callback,
2603                                        gpointer user_data)
2604 {
2605         ModestMailOperationPrivate *priv = NULL;
2606         RefreshAsyncHelper *helper = NULL;
2607
2608         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2609
2610         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2611
2612         /* Get account and set it into mail_operation */
2613         priv->account = modest_tny_folder_get_account  (folder);
2614
2615         /* Create the helper */
2616         helper = g_slice_new0 (RefreshAsyncHelper);
2617         helper->mail_op = g_object_ref(self);
2618         helper->user_callback = user_callback;
2619         helper->user_data = user_data;
2620
2621         /* Refresh the folder. TODO: tinymail could issue a status
2622            updates before the callback call then this could happen. We
2623            must review the design */
2624         tny_folder_refresh_async (folder,
2625                                   on_refresh_folder,
2626                                   on_refresh_folder_status_update,
2627                                   helper);
2628 }
2629
2630 /**
2631  *
2632  * It's used by the mail operation queue to notify the observers
2633  * attached to that signal that the operation finished. We need to use
2634  * that because tinymail does not give us the progress of a given
2635  * operation when it finishes (it directly calls the operation
2636  * callback).
2637  */
2638 static void
2639 modest_mail_operation_notify_end (ModestMailOperation *self)
2640 {
2641         ModestMailOperationState *state;
2642         ModestMailOperationPrivate *priv = NULL;
2643
2644         g_return_if_fail (self);
2645
2646         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2647
2648         /* Set the account back to not busy */
2649         if (priv->account_name) {
2650                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2651                                                      priv->account_name, FALSE);
2652                 g_free(priv->account_name);
2653                 priv->account_name = NULL;
2654         }
2655         
2656         /* Notify the observers about the mail opertation end */
2657         state = modest_mail_operation_clone_state (self);
2658         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2659         g_slice_free (ModestMailOperationState, state);
2660 }