2007-07-04 Murray Cumming <murrayc@murrayc.com>
[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         g_object_unref (G_OBJECT(header));
576
577         /* Call mail operation */
578         modest_mail_operation_send_mail (self, transport_account, new_msg);
579
580         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
581         if (folder) {
582                 if (draft_msg != NULL) {
583                         header = tny_msg_get_header (draft_msg);
584                         /* Note: This can fail (with a warning) if the message is not really already in a folder,
585                          * because this function requires it to have a UID. */
586                         tny_folder_remove_msg (folder, header, NULL);
587                         tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
588                         g_object_unref (header);
589                 }
590         }
591
592         /* Free */
593         g_object_unref (G_OBJECT (new_msg));
594 }
595
596 void
597 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
598                                       TnyTransportAccount *transport_account,
599                                       TnyMsg *draft_msg,
600                                       const gchar *from,  const gchar *to,
601                                       const gchar *cc,  const gchar *bcc,
602                                       const gchar *subject, const gchar *plain_body,
603                                       const gchar *html_body,
604                                       const GList *attachments_list,
605                                       TnyHeaderFlags priority_flags)
606 {
607         TnyMsg *msg = NULL;
608         TnyFolder *folder = NULL;
609         TnyHeader *header = NULL;
610         ModestMailOperationPrivate *priv = NULL;
611
612         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
613         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
614
615         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
616
617         /* Get account and set it into mail_operation */
618         priv->account = g_object_ref (transport_account);
619
620         if (html_body == NULL) {
621                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
622         } else {
623                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
624         }
625         if (!msg) {
626                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
627                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
628                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
629                              "modest: failed to create a new msg\n");
630                 goto end;
631         }
632
633         /* add priority flags */
634         header = tny_msg_get_header (msg);
635         tny_header_set_flags (header, priority_flags);
636         g_object_unref (G_OBJECT(header));
637
638         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
639         if (!folder) {
640                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
641                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
642                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
643                              "modest: failed to create a new msg\n");
644                 goto end;
645         }
646
647         if (draft_msg != NULL) {
648                 header = tny_msg_get_header (draft_msg);
649                 /* Remove the old draft expunging it */
650                 tny_folder_remove_msg (folder, header, NULL);
651                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
652                 tny_folder_sync (folder, FALSE, &(priv->error));  /* FALSE --> don't expunge */
653                 g_object_unref (header);
654         }
655         
656         if (!priv->error)
657                 tny_folder_add_msg (folder, msg, &(priv->error));
658
659         if (!priv->error)
660                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
661         else
662                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
663
664 end:
665         if (msg)
666                 g_object_unref (G_OBJECT(msg));
667         if (folder)
668                 g_object_unref (G_OBJECT(folder));
669
670         modest_mail_operation_notify_end (self);
671 }
672
673 typedef struct 
674 {
675         ModestMailOperation *mail_op;
676         TnyStoreAccount *account;
677         TnyTransportAccount *transport_account;
678         gint max_size;
679         gint retrieve_limit;
680         gchar *retrieve_type;
681         gchar *account_name;
682         UpdateAccountCallback callback;
683         gpointer user_data;
684         gint new_headers;
685 } UpdateAccountInfo;
686
687 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
688 /* We use this folder observer to track the headers that have been
689  * added to a folder */
690 typedef struct {
691         GObject parent;
692         TnyList *new_headers;
693 } InternalFolderObserver;
694
695 typedef struct {
696         GObjectClass parent;
697 } InternalFolderObserverClass;
698
699 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
700
701 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
702                          internal_folder_observer,
703                          G_TYPE_OBJECT,
704                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
705
706
707 static void
708 foreach_add_item (gpointer header, gpointer user_data)
709 {
710         /* printf("DEBUG: %s: header subject=%s\n", 
711          * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
712          */
713         tny_list_prepend (TNY_LIST (user_data), 
714                           g_object_ref (G_OBJECT (header)));
715 }
716
717 /* This is the method that looks for new messages in a folder */
718 static void
719 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
720 {
721         InternalFolderObserver *derived = (InternalFolderObserver *)self;
722         
723         TnyFolderChangeChanged changed;
724
725         changed = tny_folder_change_get_changed (change);
726
727         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
728                 TnyList *list;
729
730                 /* Get added headers */
731                 list = tny_simple_list_new ();
732                 tny_folder_change_get_added_headers (change, list);
733
734                 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n", 
735                  *      __FUNCTION__, tny_list_get_length(list));
736                  */
737                  
738                 /* Add them to the folder observer */
739                 tny_list_foreach (list, foreach_add_item, 
740                                   derived->new_headers);
741
742                 g_object_unref (G_OBJECT (list));
743         }
744 }
745
746 static void
747 internal_folder_observer_init (InternalFolderObserver *self) 
748 {
749         self->new_headers = tny_simple_list_new ();
750 }
751 static void
752 internal_folder_observer_finalize (GObject *object) 
753 {
754         InternalFolderObserver *self;
755
756         self = (InternalFolderObserver *) object;
757         g_object_unref (self->new_headers);
758
759         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
760 }
761 static void
762 tny_folder_observer_init (TnyFolderObserverIface *iface) 
763 {
764         iface->update_func = internal_folder_observer_update;
765 }
766 static void
767 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
768 {
769         GObjectClass *object_class;
770
771         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
772         object_class = (GObjectClass*) klass;
773         object_class->finalize = internal_folder_observer_finalize;
774 }
775
776 /*****************/
777
778 static void
779 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
780 {
781         TnyIterator *iter;
782         TnyList *folders = tny_simple_list_new ();
783
784         tny_folder_store_get_folders (store, folders, query, NULL);
785         iter = tny_list_create_iterator (folders);
786
787         while (!tny_iterator_is_done (iter)) {
788
789                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
790
791                 tny_list_prepend (all_folders, G_OBJECT (folder));
792                 recurse_folders (folder, query, all_folders);    
793                 g_object_unref (G_OBJECT (folder));
794
795                 tny_iterator_next (iter);
796         }
797          g_object_unref (G_OBJECT (iter));
798          g_object_unref (G_OBJECT (folders));
799 }
800
801 /* 
802  * Issues the "progress-changed" signal. The timer won't be removed,
803  * so you must call g_source_remove to stop the signal emission
804  */
805 static gboolean
806 idle_notify_progress (gpointer data)
807 {
808         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
809         ModestMailOperationState *state;
810
811         state = modest_mail_operation_clone_state (mail_op);
812         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
813         g_slice_free (ModestMailOperationState, state);
814         
815         return TRUE;
816 }
817
818 /* 
819  * Issues the "progress-changed" signal and removes the timer. It uses
820  * a lock to ensure that the progress information of the mail
821  * operation is not modified while there are notifications pending
822  */
823 static gboolean
824 idle_notify_progress_once (gpointer data)
825 {
826         ModestPair *pair;
827
828         pair = (ModestPair *) data;
829
830         g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
831
832         /* Free the state and the reference to the mail operation */
833         g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
834         g_object_unref (pair->first);
835
836         return FALSE;
837 }
838
839 /* 
840  * Used by update_account_thread to notify the queue from the main
841  * loop. We call it inside an idle call to achieve that
842  */
843 static gboolean
844 idle_notify_update_account_queue (gpointer data)
845 {
846         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
847         ModestMailOperationPrivate *priv = NULL;
848
849         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
850
851         /* Do not need to block, the notify end will do it for us */    
852         modest_mail_operation_notify_end (mail_op);
853         g_object_unref (mail_op);
854
855         return FALSE;
856 }
857
858 static int
859 compare_headers_by_date (gconstpointer a, 
860                          gconstpointer b)
861 {
862         TnyHeader **header1, **header2;
863         time_t sent1, sent2;
864
865         header1 = (TnyHeader **) a;
866         header2 = (TnyHeader **) b;
867
868         sent1 = tny_header_get_date_sent (*header1);
869         sent2 = tny_header_get_date_sent (*header2);
870
871         /* We want the most recent ones (greater time_t) at the
872            beginning */
873         if (sent1 < sent2)
874                 return 1;
875         else
876                 return -1;
877 }
878
879 static gboolean 
880 set_last_updated_idle (gpointer data)
881 {
882         gdk_threads_enter ();
883
884         /* It does not matter if the time is not exactly the same than
885            the time when this idle was called, it's just an
886            approximation and it won't be very different */
887         modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
888                                     (gchar *) data, 
889                                     MODEST_ACCOUNT_LAST_UPDATED, 
890                                     time(NULL), 
891                                     TRUE);
892
893         gdk_threads_leave ();
894
895         return FALSE;
896 }
897
898 static gboolean
899 idle_update_account_cb (gpointer data)
900 {
901         UpdateAccountInfo *idle_info;
902
903         idle_info = (UpdateAccountInfo *) data;
904
905         gdk_threads_enter ();
906         idle_info->callback (idle_info->mail_op,
907                              idle_info->new_headers,
908                              idle_info->user_data);
909         gdk_threads_leave ();
910
911         /* Frees */
912         g_object_unref (idle_info->mail_op);
913         g_free (idle_info);
914
915         return FALSE;
916 }
917
918
919 static gpointer
920 update_account_thread (gpointer thr_user_data)
921 {
922         static gboolean first_time = TRUE;
923         UpdateAccountInfo *info;
924         TnyList *all_folders = NULL;
925         GPtrArray *new_headers = NULL;
926         TnyIterator *iter = NULL;
927         TnyFolderStoreQuery *query = NULL;
928         ModestMailOperationPrivate *priv = NULL;
929         ModestTnySendQueue *send_queue = NULL;
930
931         info = (UpdateAccountInfo *) thr_user_data;
932         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
933
934         /* Get account and set it into mail_operation */
935         priv->account = g_object_ref (info->account);
936
937         /*
938          * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
939          * show any updates unless we do that
940          */
941         if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account)) 
942                 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
943
944         /* Get all the folders. We can do it synchronously because
945            we're already running in a different thread than the UI */
946         all_folders = tny_simple_list_new ();
947         query = tny_folder_store_query_new ();
948         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
949         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
950                                       all_folders,
951                                       query,
952                                       &(priv->error));
953         if (priv->error) {
954                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
955                 goto out;
956         }
957
958         iter = tny_list_create_iterator (all_folders);
959         while (!tny_iterator_is_done (iter)) {
960                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
961
962                 recurse_folders (folder, query, all_folders);
963                 tny_iterator_next (iter);
964         }
965         g_object_unref (G_OBJECT (iter));
966
967         /* Update status and notify. We need to call the notification
968            with a source function in order to call it from the main
969            loop. We need that in order not to get into trouble with
970            Gtk+. We use a timeout in order to provide more status
971            information, because the sync tinymail call does not
972            provide it for the moment */
973         gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
974
975         /* Refresh folders */
976         new_headers = g_ptr_array_new ();
977         iter = tny_list_create_iterator (all_folders);
978
979         while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
980
981                 InternalFolderObserver *observer;
982                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
983
984                 /* Refresh the folder */
985                 /* Our observer receives notification of new emails during folder refreshes,
986                  * so we can use observer->new_headers.
987                  */
988                 observer = g_object_new (internal_folder_observer_get_type (), NULL);
989                 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
990                 
991                 /* This gets the status information (headers) from the server.
992                  * We use the blocking version, because we are already in a separate 
993                  * thread.
994                  */
995
996                 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) || 
997                     !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
998                         TnyIterator *iter;
999
1000                         /* If the retrieve type is full messages, refresh and get the messages */
1001                         tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1002
1003                         iter = tny_list_create_iterator (observer->new_headers);
1004                         while (!tny_iterator_is_done (iter)) {
1005                                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1006                                  
1007                                 /* Apply per-message size limits */
1008                                 if (tny_header_get_message_size (header) < info->max_size)
1009                                         g_ptr_array_add (new_headers, g_object_ref (header));
1010
1011                                 g_object_unref (header);
1012                                 tny_iterator_next (iter);
1013                         }
1014                         g_object_unref (iter);
1015                 } else {
1016                         /* We do not need to do it the first time
1017                            because it's automatically done by the tree
1018                            model */
1019                         if (G_UNLIKELY (!first_time))
1020                                 tny_folder_poke_status (TNY_FOLDER (folder));
1021                 }
1022                 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1023                 g_object_unref (observer);
1024                 observer = NULL;                        
1025
1026                 g_object_unref (G_OBJECT (folder));
1027                 if (priv->error)
1028                 {
1029                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1030                         goto out;
1031                 }
1032                 
1033                 tny_iterator_next (iter);
1034         }
1035
1036         did_a_cancel = FALSE;
1037
1038         g_object_unref (G_OBJECT (iter));
1039         g_source_remove (timeout);
1040
1041         if (new_headers->len > 0) {
1042                 gint msg_num = 0;
1043
1044                 /* Order by date */
1045                 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1046
1047                 /* Apply message count limit */
1048                 /* If the number of messages exceeds the maximum, ask the
1049                  * user to download them all,
1050                  * as per the UI spec "Retrieval Limits" section in 4.4: 
1051                  */
1052                 if (new_headers->len > info->retrieve_limit) {
1053                         /* TODO: Ask the user, instead of just
1054                          * failing, showing
1055                          * mail_nc_msg_count_limit_exceeded, with 'Get
1056                          * all' and 'Newest only' buttons. */
1057                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1058                              MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1059                              "The number of messages to retrieve exceeds the chosen limit for account %s\n", 
1060                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1061                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1062                         goto out;
1063                 }
1064                 
1065                 priv->done = 0;
1066                 priv->total = MIN (new_headers->len, info->retrieve_limit);
1067                 while (msg_num < priv->total) {
1068
1069                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1070                         TnyFolder *folder = tny_header_get_folder (header);
1071                         TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL);
1072                         ModestMailOperationState *state;
1073                         ModestPair* pair;
1074
1075                         priv->done++;
1076                         /* We can not just use the mail operation because the
1077                            values of done and total could change before the
1078                            idle is called */
1079                         state = modest_mail_operation_clone_state (info->mail_op);
1080                         pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1081                         g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1082                                          pair, (GDestroyNotify) modest_pair_free);
1083
1084                         g_object_unref (msg);
1085                         g_object_unref (folder);
1086
1087                         msg_num++;
1088                 }
1089                 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1090                 g_ptr_array_free (new_headers, FALSE);
1091         }
1092         
1093         /* Perform send */
1094         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1095         priv->done = 0;
1096         priv->total = 0;
1097         if (priv->account != NULL) 
1098                 g_object_unref (priv->account);
1099         priv->account = g_object_ref (info->transport_account);
1100         
1101         send_queue = modest_runtime_get_send_queue (info->transport_account);
1102         if (send_queue) {
1103                 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1104                 modest_tny_send_queue_try_to_send (send_queue);
1105                 g_source_remove (timeout);
1106         } else {
1107                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1108                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1109                              "cannot create a send queue for %s\n", 
1110                              tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1111                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1112         }
1113         
1114         /* Check if the operation was a success */
1115         if (!priv->error) {
1116                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1117
1118                 /* Update the last updated key */
1119                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
1120                                  set_last_updated_idle, 
1121                                  g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1122                                  (GDestroyNotify) g_free);
1123         }
1124
1125  out:
1126
1127         if (info->callback) {
1128                 UpdateAccountInfo *idle_info;
1129
1130                 /* This thread is not in the main lock */
1131                 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1132                 idle_info->mail_op = g_object_ref (info->mail_op);
1133                 idle_info->new_headers = (new_headers) ? new_headers->len : 0;
1134                 idle_info->callback = info->callback;
1135                 g_idle_add (idle_update_account_cb, idle_info);
1136         }
1137
1138         /* Notify about operation end. Note that the info could be
1139            freed before this idle happens, but the mail operation will
1140            be still alive */
1141         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1142
1143         /* Frees */
1144         g_object_unref (query);
1145         g_object_unref (all_folders);
1146         g_object_unref (info->account);
1147         g_object_unref (info->transport_account);
1148         g_free (info->retrieve_type);
1149         g_slice_free (UpdateAccountInfo, info);
1150
1151         first_time = FALSE;
1152
1153         return NULL;
1154 }
1155
1156 gboolean
1157 modest_mail_operation_update_account (ModestMailOperation *self,
1158                                       const gchar *account_name,
1159                                       UpdateAccountCallback callback,
1160                                       gpointer user_data)
1161 {
1162         GThread *thread;
1163         UpdateAccountInfo *info;
1164         ModestMailOperationPrivate *priv;
1165         ModestAccountMgr *mgr;
1166         TnyStoreAccount *modest_account;
1167         TnyTransportAccount *transport_account;
1168
1169         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1170         g_return_val_if_fail (account_name, FALSE);
1171
1172         /* Init mail operation. Set total and done to 0, and do not
1173            update them, this way the progress objects will know that
1174            we have no clue about the number of the objects */
1175         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1176         priv->total = 0;
1177         priv->done  = 0;
1178         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1179
1180         /* Make sure that we have a connection, and request one 
1181          * if necessary:
1182          * TODO: Is there some way to trigger this for every attempt to 
1183          * use the network? */
1184         if (!modest_platform_connect_and_wait (NULL))
1185                 goto error;
1186
1187         /* Get the Modest account */
1188         modest_account = (TnyStoreAccount *)
1189                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1190                                                                      account_name,
1191                                                                      TNY_ACCOUNT_TYPE_STORE);
1192
1193         if (!modest_account) {
1194                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1195                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1196                              "cannot get tny store account for %s\n", account_name);
1197                 goto error;
1198         }
1199
1200         
1201         /* Get the transport account, we can not do it in the thread
1202            due to some problems with dbus */
1203         transport_account = (TnyTransportAccount *)
1204                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1205                                                                                     account_name);
1206         if (!transport_account) {
1207                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1208                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1209                              "cannot get tny transport account for %s\n", account_name);
1210                 goto error;
1211         }
1212
1213         /* Create the helper object */
1214         info = g_slice_new (UpdateAccountInfo);
1215         info->mail_op = self;
1216         info->account = modest_account;
1217         info->transport_account = transport_account;
1218         info->callback = callback;
1219         info->user_data = user_data;
1220
1221         /* Get the message size limit */
1222         info->max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1223                                                MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1224         if (info->max_size == 0)
1225                 info->max_size = G_MAXINT;
1226         else
1227                 info->max_size = info->max_size * KB;
1228
1229         /* Get per-account retrieval type */
1230         mgr = modest_runtime_get_account_mgr ();
1231         info->retrieve_type = modest_account_mgr_get_string (mgr, account_name, 
1232                                                              MODEST_ACCOUNT_RETRIEVE, FALSE);
1233
1234         /* Get per-account message amount retrieval limit */
1235         info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name, 
1236                                                            MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1237         if (info->retrieve_limit == 0)
1238                 info->retrieve_limit = G_MAXINT;
1239                 
1240         /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1241
1242         /* Set account busy */
1243         modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1244         priv->account_name = g_strdup(account_name);
1245         
1246         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1247
1248         return TRUE;
1249
1250  error:
1251         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1252         if (callback) 
1253                 callback (self, 0, user_data);
1254         modest_mail_operation_notify_end (self);
1255         return FALSE;
1256 }
1257
1258 /* ******************************************************************* */
1259 /* ************************** STORE  ACTIONS ************************* */
1260 /* ******************************************************************* */
1261
1262
1263 TnyFolder *
1264 modest_mail_operation_create_folder (ModestMailOperation *self,
1265                                      TnyFolderStore *parent,
1266                                      const gchar *name)
1267 {
1268         ModestMailOperationPrivate *priv;
1269         TnyFolder *new_folder = NULL;
1270
1271         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1272         g_return_val_if_fail (name, NULL);
1273         
1274         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1275
1276         /* Check parent */
1277         if (TNY_IS_FOLDER (parent)) {
1278                 /* Check folder rules */
1279                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1280                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1281                         /* Set status failed and set an error */
1282                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1283                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1284                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1285                                      _("mail_in_ui_folder_create_error"));
1286                 }
1287         }
1288
1289         if (!strcmp (name, " ") || strchr (name, '/')) {
1290                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1291                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1292                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1293                              _("mail_in_ui_folder_create_error"));
1294         }
1295
1296         if (!priv->error) {
1297                 /* Create the folder */
1298                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1299                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1300                 if (!priv->error)
1301                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1302         }
1303
1304         /* Notify about operation end */
1305         modest_mail_operation_notify_end (self);
1306
1307         return new_folder;
1308 }
1309
1310 void
1311 modest_mail_operation_remove_folder (ModestMailOperation *self,
1312                                      TnyFolder           *folder,
1313                                      gboolean             remove_to_trash)
1314 {
1315         TnyAccount *account;
1316         ModestMailOperationPrivate *priv;
1317         ModestTnyFolderRules rules;
1318
1319         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1320         g_return_if_fail (TNY_IS_FOLDER (folder));
1321         
1322         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1323         
1324         /* Check folder rules */
1325         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1326         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1327                 /* Set status failed and set an error */
1328                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1329                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1330                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1331                              _("mail_in_ui_folder_delete_error"));
1332                 goto end;
1333         }
1334
1335         /* Get the account */
1336         account = modest_tny_folder_get_account (folder);
1337         priv->account = g_object_ref(account);
1338
1339         /* Delete folder or move to trash */
1340         if (remove_to_trash) {
1341                 TnyFolder *trash_folder = NULL;
1342                 trash_folder = modest_tny_account_get_special_folder (account,
1343                                                                       TNY_FOLDER_TYPE_TRASH);
1344                 /* TODO: error_handling */
1345                  modest_mail_operation_xfer_folder (self, folder,
1346                                                     TNY_FOLDER_STORE (trash_folder), 
1347                                                     TRUE, NULL, NULL);
1348         } else {
1349                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1350
1351                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1352                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1353
1354                 if (parent)
1355                         g_object_unref (G_OBJECT (parent));
1356         }
1357         g_object_unref (G_OBJECT (account));
1358
1359  end:
1360         /* Notify about operation end */
1361         modest_mail_operation_notify_end (self);
1362 }
1363
1364 static void
1365 transfer_folder_status_cb (GObject *obj,
1366                            TnyStatus *status,
1367                            gpointer user_data)
1368 {
1369         ModestMailOperation *self;
1370         ModestMailOperationPrivate *priv;
1371         ModestMailOperationState *state;
1372         XFerMsgAsyncHelper *helper;
1373
1374         g_return_if_fail (status != NULL);
1375         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1376
1377         helper = (XFerMsgAsyncHelper *) user_data;
1378         g_return_if_fail (helper != NULL);       
1379
1380         self = helper->mail_op;
1381         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1382
1383         priv->done = status->position;
1384         priv->total = status->of_total;
1385
1386         state = modest_mail_operation_clone_state (self);
1387         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1388         g_slice_free (ModestMailOperationState, state);
1389 }
1390
1391
1392 static void
1393 transfer_folder_cb (TnyFolder *folder, 
1394                     TnyFolderStore *into, 
1395                     gboolean cancelled, 
1396                     TnyFolder *new_folder, 
1397                     GError **err, 
1398                     gpointer user_data)
1399 {
1400         XFerMsgAsyncHelper *helper;
1401         ModestMailOperation *self = NULL;
1402         ModestMailOperationPrivate *priv = NULL;
1403
1404         helper = (XFerMsgAsyncHelper *) user_data;
1405         g_return_if_fail (helper != NULL);       
1406
1407         self = helper->mail_op;
1408         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1409
1410         if (*err) {
1411                 priv->error = g_error_copy (*err);
1412                 priv->done = 0;
1413                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1414         } else if (cancelled) {
1415                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1416                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1417                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1418                              _("Transference of %s was cancelled."),
1419                              tny_folder_get_name (folder));
1420         } else {
1421                 priv->done = 1;
1422                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1423         }
1424                 
1425         /* Notify about operation end */
1426         modest_mail_operation_notify_end (self);
1427
1428         /* If user defined callback function was defined, call it */
1429         if (helper->user_callback) {
1430                 gdk_threads_enter ();
1431                 helper->user_callback (priv->source, helper->user_data);
1432                 gdk_threads_leave ();
1433         }
1434
1435         /* Free */
1436         g_object_unref (helper->mail_op);
1437         g_slice_free   (XFerMsgAsyncHelper, helper);
1438         g_object_unref (folder);
1439         g_object_unref (into);
1440 }
1441
1442 void
1443 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1444                                    TnyFolder *folder,
1445                                    TnyFolderStore *parent,
1446                                    gboolean delete_original,
1447                                    XferMsgsAsynUserCallback user_callback,
1448                                    gpointer user_data)
1449 {
1450         ModestMailOperationPrivate *priv = NULL;
1451         ModestTnyFolderRules parent_rules = 0, rules; 
1452         XFerMsgAsyncHelper *helper = NULL;
1453
1454         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1455         g_return_if_fail (TNY_IS_FOLDER (folder));
1456
1457         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1458
1459         /* Get account and set it into mail_operation */
1460         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1461         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1462
1463         /* Get folder rules */
1464         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1465         if (TNY_IS_FOLDER (parent))
1466                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1467         
1468         /* The moveable restriction is applied also to copy operation */
1469         if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1470                 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1471                 /* Set status failed and set an error */
1472                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1473                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1474                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1475                              _("mail_in_ui_folder_move_target_error"));
1476
1477                 /* Notify the queue */
1478                 modest_mail_operation_notify_end (self);
1479         } else if (TNY_IS_FOLDER (parent) && 
1480                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1481                 /* Set status failed and set an error */
1482                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1483                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1484                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1485                              _("FIXME: parent folder does not accept new folders"));
1486
1487                 /* Notify the queue */
1488                 modest_mail_operation_notify_end (self);
1489         } else {
1490                 /* Pick references for async calls */
1491                 g_object_ref (folder);
1492                 g_object_ref (parent);
1493
1494                 /* Create the helper */
1495                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1496                 helper->mail_op = g_object_ref(self);
1497                 helper->dest_folder = NULL;
1498                 helper->headers = NULL;
1499                 helper->user_callback = user_callback;
1500                 helper->user_data = user_data;
1501                 
1502                 /* Move/Copy folder */          
1503                 tny_folder_copy_async (folder,
1504                                        parent,
1505                                        tny_folder_get_name (folder),
1506                                        delete_original,
1507                                        transfer_folder_cb,
1508                                        transfer_folder_status_cb,
1509                                        helper);
1510 /*                                     self); */
1511         }
1512 }
1513
1514 void
1515 modest_mail_operation_rename_folder (ModestMailOperation *self,
1516                                      TnyFolder *folder,
1517                                      const gchar *name)
1518 {
1519         ModestMailOperationPrivate *priv;
1520         ModestTnyFolderRules rules;
1521         XFerMsgAsyncHelper *helper;
1522
1523         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1524         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1525         g_return_if_fail (name);
1526         
1527         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1528
1529         /* Get account and set it into mail_operation */
1530         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1531
1532         /* Check folder rules */
1533         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1534         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1535                 /* Set status failed and set an error */
1536                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1537                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1538                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1539                              _("FIXME: unable to rename"));
1540
1541                 /* Notify about operation end */
1542                 modest_mail_operation_notify_end (self);
1543         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1544                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1545                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1546                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1547                              _("FIXME: unable to rename"));
1548                 /* Notify about operation end */
1549                 modest_mail_operation_notify_end (self);
1550         } else {
1551                 TnyFolderStore *into;
1552
1553                 /* Create the helper */
1554                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1555                 helper->mail_op = g_object_ref(self);
1556                 helper->dest_folder = NULL;
1557                 helper->headers = NULL;
1558                 helper->user_callback = NULL;
1559                 helper->user_data = NULL;
1560
1561                 /* Rename. Camel handles folder subscription/unsubscription */
1562                 into = tny_folder_get_folder_store (folder);
1563                 tny_folder_copy_async (folder, into, name, TRUE,
1564                                  transfer_folder_cb,
1565                                  transfer_folder_status_cb,
1566                                  helper);
1567 /*                               self); */
1568                 if (into)
1569                         g_object_unref (into);          
1570         }
1571  }
1572
1573 /* ******************************************************************* */
1574 /* **************************  MSG  ACTIONS  ************************* */
1575 /* ******************************************************************* */
1576
1577 void modest_mail_operation_get_msg (ModestMailOperation *self,
1578                                     TnyHeader *header,
1579                                     GetMsgAsyncUserCallback user_callback,
1580                                     gpointer user_data)
1581 {
1582         GetMsgAsyncHelper *helper = NULL;
1583         TnyFolder *folder;
1584         ModestMailOperationPrivate *priv;
1585         
1586         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1587         g_return_if_fail (TNY_IS_HEADER (header));
1588         
1589         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1590         folder = tny_header_get_folder (header);
1591
1592         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1593
1594         /* Get message from folder */
1595         if (folder) {
1596                 /* Get account and set it into mail_operation */
1597                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1598
1599                 helper = g_slice_new0 (GetMsgAsyncHelper);
1600                 helper->mail_op = self;
1601                 helper->user_callback = user_callback;
1602                 helper->user_data = user_data;
1603                 helper->header = g_object_ref (header);
1604
1605                 // The callback's reference so that the mail op is not
1606                 // finalized until the async operation is completed even if
1607                 // the user canceled the request meanwhile.
1608                 g_object_ref (G_OBJECT (helper->mail_op));
1609
1610                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1611
1612                 g_object_unref (G_OBJECT (folder));
1613         } else {
1614                 /* Set status failed and set an error */
1615                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1616                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1617                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1618                              _("Error trying to get a message. No folder found for header"));
1619
1620                 /* Notify the queue */
1621                 modest_mail_operation_notify_end (self);
1622         }
1623 }
1624
1625 static void
1626 get_msg_cb (TnyFolder *folder, 
1627             gboolean cancelled, 
1628             TnyMsg *msg, 
1629             GError **error, 
1630             gpointer user_data)
1631 {
1632         GetMsgAsyncHelper *helper = NULL;
1633         ModestMailOperation *self = NULL;
1634         ModestMailOperationPrivate *priv = NULL;
1635
1636         helper = (GetMsgAsyncHelper *) user_data;
1637         g_return_if_fail (helper != NULL);       
1638         self = helper->mail_op;
1639         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1640         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1641
1642         /* Check errors and cancel */
1643         if (*error) {
1644                 priv->error = g_error_copy (*error);
1645                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1646                 goto out;
1647         }
1648         if (cancelled) {
1649                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1650                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1651                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1652                              _("Error trying to refresh the contents of %s"),
1653                              tny_folder_get_name (folder));
1654                 goto out;
1655         }
1656
1657         /* The mail operation might have been canceled in which case we do not
1658            want to notify anyone anymore. */
1659         if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1660                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1661
1662                 /* If user defined callback function was defined, call it */
1663                 if (helper->user_callback) {
1664                         /* This callback is called into an iddle by tinymail,
1665                            and idles are not in the main lock */
1666                         gdk_threads_enter ();
1667                         helper->user_callback (self, helper->header, msg, helper->user_data);
1668                         gdk_threads_leave ();
1669                 }
1670         }
1671
1672  out:
1673         /* Free */
1674         g_object_unref (helper->header);
1675         g_slice_free (GetMsgAsyncHelper, helper);
1676                 
1677         /* Notify about operation end */
1678         if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED)
1679                 modest_mail_operation_notify_end (self);
1680
1681         g_object_unref (G_OBJECT (self));
1682 }
1683
1684 static void     
1685 get_msg_status_cb (GObject *obj,
1686                    TnyStatus *status,  
1687                    gpointer user_data)
1688 {
1689         GetMsgAsyncHelper *helper = NULL;
1690         ModestMailOperation *self;
1691         ModestMailOperationPrivate *priv;
1692         ModestMailOperationState *state;
1693
1694         g_return_if_fail (status != NULL);
1695         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1696
1697         helper = (GetMsgAsyncHelper *) user_data;
1698         g_return_if_fail (helper != NULL);       
1699
1700         self = helper->mail_op;
1701         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1702
1703         if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1704                 return;
1705
1706         priv->done = 1;
1707         priv->total = 1;
1708
1709         state = modest_mail_operation_clone_state (self);
1710         state->bytes_done = status->position;
1711         state->bytes_total = status->of_total;
1712         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1713         g_slice_free (ModestMailOperationState, state);
1714 }
1715
1716 /****************************************************/
1717 typedef struct {
1718         ModestMailOperation *mail_op;
1719         TnyList *headers;
1720         GetMsgAsyncUserCallback user_callback;
1721         gpointer user_data;
1722         GDestroyNotify notify;
1723 } GetFullMsgsInfo;
1724
1725 typedef struct {
1726         GetMsgAsyncUserCallback user_callback;
1727         TnyHeader *header;
1728         TnyMsg *msg;
1729         gpointer user_data;
1730         ModestMailOperation *mail_op;
1731 } NotifyGetMsgsInfo;
1732
1733
1734 /* 
1735  * Used by get_msgs_full_thread to call the user_callback for each
1736  * message that has been read
1737  */
1738 static gboolean
1739 notify_get_msgs_full (gpointer data)
1740 {
1741         NotifyGetMsgsInfo *info;
1742
1743         info = (NotifyGetMsgsInfo *) data;      
1744
1745         /* Call the user callback. Idles are not in the main lock, so
1746            lock it */
1747         gdk_threads_enter ();
1748         info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1749         gdk_threads_leave ();
1750
1751         g_slice_free (NotifyGetMsgsInfo, info);
1752
1753         return FALSE;
1754 }
1755
1756 /* 
1757  * Used by get_msgs_full_thread to free al the thread resources and to
1758  * call the destroy function for the passed user_data
1759  */
1760 static gboolean
1761 get_msgs_full_destroyer (gpointer data)
1762 {
1763         GetFullMsgsInfo *info;
1764
1765         info = (GetFullMsgsInfo *) data;
1766
1767         if (info->notify) {
1768                 gdk_threads_enter ();   
1769                 info->notify (info->user_data);
1770                 gdk_threads_leave ();
1771         }
1772
1773         /* free */
1774         g_object_unref (info->headers);
1775         g_slice_free (GetFullMsgsInfo, info);
1776
1777         return FALSE;
1778 }
1779
1780 static gpointer
1781 get_msgs_full_thread (gpointer thr_user_data)
1782 {
1783         GetFullMsgsInfo *info;
1784         ModestMailOperationPrivate *priv = NULL;
1785         TnyIterator *iter = NULL;
1786         
1787         info = (GetFullMsgsInfo *) thr_user_data;       
1788         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1789
1790         iter = tny_list_create_iterator (info->headers);
1791         while (!tny_iterator_is_done (iter)) { 
1792                 TnyHeader *header;
1793                 TnyFolder *folder;
1794                 
1795                 header = TNY_HEADER (tny_iterator_get_current (iter));
1796                 folder = tny_header_get_folder (header);
1797                                 
1798                 /* Get message from folder */
1799                 if (folder) {
1800                         TnyMsg *msg;
1801                         /* The callback will call it per each header */
1802                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1803
1804                         if (msg) {
1805                                 ModestMailOperationState *state;
1806                                 ModestPair *pair;
1807
1808                                 priv->done++;
1809
1810                                 /* notify progress */
1811                                 state = modest_mail_operation_clone_state (info->mail_op);
1812                                 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1813                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1814                                                  pair, (GDestroyNotify) modest_pair_free);
1815
1816                                 /* The callback is the responsible for
1817                                    freeing the message */
1818                                 if (info->user_callback) {
1819                                         NotifyGetMsgsInfo *info_notify;
1820                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1821                                         info_notify->user_callback = info->user_callback;
1822                                         info_notify->mail_op = info->mail_op;
1823                                         info_notify->header = g_object_ref (header);
1824                                         info_notify->msg = g_object_ref (msg);
1825                                         info_notify->user_data = info->user_data;
1826                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1827                                                          notify_get_msgs_full, 
1828                                                          info_notify, NULL);
1829                                 }
1830                                 g_object_unref (msg);
1831                         }
1832                 } else {
1833                         /* Set status failed and set an error */
1834                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1835                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1836                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1837                                      "Error trying to get a message. No folder found for header");
1838                 }
1839                 g_object_unref (header);                
1840                 tny_iterator_next (iter);
1841         }
1842
1843         /* Set operation status */
1844         if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1845                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1846
1847         /* Notify about operation end */
1848         g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1849
1850         /* Free thread resources. Will be called after all previous idles */
1851         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1852
1853         return NULL;
1854 }
1855
1856 void 
1857 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1858                                      TnyList *header_list, 
1859                                      GetMsgAsyncUserCallback user_callback,
1860                                      gpointer user_data,
1861                                      GDestroyNotify notify)
1862 {
1863         TnyHeader *header = NULL;
1864         TnyFolder *folder = NULL;
1865         GThread *thread;
1866         ModestMailOperationPrivate *priv = NULL;
1867         GetFullMsgsInfo *info = NULL;
1868         gboolean size_ok = TRUE;
1869         gint max_size;
1870         TnyIterator *iter = NULL;
1871         
1872         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1873         
1874         /* Init mail operation */
1875         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1876         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1877         priv->done = 0;
1878         priv->total = tny_list_get_length(header_list);
1879
1880         /* Get account and set it into mail_operation */
1881         if (tny_list_get_length (header_list) >= 1) {
1882                 iter = tny_list_create_iterator (header_list);
1883                 header = TNY_HEADER (tny_iterator_get_current (iter));
1884                 folder = tny_header_get_folder (header);                
1885                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1886                 g_object_unref (header);
1887                 g_object_unref (folder);
1888
1889                 if (tny_list_get_length (header_list) == 1) {
1890                         g_object_unref (iter);
1891                         iter = NULL;
1892                 }
1893         }
1894
1895         /* Get msg size limit */
1896         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1897                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1898                                          &(priv->error));
1899         if (priv->error) {
1900                 g_clear_error (&(priv->error));
1901                 max_size = G_MAXINT;
1902         } else {
1903                 max_size = max_size * KB;
1904         }
1905
1906         /* Check message size limits. If there is only one message
1907            always retrieve it */
1908         if (iter != NULL) {
1909                 while (!tny_iterator_is_done (iter) && size_ok) {
1910                         header = TNY_HEADER (tny_iterator_get_current (iter));
1911                         if (tny_header_get_message_size (header) >= max_size)
1912                                 size_ok = FALSE;
1913                         g_object_unref (header);
1914                         tny_iterator_next (iter);
1915                 }
1916                 g_object_unref (iter);
1917         }
1918
1919         if (size_ok) {
1920                 /* Create the info */
1921                 info = g_slice_new0 (GetFullMsgsInfo);
1922                 info->mail_op = self;
1923                 info->user_callback = user_callback;
1924                 info->user_data = user_data;
1925                 info->headers = g_object_ref (header_list);
1926                 info->notify = notify;
1927
1928                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1929         } else {
1930                 /* Set status failed and set an error */
1931                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1932                 /* FIXME: the error msg is different for pop */
1933                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1934                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1935                              _("emev_ni_ui_imap_msg_size_exceed_error"));
1936                 /* Remove from queue and free resources */
1937                 modest_mail_operation_notify_end (self);
1938                 if (notify)
1939                         notify (user_data);
1940         }
1941 }
1942
1943
1944 void 
1945 modest_mail_operation_remove_msg (ModestMailOperation *self,  TnyHeader *header,
1946                                   gboolean remove_to_trash /*ignored*/)
1947 {
1948         TnyFolder *folder;
1949         ModestMailOperationPrivate *priv;
1950
1951         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1952         g_return_if_fail (TNY_IS_HEADER (header));
1953
1954         if (remove_to_trash)
1955                 g_warning ("remove to trash is not implemented");
1956
1957         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1958         folder = tny_header_get_folder (header);
1959
1960         /* Get account and set it into mail_operation */
1961         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1962
1963         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1964
1965
1966         tny_folder_remove_msg (folder, header, &(priv->error));
1967         if (!priv->error) {
1968                 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1969
1970                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
1971                         tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
1972                 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
1973                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1974                 else
1975                         /* lcoal folders */
1976                         tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1977         }
1978         
1979         
1980         /* Set status */
1981         if (!priv->error)
1982                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1983         else
1984                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1985
1986         /* Free */
1987         g_object_unref (G_OBJECT (folder));
1988
1989         /* Notify about operation end */
1990         modest_mail_operation_notify_end (self);
1991 }
1992
1993 static void
1994 transfer_msgs_status_cb (GObject *obj,
1995                          TnyStatus *status,  
1996                          gpointer user_data)
1997 {
1998         XFerMsgAsyncHelper *helper = NULL;
1999         ModestMailOperation *self;
2000         ModestMailOperationPrivate *priv;
2001         ModestMailOperationState *state;
2002
2003
2004         g_return_if_fail (status != NULL);
2005         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2006
2007         helper = (XFerMsgAsyncHelper *) user_data;
2008         g_return_if_fail (helper != NULL);       
2009
2010         self = helper->mail_op;
2011         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2012
2013         priv->done = status->position;
2014         priv->total = status->of_total;
2015
2016         state = modest_mail_operation_clone_state (self);
2017         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2018         g_slice_free (ModestMailOperationState, state);
2019 }
2020
2021
2022 static void
2023 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2024 {
2025         XFerMsgAsyncHelper *helper;
2026         ModestMailOperation *self;
2027         ModestMailOperationPrivate *priv;
2028
2029         helper = (XFerMsgAsyncHelper *) user_data;
2030         self = helper->mail_op;
2031
2032         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2033
2034         if (*err) {
2035                 priv->error = g_error_copy (*err);
2036                 priv->done = 0;
2037                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2038         } else if (cancelled) {
2039                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2040                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2041                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2042                              _("Error trying to refresh the contents of %s"),
2043                              tny_folder_get_name (folder));
2044         } else {
2045                 priv->done = 1;
2046                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2047         }
2048
2049         /* Notify about operation end */
2050         modest_mail_operation_notify_end (self);
2051
2052         /* If user defined callback function was defined, call it */
2053         if (helper->user_callback) {
2054                 gdk_threads_enter ();
2055                 helper->user_callback (priv->source, helper->user_data);
2056                 gdk_threads_leave ();
2057         }
2058
2059         /* Free */
2060         g_object_unref (helper->headers);
2061         g_object_unref (helper->dest_folder);
2062         g_object_unref (helper->mail_op);
2063         g_slice_free   (XFerMsgAsyncHelper, helper);
2064         g_object_unref (folder);
2065
2066 }
2067
2068 void
2069 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2070                                  TnyList *headers, 
2071                                  TnyFolder *folder, 
2072                                  gboolean delete_original,
2073                                  XferMsgsAsynUserCallback user_callback,
2074                                  gpointer user_data)
2075 {
2076         ModestMailOperationPrivate *priv;
2077         TnyIterator *iter;
2078         TnyFolder *src_folder;
2079         XFerMsgAsyncHelper *helper;
2080         TnyHeader *header;
2081         ModestTnyFolderRules rules;
2082         const gchar *id1 = NULL;
2083         const gchar *id2 = NULL;
2084         gboolean same_folder = FALSE;
2085
2086         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2087         g_return_if_fail (TNY_IS_LIST (headers));
2088         g_return_if_fail (TNY_IS_FOLDER (folder));
2089
2090         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2091         priv->total = 1;
2092         priv->done = 0;
2093         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2094
2095         /* Apply folder rules */
2096         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2097         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2098                 /* Set status failed and set an error */
2099                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2100                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2101                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2102                              _("ckct_ib_unable_to_paste_here"));
2103                 /* Notify the queue */
2104                 modest_mail_operation_notify_end (self);
2105                 return;
2106         }
2107                 
2108         /* Get source folder */
2109         iter = tny_list_create_iterator (headers);
2110         header = TNY_HEADER (tny_iterator_get_current (iter));
2111         src_folder = tny_header_get_folder (header);
2112         g_object_unref (header);
2113         g_object_unref (iter);
2114
2115         /* Check folder source and destination */
2116         id1 = tny_folder_get_id (src_folder);
2117         id2 = tny_folder_get_id (TNY_FOLDER(folder));
2118         same_folder = !g_ascii_strcasecmp (id1, id2);
2119         if (same_folder) {
2120                 /* Set status failed and set an error */
2121                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2122                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2123                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2124                              _("mcen_ib_unable_to_copy_samefolder"));
2125                 
2126                 /* Notify the queue */
2127                 modest_mail_operation_notify_end (self);
2128                 
2129                 /* Free */
2130                 g_object_unref (src_folder);            
2131                 return;
2132         }
2133
2134         /* Create the helper */
2135         helper = g_slice_new0 (XFerMsgAsyncHelper);
2136         helper->mail_op = g_object_ref(self);
2137         helper->dest_folder = g_object_ref(folder);
2138         helper->headers = g_object_ref(headers);
2139         helper->user_callback = user_callback;
2140         helper->user_data = user_data;
2141
2142         /* Get account and set it into mail_operation */
2143         priv->account = modest_tny_folder_get_account (src_folder);
2144
2145         /* Transfer messages */
2146         tny_folder_transfer_msgs_async (src_folder, 
2147                                         headers, 
2148                                         folder, 
2149                                         delete_original, 
2150                                         transfer_msgs_cb, 
2151                                         transfer_msgs_status_cb,
2152                                         helper);
2153 }
2154
2155
2156 static void
2157 on_refresh_folder (TnyFolder   *folder, 
2158                    gboolean     cancelled, 
2159                    GError     **error,
2160                    gpointer     user_data)
2161 {
2162         RefreshAsyncHelper *helper = NULL;
2163         ModestMailOperation *self = NULL;
2164         ModestMailOperationPrivate *priv = NULL;
2165
2166         helper = (RefreshAsyncHelper *) user_data;
2167         self = helper->mail_op;
2168         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2169
2170         if (*error) {
2171                 priv->error = g_error_copy (*error);
2172                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2173                 goto out;
2174         }
2175
2176         if (cancelled) {
2177                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2178                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2179                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2180                              _("Error trying to refresh the contents of %s"),
2181                              tny_folder_get_name (folder));
2182                 goto out;
2183         }
2184
2185         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2186
2187  out:
2188         /* Call user defined callback, if it exists */
2189         if (helper->user_callback) {
2190                 gdk_threads_enter ();
2191                 helper->user_callback (priv->source, folder, helper->user_data);
2192                 gdk_threads_leave ();
2193         }
2194
2195         /* Free */
2196         g_object_unref (helper->mail_op);
2197         g_slice_free   (RefreshAsyncHelper, helper);
2198         g_object_unref (folder);
2199
2200         /* Notify about operation end */
2201         modest_mail_operation_notify_end (self);
2202 }
2203
2204 static void
2205 on_refresh_folder_status_update (GObject *obj,
2206                                  TnyStatus *status,
2207                                  gpointer user_data)
2208 {
2209         RefreshAsyncHelper *helper = NULL;
2210         ModestMailOperation *self = NULL;
2211         ModestMailOperationPrivate *priv = NULL;
2212         ModestMailOperationState *state;
2213
2214         g_return_if_fail (user_data != NULL);
2215         g_return_if_fail (status != NULL);
2216         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2217
2218         helper = (RefreshAsyncHelper *) user_data;
2219         self = helper->mail_op;
2220         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2221
2222         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2223
2224         priv->done = status->position;
2225         priv->total = status->of_total;
2226
2227         state = modest_mail_operation_clone_state (self);
2228         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2229         g_slice_free (ModestMailOperationState, state);
2230 }
2231
2232 void 
2233 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2234                                        TnyFolder *folder,
2235                                        RefreshAsyncUserCallback user_callback,
2236                                        gpointer user_data)
2237 {
2238         ModestMailOperationPrivate *priv = NULL;
2239         RefreshAsyncHelper *helper = NULL;
2240
2241         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2242
2243         /* Pick a reference */
2244         g_object_ref (folder);
2245
2246         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2247
2248         /* Get account and set it into mail_operation */
2249         priv->account = modest_tny_folder_get_account  (folder);
2250
2251         /* Create the helper */
2252         helper = g_slice_new0 (RefreshAsyncHelper);
2253         helper->mail_op = g_object_ref(self);
2254         helper->user_callback = user_callback;
2255         helper->user_data = user_data;
2256
2257         /* Refresh the folder. TODO: tinymail could issue a status
2258            updates before the callback call then this could happen. We
2259            must review the design */
2260         tny_folder_refresh_async (folder,
2261                                   on_refresh_folder,
2262                                   on_refresh_folder_status_update,
2263                                   helper);
2264 }
2265
2266 /**
2267  *
2268  * It's used by the mail operation queue to notify the observers
2269  * attached to that signal that the operation finished. We need to use
2270  * that because tinymail does not give us the progress of a given
2271  * operation when it finishes (it directly calls the operation
2272  * callback).
2273  */
2274 static void
2275 modest_mail_operation_notify_end (ModestMailOperation *self)
2276 {
2277         ModestMailOperationState *state;
2278         ModestMailOperationPrivate *priv = NULL;
2279
2280         g_return_if_fail (self);
2281
2282         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2283
2284         if (!priv) {
2285                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2286                 return;
2287         }
2288         
2289         /* Set the account back to not busy */
2290         if (priv->account_name) {
2291                 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(), 
2292                                                      priv->account_name, FALSE);
2293                 g_free(priv->account_name);
2294                 priv->account_name = NULL;
2295         }
2296         
2297         /* Notify the observers about the mail opertation end */
2298         state = modest_mail_operation_clone_state (self);
2299         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2300         g_slice_free (ModestMailOperationState, state);
2301 }