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