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