* this one too, belongs to the prev. commit
[modest] / src / modest-mail-operation.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include <stdarg.h>
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-camel-pop-folder.h>
39 #include <tny-camel-imap-folder.h>
40 #include <tny-camel-mem-stream.h>
41 #include <tny-simple-list.h>
42 #include <tny-send-queue.h>
43 #include <tny-status.h>
44 #include <tny-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include <modest-tny-account.h>
49 #include <modest-tny-send-queue.h>
50 #include <modest-runtime.h>
51 #include "modest-text-utils.h"
52 #include "modest-tny-msg.h"
53 #include "modest-tny-folder.h"
54 #include "modest-tny-account-store.h"
55 #include "modest-tny-platform-factory.h"
56 #include "modest-marshal.h"
57 #include "modest-error.h"
58 #include "modest-mail-operation.h"
59
60 #define KB 1024
61 #define GET_SIZE_BUFFER_SIZE 128
62
63 /* 
64  * Remove all these #ifdef stuff when the tinymail's idle calls become
65  * locked
66  */
67 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
68
69 /* 'private'/'protected' functions */
70 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
71 static void modest_mail_operation_init       (ModestMailOperation *obj);
72 static void modest_mail_operation_finalize   (GObject *obj);
73
74 static void     get_msg_cb (TnyFolder *folder, 
75                             gboolean cancelled, 
76                             TnyMsg *msg, 
77                             GError **err, 
78                             gpointer user_data);
79
80 static void     get_msg_status_cb (GObject *obj,
81                                    TnyStatus *status,  
82                                    gpointer user_data);
83
84 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
85
86 enum _ModestMailOperationSignals 
87 {
88         PROGRESS_CHANGED_SIGNAL,
89
90         NUM_SIGNALS
91 };
92
93 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
94 struct _ModestMailOperationPrivate {
95         TnyAccount                 *account;
96         gchar                                                                            *account_name;
97         guint                      done;
98         guint                      total;
99         GObject                   *source;
100         GError                    *error;
101         ErrorCheckingUserCallback  error_checking;
102         gpointer                   error_checking_user_data;
103         ModestMailOperationStatus  status;      
104         ModestMailOperationTypeOperation op_type;
105 };
106
107 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
108                                                    MODEST_TYPE_MAIL_OPERATION, \
109                                                    ModestMailOperationPrivate))
110
111 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
112                                                    priv->status = new_status;\
113                                                }
114
115 typedef struct _GetMsgAsyncHelper {     
116         ModestMailOperation *mail_op;
117         TnyHeader *header;
118         GetMsgAsyncUserCallback user_callback;  
119         gpointer user_data;
120 } GetMsgAsyncHelper;
121
122 typedef struct _RefreshAsyncHelper {    
123         ModestMailOperation *mail_op;
124         RefreshAsyncUserCallback user_callback; 
125         gpointer user_data;
126 } RefreshAsyncHelper;
127
128 typedef struct _XFerMsgAsyncHelper
129 {
130         ModestMailOperation *mail_op;
131         TnyList *headers;
132         TnyFolder *dest_folder;
133         XferMsgsAsynUserCallback user_callback; 
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         gdk_threads_enter ();
579         info->callback (info->mail_op, info->msg, info->userdata);
580         gdk_threads_leave ();
581         g_object_unref (info->mail_op);
582         if (info->msg)
583                 g_object_unref (info->msg);
584         g_slice_free (CreateMsgIdleInfo, info);
585
586         return FALSE;
587 }
588
589 static gpointer 
590 create_msg_thread (gpointer thread_data)
591 {
592         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
593         TnyMsg *new_msg = NULL;
594         ModestMailOperationPrivate *priv;
595
596         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
597         if (info->html_body == NULL) {
598                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
599                                               info->bcc, info->subject, info->plain_body, 
600                                               info->attachments_list); /* FIXME: attachments */
601         } else {
602                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
603                                                          info->bcc, info->subject, info->html_body,
604                                                          info->plain_body, info->attachments_list);
605         }
606
607         if (new_msg) {
608                 TnyHeader *header;
609                 /* Set priority flags in message */
610                 header = tny_msg_get_header (new_msg);
611                 if (info->priority_flags != 0)
612                         tny_header_set_flags (header, info->priority_flags);
613                 if (info->attachments_list != NULL) {
614                         tny_header_set_flags (header, TNY_HEADER_FLAG_ATTACHMENTS);
615                 }
616                 g_object_unref (G_OBJECT(header));
617         } else {
618                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
619                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
620                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
621                              "modest: failed to create a new msg\n");
622         }
623
624
625         g_free (info->to);
626         g_free (info->from);
627         g_free (info->cc);
628         g_free (info->bcc);
629         g_free (info->plain_body);
630         g_free (info->html_body);
631         g_free (info->subject);
632         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
633         g_list_free (info->attachments_list);
634
635         if (info->callback) {
636                 CreateMsgIdleInfo *idle_info;
637                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
638                 idle_info->mail_op = info->mail_op;
639                 g_object_ref (info->mail_op);
640                 idle_info->msg = new_msg;
641                 if (new_msg)
642                         g_object_ref (new_msg);
643                 idle_info->callback = info->callback;
644                 idle_info->userdata = info->userdata;
645                 g_idle_add (idle_create_msg_cb, idle_info);
646         } else {
647                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
648         }
649
650         g_object_unref (info->mail_op);
651         g_slice_free (CreateMsgInfo, info);
652         return NULL;
653 }
654
655 void
656 modest_mail_operation_create_msg (ModestMailOperation *self,
657                                   const gchar *from, const gchar *to,
658                                   const gchar *cc, const gchar *bcc,
659                                   const gchar *subject, const gchar *plain_body,
660                                   const gchar *html_body,
661                                   const GList *attachments_list,
662                                   TnyHeaderFlags priority_flags,
663                                   ModestMailOperationCreateMsgCallback callback,
664                                   gpointer userdata)
665 {
666         CreateMsgInfo *info = NULL;
667
668         info = g_slice_new0 (CreateMsgInfo);
669         info->mail_op = self;
670         g_object_ref (self);
671
672         info->from = g_strdup (from);
673         info->to = g_strdup (to);
674         info->cc = g_strdup (cc);
675         info->bcc  = g_strdup (bcc);
676         info->subject = g_strdup (subject);
677         info->plain_body = g_strdup (plain_body);
678         info->html_body = g_strdup (html_body);
679         info->attachments_list = g_list_copy ((GList *) attachments_list);
680         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
681         info->priority_flags = priority_flags;
682
683         info->callback = callback;
684         info->userdata = userdata;
685
686         g_thread_create (create_msg_thread, info, FALSE, NULL);
687 }
688
689 typedef struct
690 {
691         TnyTransportAccount *transport_account;
692         TnyMsg *draft_msg;
693 } SendNewMailInfo;
694
695 static void
696 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
697                                         TnyMsg *msg,
698                                         gpointer userdata)
699 {
700         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
701         TnyFolder *draft_folder = NULL;
702         TnyFolder *outbox_folder = NULL;
703         TnyHeader *header;
704
705         if (!msg) {
706                 goto end;
707         }
708
709         /* Call mail operation */
710         modest_mail_operation_send_mail (self, info->transport_account, msg);
711
712         /* Remove old mail from its source folder */
713         draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
714         outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
715         if (info->draft_msg != NULL) {
716                 TnyFolder *folder = NULL;
717                 TnyFolder *src_folder = NULL;
718                 TnyFolderType folder_type;              
719                 folder = tny_msg_get_folder (info->draft_msg);          
720                 if (folder == NULL) goto end;
721                 folder_type = modest_tny_folder_guess_folder_type (folder);
722                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
723                         src_folder = outbox_folder;
724                 else 
725                         src_folder = draft_folder;
726
727                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
728                  * because this function requires it to have a UID. */          
729                 header = tny_msg_get_header (info->draft_msg);
730                 tny_folder_remove_msg (src_folder, header, NULL);
731                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
732                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
733                 g_object_unref (header);
734                 g_object_unref (folder);
735         }
736
737 end:
738         if (info->draft_msg)
739                 g_object_unref (info->draft_msg);
740         if (draft_folder)
741                 g_object_unref (draft_folder);
742         if (outbox_folder)
743                 g_object_unref (outbox_folder);
744         if (info->transport_account)
745                 g_object_unref (info->transport_account);
746         g_slice_free (SendNewMailInfo, info);
747         modest_mail_operation_notify_end (self);
748 }
749
750 void
751 modest_mail_operation_send_new_mail (ModestMailOperation *self,
752                                      TnyTransportAccount *transport_account,
753                                      TnyMsg *draft_msg,
754                                      const gchar *from,  const gchar *to,
755                                      const gchar *cc,  const gchar *bcc,
756                                      const gchar *subject, const gchar *plain_body,
757                                      const gchar *html_body,
758                                      const GList *attachments_list,
759                                      TnyHeaderFlags priority_flags)
760 {
761         ModestMailOperationPrivate *priv = NULL;
762         SendNewMailInfo *info;
763
764         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
765         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
766
767         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
768
769         /* Check parametters */
770         if (to == NULL) {
771                 /* Set status failed and set an error */
772                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
773                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
774                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
775                              _("Error trying to send a mail. You need to set at least one recipient"));
776                 return;
777         }
778         info = g_slice_new0 (SendNewMailInfo);
779         info->transport_account = transport_account;
780         if (transport_account)
781                 g_object_ref (transport_account);
782         info->draft_msg = draft_msg;
783         if (draft_msg)
784                 g_object_ref (draft_msg);
785         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
786                                           attachments_list, priority_flags,
787                                           modest_mail_operation_send_new_mail_cb, info);
788
789 }
790
791 typedef struct
792 {
793         TnyTransportAccount *transport_account;
794         TnyMsg *draft_msg;
795         ModestMsgEditWindow *edit_window;
796 } SaveToDraftsInfo;
797
798 static void
799 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
800                                          TnyMsg *msg,
801                                          gpointer userdata)
802 {
803         TnyFolder *src_folder = NULL;
804         TnyFolder *folder = NULL;
805         TnyHeader *header = NULL;
806         ModestMailOperationPrivate *priv = NULL;
807         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
808
809         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
810         if (!msg) {
811                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
812                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
813                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
814                              "modest: failed to create a new msg\n");
815                 goto end;
816         }
817
818         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
819         if (!folder) {
820                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
821                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
822                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
823                              "modest: failed to create a new msg\n");
824                 goto end;
825         }
826
827         if (info->draft_msg != NULL) {
828                 header = tny_msg_get_header (info->draft_msg);
829                 src_folder = tny_header_get_folder (header); 
830                 /* Remove the old draft expunging it */
831                 tny_folder_remove_msg (src_folder, header, NULL);
832                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
833                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
834                 tny_folder_sync (src_folder, TRUE, &(priv->error));  /* expunge */
835                 g_object_unref (header);
836         }
837         
838         if (!priv->error)
839                 tny_folder_add_msg (folder, msg, &(priv->error));
840
841         if (!priv->error)
842                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
843         else
844                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
845
846         if (info->edit_window)
847                 modest_msg_edit_window_set_draft (info->edit_window, msg);
848
849
850 end:
851         if (folder)
852                 g_object_unref (G_OBJECT(folder));
853         if (src_folder)
854                 g_object_unref (G_OBJECT(src_folder));
855         if (info->edit_window)
856                 g_object_unref (G_OBJECT(info->edit_window));
857         if (info->draft_msg)
858                 g_object_unref (G_OBJECT (info->draft_msg));
859         if (info->transport_account)
860                 g_object_unref (G_OBJECT(info->transport_account));
861         g_slice_free (SaveToDraftsInfo, info);
862
863         modest_mail_operation_notify_end (self);
864 }
865
866 void
867 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
868                                       TnyTransportAccount *transport_account,
869                                       TnyMsg *draft_msg,
870                                       ModestMsgEditWindow *edit_window,
871                                       const gchar *from,  const gchar *to,
872                                       const gchar *cc,  const gchar *bcc,
873                                       const gchar *subject, const gchar *plain_body,
874                                       const gchar *html_body,
875                                       const GList *attachments_list,
876                                       TnyHeaderFlags priority_flags)
877 {
878         ModestMailOperationPrivate *priv = NULL;
879         SaveToDraftsInfo *info = NULL;
880
881         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
882         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
883
884         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
885
886         /* Get account and set it into mail_operation */
887         priv->account = g_object_ref (transport_account);
888
889         info = g_slice_new0 (SaveToDraftsInfo);
890         info->transport_account = g_object_ref (transport_account);
891         info->draft_msg = draft_msg;
892         if (draft_msg)
893                 g_object_ref (draft_msg);
894         info->edit_window = edit_window;
895         if (edit_window)
896                 g_object_ref (edit_window);
897
898         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
899                                           attachments_list, priority_flags,
900                                           modest_mail_operation_save_to_drafts_cb, info);
901
902 }
903
904 typedef struct 
905 {
906         ModestMailOperation *mail_op;
907         TnyStoreAccount *account;
908         TnyTransportAccount *transport_account;
909         gint max_size;
910         gint retrieve_limit;
911         gchar *retrieve_type;
912         gchar *account_name;
913         UpdateAccountCallback callback;
914         gpointer user_data;
915         gint new_headers;
916 } UpdateAccountInfo;
917
918 typedef struct
919 {
920         ModestMailOperation *mail_op;
921         TnyMimePart *mime_part;
922         gssize size;
923         GetMimePartSizeCallback callback;
924         gpointer userdata;
925 } GetMimePartSizeInfo;
926
927 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
928 /* We use this folder observer to track the headers that have been
929  * added to a folder */
930 typedef struct {
931         GObject parent;
932         TnyList *new_headers;
933 } InternalFolderObserver;
934
935 typedef struct {
936         GObjectClass parent;
937 } InternalFolderObserverClass;
938
939 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
940
941 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
942                          internal_folder_observer,
943                          G_TYPE_OBJECT,
944                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
945
946
947 static void
948 foreach_add_item (gpointer header, gpointer user_data)
949 {
950         tny_list_prepend (TNY_LIST (user_data), 
951                           g_object_ref (G_OBJECT (header)));
952 }
953
954 /* This is the method that looks for new messages in a folder */
955 static void
956 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
957 {
958         InternalFolderObserver *derived = (InternalFolderObserver *)self;
959         
960         TnyFolderChangeChanged changed;
961
962         changed = tny_folder_change_get_changed (change);
963
964         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
965                 TnyList *list;
966
967                 /* Get added headers */
968                 list = tny_simple_list_new ();
969                 tny_folder_change_get_added_headers (change, list);
970
971                 /* Add them to the folder observer */
972                 tny_list_foreach (list, foreach_add_item, 
973                                   derived->new_headers);
974
975                 g_object_unref (G_OBJECT (list));
976         }
977 }
978
979 static void
980 internal_folder_observer_init (InternalFolderObserver *self) 
981 {
982         self->new_headers = tny_simple_list_new ();
983 }
984 static void
985 internal_folder_observer_finalize (GObject *object) 
986 {
987         InternalFolderObserver *self;
988
989         self = (InternalFolderObserver *) object;
990         g_object_unref (self->new_headers);
991
992         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
993 }
994 static void
995 tny_folder_observer_init (TnyFolderObserverIface *iface) 
996 {
997         iface->update_func = internal_folder_observer_update;
998 }
999 static void
1000 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1001 {
1002         GObjectClass *object_class;
1003
1004         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1005         object_class = (GObjectClass*) klass;
1006         object_class->finalize = internal_folder_observer_finalize;
1007 }
1008
1009 /*****************/
1010
1011 static void
1012 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1013 {
1014         TnyIterator *iter;
1015         TnyList *folders = tny_simple_list_new ();
1016
1017         tny_folder_store_get_folders (store, folders, query, NULL);
1018         iter = tny_list_create_iterator (folders);
1019
1020         while (!tny_iterator_is_done (iter)) {
1021
1022                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1023                 if (folder) {
1024                         tny_list_prepend (all_folders, G_OBJECT (folder));
1025                         recurse_folders (folder, query, all_folders);    
1026                         g_object_unref (G_OBJECT (folder));
1027                 }
1028
1029                 tny_iterator_next (iter);
1030         }
1031          g_object_unref (G_OBJECT (iter));
1032          g_object_unref (G_OBJECT (folders));
1033 }
1034
1035 /* 
1036  * Issues the "progress-changed" signal. The timer won't be removed,
1037  * so you must call g_source_remove to stop the signal emission
1038  */
1039 static gboolean
1040 idle_notify_progress (gpointer data)
1041 {
1042         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1043         ModestMailOperationState *state;
1044
1045         state = modest_mail_operation_clone_state (mail_op);
1046 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1047         gdk_threads_enter ();
1048 #endif
1049         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1050 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1051         gdk_threads_leave ();
1052 #endif
1053         g_slice_free (ModestMailOperationState, state);
1054         
1055         return TRUE;
1056 }
1057
1058 /* 
1059  * Issues the "progress-changed" signal and removes the timer. It uses
1060  * a lock to ensure that the progress information of the mail
1061  * operation is not modified while there are notifications pending
1062  */
1063 static gboolean
1064 idle_notify_progress_once (gpointer data)
1065 {
1066         ModestPair *pair;
1067
1068         pair = (ModestPair *) data;
1069
1070 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1071         gdk_threads_enter ();
1072 #endif
1073         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1074 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1075         gdk_threads_leave ();
1076 #endif
1077
1078         /* Free the state and the reference to the mail operation */
1079         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1080         g_object_unref (pair->first);
1081
1082         return FALSE;
1083 }
1084
1085 /* 
1086  * Used to notify the queue from the main
1087  * loop. We call it inside an idle call to achieve that
1088  */
1089 static gboolean
1090 idle_notify_queue (gpointer data)
1091 {
1092         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1093
1094         /* Do not need to block, the notify end will do it for us */    
1095         modest_mail_operation_notify_end (mail_op);
1096         g_object_unref (mail_op);
1097
1098         return FALSE;
1099 }
1100
1101 static int
1102 compare_headers_by_date (gconstpointer a, 
1103                          gconstpointer b)
1104 {
1105         TnyHeader **header1, **header2;
1106         time_t sent1, sent2;
1107
1108         header1 = (TnyHeader **) a;
1109         header2 = (TnyHeader **) b;
1110
1111         sent1 = tny_header_get_date_sent (*header1);
1112         sent2 = tny_header_get_date_sent (*header2);
1113
1114         /* We want the most recent ones (greater time_t) at the
1115            beginning */
1116         if (sent1 < sent2)
1117                 return 1;
1118         else
1119                 return -1;
1120 }
1121
1122 static gboolean 
1123 set_last_updated_idle (gpointer data)
1124 {
1125         gdk_threads_enter ();
1126
1127         /* It does not matter if the time is not exactly the same than
1128            the time when this idle was called, it's just an
1129            approximation and it won't be very different */
1130         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
1131                                     (gchar *) data, 
1132                                     MODEST_ACCOUNT_LAST_UPDATED, 
1133                                     time(NULL), 
1134                                     TRUE);
1135
1136         gdk_threads_leave ();
1137
1138         return FALSE;
1139 }
1140
1141 static gboolean
1142 idle_update_account_cb (gpointer data)
1143 {
1144         UpdateAccountInfo *idle_info;
1145
1146         idle_info = (UpdateAccountInfo *) data;
1147
1148         gdk_threads_enter ();
1149         idle_info->callback (idle_info->mail_op,
1150                              idle_info->new_headers,
1151                              idle_info->user_data);
1152         gdk_threads_leave ();
1153
1154         /* Frees */
1155         g_object_unref (idle_info->mail_op);
1156         g_free (idle_info);
1157
1158         return FALSE;
1159 }
1160
1161
1162 static gpointer
1163 update_account_thread (gpointer thr_user_data)
1164 {
1165         static gboolean first_time = TRUE;
1166         UpdateAccountInfo *info = NULL;
1167         TnyList *all_folders = NULL;
1168         GPtrArray *new_headers = NULL;
1169         TnyIterator *iter = NULL;
1170         TnyFolderStoreQuery *query = NULL;
1171         ModestMailOperationPrivate *priv = NULL;
1172         ModestTnySendQueue *send_queue = NULL;
1173         gint num_new_headers = 0;
1174
1175         info = (UpdateAccountInfo *) thr_user_data;
1176         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1177
1178         /* Get account and set it into mail_operation */
1179         priv->account = g_object_ref (info->account);
1180
1181         /*
1182          * Previousl, we did this for POP3, to do a logout-login upon send/receive, 
1183          * because many POP-servers (like Gmail) do not
1184          * show any updates unless we do that.
1185          * But that didn't work with gmail anyway, 
1186          * and tinymail now takes care of this itself by disconnecting 
1187          * automatically after using the connection.
1188          */
1189         /*
1190         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account)) 
1191                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1192         */
1193
1194         /* Get all the folders. We can do it synchronously because
1195            we're already running in a different thread than the UI */
1196         all_folders = tny_simple_list_new ();
1197         query = tny_folder_store_query_new ();
1198         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1199         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1200                                       all_folders,
1201                                       query,
1202                                       &(priv->error));
1203         if (priv->error) {
1204                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1205                 goto out;
1206         }
1207
1208         iter = tny_list_create_iterator (all_folders);
1209         while (!tny_iterator_is_done (iter)) {
1210                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1211                 if (folder) {
1212                         recurse_folders (folder, query, all_folders);
1213                         g_object_unref (folder);
1214                 }
1215                 tny_iterator_next (iter);
1216         }
1217         g_object_unref (G_OBJECT (iter));
1218
1219         /* Update status and notify. We need to call the notification
1220            with a source function in order to call it from the main
1221            loop. We need that in order not to get into trouble with
1222            Gtk+. We use a timeout in order to provide more status
1223            information, because the sync tinymail call does not
1224            provide it for the moment */
1225         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1226
1227         /* Refresh folders */
1228         num_new_headers = 0;
1229         new_headers = g_ptr_array_new ();
1230         iter = tny_list_create_iterator (all_folders);
1231
1232         while (!tny_iterator_is_done (iter) && !priv->error && 
1233                priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1234
1235                 InternalFolderObserver *observer;
1236                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1237
1238                 /* Refresh the folder */
1239                 /* Our observer receives notification of new emails during folder refreshes,
1240                  * so we can use observer->new_headers.
1241                  */
1242                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1243                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1244                 
1245                 /* This gets the status information (headers) from the server.
1246                  * We use the blocking version, because we are already in a separate 
1247                  * thread.
1248                  */
1249
1250                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
1251                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1252                         TnyIterator *iter;
1253
1254                         /* If the retrieve type is full messages, refresh and get the messages */
1255                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1256
1257                         iter = tny_list_create_iterator (observer->new_headers);
1258                         while (!tny_iterator_is_done (iter)) {
1259                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1260                                  
1261                                 /* Apply per-message size limits */
1262                                 if (tny_header_get_message_size (header) < info->max_size)
1263                                         g_ptr_array_add (new_headers, g_object_ref (header));
1264
1265                                 g_object_unref (header);
1266                                 tny_iterator_next (iter);
1267                         }
1268                         g_object_unref (iter);
1269                 } else {
1270                         /* We do not need to do it the first time
1271                            because it's automatically done by the tree
1272                            model */
1273                         if (G_UNLIKELY (!first_time))
1274                                 tny_folder_poke_status (TNY_FOLDER (folder));
1275                 }
1276                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1277                 g_object_unref (observer);
1278                 observer = NULL;                        
1279
1280                 if (folder)
1281                         g_object_unref (G_OBJECT (folder));
1282
1283                 if (priv->error)
1284                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1285
1286                 tny_iterator_next (iter);
1287         }
1288
1289         g_object_unref (G_OBJECT (iter));
1290         g_source_remove (timeout);
1291
1292         if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED && 
1293             priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1294             new_headers->len > 0) {
1295                 gint msg_num = 0;
1296
1297                 /* Order by date */
1298                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1299
1300                 /* Apply message count limit */
1301                 /* If the number of messages exceeds the maximum, ask the
1302                  * user to download them all,
1303                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1304                  */
1305                 if (new_headers->len > info->retrieve_limit) {
1306                         /* TODO: Ask the user, instead of just
1307                          * failing, showing
1308                          * mail_nc_msg_count_limit_exceeded, with 'Get
1309                          * all' and 'Newest only' buttons. */
1310                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1311                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1312                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1313                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1314                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1315                         goto out;
1316                 }
1317                 
1318                 priv->done = 0;
1319                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1320                 while (msg_num < priv->total) {
1321
1322                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1323                         TnyFolder *folder = tny_header_get_folder (header);
1324                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1325                         ModestMailOperationState *state;
1326                         ModestPair* pair;
1327
1328                         priv->done++;
1329                         /* We can not just use the mail operation because the
1330                            values of done and total could change before the
1331                            idle is called */
1332                         state = modest_mail_operation_clone_state (info->mail_op);
1333                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1334                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1335                                          pair, (GDestroyNotify) modest_pair_free);
1336
1337                         g_object_unref (msg);
1338                         g_object_unref (folder);
1339
1340                         msg_num++;
1341                 }
1342         }
1343
1344         /* Get the number of new headers and free them */
1345         num_new_headers = new_headers->len;
1346         g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1347         g_ptr_array_free (new_headers, FALSE);
1348         
1349         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1350                 goto out;
1351
1352         /* Perform send (if operation was not cancelled) */
1353 /*      priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND; */
1354         priv->done = 0;
1355         priv->total = 0;
1356         if (priv->account != NULL) 
1357                 g_object_unref (priv->account);
1358         priv->account = g_object_ref (info->transport_account);
1359         
1360         send_queue = modest_runtime_get_send_queue (info->transport_account);
1361         if (send_queue) {
1362 /*              timeout = g_timeout_add (250, idle_notify_progress, info->mail_op); */
1363                 modest_tny_send_queue_try_to_send (send_queue);
1364 /*              g_source_remove (timeout); */
1365         } else {
1366                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1367                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1368                              "cannot create a send queue for %s\n", 
1369                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1370                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1371         }
1372         
1373         /* Check if the operation was a success */
1374         if (!priv->error) {
1375                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1376
1377                 /* Update the last updated key */
1378                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1379                                  set_last_updated_idle, 
1380                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1381                                  (GDestroyNotify) g_free);
1382         }
1383
1384  out:
1385
1386         if (info->callback) {
1387                 UpdateAccountInfo *idle_info;
1388
1389                 /* This thread is not in the main lock */
1390                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1391                 idle_info->mail_op = g_object_ref (info->mail_op);
1392                 idle_info->new_headers = num_new_headers;
1393                 idle_info->callback = info->callback;
1394                 g_idle_add (idle_update_account_cb, idle_info);
1395         }
1396
1397         /* Notify about operation end. Note that the info could be
1398            freed before this idle happens, but the mail operation will
1399            be still alive */
1400         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1401
1402         /* Frees */
1403         g_object_unref (query);
1404         g_object_unref (all_folders);
1405         g_object_unref (info->account);
1406         g_object_unref (info->transport_account);
1407         g_free (info->retrieve_type);
1408         g_slice_free (UpdateAccountInfo, info);
1409
1410         first_time = FALSE;
1411
1412         return NULL;
1413 }
1414
1415 gboolean
1416 modest_mail_operation_update_account (ModestMailOperation *self,
1417                                       const gchar *account_name,
1418                                       UpdateAccountCallback callback,
1419                                       gpointer user_data)
1420 {
1421         GThread *thread = NULL;
1422         UpdateAccountInfo *info = NULL;
1423         ModestMailOperationPrivate *priv = NULL;
1424         ModestAccountMgr *mgr = NULL;
1425         TnyStoreAccount *store_account = NULL;
1426         TnyTransportAccount *transport_account = NULL;
1427
1428         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1429         g_return_val_if_fail (account_name, FALSE);
1430
1431         /* Init mail operation. Set total and done to 0, and do not
1432            update them, this way the progress objects will know that
1433            we have no clue about the number of the objects */
1434         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1435         priv->total = 0;
1436         priv->done  = 0;
1437         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1438
1439         /* Get the Modest account */
1440         store_account = (TnyStoreAccount *)
1441                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1442                                                                      account_name,
1443                                                                      TNY_ACCOUNT_TYPE_STORE);
1444                                                                      
1445         /* Make sure that we have a connection, and request one 
1446          * if necessary:
1447          * TODO: Is there some way to trigger this for every attempt to 
1448          * use the network? */
1449         if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1450                 goto error;
1451
1452         if (!store_account) {
1453                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1454                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1455                              "cannot get tny store account for %s\n", account_name);
1456                 goto error;
1457         }
1458
1459         
1460         /* Get the transport account, we can not do it in the thread
1461            due to some problems with dbus */
1462         transport_account = (TnyTransportAccount *)
1463                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1464                                                                                     account_name);
1465         if (!transport_account) {
1466                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1467                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1468                              "cannot get tny transport account for %s\n", account_name);
1469                 goto error;
1470         }
1471
1472         /* Create the helper object */
1473         info = g_slice_new (UpdateAccountInfo);
1474         info->mail_op = self;
1475         info->account = store_account;
1476         info->transport_account = transport_account;
1477         info->callback = callback;
1478         info->user_data = user_data;
1479
1480         /* Get the message size limit */
1481         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1482                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1483         if (info->max_size == 0)
1484                 info->max_size = G_MAXINT;
1485         else
1486                 info->max_size = info->max_size * KB;
1487
1488         /* Get per-account retrieval type */
1489         mgr = modest_runtime_get_account_mgr ();
1490         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1491                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1492
1493         /* Get per-account message amount retrieval limit */
1494         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1495                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1496         if (info->retrieve_limit == 0)
1497                 info->retrieve_limit = G_MAXINT;
1498                 
1499         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1500
1501         /* Set account busy */
1502         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1503         priv->account_name = g_strdup(account_name);
1504         
1505         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1506
1507         return TRUE;
1508
1509  error:
1510         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1511         if (callback) 
1512                 callback (self, 0, user_data);
1513         modest_mail_operation_notify_end (self);
1514         return FALSE;
1515 }
1516
1517 /* ******************************************************************* */
1518 /* ************************** STORE  ACTIONS ************************* */
1519 /* ******************************************************************* */
1520
1521
1522 TnyFolder *
1523 modest_mail_operation_create_folder (ModestMailOperation *self,
1524                                      TnyFolderStore *parent,
1525                                      const gchar *name)
1526 {
1527         ModestMailOperationPrivate *priv;
1528         TnyFolder *new_folder = NULL;
1529
1530         TnyList *list = tny_simple_list_new ();
1531         TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1532
1533         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1534         g_return_val_if_fail (name, NULL);
1535         
1536         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1537
1538         /* Check for already existing folder */
1539         tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1540         tny_folder_store_get_folders (parent, list, query, NULL);
1541         g_object_unref (G_OBJECT (query));
1542
1543         if (tny_list_get_length (list) > 0) {
1544                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1545                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1546                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1547                              _CS("ckdg_ib_folder_already_exists"));
1548         }
1549
1550         g_object_unref (G_OBJECT (list));
1551
1552         /* Check parent */
1553         if (TNY_IS_FOLDER (parent)) {
1554                 /* Check folder rules */
1555                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1556                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1557                         /* Set status failed and set an error */
1558                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1559                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1560                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1561                                      _("mail_in_ui_folder_create_error"));
1562                 }
1563         }
1564
1565         if (!strcmp (name, " ") || strchr (name, '/')) {
1566                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1567                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1568                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1569                              _("mail_in_ui_folder_create_error"));
1570         }
1571
1572         if (!priv->error) {
1573                 /* Create the folder */
1574                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1575                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1576                 if (!priv->error)
1577                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1578         }
1579
1580         /* Notify about operation end */
1581         modest_mail_operation_notify_end (self);
1582
1583         return new_folder;
1584 }
1585
1586 void
1587 modest_mail_operation_remove_folder (ModestMailOperation *self,
1588                                      TnyFolder           *folder,
1589                                      gboolean             remove_to_trash)
1590 {
1591         TnyAccount *account;
1592         ModestMailOperationPrivate *priv;
1593         ModestTnyFolderRules rules;
1594
1595         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1596         g_return_if_fail (TNY_IS_FOLDER (folder));
1597         
1598         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1599         
1600         /* Check folder rules */
1601         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1602         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1603                 /* Set status failed and set an error */
1604                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1605                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1606                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1607                              _("mail_in_ui_folder_delete_error"));
1608                 goto end;
1609         }
1610
1611         /* Get the account */
1612         account = modest_tny_folder_get_account (folder);
1613         priv->account = g_object_ref(account);
1614
1615         /* Delete folder or move to trash */
1616         if (remove_to_trash) {
1617                 TnyFolder *trash_folder = NULL;
1618                 trash_folder = modest_tny_account_get_special_folder (account,
1619                                                                       TNY_FOLDER_TYPE_TRASH);
1620                 /* TODO: error_handling */
1621                 if (trash_folder) {
1622                         modest_mail_operation_xfer_folder (self, folder,
1623                                                     TNY_FOLDER_STORE (trash_folder), 
1624                                                     TRUE, NULL, NULL);
1625                         g_object_unref (trash_folder);
1626                 }
1627         } else {
1628                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1629
1630                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1631                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1632
1633                 if (parent)
1634                         g_object_unref (G_OBJECT (parent));
1635         }
1636         g_object_unref (G_OBJECT (account));
1637
1638  end:
1639         /* Notify about operation end */
1640         modest_mail_operation_notify_end (self);
1641 }
1642
1643 static void
1644 transfer_folder_status_cb (GObject *obj,
1645                            TnyStatus *status,
1646                            gpointer user_data)
1647 {
1648         ModestMailOperation *self;
1649         ModestMailOperationPrivate *priv;
1650         ModestMailOperationState *state;
1651         XFerMsgAsyncHelper *helper;
1652
1653         g_return_if_fail (status != NULL);
1654         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1655
1656         helper = (XFerMsgAsyncHelper *) user_data;
1657         g_return_if_fail (helper != NULL);
1658
1659         self = helper->mail_op;
1660         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1661
1662         priv->done = status->position;
1663         priv->total = status->of_total;
1664
1665         state = modest_mail_operation_clone_state (self);
1666 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1667         gdk_threads_enter ();
1668 #endif
1669         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1670 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1671         gdk_threads_leave ();
1672 #endif
1673         g_slice_free (ModestMailOperationState, state);
1674 }
1675
1676
1677 static void
1678 transfer_folder_cb (TnyFolder *folder, 
1679                     TnyFolderStore *into, 
1680                     gboolean cancelled, 
1681                     TnyFolder *new_folder, 
1682                     GError **err, 
1683                     gpointer user_data)
1684 {
1685         XFerMsgAsyncHelper *helper;
1686         ModestMailOperation *self = NULL;
1687         ModestMailOperationPrivate *priv = NULL;
1688
1689         helper = (XFerMsgAsyncHelper *) user_data;
1690         g_return_if_fail (helper != NULL);       
1691
1692         self = helper->mail_op;
1693         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1694
1695         if (*err) {
1696                 priv->error = g_error_copy (*err);
1697                 priv->done = 0;
1698                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1699         } else if (cancelled) {
1700                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1701                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1702                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1703                              _("Transference of %s was cancelled."),
1704                              tny_folder_get_name (folder));
1705         } else {
1706                 priv->done = 1;
1707                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1708         }
1709                 
1710         /* Notify about operation end */
1711         modest_mail_operation_notify_end (self);
1712
1713         /* If user defined callback function was defined, call it */
1714         if (helper->user_callback) {
1715                 gdk_threads_enter ();
1716                 helper->user_callback (priv->source, helper->user_data);
1717                 gdk_threads_leave ();
1718         }
1719
1720         /* Free */
1721         g_object_unref (helper->mail_op);
1722         g_slice_free   (XFerMsgAsyncHelper, helper);
1723 }
1724
1725 /**
1726  *
1727  * This function checks if the new name is a valid name for our local
1728  * folders account. The new name could not be the same than then name
1729  * of any of the mandatory local folders
1730  *
1731  * We can not rely on tinymail because tinymail does not check the
1732  * name of the virtual folders that the account could have in the case
1733  * that we're doing a rename (because it directly calls Camel which
1734  * knows nothing about our virtual folders). 
1735  *
1736  * In the case of an actual copy/move (i.e. move/copy a folder between
1737  * accounts) tinymail uses the tny_folder_store_create_account which
1738  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1739  * checks the new name of the folder, so this call in that case
1740  * wouldn't be needed. *But* NOTE that if tinymail changes its
1741  * implementation (if folder transfers within the same account is no
1742  * longer implemented as a rename) this call will allow Modest to work
1743  * perfectly
1744  *
1745  * If the new name is not valid, this function will set the status to
1746  * failed and will set also an error in the mail operation
1747  */
1748 static gboolean
1749 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1750                                  TnyFolderStore *into,
1751                                  const gchar *new_name)
1752 {
1753         if (TNY_IS_ACCOUNT (into) && 
1754             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1755             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1756                                                                  new_name)) {
1757                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1758                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1759                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1760                              _("FIXME: folder name already in use"));
1761                 return FALSE;
1762         } else
1763                 return TRUE;
1764 }
1765
1766 void
1767 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1768                                    TnyFolder *folder,
1769                                    TnyFolderStore *parent,
1770                                    gboolean delete_original,
1771                                    XferMsgsAsynUserCallback user_callback,
1772                                    gpointer user_data)
1773 {
1774         ModestMailOperationPrivate *priv = NULL;
1775         ModestTnyFolderRules parent_rules = 0, rules; 
1776         XFerMsgAsyncHelper *helper = NULL;
1777
1778         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1779         g_return_if_fail (TNY_IS_FOLDER (folder));
1780
1781         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1782
1783         /* Get account and set it into mail_operation */
1784         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1785         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1786
1787         /* Get folder rules */
1788         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1789         if (TNY_IS_FOLDER (parent))
1790                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1791         
1792         /* The moveable restriction is applied also to copy operation */
1793         if ((gpointer) parent == (gpointer) folder ||
1794             (!TNY_IS_FOLDER_STORE (parent)) || 
1795             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1796                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1797                 /* Set status failed and set an error */
1798                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1799                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1800                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1801                              _("mail_in_ui_folder_move_target_error"));
1802
1803                 /* Notify the queue */
1804                 modest_mail_operation_notify_end (self);
1805         } else if (TNY_IS_FOLDER (parent) && 
1806                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1807                 /* Set status failed and set an error */
1808                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1809                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1810                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1811                              _("FIXME: parent folder does not accept new folders"));
1812
1813                 /* Notify the queue */
1814                 modest_mail_operation_notify_end (self);
1815         } else {
1816
1817
1818                 /* Check that the new folder name is not used by any
1819                    special local folder */
1820                 if (new_name_valid_if_local_account (priv, parent, 
1821                                                      tny_folder_get_name (folder))) {
1822                         /* Create the helper */
1823                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1824                         helper->mail_op = g_object_ref(self);
1825                         helper->dest_folder = NULL;
1826                         helper->headers = NULL;
1827                         helper->user_callback = user_callback;
1828                         helper->user_data = user_data;
1829                         
1830                         /* Move/Copy folder */          
1831                         tny_folder_copy_async (folder,
1832                                                parent,
1833                                                tny_folder_get_name (folder),
1834                                                delete_original,
1835                                                transfer_folder_cb,
1836                                                transfer_folder_status_cb,
1837                                                helper);
1838                 } else {
1839                         modest_mail_operation_notify_end (self);
1840                 }
1841         }
1842 }
1843
1844 void
1845 modest_mail_operation_rename_folder (ModestMailOperation *self,
1846                                      TnyFolder *folder,
1847                                      const gchar *name)
1848 {
1849         ModestMailOperationPrivate *priv;
1850         ModestTnyFolderRules rules;
1851         XFerMsgAsyncHelper *helper;
1852
1853         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1854         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1855         g_return_if_fail (name);
1856         
1857         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1858
1859         /* Get account and set it into mail_operation */
1860         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1861
1862         /* Check folder rules */
1863         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1864         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1865                 /* Set status failed and set an error */
1866                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1867                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1868                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1869                              _("FIXME: unable to rename"));
1870
1871                 /* Notify about operation end */
1872                 modest_mail_operation_notify_end (self);
1873         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1874                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1875                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1876                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1877                              _("FIXME: unable to rename"));
1878                 /* Notify about operation end */
1879                 modest_mail_operation_notify_end (self);
1880         } else {
1881                 TnyFolderStore *into;
1882
1883                 into = tny_folder_get_folder_store (folder);    
1884
1885                 /* Check that the new folder name is not used by any
1886                    special local folder */
1887                 if (new_name_valid_if_local_account (priv, into, name)) {
1888                         /* Create the helper */
1889                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1890                         helper->mail_op = g_object_ref(self);
1891                         helper->dest_folder = NULL;
1892                         helper->headers = NULL;
1893                         helper->user_callback = NULL;
1894                         helper->user_data = NULL;
1895                 
1896                         /* Rename. Camel handles folder subscription/unsubscription */
1897                         tny_folder_copy_async (folder, into, name, TRUE,
1898                                                transfer_folder_cb,
1899                                                transfer_folder_status_cb,
1900                                                helper);
1901                 } else {
1902                         modest_mail_operation_notify_end (self);
1903                 }
1904                 g_object_unref (into);
1905         }
1906 }
1907
1908 /* ******************************************************************* */
1909 /* **************************  MSG  ACTIONS  ************************* */
1910 /* ******************************************************************* */
1911
1912 void modest_mail_operation_get_msg (ModestMailOperation *self,
1913                                     TnyHeader *header,
1914                                     GetMsgAsyncUserCallback user_callback,
1915                                     gpointer user_data)
1916 {
1917         GetMsgAsyncHelper *helper = NULL;
1918         TnyFolder *folder;
1919         ModestMailOperationPrivate *priv;
1920         
1921         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1922         g_return_if_fail (TNY_IS_HEADER (header));
1923         
1924         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1925         folder = tny_header_get_folder (header);
1926
1927         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1928
1929         /* Get message from folder */
1930         if (folder) {
1931                 /* Get account and set it into mail_operation */
1932                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1933
1934                 helper = g_slice_new0 (GetMsgAsyncHelper);
1935                 helper->mail_op = self;
1936                 helper->user_callback = user_callback;
1937                 helper->user_data = user_data;
1938                 helper->header = g_object_ref (header);
1939
1940                 // The callback's reference so that the mail op is not
1941                 // finalized until the async operation is completed even if
1942                 // the user canceled the request meanwhile.
1943                 g_object_ref (G_OBJECT (helper->mail_op));
1944
1945                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1946
1947                 g_object_unref (G_OBJECT (folder));
1948         } else {
1949                 /* Set status failed and set an error */
1950                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1951                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1952                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1953                              _("Error trying to get a message. No folder found for header"));
1954
1955                 /* Notify the queue */
1956                 modest_mail_operation_notify_end (self);
1957         }
1958 }
1959
1960 static gboolean
1961 idle_get_mime_part_size_cb (gpointer userdata)
1962 {
1963         GetMimePartSizeInfo *idle_info;
1964
1965         idle_info = (GetMimePartSizeInfo *) userdata;
1966
1967         gdk_threads_enter ();
1968         idle_info->callback (idle_info->mail_op,
1969                              idle_info->size,
1970                              idle_info->userdata);
1971         gdk_threads_leave ();
1972
1973         g_object_unref (idle_info->mail_op);
1974         g_slice_free (GetMimePartSizeInfo, idle_info);
1975
1976         return FALSE;
1977 }
1978
1979 static gpointer
1980 get_mime_part_size_thread (gpointer thr_user_data)
1981 {
1982         GetMimePartSizeInfo *info;
1983         gchar read_buffer[GET_SIZE_BUFFER_SIZE];
1984         TnyStream *stream;
1985         gssize readed_size;
1986         gssize total = 0;
1987         ModestMailOperationPrivate *priv;
1988
1989         info = (GetMimePartSizeInfo *) thr_user_data;
1990         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1991
1992         stream = tny_camel_mem_stream_new ();
1993         tny_mime_part_decode_to_stream (info->mime_part, stream);
1994         tny_stream_reset (stream);
1995         if (tny_stream_is_eos (stream)) {
1996                 tny_stream_close (stream);
1997                 stream = tny_mime_part_get_stream (info->mime_part);
1998         }
1999         
2000         while (!tny_stream_is_eos (stream)) {
2001                 readed_size = tny_stream_read (stream, read_buffer, GET_SIZE_BUFFER_SIZE);
2002                 total += readed_size;
2003         }
2004
2005         if (info->callback) {
2006                 GetMimePartSizeInfo *idle_info;
2007
2008                 idle_info = g_slice_new0 (GetMimePartSizeInfo);
2009                 idle_info->mail_op = g_object_ref (info->mail_op);
2010                 idle_info->size = total;
2011                 idle_info->callback = info->callback;
2012                 idle_info->userdata = info->userdata;
2013                 g_idle_add (idle_get_mime_part_size_cb, idle_info);
2014         }
2015
2016         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2017
2018         g_object_unref (info->mail_op);
2019         g_object_unref (stream);
2020         g_object_unref (info->mime_part);
2021         g_slice_free  (GetMimePartSizeInfo, info);
2022
2023         return NULL;
2024 }
2025
2026 void          
2027 modest_mail_operation_get_mime_part_size (ModestMailOperation *self,
2028                                           TnyMimePart *part,
2029                                           GetMimePartSizeCallback user_callback,
2030                                           gpointer user_data,
2031                                           GDestroyNotify notify)
2032 {
2033         GetMimePartSizeInfo *info;
2034         ModestMailOperationPrivate *priv;
2035         GThread *thread;
2036         
2037         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2038         g_return_if_fail (TNY_IS_MIME_PART (part));
2039         
2040         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2041
2042         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2043         info = g_slice_new0 (GetMimePartSizeInfo);
2044         info->mail_op = g_object_ref (self);
2045         info->mime_part = g_object_ref (part);
2046         info->callback = user_callback;
2047         info->userdata = user_data;
2048
2049         thread = g_thread_create (get_mime_part_size_thread, info, FALSE, NULL);
2050
2051 }
2052
2053 static void
2054 get_msg_cb (TnyFolder *folder, 
2055             gboolean cancelled, 
2056             TnyMsg *msg, 
2057             GError **error, 
2058             gpointer user_data)
2059 {
2060         GetMsgAsyncHelper *helper = NULL;
2061         ModestMailOperation *self = NULL;
2062         ModestMailOperationPrivate *priv = NULL;
2063
2064         helper = (GetMsgAsyncHelper *) user_data;
2065         g_return_if_fail (helper != NULL);       
2066         self = helper->mail_op;
2067         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2068         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2069
2070         /* Check errors and cancel */
2071         if (*error) {
2072                 priv->error = g_error_copy (*error);
2073                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2074         } else if (cancelled) {
2075                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2076                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2077                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2078                              _("Error trying to refresh the contents of %s"),
2079                              tny_folder_get_name (folder));
2080         } else {
2081                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2082         }
2083
2084         /* If user defined callback function was defined, call it even
2085            if the operation failed*/
2086         if (helper->user_callback) {
2087                 /* This callback is called into an iddle by tinymail,
2088                    and idles are not in the main lock */
2089                 gdk_threads_enter ();
2090                 helper->user_callback (self, helper->header, msg, helper->user_data);
2091                 gdk_threads_leave ();   
2092         }
2093
2094         /* Notify about operation end */
2095         modest_mail_operation_notify_end (self);
2096         /* Free */
2097         g_object_unref (helper->mail_op);
2098         g_object_unref (helper->header);
2099         g_slice_free (GetMsgAsyncHelper, helper);
2100                 
2101 }
2102
2103 static void     
2104 get_msg_status_cb (GObject *obj,
2105                    TnyStatus *status,  
2106                    gpointer user_data)
2107 {
2108         GetMsgAsyncHelper *helper = NULL;
2109         ModestMailOperation *self;
2110         ModestMailOperationPrivate *priv;
2111         ModestMailOperationState *state;
2112
2113         g_return_if_fail (status != NULL);
2114         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2115
2116         helper = (GetMsgAsyncHelper *) user_data;
2117         g_return_if_fail (helper != NULL);       
2118
2119         self = helper->mail_op;
2120         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2121
2122         priv->done = 1;
2123         priv->total = 1;
2124
2125         state = modest_mail_operation_clone_state (self);
2126         state->bytes_done = status->position;
2127         state->bytes_total = status->of_total;
2128 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2129         gdk_threads_enter ();
2130 #endif
2131         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2132 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2133         gdk_threads_leave ();
2134 #endif
2135         g_slice_free (ModestMailOperationState, state);
2136 }
2137
2138 /****************************************************/
2139 typedef struct {
2140         ModestMailOperation *mail_op;
2141         TnyList *headers;
2142         GetMsgAsyncUserCallback user_callback;
2143         gpointer user_data;
2144         GDestroyNotify notify;
2145 } GetFullMsgsInfo;
2146
2147 typedef struct {
2148         GetMsgAsyncUserCallback user_callback;
2149         TnyHeader *header;
2150         TnyMsg *msg;
2151         gpointer user_data;
2152         ModestMailOperation *mail_op;
2153 } NotifyGetMsgsInfo;
2154
2155
2156 /* 
2157  * Used by get_msgs_full_thread to call the user_callback for each
2158  * message that has been read
2159  */
2160 static gboolean
2161 notify_get_msgs_full (gpointer data)
2162 {
2163         NotifyGetMsgsInfo *info;
2164
2165         info = (NotifyGetMsgsInfo *) data;      
2166
2167         /* Call the user callback. Idles are not in the main lock, so
2168            lock it */
2169         gdk_threads_enter ();
2170         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2171         gdk_threads_leave ();
2172
2173         g_slice_free (NotifyGetMsgsInfo, info);
2174
2175         return FALSE;
2176 }
2177
2178 /* 
2179  * Used by get_msgs_full_thread to free al the thread resources and to
2180  * call the destroy function for the passed user_data
2181  */
2182 static gboolean
2183 get_msgs_full_destroyer (gpointer data)
2184 {
2185         GetFullMsgsInfo *info;
2186
2187         info = (GetFullMsgsInfo *) data;
2188
2189         if (info->notify) {
2190                 gdk_threads_enter ();   
2191                 info->notify (info->user_data);
2192                 gdk_threads_leave ();
2193         }
2194
2195         /* free */
2196         g_object_unref (info->headers);
2197         g_slice_free (GetFullMsgsInfo, info);
2198
2199         return FALSE;
2200 }
2201
2202 static gpointer
2203 get_msgs_full_thread (gpointer thr_user_data)
2204 {
2205         GetFullMsgsInfo *info;
2206         ModestMailOperationPrivate *priv = NULL;
2207         TnyIterator *iter = NULL;
2208         
2209         info = (GetFullMsgsInfo *) thr_user_data;       
2210         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2211
2212         iter = tny_list_create_iterator (info->headers);
2213         while (!tny_iterator_is_done (iter)) { 
2214                 TnyHeader *header;
2215                 TnyFolder *folder;
2216                 
2217                 header = TNY_HEADER (tny_iterator_get_current (iter));
2218                 folder = tny_header_get_folder (header);
2219                                 
2220                 /* Get message from folder */
2221                 if (folder) {
2222                         TnyMsg *msg;
2223                         /* The callback will call it per each header */
2224                         msg = tny_folder_get_msg (folder, header, &(priv->error));
2225
2226                         if (msg) {
2227                                 ModestMailOperationState *state;
2228                                 ModestPair *pair;
2229
2230                                 priv->done++;
2231
2232                                 /* notify progress */
2233                                 state = modest_mail_operation_clone_state (info->mail_op);
2234                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2235                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2236                                                  pair, (GDestroyNotify) modest_pair_free);
2237
2238                                 /* The callback is the responsible for
2239                                    freeing the message */
2240                                 if (info->user_callback) {
2241                                         NotifyGetMsgsInfo *info_notify;
2242                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2243                                         info_notify->user_callback = info->user_callback;
2244                                         info_notify->mail_op = info->mail_op;
2245                                         info_notify->header = g_object_ref (header);
2246                                         info_notify->msg = g_object_ref (msg);
2247                                         info_notify->user_data = info->user_data;
2248                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2249                                                          notify_get_msgs_full, 
2250                                                          info_notify, NULL);
2251                                 }
2252                                 g_object_unref (msg);
2253                         } 
2254                 } else {
2255                         /* Set status failed and set an error */
2256                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2257                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2258                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2259                                      "Error trying to get a message. No folder found for header");
2260                 }
2261
2262                 if (header)
2263                         g_object_unref (header);
2264                 
2265                 tny_iterator_next (iter);
2266         }
2267
2268         /* Set operation status */
2269         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2270                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2271
2272         /* Notify about operation end */
2273         g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2274
2275         /* Free thread resources. Will be called after all previous idles */
2276         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2277
2278         return NULL;
2279 }
2280
2281 void 
2282 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2283                                      TnyList *header_list, 
2284                                      GetMsgAsyncUserCallback user_callback,
2285                                      gpointer user_data,
2286                                      GDestroyNotify notify)
2287 {
2288         TnyHeader *header = NULL;
2289         TnyFolder *folder = NULL;
2290         GThread *thread;
2291         ModestMailOperationPrivate *priv = NULL;
2292         GetFullMsgsInfo *info = NULL;
2293         gboolean size_ok = TRUE;
2294         gint max_size;
2295         TnyIterator *iter = NULL;
2296         
2297         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2298         
2299         /* Init mail operation */
2300         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2301         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2302         priv->done = 0;
2303         priv->total = tny_list_get_length(header_list);
2304
2305         /* Get account and set it into mail_operation */
2306         if (tny_list_get_length (header_list) >= 1) {
2307                 iter = tny_list_create_iterator (header_list);
2308                 header = TNY_HEADER (tny_iterator_get_current (iter));
2309                 if (header) {
2310                         folder = tny_header_get_folder (header);
2311                         if (folder) {           
2312                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2313
2314                                 g_object_unref (folder);
2315                         }
2316
2317                         g_object_unref (header);
2318                 }
2319
2320                 if (tny_list_get_length (header_list) == 1) {
2321                         g_object_unref (iter);
2322                         iter = NULL;
2323                 }
2324         }
2325
2326         /* Get msg size limit */
2327         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2328                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2329                                          &(priv->error));
2330         if (priv->error) {
2331                 g_clear_error (&(priv->error));
2332                 max_size = G_MAXINT;
2333         } else {
2334                 max_size = max_size * KB;
2335         }
2336
2337         /* Check message size limits. If there is only one message
2338            always retrieve it */
2339         if (iter != NULL) {
2340                 while (!tny_iterator_is_done (iter) && size_ok) {
2341                         header = TNY_HEADER (tny_iterator_get_current (iter));
2342                         if (header) {
2343                                 if (tny_header_get_message_size (header) >= max_size)
2344                                         size_ok = FALSE;
2345                                 g_object_unref (header);
2346                         }
2347
2348                         tny_iterator_next (iter);
2349                 }
2350                 g_object_unref (iter);
2351         }
2352
2353         if (size_ok) {
2354                 /* Create the info */
2355                 info = g_slice_new0 (GetFullMsgsInfo);
2356                 info->mail_op = self;
2357                 info->user_callback = user_callback;
2358                 info->user_data = user_data;
2359                 info->headers = g_object_ref (header_list);
2360                 info->notify = notify;
2361
2362                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2363         } else {
2364                 /* Set status failed and set an error */
2365                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2366                 /* FIXME: the error msg is different for pop */
2367                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2368                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2369                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2370                 /* Remove from queue and free resources */
2371                 modest_mail_operation_notify_end (self);
2372                 if (notify)
2373                         notify (user_data);
2374         }
2375 }
2376
2377
2378 void 
2379 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
2380                                   gboolean remove_to_trash /*ignored*/)
2381 {
2382         TnyFolder *folder;
2383         ModestMailOperationPrivate *priv;
2384
2385         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2386         g_return_if_fail (TNY_IS_HEADER (header));
2387
2388         if (remove_to_trash)
2389                 g_warning ("remove to trash is not implemented");
2390
2391         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2392         folder = tny_header_get_folder (header);
2393
2394         /* Get account and set it into mail_operation */
2395         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2396
2397         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2398
2399
2400         tny_folder_remove_msg (folder, header, &(priv->error));
2401         if (!priv->error) {
2402                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2403                 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2404
2405                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2406                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2407                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2408                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2409                 else
2410                         /* lcoal folders */
2411                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2412         }
2413         
2414         
2415         /* Set status */
2416         if (!priv->error)
2417                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2418         else
2419                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2420
2421         /* Free */
2422         g_object_unref (G_OBJECT (folder));
2423
2424         /* Notify about operation end */
2425         modest_mail_operation_notify_end (self);
2426 }
2427
2428 static void
2429 transfer_msgs_status_cb (GObject *obj,
2430                          TnyStatus *status,  
2431                          gpointer user_data)
2432 {
2433         XFerMsgAsyncHelper *helper = NULL;
2434         ModestMailOperation *self;
2435         ModestMailOperationPrivate *priv;
2436         ModestMailOperationState *state;
2437
2438
2439         g_return_if_fail (status != NULL);
2440         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2441
2442         helper = (XFerMsgAsyncHelper *) user_data;
2443         g_return_if_fail (helper != NULL);       
2444
2445         self = helper->mail_op;
2446         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2447
2448         priv->done = status->position;
2449         priv->total = status->of_total;
2450
2451         state = modest_mail_operation_clone_state (self);
2452 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2453         gdk_threads_enter ();
2454 #endif
2455         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2456 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2457         gdk_threads_leave ();
2458 #endif
2459         g_slice_free (ModestMailOperationState, state);
2460 }
2461
2462
2463 static void
2464 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2465 {
2466         XFerMsgAsyncHelper *helper;
2467         ModestMailOperation *self;
2468         ModestMailOperationPrivate *priv;
2469
2470         helper = (XFerMsgAsyncHelper *) user_data;
2471         self = helper->mail_op;
2472
2473         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2474
2475         if (*err) {
2476                 priv->error = g_error_copy (*err);
2477                 priv->done = 0;
2478                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2479         } else if (cancelled) {
2480                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2481                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2482                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2483                              _("Error trying to refresh the contents of %s"),
2484                              tny_folder_get_name (folder));
2485         } else {
2486                 priv->done = 1;
2487                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2488         }
2489
2490         /* Notify about operation end */
2491         modest_mail_operation_notify_end (self);
2492
2493         /* If user defined callback function was defined, call it */
2494         if (helper->user_callback) {
2495                 gdk_threads_enter ();
2496                 helper->user_callback (priv->source, helper->user_data);
2497                 gdk_threads_leave ();
2498         }
2499
2500         /* Free */
2501         g_object_unref (helper->headers);
2502         g_object_unref (helper->dest_folder);
2503         g_object_unref (helper->mail_op);
2504         g_slice_free   (XFerMsgAsyncHelper, helper);
2505         g_object_unref (folder);
2506
2507 }
2508
2509 void
2510 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2511                                  TnyList *headers, 
2512                                  TnyFolder *folder, 
2513                                  gboolean delete_original,
2514                                  XferMsgsAsynUserCallback user_callback,
2515                                  gpointer user_data)
2516 {
2517         ModestMailOperationPrivate *priv = NULL;
2518         TnyIterator *iter = NULL;
2519         TnyFolder *src_folder = NULL;
2520         XFerMsgAsyncHelper *helper = NULL;
2521         TnyHeader *header = NULL;
2522         ModestTnyFolderRules rules = 0;
2523         const gchar *id1 = NULL;
2524         const gchar *id2 = NULL;
2525         gboolean same_folder = FALSE;
2526
2527         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2528         g_return_if_fail (TNY_IS_LIST (headers));
2529         g_return_if_fail (TNY_IS_FOLDER (folder));
2530
2531         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2532         priv->total = 1;
2533         priv->done = 0;
2534         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2535
2536         /* Apply folder rules */
2537         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2538         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2539                 /* Set status failed and set an error */
2540                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2541                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2542                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2543                              _CS("ckct_ib_unable_to_paste_here"));
2544                 /* Notify the queue */
2545                 modest_mail_operation_notify_end (self);
2546                 return;
2547         }
2548                 
2549         /* Get source folder */
2550         iter = tny_list_create_iterator (headers);
2551         header = TNY_HEADER (tny_iterator_get_current (iter));
2552         if (header) {
2553                 src_folder = tny_header_get_folder (header);
2554                 g_object_unref (header);
2555         }
2556
2557         g_object_unref (iter);
2558
2559         /* Check folder source and destination */
2560         id1 = tny_folder_get_id (src_folder);
2561         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2562         same_folder = !g_ascii_strcasecmp (id1, id2);
2563         if (same_folder) {
2564                 /* Set status failed and set an error */
2565                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2566                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2567                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2568                              _("mcen_ib_unable_to_copy_samefolder"));
2569                 
2570                 /* Notify the queue */
2571                 modest_mail_operation_notify_end (self);
2572                 
2573                 /* Free */
2574                 g_object_unref (src_folder);            
2575                 return;
2576         }
2577
2578         /* Create the helper */
2579         helper = g_slice_new0 (XFerMsgAsyncHelper);
2580         helper->mail_op = g_object_ref(self);
2581         helper->dest_folder = g_object_ref(folder);
2582         helper->headers = g_object_ref(headers);
2583         helper->user_callback = user_callback;
2584         helper->user_data = user_data;
2585
2586         /* Get account and set it into mail_operation */
2587         priv->account = modest_tny_folder_get_account (src_folder);
2588
2589         /* Transfer messages */
2590         tny_folder_transfer_msgs_async (src_folder, 
2591                                         headers, 
2592                                         folder, 
2593                                         delete_original, 
2594                                         transfer_msgs_cb, 
2595                                         transfer_msgs_status_cb,
2596                                         helper);
2597 }
2598
2599
2600 static void
2601 on_refresh_folder (TnyFolder   *folder, 
2602                    gboolean     cancelled, 
2603                    GError     **error,
2604                    gpointer     user_data)
2605 {
2606         RefreshAsyncHelper *helper = NULL;
2607         ModestMailOperation *self = NULL;
2608         ModestMailOperationPrivate *priv = NULL;
2609
2610         helper = (RefreshAsyncHelper *) user_data;
2611         self = helper->mail_op;
2612         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2613
2614         if (*error) {
2615                 priv->error = g_error_copy (*error);
2616                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2617                 goto out;
2618         }
2619
2620         if (cancelled) {
2621                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2622                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2623                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2624                              _("Error trying to refresh the contents of %s"),
2625                              tny_folder_get_name (folder));
2626                 goto out;
2627         }
2628
2629         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2630  out:
2631         /* Call user defined callback, if it exists */
2632         if (helper->user_callback) {
2633                 gdk_threads_enter ();
2634                 helper->user_callback (self, folder, helper->user_data);
2635                 gdk_threads_leave ();
2636         }
2637
2638         /* Free */
2639         g_slice_free   (RefreshAsyncHelper, helper);
2640
2641         /* Notify about operation end */
2642         modest_mail_operation_notify_end (self);
2643 }
2644
2645 static void
2646 on_refresh_folder_status_update (GObject *obj,
2647                                  TnyStatus *status,
2648                                  gpointer user_data)
2649 {
2650         RefreshAsyncHelper *helper = NULL;
2651         ModestMailOperation *self = NULL;
2652         ModestMailOperationPrivate *priv = NULL;
2653         ModestMailOperationState *state;
2654
2655         g_return_if_fail (user_data != NULL);
2656         g_return_if_fail (status != NULL);
2657         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2658
2659         helper = (RefreshAsyncHelper *) user_data;
2660         self = helper->mail_op;
2661         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2662
2663         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2664
2665         priv->done = status->position;
2666         priv->total = status->of_total;
2667
2668         state = modest_mail_operation_clone_state (self);
2669 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2670         gdk_threads_enter ();
2671 #endif
2672         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2673 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2674         gdk_threads_leave ();
2675 #endif
2676         g_slice_free (ModestMailOperationState, state);
2677 }
2678
2679 void 
2680 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2681                                        TnyFolder *folder,
2682                                        RefreshAsyncUserCallback user_callback,
2683                                        gpointer user_data)
2684 {
2685         ModestMailOperationPrivate *priv = NULL;
2686         RefreshAsyncHelper *helper = NULL;
2687
2688         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2689
2690         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2691
2692         /* Get account and set it into mail_operation */
2693         priv->account = modest_tny_folder_get_account  (folder);
2694
2695         /* Create the helper */
2696         helper = g_slice_new0 (RefreshAsyncHelper);
2697         helper->mail_op = self;
2698         helper->user_callback = user_callback;
2699         helper->user_data = user_data;
2700
2701         /* Refresh the folder. TODO: tinymail could issue a status
2702            updates before the callback call then this could happen. We
2703            must review the design */
2704         tny_folder_refresh_async (folder,
2705                                   on_refresh_folder,
2706                                   on_refresh_folder_status_update,
2707                                   helper);
2708 }
2709
2710 /**
2711  *
2712  * It's used by the mail operation queue to notify the observers
2713  * attached to that signal that the operation finished. We need to use
2714  * that because tinymail does not give us the progress of a given
2715  * operation when it finishes (it directly calls the operation
2716  * callback).
2717  */
2718 static void
2719 modest_mail_operation_notify_end (ModestMailOperation *self)
2720 {
2721         ModestMailOperationState *state;
2722         ModestMailOperationPrivate *priv = NULL;
2723
2724         g_return_if_fail (self);
2725
2726         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2727
2728         /* Set the account back to not busy */
2729         if (priv->account_name) {
2730                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2731                                                      priv->account_name, FALSE);
2732                 g_free(priv->account_name);
2733                 priv->account_name = NULL;
2734         }
2735         
2736         /* Notify the observers about the mail operation end */
2737         /* We do not wrapp this emission because we assume that this
2738            function is always called from within the main lock */
2739         state = modest_mail_operation_clone_state (self);
2740         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2741         g_slice_free (ModestMailOperationState, state);
2742 }