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