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