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