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