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