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