* Fixes NB#66132
[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 /**
1788  * This function checks if @ancestor is an acestor of @folder and
1789  * returns TRUE in that case
1790  */
1791 static gboolean
1792 folder_is_ancestor (TnyFolder *folder,
1793                     TnyFolderStore *ancestor)
1794 {
1795         TnyFolder *tmp = NULL;
1796         gboolean found = FALSE;
1797
1798         tmp = folder;
1799         while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1800                 TnyFolderStore *folder_store;
1801
1802                 folder_store = tny_folder_get_folder_store (tmp);
1803                 if (ancestor == folder_store)
1804                         found = TRUE;
1805                 else
1806                         tmp = g_object_ref (folder_store);
1807                 g_object_unref (folder_store);
1808         }
1809         return found;
1810 }
1811
1812 void
1813 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1814                                    TnyFolder *folder,
1815                                    TnyFolderStore *parent,
1816                                    gboolean delete_original,
1817                                    XferMsgsAsynUserCallback user_callback,
1818                                    gpointer user_data)
1819 {
1820         ModestMailOperationPrivate *priv = NULL;
1821         ModestTnyFolderRules parent_rules = 0, rules; 
1822         XFerMsgAsyncHelper *helper = NULL;
1823
1824         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1825         g_return_if_fail (TNY_IS_FOLDER (folder));
1826         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1827
1828         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1829
1830         /* Get account and set it into mail_operation */
1831         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1832         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1833
1834         /* Get folder rules */
1835         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1836         if (TNY_IS_FOLDER (parent))
1837                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1838         
1839         /* The moveable restriction is applied also to copy operation */
1840         if ((gpointer) parent == (gpointer) folder ||
1841             (!TNY_IS_FOLDER_STORE (parent)) || 
1842             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1843                 /* Set status failed and set an error */
1844                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1845                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1846                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1847                              _("mail_in_ui_folder_move_target_error"));
1848
1849                 /* Notify the queue */
1850                 modest_mail_operation_notify_end (self);
1851         } else if (TNY_IS_FOLDER (parent) && 
1852                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1853                 /* Set status failed and set an error */
1854                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1855                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1856                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1857                              _("FIXME: parent folder does not accept new folders"));
1858
1859                 /* Notify the queue */
1860                 modest_mail_operation_notify_end (self);
1861         } else if (TNY_IS_FOLDER (parent) &&
1862                    TNY_IS_FOLDER_STORE (folder) &&
1863                    folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1864                 /* Set status failed and set an error */
1865                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1866                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1867                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1868                              _("mail_in_ui_folder_copy_target_error"));
1869
1870                 /* Notify the queue */
1871                 modest_mail_operation_notify_end (self);
1872         } else {
1873                 /* Check that the new folder name is not used by any
1874                    special local folder */
1875                 if (new_name_valid_if_local_account (priv, parent, 
1876                                                      tny_folder_get_name (folder))) {
1877                         /* Create the helper */
1878                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1879                         helper->mail_op = g_object_ref(self);
1880                         helper->dest_folder = NULL;
1881                         helper->headers = NULL;
1882                         helper->user_callback = user_callback;
1883                         helper->user_data = user_data;
1884                         
1885                         /* Move/Copy folder */          
1886                         tny_folder_copy_async (folder,
1887                                                parent,
1888                                                tny_folder_get_name (folder),
1889                                                delete_original,
1890                                                transfer_folder_cb,
1891                                                transfer_folder_status_cb,
1892                                                helper);
1893                 } else {
1894                         modest_mail_operation_notify_end (self);
1895                 }
1896         }
1897 }
1898
1899 void
1900 modest_mail_operation_rename_folder (ModestMailOperation *self,
1901                                      TnyFolder *folder,
1902                                      const gchar *name)
1903 {
1904         ModestMailOperationPrivate *priv;
1905         ModestTnyFolderRules rules;
1906         XFerMsgAsyncHelper *helper;
1907
1908         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1909         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1910         g_return_if_fail (name);
1911         
1912         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1913
1914         /* Get account and set it into mail_operation */
1915         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1916
1917         /* Check folder rules */
1918         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1919         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1920                 /* Set status failed and set an error */
1921                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1922                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1923                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1924                              _("FIXME: unable to rename"));
1925
1926                 /* Notify about operation end */
1927                 modest_mail_operation_notify_end (self);
1928         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1929                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1930                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1931                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1932                              _("FIXME: unable to rename"));
1933                 /* Notify about operation end */
1934                 modest_mail_operation_notify_end (self);
1935         } else {
1936                 TnyFolderStore *into;
1937
1938                 into = tny_folder_get_folder_store (folder);    
1939
1940                 /* Check that the new folder name is not used by any
1941                    special local folder */
1942                 if (new_name_valid_if_local_account (priv, into, name)) {
1943                         /* Create the helper */
1944                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1945                         helper->mail_op = g_object_ref(self);
1946                         helper->dest_folder = NULL;
1947                         helper->headers = NULL;
1948                         helper->user_callback = NULL;
1949                         helper->user_data = NULL;
1950                 
1951                         /* Rename. Camel handles folder subscription/unsubscription */
1952                         tny_folder_copy_async (folder, into, name, TRUE,
1953                                                transfer_folder_cb,
1954                                                transfer_folder_status_cb,
1955                                                helper);
1956                 } else {
1957                         modest_mail_operation_notify_end (self);
1958                 }
1959                 g_object_unref (into);
1960         }
1961 }
1962
1963 /* ******************************************************************* */
1964 /* **************************  MSG  ACTIONS  ************************* */
1965 /* ******************************************************************* */
1966
1967 void modest_mail_operation_get_msg (ModestMailOperation *self,
1968                                     TnyHeader *header,
1969                                     GetMsgAsyncUserCallback user_callback,
1970                                     gpointer user_data)
1971 {
1972         GetMsgAsyncHelper *helper = NULL;
1973         TnyFolder *folder;
1974         ModestMailOperationPrivate *priv;
1975         
1976         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1977         g_return_if_fail (TNY_IS_HEADER (header));
1978         
1979         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1980         folder = tny_header_get_folder (header);
1981
1982         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1983
1984         /* Get message from folder */
1985         if (folder) {
1986                 /* Get account and set it into mail_operation */
1987                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1988                 
1989                 /* Check for cached messages */
1990                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
1991                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1992                 else 
1993                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1994
1995                 helper = g_slice_new0 (GetMsgAsyncHelper);
1996                 helper->mail_op = self;
1997                 helper->user_callback = user_callback;
1998                 helper->user_data = user_data;
1999                 helper->header = g_object_ref (header);
2000
2001                 // The callback's reference so that the mail op is not
2002                 // finalized until the async operation is completed even if
2003                 // the user canceled the request meanwhile.
2004                 g_object_ref (G_OBJECT (helper->mail_op));
2005
2006                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2007
2008                 g_object_unref (G_OBJECT (folder));
2009         } else {
2010                 /* Set status failed and set an error */
2011                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2012                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2013                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2014                              _("Error trying to get a message. No folder found for header"));
2015
2016                 /* Notify the queue */
2017                 modest_mail_operation_notify_end (self);
2018         }
2019 }
2020
2021 static void
2022 get_msg_cb (TnyFolder *folder, 
2023             gboolean cancelled, 
2024             TnyMsg *msg, 
2025             GError *error, 
2026             gpointer user_data)
2027 {
2028         GetMsgAsyncHelper *helper = NULL;
2029         ModestMailOperation *self = NULL;
2030         ModestMailOperationPrivate *priv = NULL;
2031
2032         helper = (GetMsgAsyncHelper *) user_data;
2033         g_return_if_fail (helper != NULL);       
2034         self = helper->mail_op;
2035         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2036         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2037
2038         /* Check errors and cancel */
2039         if (error) {
2040                 priv->error = g_error_copy (error);
2041                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2042         } else if (cancelled) {
2043                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2044                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2045                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2046                              _("Error trying to refresh the contents of %s"),
2047                              tny_folder_get_name (folder));
2048         } else {
2049                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2050         }
2051
2052         /* If user defined callback function was defined, call it even
2053            if the operation failed*/
2054         if (helper->user_callback) {
2055                 /* This is not a GDK lock because we are a Tinymail callback
2056                  * which is already GDK locked by Tinymail */
2057
2058                 /* no gdk_threads_enter (), CHECKED */
2059                 helper->user_callback (self, helper->header, msg, helper->user_data);
2060                 /* no gdk_threads_leave (), CHECKED */
2061         }
2062
2063         /* Notify about operation end */
2064         modest_mail_operation_notify_end (self);
2065         /* Free */
2066         g_object_unref (helper->mail_op);
2067         g_object_unref (helper->header);
2068         g_slice_free (GetMsgAsyncHelper, helper);
2069                 
2070 }
2071
2072 static void     
2073 get_msg_status_cb (GObject *obj,
2074                    TnyStatus *status,  
2075                    gpointer user_data)
2076 {
2077         GetMsgAsyncHelper *helper = NULL;
2078         ModestMailOperation *self;
2079         ModestMailOperationPrivate *priv;
2080         ModestMailOperationState *state;
2081
2082         g_return_if_fail (status != NULL);
2083         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2084
2085         helper = (GetMsgAsyncHelper *) user_data;
2086         g_return_if_fail (helper != NULL);       
2087
2088         self = helper->mail_op;
2089         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2090
2091         priv->done = 1;
2092         priv->total = 1;
2093
2094         state = modest_mail_operation_clone_state (self);
2095         state->bytes_done = status->position;
2096         state->bytes_total = status->of_total;
2097
2098         /* This is not a GDK lock because we are a Tinymail callback
2099          * which is already GDK locked by Tinymail */
2100
2101         /* no gdk_threads_enter (), CHECKED */
2102         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2103         /* no gdk_threads_leave (), CHECKED */
2104
2105         g_slice_free (ModestMailOperationState, state);
2106 }
2107
2108 /****************************************************/
2109 typedef struct {
2110         ModestMailOperation *mail_op;
2111         TnyList *headers;
2112         GetMsgAsyncUserCallback user_callback;
2113         gpointer user_data;
2114         GDestroyNotify notify;
2115 } GetFullMsgsInfo;
2116
2117 typedef struct {
2118         GetMsgAsyncUserCallback user_callback;
2119         TnyHeader *header;
2120         TnyMsg *msg;
2121         gpointer user_data;
2122         ModestMailOperation *mail_op;
2123 } NotifyGetMsgsInfo;
2124
2125
2126 /* 
2127  * Used by get_msgs_full_thread to call the user_callback for each
2128  * message that has been read
2129  */
2130 static gboolean
2131 notify_get_msgs_full (gpointer data)
2132 {
2133         NotifyGetMsgsInfo *info;
2134
2135         info = (NotifyGetMsgsInfo *) data;      
2136
2137         /* This is a GDK lock because we are an idle callback and
2138          * because info->user_callback can contain Gtk+ code */
2139
2140         gdk_threads_enter (); /* CHECKED */
2141         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2142         gdk_threads_leave (); /* CHECKED */
2143
2144         g_slice_free (NotifyGetMsgsInfo, info);
2145
2146         return FALSE;
2147 }
2148
2149 /* 
2150  * Used by get_msgs_full_thread to free al the thread resources and to
2151  * call the destroy function for the passed user_data
2152  */
2153 static gboolean
2154 get_msgs_full_destroyer (gpointer data)
2155 {
2156         GetFullMsgsInfo *info;
2157
2158         info = (GetFullMsgsInfo *) data;
2159
2160         if (info->notify) {
2161
2162                 /* This is a GDK lock because we are an idle callback and
2163                  * because info->notify can contain Gtk+ code */
2164
2165                 gdk_threads_enter (); /* CHECKED */
2166                 info->notify (info->user_data);
2167                 gdk_threads_leave (); /* CHECKED */
2168         }
2169
2170         /* free */
2171         g_object_unref (info->headers);
2172         g_slice_free (GetFullMsgsInfo, info);
2173
2174         return FALSE;
2175 }
2176
2177 static gpointer
2178 get_msgs_full_thread (gpointer thr_user_data)
2179 {
2180         GetFullMsgsInfo *info;
2181         ModestMailOperationPrivate *priv = NULL;
2182         TnyIterator *iter = NULL;
2183         
2184         info = (GetFullMsgsInfo *) thr_user_data;       
2185         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2186
2187         iter = tny_list_create_iterator (info->headers);
2188         while (!tny_iterator_is_done (iter)) { 
2189                 TnyHeader *header;
2190                 TnyFolder *folder;
2191                 
2192                 header = TNY_HEADER (tny_iterator_get_current (iter));
2193                 folder = tny_header_get_folder (header);
2194                                 
2195                 /* Check for cached messages */
2196                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2197                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2198                 else 
2199                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2200
2201                 /* Get message from folder */
2202                 if (folder) {
2203                         TnyMsg *msg;
2204                         /* The callback will call it per each header */
2205                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2206
2207                         if (msg) {
2208                                 ModestMailOperationState *state;
2209                                 ModestPair *pair;
2210
2211                                 priv->done++;
2212
2213                                 /* notify progress */
2214                                 state = modest_mail_operation_clone_state (info->mail_op);
2215                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2216                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2217                                                  pair, (GDestroyNotify) modest_pair_free);
2218
2219                                 /* The callback is the responsible for
2220                                    freeing the message */
2221                                 if (info->user_callback) {
2222                                         NotifyGetMsgsInfo *info_notify;
2223                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2224                                         info_notify->user_callback = info->user_callback;
2225                                         info_notify->mail_op = info->mail_op;
2226                                         info_notify->header = g_object_ref (header);
2227                                         info_notify->msg = g_object_ref (msg);
2228                                         info_notify->user_data = info->user_data;
2229                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2230                                                          notify_get_msgs_full, 
2231                                                          info_notify, NULL);
2232                                 }
2233                                 g_object_unref (msg);
2234                         } 
2235                 } else {
2236                         /* Set status failed and set an error */
2237                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2238                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2239                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2240                                      "Error trying to get a message. No folder found for header");
2241                 }
2242
2243                 if (header)
2244                         g_object_unref (header);
2245                 
2246                 tny_iterator_next (iter);
2247         }
2248
2249         /* Set operation status */
2250         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2251                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2252
2253         /* Notify about operation end */
2254         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2255
2256         /* Free thread resources. Will be called after all previous idles */
2257         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2258
2259         return NULL;
2260 }
2261
2262 void 
2263 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2264                                      TnyList *header_list, 
2265                                      GetMsgAsyncUserCallback user_callback,
2266                                      gpointer user_data,
2267                                      GDestroyNotify notify)
2268 {
2269         TnyHeader *header = NULL;
2270         TnyFolder *folder = NULL;
2271         GThread *thread;
2272         ModestMailOperationPrivate *priv = NULL;
2273         GetFullMsgsInfo *info = NULL;
2274         gboolean size_ok = TRUE;
2275         gint max_size;
2276         TnyIterator *iter = NULL;
2277         
2278         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2279         
2280         /* Init mail operation */
2281         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2282         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2283         priv->done = 0;
2284         priv->total = tny_list_get_length(header_list);
2285
2286         /* Get account and set it into mail_operation */
2287         if (tny_list_get_length (header_list) >= 1) {
2288                 iter = tny_list_create_iterator (header_list);
2289                 header = TNY_HEADER (tny_iterator_get_current (iter));
2290                 if (header) {
2291                         folder = tny_header_get_folder (header);
2292                         if (folder) {           
2293                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2294
2295                                 g_object_unref (folder);
2296                         }
2297
2298                         g_object_unref (header);
2299                 }
2300
2301                 if (tny_list_get_length (header_list) == 1) {
2302                         g_object_unref (iter);
2303                         iter = NULL;
2304                 }
2305         }
2306
2307         /* Get msg size limit */
2308         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2309                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2310                                          &(priv->error));
2311         if (priv->error) {
2312                 g_clear_error (&(priv->error));
2313                 max_size = G_MAXINT;
2314         } else {
2315                 max_size = max_size * KB;
2316         }
2317
2318         /* Check message size limits. If there is only one message
2319            always retrieve it */
2320         if (iter != NULL) {
2321                 while (!tny_iterator_is_done (iter) && size_ok) {
2322                         header = TNY_HEADER (tny_iterator_get_current (iter));
2323                         if (header) {
2324                                 if (tny_header_get_message_size (header) >= max_size)
2325                                         size_ok = FALSE;
2326                                 g_object_unref (header);
2327                         }
2328
2329                         tny_iterator_next (iter);
2330                 }
2331                 g_object_unref (iter);
2332         }
2333
2334         if (size_ok) {
2335                 /* Create the info */
2336                 info = g_slice_new0 (GetFullMsgsInfo);
2337                 info->mail_op = self;
2338                 info->user_callback = user_callback;
2339                 info->user_data = user_data;
2340                 info->headers = g_object_ref (header_list);
2341                 info->notify = notify;
2342
2343                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2344         } else {
2345                 /* Set status failed and set an error */
2346                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2347                 /* FIXME: the error msg is different for pop */
2348                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2349                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2350                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2351                 /* Remove from queue and free resources */
2352                 modest_mail_operation_notify_end (self);
2353                 if (notify)
2354                         notify (user_data);
2355         }
2356 }
2357
2358
2359 void 
2360 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
2361                                   gboolean remove_to_trash /*ignored*/)
2362 {
2363         TnyFolder *folder;
2364         ModestMailOperationPrivate *priv;
2365
2366         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2367         g_return_if_fail (TNY_IS_HEADER (header));
2368
2369         if (remove_to_trash)
2370                 g_warning ("remove to trash is not implemented");
2371
2372         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2373         folder = tny_header_get_folder (header);
2374
2375         /* Get account and set it into mail_operation */
2376         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2377
2378         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2379
2380         /* remove message from folder */
2381         tny_folder_remove_msg (folder, header, &(priv->error));
2382 /*      if (!priv->error) { */
2383 /*              tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
2384 /*              tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
2385
2386 /*              if (TNY_IS_CAMEL_IMAP_FOLDER (folder)) */
2387 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2388 /* /\*                  tny_folder_sync (folder, FALSE, &(priv->error)); /\\* FALSE --> don't expunge *\\/ *\/ */
2389 /*              else if (TNY_IS_CAMEL_POP_FOLDER (folder)) */
2390 /*                      tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2391 /* /\*                  tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2392 /*              else */
2393 /*                      /\* local folders *\/ */
2394 /*                      tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2395 /* /\*                  tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2396 /*      } */
2397         
2398         
2399         /* Set status */
2400         if (!priv->error)
2401                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2402         else
2403                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2404
2405         /* Free */
2406         g_object_unref (G_OBJECT (folder));
2407
2408         /* Notify about operation end */
2409         modest_mail_operation_notify_end (self);
2410 }
2411
2412 static void
2413 transfer_msgs_status_cb (GObject *obj,
2414                          TnyStatus *status,  
2415                          gpointer user_data)
2416 {
2417         XFerMsgAsyncHelper *helper = NULL;
2418         ModestMailOperation *self;
2419         ModestMailOperationPrivate *priv;
2420         ModestMailOperationState *state;
2421
2422
2423         g_return_if_fail (status != NULL);
2424         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2425
2426         helper = (XFerMsgAsyncHelper *) user_data;
2427         g_return_if_fail (helper != NULL);       
2428
2429         self = helper->mail_op;
2430         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2431
2432         priv->done = status->position;
2433         priv->total = status->of_total;
2434
2435         state = modest_mail_operation_clone_state (self);
2436
2437         /* This is not a GDK lock because we are a Tinymail callback and
2438          * Tinymail already acquires the Gdk lock */
2439
2440         /* no gdk_threads_enter (), CHECKED */
2441
2442         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2443
2444         /* no gdk_threads_leave (), CHECKED */
2445
2446         g_slice_free (ModestMailOperationState, state);
2447 }
2448
2449
2450 static void
2451 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2452 {
2453         XFerMsgAsyncHelper *helper;
2454         ModestMailOperation *self;
2455         ModestMailOperationPrivate *priv;
2456         TnyIterator *iter = NULL;
2457         TnyHeader *header = NULL;
2458
2459         helper = (XFerMsgAsyncHelper *) user_data;
2460         self = helper->mail_op;
2461
2462         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2463
2464         if (err) {
2465                 priv->error = g_error_copy (err);
2466                 priv->done = 0;
2467                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2468         } else if (cancelled) {
2469                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2470                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2471                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2472                              _("Error trying to refresh the contents of %s"),
2473                              tny_folder_get_name (folder));
2474         } else {
2475                 priv->done = 1;
2476                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2477         }
2478
2479         
2480         /* Mark headers as deleted and seen */
2481         if ((helper->delete) && 
2482             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2483                 iter = tny_list_create_iterator (helper->headers);
2484                 while (!tny_iterator_is_done (iter)) {
2485                         header = TNY_HEADER (tny_iterator_get_current (iter));
2486                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2487                         tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2488                         g_object_unref (header);
2489
2490                         tny_iterator_next (iter);
2491                 }
2492
2493         }
2494                 
2495
2496         /* Notify about operation end */
2497         modest_mail_operation_notify_end (self);
2498
2499         /* If user defined callback function was defined, call it */
2500         if (helper->user_callback) {
2501                 /* This is not a GDK lock because we are a Tinymail callback and
2502                  * Tinymail already acquires the Gdk lock */
2503
2504                 /* no gdk_threads_enter (), CHECKED */
2505                 helper->user_callback (priv->source, helper->user_data);
2506                 /* no gdk_threads_leave (), CHECKED */
2507         }
2508
2509         /* Free */
2510         if (helper->headers)
2511                 g_object_unref (helper->headers);
2512         if (helper->dest_folder)
2513                 g_object_unref (helper->dest_folder);
2514         if (helper->mail_op)
2515                 g_object_unref (helper->mail_op);
2516         if (folder)
2517                 g_object_unref (folder);
2518         if (iter)
2519                 g_object_unref (iter);
2520         g_slice_free   (XFerMsgAsyncHelper, helper);
2521
2522 }
2523
2524 void
2525 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2526                                  TnyList *headers, 
2527                                  TnyFolder *folder, 
2528                                  gboolean delete_original,
2529                                  XferMsgsAsynUserCallback user_callback,
2530                                  gpointer user_data)
2531 {
2532         ModestMailOperationPrivate *priv = NULL;
2533         TnyIterator *iter = NULL;
2534         TnyFolder *src_folder = NULL;
2535         XFerMsgAsyncHelper *helper = NULL;
2536         TnyHeader *header = NULL;
2537         ModestTnyFolderRules rules = 0;
2538         const gchar *id1 = NULL;
2539         const gchar *id2 = NULL;
2540         gboolean same_folder = FALSE;
2541
2542         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2543         g_return_if_fail (TNY_IS_LIST (headers));
2544         g_return_if_fail (TNY_IS_FOLDER (folder));
2545
2546         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2547         priv->total = 1;
2548         priv->done = 0;
2549         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2550
2551         /* Apply folder rules */
2552         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2553         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2554                 /* Set status failed and set an error */
2555                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2556                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2557                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2558                              _CS("ckct_ib_unable_to_paste_here"));
2559                 /* Notify the queue */
2560                 modest_mail_operation_notify_end (self);
2561                 return;
2562         }
2563                 
2564         /* Get source folder */
2565         iter = tny_list_create_iterator (headers);
2566         header = TNY_HEADER (tny_iterator_get_current (iter));
2567         if (header) {
2568                 src_folder = tny_header_get_folder (header);
2569                 g_object_unref (header);
2570         }
2571
2572         g_object_unref (iter);
2573
2574         /* Check folder source and destination */
2575         id1 = tny_folder_get_id (src_folder);
2576         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2577         same_folder = !g_ascii_strcasecmp (id1, id2);
2578         if (same_folder) {
2579                 /* Set status failed and set an error */
2580                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2581                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2582                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2583                              _("mcen_ib_unable_to_copy_samefolder"));
2584                 
2585                 /* Notify the queue */
2586                 modest_mail_operation_notify_end (self);
2587                 
2588                 /* Free */
2589                 g_object_unref (src_folder);            
2590                 return;
2591         }
2592
2593         /* Create the helper */
2594         helper = g_slice_new0 (XFerMsgAsyncHelper);
2595         helper->mail_op = g_object_ref(self);
2596         helper->dest_folder = g_object_ref(folder);
2597         helper->headers = g_object_ref(headers);
2598         helper->user_callback = user_callback;
2599         helper->user_data = user_data;
2600         helper->delete = delete_original;
2601
2602         /* Get account and set it into mail_operation */
2603         priv->account = modest_tny_folder_get_account (src_folder);
2604
2605         /* Transfer messages */
2606         tny_folder_transfer_msgs_async (src_folder, 
2607                                         headers, 
2608                                         folder, 
2609                                         delete_original, 
2610                                         transfer_msgs_cb, 
2611                                         transfer_msgs_status_cb,
2612                                         helper);
2613 }
2614
2615
2616 static void
2617 on_refresh_folder (TnyFolder   *folder, 
2618                    gboolean     cancelled, 
2619                    GError     *error,
2620                    gpointer     user_data)
2621 {
2622         RefreshAsyncHelper *helper = NULL;
2623         ModestMailOperation *self = NULL;
2624         ModestMailOperationPrivate *priv = NULL;
2625
2626         helper = (RefreshAsyncHelper *) user_data;
2627         self = helper->mail_op;
2628         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2629
2630         g_return_if_fail(priv!=NULL);
2631
2632         if (error) {
2633                 priv->error = g_error_copy (error);
2634                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2635                 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2636                        error->message);
2637                 goto out;
2638         }
2639
2640         if (cancelled) {
2641                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2642                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2643                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2644                              _("Error trying to refresh the contents of %s"),
2645                              tny_folder_get_name (folder));
2646                 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2647                 goto out;
2648         }
2649
2650         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2651
2652         /* Call user defined callback, if it exists */
2653         if (helper->user_callback) {
2654
2655                 /* This is not a GDK lock because we are a Tinymail callback and
2656                  * Tinymail already acquires the Gdk lock */
2657
2658                 /* no gdk_threads_enter (), CHECKED */
2659                 helper->user_callback (self, folder, helper->user_data);
2660                 /* no gdk_threads_leave (), CHECKED */
2661         }
2662
2663  out:
2664         /* Free */
2665         g_slice_free   (RefreshAsyncHelper, helper);
2666
2667         /* Notify about operation end */
2668         modest_mail_operation_notify_end (self);
2669         g_object_unref(self);
2670 }
2671
2672 static void
2673 on_refresh_folder_status_update (GObject *obj,
2674                                  TnyStatus *status,
2675                                  gpointer user_data)
2676 {
2677         RefreshAsyncHelper *helper = NULL;
2678         ModestMailOperation *self = NULL;
2679         ModestMailOperationPrivate *priv = NULL;
2680         ModestMailOperationState *state;
2681
2682         g_return_if_fail (user_data != NULL);
2683         g_return_if_fail (status != NULL);
2684         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2685
2686         helper = (RefreshAsyncHelper *) user_data;
2687         self = helper->mail_op;
2688         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2689
2690         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2691
2692         priv->done = status->position;
2693         priv->total = status->of_total;
2694
2695         state = modest_mail_operation_clone_state (self);
2696
2697         /* This is not a GDK lock because we are a Tinymail callback and
2698          * Tinymail already acquires the Gdk lock */
2699
2700         /* no gdk_threads_enter (), CHECKED */
2701
2702         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2703
2704         /* no gdk_threads_leave (), CHECKED */
2705
2706         g_slice_free (ModestMailOperationState, state);
2707 }
2708
2709 void 
2710 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2711                                        TnyFolder *folder,
2712                                        RefreshAsyncUserCallback user_callback,
2713                                        gpointer user_data)
2714 {
2715         ModestMailOperationPrivate *priv = NULL;
2716         RefreshAsyncHelper *helper = NULL;
2717
2718         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2719
2720         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2721
2722         /* Get account and set it into mail_operation */
2723         priv->account = modest_tny_folder_get_account  (folder);
2724
2725         /* Create the helper */
2726         helper = g_slice_new0 (RefreshAsyncHelper);
2727         helper->mail_op = g_object_ref(self);
2728         helper->user_callback = user_callback;
2729         helper->user_data = user_data;
2730
2731         /* Refresh the folder. TODO: tinymail could issue a status
2732            updates before the callback call then this could happen. We
2733            must review the design */
2734         tny_folder_refresh_async (folder,
2735                                   on_refresh_folder,
2736                                   on_refresh_folder_status_update,
2737                                   helper);
2738 }
2739
2740 /**
2741  *
2742  * It's used by the mail operation queue to notify the observers
2743  * attached to that signal that the operation finished. We need to use
2744  * that because tinymail does not give us the progress of a given
2745  * operation when it finishes (it directly calls the operation
2746  * callback).
2747  */
2748 static void
2749 modest_mail_operation_notify_end (ModestMailOperation *self)
2750 {
2751         ModestMailOperationState *state;
2752         ModestMailOperationPrivate *priv = NULL;
2753
2754         g_return_if_fail (self);
2755
2756         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2757
2758         /* Set the account back to not busy */
2759         if (priv->account_name) {
2760                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2761                                                      priv->account_name, FALSE);
2762                 g_free(priv->account_name);
2763                 priv->account_name = NULL;
2764         }
2765         
2766         /* Notify the observers about the mail operation end */
2767         /* We do not wrapp this emission because we assume that this
2768            function is always called from within the main lock */
2769         state = modest_mail_operation_clone_state (self);
2770         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2771         g_slice_free (ModestMailOperationState, state);
2772 }