bb0019d77835a73e84e1bc6d5c317aeb1b1d9e8e
[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-camel-pop-folder.h>
39 #include <tny-camel-imap-folder.h>
40 #include <tny-camel-mem-stream.h>
41 #include <tny-simple-list.h>
42 #include <tny-send-queue.h>
43 #include <tny-status.h>
44 #include <tny-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include "modest-account-mgr-helpers.h"
49 #include <modest-tny-account.h>
50 #include <modest-tny-send-queue.h>
51 #include <modest-runtime.h>
52 #include "modest-text-utils.h"
53 #include "modest-tny-msg.h"
54 #include "modest-tny-folder.h"
55 #include "modest-tny-account-store.h"
56 #include "modest-tny-platform-factory.h"
57 #include "modest-marshal.h"
58 #include "modest-error.h"
59 #include "modest-mail-operation.h"
60
61 #define KB 1024
62
63 /* 
64  * Remove all these #ifdef stuff when the tinymail's idle calls become
65  * locked
66  */
67 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
68
69 /* 'private'/'protected' functions */
70 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
71 static void modest_mail_operation_init       (ModestMailOperation *obj);
72 static void modest_mail_operation_finalize   (GObject *obj);
73
74 static void     get_msg_async_cb (TnyFolder *folder, 
75                                   gboolean cancelled, 
76                                   TnyMsg *msg, 
77                                   GError *rr, 
78                                   gpointer user_data);
79
80 static void     get_msg_status_cb (GObject *obj,
81                                    TnyStatus *status,  
82                                    gpointer user_data);
83
84 static void     modest_mail_operation_notify_start (ModestMailOperation *self);
85 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
86
87 static void     notify_progress_of_multiple_messages (ModestMailOperation *self,
88                                                       TnyStatus *status,
89                                                       gint *last_total_bytes,
90                                                       gint *sum_total_bytes,
91                                                       gint total_bytes,
92                                                       gboolean increment_done);
93
94 static guint    compute_message_list_size (TnyList *headers);
95
96 static guint    compute_message_array_size (GPtrArray *headers);
97
98 static int      compare_headers_by_date   (gconstpointer a,
99                                            gconstpointer b);
100
101 enum _ModestMailOperationSignals 
102 {
103         PROGRESS_CHANGED_SIGNAL,
104         OPERATION_STARTED_SIGNAL,
105         OPERATION_FINISHED_SIGNAL,
106         NUM_SIGNALS
107 };
108
109 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
110 struct _ModestMailOperationPrivate {
111         TnyAccount                *account;
112         guint                      done;
113         guint                      total;
114         GObject                   *source;
115         GError                    *error;
116         ErrorCheckingUserCallback  error_checking;
117         gpointer                   error_checking_user_data;
118         ErrorCheckingUserDataDestroyer error_checking_user_data_destroyer;
119         ModestMailOperationStatus  status;      
120         ModestMailOperationTypeOperation op_type;
121 };
122
123 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
124                                                    MODEST_TYPE_MAIL_OPERATION, \
125                                                    ModestMailOperationPrivate))
126
127 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
128                                                    priv->status = new_status;\
129                                                }
130
131
132 typedef struct {
133         GetMsgAsyncUserCallback user_callback;
134         TnyHeader *header;
135         gpointer user_data;
136         ModestMailOperation *mail_op;
137         GDestroyNotify destroy_notify;
138         gint last_total_bytes;
139         gint sum_total_bytes;
140         gint total_bytes;
141 } GetMsgInfo;
142
143 typedef struct {
144         ModestMailOperation *mail_op;
145         TnyMsg *msg;
146         gulong msg_sent_handler;
147         gulong error_happened_handler;
148 } SendMsgInfo;
149
150 static void     send_mail_msg_sent_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
151                                             guint nth, guint total, gpointer userdata);
152 static void     send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
153                                                   GError *error, gpointer userdata);
154 static void     common_send_mail_operation_end (TnySendQueue *queue, TnyMsg *msg, 
155                                                 SendMsgInfo *info);
156
157 typedef struct _RefreshAsyncHelper {    
158         ModestMailOperation *mail_op;
159         RefreshAsyncUserCallback user_callback; 
160         gpointer user_data;
161 } RefreshAsyncHelper;
162
163 typedef struct _XFerMsgAsyncHelper
164 {
165         ModestMailOperation *mail_op;
166         TnyList *headers;
167         TnyFolder *dest_folder;
168         XferAsyncUserCallback user_callback;    
169         gboolean delete;
170         gpointer user_data;
171         gint last_total_bytes;
172         gint sum_total_bytes;
173         gint total_bytes;
174 } XFerMsgAsyncHelper;
175
176 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
177                                                       TnyMsg *msg,
178                                                       gpointer userdata);
179
180 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
181                                                        const gchar *from, const gchar *to,
182                                                        const gchar *cc, const gchar *bcc,
183                                                        const gchar *subject, const gchar *plain_body,
184                                                        const gchar *html_body, const GList *attachments_list,
185                                                        const GList *images_list,
186                                                        TnyHeaderFlags priority_flags,
187                                                        ModestMailOperationCreateMsgCallback callback,
188                                                        gpointer userdata);
189
190 static gboolean      idle_notify_queue (gpointer data);
191 typedef struct
192 {
193         ModestMailOperation *mail_op;
194         gchar *from;
195         gchar *to;
196         gchar *cc;
197         gchar *bcc;
198         gchar *subject;
199         gchar *plain_body;
200         gchar *html_body;
201         GList *attachments_list;
202         GList *images_list;
203         TnyHeaderFlags priority_flags;
204         ModestMailOperationCreateMsgCallback callback;
205         gpointer userdata;
206 } CreateMsgInfo;
207
208 typedef struct
209 {
210         ModestMailOperation *mail_op;
211         TnyMsg *msg;
212         ModestMailOperationCreateMsgCallback callback;
213         gpointer userdata;
214 } CreateMsgIdleInfo;
215
216 /* globals */
217 static GObjectClass *parent_class = NULL;
218
219 static guint signals[NUM_SIGNALS] = {0};
220
221 GType
222 modest_mail_operation_get_type (void)
223 {
224         static GType my_type = 0;
225         if (!my_type) {
226                 static const GTypeInfo my_info = {
227                         sizeof(ModestMailOperationClass),
228                         NULL,           /* base init */
229                         NULL,           /* base finalize */
230                         (GClassInitFunc) modest_mail_operation_class_init,
231                         NULL,           /* class finalize */
232                         NULL,           /* class data */
233                         sizeof(ModestMailOperation),
234                         1,              /* n_preallocs */
235                         (GInstanceInitFunc) modest_mail_operation_init,
236                         NULL
237                 };
238                 my_type = g_type_register_static (G_TYPE_OBJECT,
239                                                   "ModestMailOperation",
240                                                   &my_info, 0);
241         }
242         return my_type;
243 }
244
245 static void
246 modest_mail_operation_class_init (ModestMailOperationClass *klass)
247 {
248         GObjectClass *gobject_class;
249         gobject_class = (GObjectClass*) klass;
250
251         parent_class            = g_type_class_peek_parent (klass);
252         gobject_class->finalize = modest_mail_operation_finalize;
253
254         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
255
256         /**
257          * ModestMailOperation::progress-changed
258          * @self: the #MailOperation that emits the signal
259          * @user_data: user data set when the signal handler was connected
260          *
261          * Emitted when the progress of a mail operation changes
262          */
263         signals[PROGRESS_CHANGED_SIGNAL] = 
264                 g_signal_new ("progress-changed",
265                               G_TYPE_FROM_CLASS (gobject_class),
266                               G_SIGNAL_RUN_FIRST,
267                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
268                               NULL, NULL,
269                               g_cclosure_marshal_VOID__POINTER,
270                               G_TYPE_NONE, 1, G_TYPE_POINTER);
271         /**
272          * operation-started
273          *
274          * This signal is issued whenever a mail operation starts, and
275          * starts mean when the tinymail operation is issued. This
276          * means that it could happen that something wrong happens and
277          * the tinymail function is never called. In this situation a
278          * operation-finished will be issued without any
279          * operation-started
280          */
281         signals[OPERATION_STARTED_SIGNAL] =
282                 g_signal_new ("operation-started",
283                               G_TYPE_FROM_CLASS (gobject_class),
284                               G_SIGNAL_RUN_FIRST,
285                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_started),
286                               NULL, NULL,
287                               g_cclosure_marshal_VOID__VOID,
288                               G_TYPE_NONE, 0);
289         /**
290          * operation-started
291          *
292          * This signal is issued whenever a mail operation
293          * finishes. Note that this signal could be issued without any
294          * previous "operation-started" signal, because this last one
295          * is only issued when the tinymail operation is successfully
296          * started
297          */
298         signals[OPERATION_FINISHED_SIGNAL] =
299                 g_signal_new ("operation-finished",
300                               G_TYPE_FROM_CLASS (gobject_class),
301                               G_SIGNAL_RUN_FIRST,
302                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_finished),
303                               NULL, NULL,
304                               g_cclosure_marshal_VOID__VOID,
305                               G_TYPE_NONE, 0);
306 }
307
308 static void
309 modest_mail_operation_init (ModestMailOperation *obj)
310 {
311         ModestMailOperationPrivate *priv;
312
313         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
314
315         priv->account        = NULL;
316         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
317         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
318         priv->error          = NULL;
319         priv->done           = 0;
320         priv->total          = 0;
321         priv->source         = NULL;
322         priv->error_checking = NULL;
323         priv->error_checking_user_data = NULL;
324 }
325
326 static void
327 modest_mail_operation_finalize (GObject *obj)
328 {
329         ModestMailOperationPrivate *priv;
330
331         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
332
333         
334         
335         if (priv->error) {
336                 g_error_free (priv->error);
337                 priv->error = NULL;
338         }
339         if (priv->source) {
340                 g_object_unref (priv->source);
341                 priv->source = NULL;
342         }
343         if (priv->account) {
344                 g_object_unref (priv->account);
345                 priv->account = NULL;
346         }
347
348
349         G_OBJECT_CLASS(parent_class)->finalize (obj);
350 }
351
352 ModestMailOperation*
353 modest_mail_operation_new (GObject *source)
354 {
355         ModestMailOperation *obj;
356         ModestMailOperationPrivate *priv;
357                 
358         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
359         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
360
361         if (source != NULL)
362                 priv->source = g_object_ref(source);
363
364         return obj;
365 }
366
367 ModestMailOperation*
368 modest_mail_operation_new_with_error_handling (GObject *source,
369                                                ErrorCheckingUserCallback error_handler,
370                                                gpointer user_data,
371                                                ErrorCheckingUserDataDestroyer error_handler_destroyer)
372 {
373         ModestMailOperation *obj;
374         ModestMailOperationPrivate *priv;
375                 
376         obj = modest_mail_operation_new (source);
377         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
378         
379         g_return_val_if_fail (error_handler != NULL, obj);
380         priv->error_checking = error_handler;
381         priv->error_checking_user_data = user_data;
382         priv->error_checking_user_data_destroyer = error_handler_destroyer;
383
384         return obj;
385 }
386
387 void
388 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
389 {
390         ModestMailOperationPrivate *priv;
391         
392         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
393         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
394
395         /* Call the user callback */
396         if (priv->error_checking != NULL)
397                 priv->error_checking (self, priv->error_checking_user_data);
398 }
399
400
401 ModestMailOperationTypeOperation
402 modest_mail_operation_get_type_operation (ModestMailOperation *self)
403 {
404         ModestMailOperationPrivate *priv;
405
406         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
407         
408         return priv->op_type;
409 }
410
411 gboolean 
412 modest_mail_operation_is_mine (ModestMailOperation *self, 
413                                GObject *me)
414 {
415         ModestMailOperationPrivate *priv;
416
417         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
418         if (priv->source == NULL) return FALSE;
419
420         return priv->source == me;
421 }
422
423 GObject *
424 modest_mail_operation_get_source (ModestMailOperation *self)
425 {
426         ModestMailOperationPrivate *priv;
427
428         g_return_val_if_fail (self, NULL);
429         
430         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
431         if (!priv) {
432                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
433                 return NULL;
434         }
435         
436         return (priv->source) ? g_object_ref (priv->source) : NULL;
437 }
438
439 ModestMailOperationStatus
440 modest_mail_operation_get_status (ModestMailOperation *self)
441 {
442         ModestMailOperationPrivate *priv;
443
444         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
445         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
446                               MODEST_MAIL_OPERATION_STATUS_INVALID);
447
448         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
449         if (!priv) {
450                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
451                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
452         }
453         
454         return priv->status;
455 }
456
457 const GError *
458 modest_mail_operation_get_error (ModestMailOperation *self)
459 {
460         ModestMailOperationPrivate *priv;
461
462         g_return_val_if_fail (self, NULL);
463         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
464
465         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
466
467         if (!priv) {
468                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
469                 return NULL;
470         }
471
472         return priv->error;
473 }
474
475 gboolean 
476 modest_mail_operation_cancel (ModestMailOperation *self)
477 {
478         ModestMailOperationPrivate *priv;
479         gboolean canceled = FALSE;
480
481         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
482
483         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
484
485         /* Note that if we call cancel with an already canceled mail
486            operation the progress changed signal won't be emitted */
487         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
488                 return FALSE;
489
490         /* Set new status */
491         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
492         
493         /* Cancel the mail operation. We need to wrap it between this
494            start/stop operations to allow following calls to the
495            account */
496         g_return_val_if_fail (priv->account, FALSE);
497
498         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SEND) {
499                 ModestTnySendQueue *queue;
500                 queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (priv->account));
501                 /* Cancel sending without removing the item */
502                 tny_send_queue_cancel (TNY_SEND_QUEUE (queue), FALSE, NULL);
503         } else {
504                 /* Cancel operation */
505                 tny_account_cancel (priv->account);
506         }
507
508         return canceled;
509 }
510
511 guint 
512 modest_mail_operation_get_task_done (ModestMailOperation *self)
513 {
514         ModestMailOperationPrivate *priv;
515
516         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
517
518         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
519         return priv->done;
520 }
521
522 guint 
523 modest_mail_operation_get_task_total (ModestMailOperation *self)
524 {
525         ModestMailOperationPrivate *priv;
526
527         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
528
529         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
530         return priv->total;
531 }
532
533 gboolean
534 modest_mail_operation_is_finished (ModestMailOperation *self)
535 {
536         ModestMailOperationPrivate *priv;
537         gboolean retval = FALSE;
538
539         if (!MODEST_IS_MAIL_OPERATION (self)) {
540                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
541                 return retval;
542         }
543
544         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
545
546         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
547             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
548             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
549             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
550                 retval = TRUE;
551         } else {
552                 retval = FALSE;
553         }
554
555         return retval;
556 }
557
558 /*
559  * Creates an image of the current state of a mail operation, the
560  * caller must free it
561  */
562 static ModestMailOperationState *
563 modest_mail_operation_clone_state (ModestMailOperation *self)
564 {
565         ModestMailOperationState *state;
566         ModestMailOperationPrivate *priv;
567
568         /* FIXME: this should be fixed properly
569          * 
570          * in some cases, priv was NULL, so checking here to
571          * make sure.
572          */
573         g_return_val_if_fail (self, NULL);
574         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
575         g_return_val_if_fail (priv, NULL);
576
577         if (!priv)
578                 return NULL;
579
580         state = g_slice_new (ModestMailOperationState);
581
582         state->status = priv->status;
583         state->op_type = priv->op_type;
584         state->done = priv->done;
585         state->total = priv->total;
586         state->finished = modest_mail_operation_is_finished (self);
587         state->bytes_done = 0;
588         state->bytes_total = 0;
589
590         return state;
591 }
592
593 /* ******************************************************************* */
594 /* ************************** SEND   ACTIONS ************************* */
595 /* ******************************************************************* */
596
597 void
598 modest_mail_operation_send_mail (ModestMailOperation *self,
599                                  TnyTransportAccount *transport_account,
600                                  TnyMsg* msg)
601 {
602         TnySendQueue *send_queue = NULL;
603         ModestMailOperationPrivate *priv;
604         SendMsgInfo *info;
605         
606         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
607         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
608         g_return_if_fail (TNY_IS_MSG (msg));
609         
610         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
611
612         /* Get account and set it into mail_operation */
613         priv->account = g_object_ref (transport_account);
614         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
615         priv->done = 1;
616         priv->total = 1;
617
618         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
619         if (!TNY_IS_SEND_QUEUE(send_queue)) {
620                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
621                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
622                              "modest: could not find send queue for account\n");
623                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
624                 modest_mail_operation_notify_end (self);
625
626         } else {
627                 /* Add the msg to the queue */
628                 modest_mail_operation_notify_start (self);
629                 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue), 
630                                            msg, 
631                                            &(priv->error));
632
633                 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
634
635                 info = g_slice_new0 (SendMsgInfo);
636
637                 info->mail_op = g_object_ref (self);
638                 info->msg = g_object_ref (msg);
639                 info->msg_sent_handler = g_signal_connect (G_OBJECT (send_queue), "msg-sent",
640                                                            G_CALLBACK (send_mail_msg_sent_handler), info);
641                 info->error_happened_handler = g_signal_connect (G_OBJECT (send_queue), "error-happened",
642                                                                  G_CALLBACK (send_mail_error_happened_handler), info);
643         }
644
645 }
646
647 static void
648 common_send_mail_operation_end (TnySendQueue *queue, TnyMsg *msg,
649                                 SendMsgInfo *info)
650 {
651         if (msg == info->msg) {
652                 g_signal_handler_disconnect (queue, info->msg_sent_handler);
653                 info->msg_sent_handler = 0;
654                 g_signal_handler_disconnect (queue, info->error_happened_handler);
655                 info->error_happened_handler = 0;
656                 g_object_unref (info->msg);
657                 modest_mail_operation_notify_end (info->mail_op);
658                 g_object_unref (info->mail_op);
659                 g_slice_free (SendMsgInfo, info);
660         }
661 }
662
663 static void     
664 send_mail_msg_sent_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
665                             guint nth, guint total, gpointer userdata)
666 {
667         SendMsgInfo *info = (SendMsgInfo *) info;
668
669         if (msg == info->msg) {
670                 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
671                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
672         }
673                 
674         common_send_mail_operation_end (queue, msg, info);
675 }
676
677 static void     
678 send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
679                                   GError *error, gpointer userdata)
680 {
681         SendMsgInfo *info = (SendMsgInfo *) info;
682
683         if (msg == info->msg) {
684                 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
685                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
686                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
687                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
688                              "modest: send mail failed\n");
689         }
690                 
691         common_send_mail_operation_end (queue, msg, info);
692 }
693
694
695 static gboolean
696 idle_create_msg_cb (gpointer idle_data)
697 {
698         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
699
700         /* This is a GDK lock because we are an idle callback and
701          * info->callback can contain Gtk+ code */
702
703         gdk_threads_enter (); /* CHECKED */
704         info->callback (info->mail_op, info->msg, info->userdata);
705
706         g_object_unref (info->mail_op);
707         if (info->msg)
708                 g_object_unref (info->msg);
709         g_slice_free (CreateMsgIdleInfo, info);
710         gdk_threads_leave (); /* CHECKED */
711
712         return FALSE;
713 }
714
715 static gpointer 
716 create_msg_thread (gpointer thread_data)
717 {
718         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
719         TnyMsg *new_msg = NULL;
720         ModestMailOperationPrivate *priv;
721
722         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
723         if (info->html_body == NULL) {
724                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
725                                               info->bcc, info->subject, info->plain_body, 
726                                               info->attachments_list);
727         } else {
728                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
729                                                          info->bcc, info->subject, info->html_body,
730                                                          info->plain_body, info->attachments_list,
731                                                          info->images_list);
732         }
733
734         if (new_msg) {
735                 TnyHeader *header;
736
737                 /* Set priority flags in message */
738                 header = tny_msg_get_header (new_msg);
739                 tny_header_set_flag (header, info->priority_flags);
740
741                 /* Set attachment flags in message */
742                 if (info->attachments_list != NULL)
743                         tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
744
745                 g_object_unref (G_OBJECT(header));
746         } else {
747                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
748                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
749                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
750                              "modest: failed to create a new msg\n");
751         }
752
753
754         g_free (info->to);
755         g_free (info->from);
756         g_free (info->cc);
757         g_free (info->bcc);
758         g_free (info->plain_body);
759         g_free (info->html_body);
760         g_free (info->subject);
761         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
762         g_list_free (info->attachments_list);
763         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
764         g_list_free (info->images_list);
765
766         if (info->callback) {
767                 CreateMsgIdleInfo *idle_info;
768                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
769                 idle_info->mail_op = g_object_ref (info->mail_op);
770                 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
771                 idle_info->callback = info->callback;
772                 idle_info->userdata = info->userdata;
773                 g_idle_add (idle_create_msg_cb, idle_info);
774         } else {
775                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
776         }
777
778         g_object_unref (info->mail_op);
779         g_slice_free (CreateMsgInfo, info);
780         return NULL;
781 }
782
783 void
784 modest_mail_operation_create_msg (ModestMailOperation *self,
785                                   const gchar *from, const gchar *to,
786                                   const gchar *cc, const gchar *bcc,
787                                   const gchar *subject, const gchar *plain_body,
788                                   const gchar *html_body,
789                                   const GList *attachments_list,
790                                   const GList *images_list,
791                                   TnyHeaderFlags priority_flags,
792                                   ModestMailOperationCreateMsgCallback callback,
793                                   gpointer userdata)
794 {
795         CreateMsgInfo *info = NULL;
796
797         info = g_slice_new0 (CreateMsgInfo);
798         info->mail_op = g_object_ref (self);
799
800         info->from = g_strdup (from);
801         info->to = g_strdup (to);
802         info->cc = g_strdup (cc);
803         info->bcc  = g_strdup (bcc);
804         info->subject = g_strdup (subject);
805         info->plain_body = g_strdup (plain_body);
806         info->html_body = g_strdup (html_body);
807         info->attachments_list = g_list_copy ((GList *) attachments_list);
808         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
809         info->images_list = g_list_copy ((GList *) images_list);
810         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
811         info->priority_flags = priority_flags;
812
813         info->callback = callback;
814         info->userdata = userdata;
815
816         g_thread_create (create_msg_thread, info, FALSE, NULL);
817 }
818
819 typedef struct
820 {
821         TnyTransportAccount *transport_account;
822         TnyMsg *draft_msg;
823 } SendNewMailInfo;
824
825 static void
826 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
827                                         TnyMsg *msg,
828                                         gpointer userdata)
829 {
830         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
831         TnyFolder *draft_folder = NULL;
832         TnyFolder *outbox_folder = NULL;
833         TnyHeader *header;
834         GError *err = NULL;
835
836         if (!msg) {
837                 goto end;
838         }
839
840         /* Call mail operation */
841         modest_mail_operation_send_mail (self, info->transport_account, msg);
842
843         /* Remove old mail from its source folder */
844         draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
845                                                               TNY_FOLDER_TYPE_DRAFTS);
846         outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
847                                                                TNY_FOLDER_TYPE_OUTBOX);
848         if (info->draft_msg != NULL) {
849                 TnyFolder *folder = NULL;
850                 TnyFolder *src_folder = NULL;
851                 TnyFolderType folder_type;              
852                 folder = tny_msg_get_folder (info->draft_msg);          
853                 if (folder == NULL) goto end;
854                 folder_type = modest_tny_folder_guess_folder_type (folder);
855
856                 if (folder_type == TNY_FOLDER_TYPE_INVALID)
857                         g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
858                 
859                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
860                         src_folder = outbox_folder;
861                 else 
862                         src_folder = draft_folder;
863
864                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
865                  * because this function requires it to have a UID. */          
866                 header = tny_msg_get_header (info->draft_msg);
867                 tny_folder_remove_msg (src_folder, header, NULL);
868
869                 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
870 /*              tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);  /\* expunge *\/ */
871                 
872                 g_object_unref (header);
873                 g_object_unref (folder);
874         }
875
876 end:
877         if (err != NULL)
878                 g_error_free(err);      
879         if (info->draft_msg)
880                 g_object_unref (info->draft_msg);
881         if (draft_folder)
882                 g_object_unref (draft_folder);
883         if (outbox_folder)
884                 g_object_unref (outbox_folder);
885         if (info->transport_account)
886                 g_object_unref (info->transport_account);
887         g_slice_free (SendNewMailInfo, info);
888 }
889
890 void
891 modest_mail_operation_send_new_mail (ModestMailOperation *self,
892                                      TnyTransportAccount *transport_account,
893                                      TnyMsg *draft_msg,
894                                      const gchar *from,  const gchar *to,
895                                      const gchar *cc,  const gchar *bcc,
896                                      const gchar *subject, const gchar *plain_body,
897                                      const gchar *html_body,
898                                      const GList *attachments_list,
899                                      const GList *images_list,
900                                      TnyHeaderFlags priority_flags)
901 {
902         ModestMailOperationPrivate *priv = NULL;
903         SendNewMailInfo *info;
904
905         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
906         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
907
908         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
909         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
910         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
911
912         /* Check parametters */
913         if (to == NULL) {
914                 /* Set status failed and set an error */
915                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
916                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
917                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
918                              _("Error trying to send a mail. You need to set at least one recipient"));
919                 return;
920         }
921         info = g_slice_new0 (SendNewMailInfo);
922         info->transport_account = transport_account;
923         if (transport_account)
924                 g_object_ref (transport_account);
925         info->draft_msg = draft_msg;
926         if (draft_msg)
927                 g_object_ref (draft_msg);
928
929
930         modest_mail_operation_notify_start (self);
931         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
932                                           attachments_list, images_list, priority_flags,
933                                           modest_mail_operation_send_new_mail_cb, info);
934
935 }
936
937 typedef struct
938 {
939         TnyTransportAccount *transport_account;
940         TnyMsg *draft_msg;
941         SaveToDraftstCallback callback;
942         gpointer user_data;
943         TnyFolder *drafts;
944         TnyMsg *msg;
945         ModestMailOperation *mailop;
946 } SaveToDraftsAddMsgInfo;
947
948 static void
949 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
950                                                 gboolean canceled,
951                                                 GError *err,
952                                                 gpointer userdata)
953 {
954         ModestMailOperationPrivate *priv = NULL;
955         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
956
957         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
958
959         if (priv->error) {
960                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
961                 g_error_free(priv->error);
962         }
963
964         priv->error = (err == NULL) ? NULL : g_error_copy(err);
965
966         if ((!priv->error) && (info->draft_msg != NULL)) {
967                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
968                 TnyFolder *src_folder = tny_header_get_folder (header);
969
970                 /* Remove the old draft */
971                 tny_folder_remove_msg (src_folder, header, NULL);
972
973                 /* Synchronize to expunge and to update the msg counts */
974                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
975                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
976
977                 g_object_unref (G_OBJECT(header));
978                 g_object_unref (G_OBJECT(src_folder));
979         }
980
981         if (!priv->error)
982                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
983         else
984                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
985
986         /* Call the user callback */
987         if (info->callback)
988                 info->callback (info->mailop, info->msg, info->user_data);
989
990         if (info->transport_account)
991                 g_object_unref (G_OBJECT(info->transport_account));
992         if (info->draft_msg)
993                 g_object_unref (G_OBJECT (info->draft_msg));
994         if (info->drafts)
995                 g_object_unref (G_OBJECT(info->drafts));
996         if (info->msg)
997                 g_object_unref (G_OBJECT (info->msg));
998         g_slice_free (SaveToDraftsAddMsgInfo, info);
999
1000         modest_mail_operation_notify_end (info->mailop);
1001         g_object_unref(info->mailop);
1002 }
1003
1004 typedef struct
1005 {
1006         TnyTransportAccount *transport_account;
1007         TnyMsg *draft_msg;
1008         SaveToDraftstCallback callback;
1009         gpointer user_data;
1010 } SaveToDraftsInfo;
1011
1012 static void
1013 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1014                                          TnyMsg *msg,
1015                                          gpointer userdata)
1016 {
1017         TnyFolder *drafts = NULL;
1018         ModestMailOperationPrivate *priv = NULL;
1019         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1020
1021         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1022
1023         if (!msg) {
1024                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1025                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1026                              "modest: failed to create a new msg\n");
1027         } else {
1028                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1029                                                                 TNY_FOLDER_TYPE_DRAFTS);
1030                 if (!drafts) {
1031                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1032                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1033                                      "modest: failed to create a new msg\n");
1034                 }
1035         }
1036
1037         if (!priv->error) {
1038                 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1039                 cb_info->transport_account = g_object_ref(info->transport_account);
1040                 cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1041                 cb_info->callback = info->callback;
1042                 cb_info->user_data = info->user_data;
1043                 cb_info->drafts = g_object_ref(drafts);
1044                 cb_info->msg = g_object_ref(msg);
1045                 cb_info->mailop = g_object_ref(self);
1046                 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1047                                          NULL, cb_info);
1048         } else {
1049                 /* Call the user callback */
1050                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1051                 if (info->callback)
1052                         info->callback (self, msg, info->user_data);
1053                 modest_mail_operation_notify_end (self);
1054         }
1055
1056         if (drafts)
1057                 g_object_unref (G_OBJECT(drafts));
1058         if (info->draft_msg)
1059                 g_object_unref (G_OBJECT (info->draft_msg));
1060         if (info->transport_account)
1061                 g_object_unref (G_OBJECT(info->transport_account));
1062         g_slice_free (SaveToDraftsInfo, info);
1063 }
1064
1065 void
1066 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1067                                       TnyTransportAccount *transport_account,
1068                                       TnyMsg *draft_msg,
1069                                       const gchar *from,  const gchar *to,
1070                                       const gchar *cc,  const gchar *bcc,
1071                                       const gchar *subject, const gchar *plain_body,
1072                                       const gchar *html_body,
1073                                       const GList *attachments_list,
1074                                       const GList *images_list,
1075                                       TnyHeaderFlags priority_flags,
1076                                       SaveToDraftstCallback callback,
1077                                       gpointer user_data)
1078 {
1079         ModestMailOperationPrivate *priv = NULL;
1080         SaveToDraftsInfo *info = NULL;
1081
1082         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1083         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1084
1085         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1086
1087         /* Get account and set it into mail_operation */
1088         priv->account = g_object_ref (transport_account);
1089         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1090
1091         info = g_slice_new0 (SaveToDraftsInfo);
1092         info->transport_account = g_object_ref (transport_account);
1093         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1094         info->callback = callback;
1095         info->user_data = user_data;
1096
1097         modest_mail_operation_notify_start (self);
1098         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1099                                           attachments_list, images_list, priority_flags,
1100                                           modest_mail_operation_save_to_drafts_cb, info);
1101 }
1102
1103 typedef struct
1104 {
1105         ModestMailOperation *mail_op;
1106         TnyMimePart *mime_part;
1107         gssize size;
1108         GetMimePartSizeCallback callback;
1109         gpointer userdata;
1110 } GetMimePartSizeInfo;
1111
1112 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1113 /* We use this folder observer to track the headers that have been
1114  * added to a folder */
1115 typedef struct {
1116         GObject parent;
1117         TnyList *new_headers;
1118 } InternalFolderObserver;
1119
1120 typedef struct {
1121         GObjectClass parent;
1122 } InternalFolderObserverClass;
1123
1124 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1125
1126 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1127                          internal_folder_observer,
1128                          G_TYPE_OBJECT,
1129                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1130
1131
1132 static void
1133 foreach_add_item (gpointer header, gpointer user_data)
1134 {
1135         tny_list_prepend (TNY_LIST (user_data), 
1136                           g_object_ref (G_OBJECT (header)));
1137 }
1138
1139 /* This is the method that looks for new messages in a folder */
1140 static void
1141 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1142 {
1143         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1144         
1145         TnyFolderChangeChanged changed;
1146
1147         changed = tny_folder_change_get_changed (change);
1148
1149         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1150                 TnyList *list;
1151
1152                 /* Get added headers */
1153                 list = tny_simple_list_new ();
1154                 tny_folder_change_get_added_headers (change, list);
1155
1156                 /* Add them to the folder observer */
1157                 tny_list_foreach (list, foreach_add_item, 
1158                                   derived->new_headers);
1159
1160                 g_object_unref (G_OBJECT (list));
1161         }
1162 }
1163
1164 static void
1165 internal_folder_observer_init (InternalFolderObserver *self) 
1166 {
1167         self->new_headers = tny_simple_list_new ();
1168 }
1169 static void
1170 internal_folder_observer_finalize (GObject *object) 
1171 {
1172         InternalFolderObserver *self;
1173
1174         self = (InternalFolderObserver *) object;
1175         g_object_unref (self->new_headers);
1176
1177         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1178 }
1179 static void
1180 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1181 {
1182         iface->update_func = internal_folder_observer_update;
1183 }
1184 static void
1185 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1186 {
1187         GObjectClass *object_class;
1188
1189         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1190         object_class = (GObjectClass*) klass;
1191         object_class->finalize = internal_folder_observer_finalize;
1192 }
1193
1194 typedef struct 
1195 {
1196         ModestMailOperation *mail_op;
1197         gchar *account_name;
1198         UpdateAccountCallback callback;
1199         gpointer user_data;
1200         TnyList *folders;
1201         gint pending_calls;
1202         gboolean poke_all;
1203         TnyFolderObserver *inbox_observer;
1204         guint update_timeout;
1205 } UpdateAccountInfo;
1206
1207
1208 static void
1209 destroy_update_account_info (UpdateAccountInfo *info)
1210 {
1211         if (info->update_timeout) {
1212                 g_source_remove (info->update_timeout);
1213                 info->update_timeout = 0;
1214         }
1215
1216         g_free (info->account_name);
1217         g_object_unref (info->folders);
1218         g_object_unref (info->mail_op);
1219         g_slice_free (UpdateAccountInfo, info);
1220 }
1221
1222 static void
1223 update_account_get_msg_async_cb (TnyFolder *folder, 
1224                                  gboolean canceled, 
1225                                  TnyMsg *msg, 
1226                                  GError *err, 
1227                                  gpointer user_data)
1228 {
1229         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1230
1231         /* Just delete the helper. Don't do anything with the new
1232            msg. There is also no need to check for errors */
1233         g_object_unref (msg_info->mail_op);
1234         g_object_unref (msg_info->header);
1235         g_slice_free (GetMsgInfo, msg_info);
1236 }
1237
1238
1239 static void
1240 inbox_refreshed_cb (TnyFolder *inbox, 
1241                     gboolean canceled, 
1242                     GError *err, 
1243                     gpointer user_data)
1244 {       
1245         UpdateAccountInfo *info;
1246         ModestMailOperationPrivate *priv;
1247         TnyIterator *new_headers_iter;
1248         GPtrArray *new_headers_array = NULL;   
1249         gint max_size, retrieve_limit, i;
1250         ModestAccountMgr *mgr;
1251         gchar *retrieve_type = NULL;
1252         TnyList *new_headers = NULL;
1253         gboolean headers_only;
1254         TnyTransportAccount *transport_account;
1255         ModestTnySendQueue *send_queue;
1256
1257         info = (UpdateAccountInfo *) user_data;
1258         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1259         mgr = modest_runtime_get_account_mgr ();
1260
1261         if (canceled || err || !inbox) {
1262                 /* Try to send anyway */
1263                 goto send_mail;
1264         }
1265
1266         /* Get the message max size */
1267         max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1268                                          MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1269         if (max_size == 0)
1270                 max_size = G_MAXINT;
1271         else
1272                 max_size = max_size * KB;
1273
1274         /* Create the new headers array. We need it to sort the
1275            new headers by date */
1276         new_headers_array = g_ptr_array_new ();
1277         new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1278         while (!tny_iterator_is_done (new_headers_iter)) {
1279                 TnyHeader *header = NULL;
1280
1281                 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1282                 /* Apply per-message size limits */
1283                 if (tny_header_get_message_size (header) < max_size)
1284                         g_ptr_array_add (new_headers_array, g_object_ref (header));
1285                                 
1286                 g_object_unref (header);
1287                 tny_iterator_next (new_headers_iter);
1288         }
1289         g_object_unref (new_headers_iter);
1290         tny_folder_remove_observer (inbox, info->inbox_observer);
1291         g_object_unref (info->inbox_observer);
1292         info->inbox_observer = NULL;
1293
1294         if (new_headers_array->len == 0)
1295                 goto send_mail;
1296
1297         /* Get per-account message amount retrieval limit */
1298         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1299         if (retrieve_limit == 0)
1300                 retrieve_limit = G_MAXINT;
1301         
1302         /* Get per-account retrieval type */
1303         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name); 
1304         headers_only = !g_ascii_strcasecmp (retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY);
1305         g_free (retrieve_type);
1306
1307         /* Order by date */
1308         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1309         
1310         /* TODO: Ask the user, instead of just failing,
1311          * showing mail_nc_msg_count_limit_exceeded, with 'Get
1312          * all' and 'Newest only' buttons. */
1313         if (new_headers_array->len > retrieve_limit) {
1314                 /* TODO */
1315         }
1316         
1317         if (!headers_only) {
1318                 gint msg_num = 0;
1319                 const gint msg_list_size = compute_message_array_size (new_headers_array);
1320
1321                 priv->done = 0;
1322                 priv->total = MIN (new_headers_array->len, retrieve_limit);
1323                 while (msg_num < priv->total) {         
1324                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1325                         TnyFolder *folder = tny_header_get_folder (header);
1326                         GetMsgInfo *msg_info;
1327
1328                         /* Create the message info */
1329                         msg_info = g_slice_new0 (GetMsgInfo);
1330                         msg_info->mail_op = g_object_ref (info->mail_op);
1331                         msg_info->header = g_object_ref (header);
1332                         msg_info->total_bytes = msg_list_size;
1333
1334                         /* Get message in an async way */
1335                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb, 
1336                                                   get_msg_status_cb, msg_info);
1337
1338                         g_object_unref (folder);
1339                         
1340                         msg_num++;
1341                 }
1342         }
1343
1344         /* Copy the headers to a list and free the array */
1345         new_headers = tny_simple_list_new ();
1346         for (i=0; i < new_headers_array->len; i++) {
1347                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1348                 tny_list_append (new_headers, G_OBJECT (header));
1349         }
1350         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1351         g_ptr_array_free (new_headers_array, FALSE);
1352
1353         /* Update the last updated key */
1354         modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1355
1356  send_mail:
1357         /* Send mails */
1358         priv->done = 0;
1359         priv->total = 0;
1360
1361         /* Get the transport account */
1362         transport_account = (TnyTransportAccount *)
1363                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1364                                                                                     info->account_name);
1365         
1366         /* Try to send */
1367         send_queue = modest_runtime_get_send_queue (transport_account);
1368         modest_tny_send_queue_try_to_send (send_queue);
1369
1370         /* Check if the operation was a success */
1371         if (!priv->error)
1372                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1373
1374         /* Set the account back to not busy */
1375         modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1376
1377         /* Call the user callback */
1378         if (info->callback)
1379                 info->callback (info->mail_op, new_headers, info->user_data);
1380
1381         /* Notify about operation end */
1382         modest_mail_operation_notify_end (info->mail_op);
1383
1384         /* Frees */
1385         if (new_headers)
1386                 g_object_unref (new_headers);
1387         destroy_update_account_info (info);
1388 }
1389
1390 static void 
1391 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1392                           gboolean canceled,
1393                           TnyList *list, 
1394                           GError *err, 
1395                           gpointer user_data)
1396 {
1397         UpdateAccountInfo *info;
1398         ModestMailOperationPrivate *priv;
1399     
1400         info = (UpdateAccountInfo *) user_data;
1401         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1402
1403         if (err || canceled) {
1404                 /* Try to continue anyway */
1405         } else {
1406                 TnyIterator *iter = tny_list_create_iterator (list);
1407                 while (!tny_iterator_is_done (iter)) {
1408                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1409                         TnyList *folders = tny_simple_list_new ();
1410
1411                         /* Add to the list of all folders */
1412                         tny_list_append (info->folders, (GObject *) folder);
1413                         
1414                         /* Add pending call */
1415                         info->pending_calls++;
1416                         
1417                         tny_folder_store_get_folders_async (folder, folders, recurse_folders_async_cb, 
1418                                                             NULL, NULL, info);
1419                         
1420                         g_object_unref (G_OBJECT (folder));
1421                         
1422                         tny_iterator_next (iter);           
1423                 }
1424                 g_object_unref (G_OBJECT (iter));
1425                 g_object_unref (G_OBJECT (list));
1426         }
1427
1428         /* Remove my own pending call */
1429         info->pending_calls--;
1430
1431         /* This means that we have all the folders */
1432         if (info->pending_calls == 0) {
1433                 TnyIterator *iter_all_folders;
1434                 TnyFolder *inbox = NULL;
1435
1436                 iter_all_folders = tny_list_create_iterator (info->folders);
1437
1438                 /* Do a poke status over all folders */
1439                 while (!tny_iterator_is_done (iter_all_folders) &&
1440                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1441                         TnyFolder *folder = NULL;
1442
1443                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1444
1445                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1446                                 /* Get a reference to the INBOX */
1447                                 inbox = g_object_ref (folder);
1448                         } else {
1449                                 /* Issue a poke status over the folder */
1450                                 if (info->poke_all)
1451                                         tny_folder_poke_status (folder);
1452                         }
1453
1454                         /* Free and go to next */
1455                         g_object_unref (folder);
1456                         tny_iterator_next (iter_all_folders);
1457                 }
1458                 g_object_unref (iter_all_folders);
1459
1460                 /* Stop the progress notification */
1461                 g_source_remove (info->update_timeout);
1462                 info->update_timeout = 0;
1463
1464                 /* Refresh the INBOX */
1465                 if (inbox) {
1466                         /* Refresh the folder. Our observer receives
1467                          * the new emails during folder refreshes, so
1468                          * we can use observer->new_headers
1469                          */
1470                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1471                         tny_folder_add_observer (inbox, info->inbox_observer);
1472
1473                         /* Refresh the INBOX */
1474                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1475                         g_object_unref (inbox);
1476                 } else {
1477                         /* We could not perform the inbox refresh but
1478                            we'll try to send mails anyway */
1479                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1480                 }
1481         }
1482 }
1483
1484 /* 
1485  * Issues the "progress-changed" signal. The timer won't be removed,
1486  * so you must call g_source_remove to stop the signal emission
1487  */
1488 static gboolean
1489 timeout_notify_progress (gpointer data)
1490 {
1491         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1492         ModestMailOperationState *state;
1493
1494         state = modest_mail_operation_clone_state (mail_op);
1495
1496         /* This is a GDK lock because we are an idle callback and
1497          * the handlers of this signal can contain Gtk+ code */
1498
1499         gdk_threads_enter (); /* CHECKED */
1500         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1501         gdk_threads_leave (); /* CHECKED */
1502
1503         g_slice_free (ModestMailOperationState, state);
1504         
1505         return TRUE;
1506 }
1507
1508 void
1509 modest_mail_operation_update_account (ModestMailOperation *self,
1510                                       const gchar *account_name,
1511                                       gboolean poke_all,
1512                                       UpdateAccountCallback callback,
1513                                       gpointer user_data)
1514 {
1515         UpdateAccountInfo *info = NULL;
1516         ModestMailOperationPrivate *priv = NULL;
1517         ModestTnyAccountStore *account_store = NULL;
1518         TnyStoreAccount *store_account = NULL;
1519         TnyList *folders;
1520
1521         /* Init mail operation */
1522         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1523         priv->total = 0;
1524         priv->done  = 0;
1525         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1526         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1527
1528         /* Get the store account */
1529         account_store = modest_runtime_get_account_store ();
1530         store_account = (TnyStoreAccount *)
1531                 modest_tny_account_store_get_server_account (account_store,
1532                                                              account_name,
1533                                                              TNY_ACCOUNT_TYPE_STORE);
1534         priv->account = g_object_ref (store_account);
1535
1536         /* Create the helper object */
1537         info = g_slice_new0 (UpdateAccountInfo);
1538         info->pending_calls = 1;
1539         info->folders = tny_simple_list_new ();
1540         info->mail_op = g_object_ref (self);
1541         info->poke_all = poke_all;
1542         info->account_name = g_strdup (account_name);
1543         info->callback = callback;
1544         info->user_data = user_data;
1545         info->update_timeout = g_timeout_add (250, timeout_notify_progress, self);
1546
1547         /* Set account busy */
1548         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1549         modest_mail_operation_notify_start (self);
1550
1551         /* Get all folders and continue in the callback */    
1552         folders = tny_simple_list_new ();
1553         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
1554                                             folders, recurse_folders_async_cb, 
1555                                             NULL, NULL, info);
1556 }
1557
1558 /*
1559  * Used to notify the queue from the main
1560  * loop. We call it inside an idle call to achieve that
1561  */
1562 static gboolean
1563 idle_notify_queue (gpointer data)
1564 {
1565         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1566
1567         gdk_threads_enter ();
1568         modest_mail_operation_notify_end (mail_op);
1569         gdk_threads_leave ();
1570         g_object_unref (mail_op);
1571
1572         return FALSE;
1573 }
1574
1575 static int
1576 compare_headers_by_date (gconstpointer a,
1577                          gconstpointer b)
1578 {
1579         TnyHeader **header1, **header2;
1580         time_t sent1, sent2;
1581
1582         header1 = (TnyHeader **) a;
1583         header2 = (TnyHeader **) b;
1584
1585         sent1 = tny_header_get_date_sent (*header1);
1586         sent2 = tny_header_get_date_sent (*header2);
1587
1588         /* We want the most recent ones (greater time_t) at the
1589            beginning */
1590         if (sent1 < sent2)
1591                 return 1;
1592         else
1593                 return -1;
1594 }
1595
1596 /* static gpointer */
1597 /* update_account_thread (gpointer thr_user_data) */
1598 /* { */
1599 /*      static gboolean first_time = TRUE; */
1600 /*      UpdateAccountInfo *info = NULL; */
1601 /*      TnyList *all_folders = NULL, *new_headers = NULL; */
1602 /*      GPtrArray *new_headers_array = NULL; */
1603 /*      TnyIterator *iter = NULL; */
1604 /*      ModestMailOperationPrivate *priv = NULL; */
1605 /*      ModestTnySendQueue *send_queue = NULL; */
1606 /*      gint i = 0, timeout = 0; */
1607
1608 /*      info = (UpdateAccountInfo *) thr_user_data; */
1609 /*      priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op); */
1610
1611 /*      /\* Get account and set it into mail_operation *\/ */
1612 /*      priv->account = g_object_ref (info->account); */
1613
1614 /*      /\* Get all the folders. We can do it synchronously because */
1615 /*         we're already running in a different thread than the UI *\/ */
1616 /*      all_folders = get_all_folders_from_account (info->account, &(priv->error)); */
1617 /*      if (!all_folders) { */
1618 /*              priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED; */
1619 /*              goto out; */
1620 /*      } */
1621
1622 /*      /\* Update status and notify. We need to call the notification */
1623 /*         with a source function in order to call it from the main */
1624 /*         loop. We need that in order not to get into trouble with */
1625 /*         Gtk+. We use a timeout in order to provide more status */
1626 /*         information, because the sync tinymail call does not */
1627 /*         provide it for the moment *\/ */
1628 /*      timeout = g_timeout_add (100, idle_notify_progress, info->mail_op); */
1629
1630 /*      new_headers_array = g_ptr_array_new (); */
1631 /*      iter = tny_list_create_iterator (all_folders); */
1632
1633 /*      while (!tny_iterator_is_done (iter) && !priv->error &&  */
1634 /*             priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) { */
1635
1636 /*              TnyFolderType folder_type; */
1637 /*              TnyFolder *folder = NULL; */
1638
1639 /*              folder = TNY_FOLDER (tny_iterator_get_current (iter)); */
1640 /*              folder_type = tny_folder_get_folder_type (folder); */
1641
1642 /*              /\* Refresh it only if it's the INBOX *\/ */
1643 /*              if (folder_type == TNY_FOLDER_TYPE_INBOX) { */
1644 /*                      InternalFolderObserver *observer = NULL; */
1645 /*                      TnyIterator *new_headers_iter = NULL; */
1646
1647 /*                      /\* Refresh the folder. Our observer receives */
1648 /*                       * the new emails during folder refreshes, so */
1649 /*                       * we can use observer->new_headers */
1650 /*                       *\/ */
1651 /*                      observer = g_object_new (internal_folder_observer_get_type (), NULL); */
1652 /*                      tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer)); */
1653                 
1654 /*                      tny_folder_refresh (TNY_FOLDER (folder), &(priv->error)); */
1655
1656 /*                      new_headers_iter = tny_list_create_iterator (observer->new_headers); */
1657 /*                      while (!tny_iterator_is_done (new_headers_iter)) { */
1658 /*                              TnyHeader *header = NULL; */
1659
1660 /*                              header = TNY_HEADER (tny_iterator_get_current (new_headers_iter)); */
1661 /*                              /\* Apply per-message size limits *\/ */
1662 /*                              if (tny_header_get_message_size (header) < info->max_size) */
1663 /*                                      g_ptr_array_add (new_headers_array, g_object_ref (header)); */
1664                                 
1665 /*                              g_object_unref (header); */
1666 /*                              tny_iterator_next (new_headers_iter); */
1667 /*                      } */
1668 /*                      g_object_unref (new_headers_iter); */
1669
1670 /*                      tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer)); */
1671 /*                      g_object_unref (observer); */
1672 /*              } else { */
1673 /*                      /\* We no not need to do it the first time, */
1674 /*                         because it's automatically done by the tree */
1675 /*                         model *\/ */
1676 /*                      if (G_LIKELY (!first_time)) */
1677 /*                              tny_folder_poke_status (folder); */
1678 /*              } */
1679 /*              g_object_unref (folder); */
1680
1681 /*              tny_iterator_next (iter); */
1682 /*      } */
1683 /*      g_object_unref (G_OBJECT (iter)); */
1684 /*      g_source_remove (timeout); */
1685
1686 /*      if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&  */
1687 /*          priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED && */
1688 /*          new_headers_array->len > 0) { */
1689 /*              gint msg_num = 0; */
1690
1691 /*              /\* Order by date *\/ */
1692 /*              g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date); */
1693
1694 /*              /\* TODO: Ask the user, instead of just failing, */
1695 /*               * showing mail_nc_msg_count_limit_exceeded, with 'Get */
1696 /*               * all' and 'Newest only' buttons. *\/ */
1697 /*              if (new_headers_array->len > info->retrieve_limit) { */
1698 /*                      /\* TODO *\/ */
1699 /*              } */
1700
1701 /*              /\* Should be get only the headers or the message as well? *\/ */
1702 /*              if (g_ascii_strcasecmp (info->retrieve_type,  */
1703 /*                                      MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {      */
1704 /*                      priv->done = 0; */
1705 /*                      priv->total = MIN (new_headers_array->len, info->retrieve_limit); */
1706 /*                      while (msg_num < priv->total) { */
1707
1708 /*                              TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num)); */
1709 /*                              TnyFolder *folder = tny_header_get_folder (header); */
1710 /*                              TnyMsg *msg       = tny_folder_get_msg (folder, header, NULL); */
1711 /*                              ModestMailOperationState *state; */
1712 /*                              ModestPair* pair; */
1713
1714 /*                              priv->done++; */
1715 /*                              /\* We can not just use the mail operation because the */
1716 /*                                 values of done and total could change before the */
1717 /*                                 idle is called *\/ */
1718 /*                              state = modest_mail_operation_clone_state (info->mail_op); */
1719 /*                              pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE); */
1720 /*                              g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once, */
1721 /*                                               pair, (GDestroyNotify) modest_pair_free); */
1722
1723 /*                              g_object_unref (msg); */
1724 /*                              g_object_unref (folder); */
1725
1726 /*                              msg_num++; */
1727 /*                      } */
1728 /*              } */
1729 /*      } */
1730
1731 /*      if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED) */
1732 /*              goto out; */
1733
1734 /*      /\* Copy the headers to a list and free the array *\/ */
1735 /*      new_headers = tny_simple_list_new (); */
1736 /*      for (i=0; i < new_headers_array->len; i++) { */
1737 /*              TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i)); */
1738 /*              tny_list_append (new_headers, G_OBJECT (header)); */
1739 /*      } */
1740 /*      g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL); */
1741 /*      g_ptr_array_free (new_headers_array, FALSE); */
1742         
1743
1744 /*      /\* Perform send (if operation was not cancelled) *\/ */
1745 /*      priv->done = 0; */
1746 /*      priv->total = 0; */
1747 /*      if (priv->account != NULL)  */
1748 /*              g_object_unref (priv->account); */
1749
1750 /*      if (info->transport_account) { */
1751 /*              priv->account = g_object_ref (info->transport_account); */
1752         
1753 /*              send_queue = modest_runtime_get_send_queue (info->transport_account); */
1754 /*              if (send_queue) { */
1755 /*                      modest_tny_send_queue_try_to_send (send_queue); */
1756 /*              } else { */
1757 /*                      g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR, */
1758 /*                                   MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED, */
1759 /*                                   "cannot create a send queue for %s\n",  */
1760 /*                                   tny_account_get_name (TNY_ACCOUNT (info->transport_account))); */
1761 /*                      priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED; */
1762 /*              } */
1763 /*      } */
1764         
1765 /*      /\* Check if the operation was a success *\/ */
1766 /*      if (!priv->error) { */
1767 /*              priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS; */
1768
1769 /*              /\* Update the last updated key *\/ */
1770 /*              g_idle_add_full (G_PRIORITY_HIGH_IDLE,  */
1771 /*                               set_last_updated_idle,  */
1772 /*                               g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))), */
1773 /*                               (GDestroyNotify) g_free); */
1774 /*      } */
1775
1776 /*  out: */
1777 /*      /\* Set the account back to not busy *\/ */
1778 /*      modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),  */
1779 /*                                           info->account_name, FALSE); */
1780         
1781 /*      if (info->callback) { */
1782 /*              UpdateAccountInfo *idle_info; */
1783
1784 /*              /\* This thread is not in the main lock *\/ */
1785 /*              idle_info = g_malloc0 (sizeof (UpdateAccountInfo)); */
1786 /*              idle_info->mail_op = g_object_ref (info->mail_op); */
1787 /*              idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL; */
1788 /*              idle_info->callback = info->callback; */
1789 /*              idle_info->user_data = info->user_data; */
1790 /*              g_idle_add (idle_update_account_cb, idle_info); */
1791 /*      } */
1792
1793 /*      /\* Notify about operation end. Note that the info could be */
1794 /*         freed before this idle happens, but the mail operation will */
1795 /*         be still alive *\/ */
1796 /*      g_idle_add (idle_notify_queue, g_object_ref (info->mail_op)); */
1797
1798 /*      /\* Frees *\/ */
1799 /*      if (new_headers) */
1800 /*              g_object_unref (new_headers); */
1801 /*      if (all_folders) */
1802 /*              g_object_unref (all_folders); */
1803 /*      g_object_unref (info->account); */
1804 /*      if (info->transport_account) */
1805 /*              g_object_unref (info->transport_account); */
1806 /*      g_free (info->account_name); */
1807 /*      g_free (info->retrieve_type); */
1808 /*      g_slice_free (UpdateAccountInfo, info); */
1809
1810 /*      first_time = FALSE; */
1811
1812 /*      return NULL; */
1813 /* } */
1814
1815 /* gboolean */
1816 /* modest_mail_operation_update_account (ModestMailOperation *self, */
1817 /*                                    const gchar *account_name, */
1818 /*                                    UpdateAccountCallback callback, */
1819 /*                                    gpointer user_data) */
1820 /* { */
1821 /*      GThread *thread = NULL; */
1822 /*      UpdateAccountInfo *info = NULL; */
1823 /*      ModestMailOperationPrivate *priv = NULL; */
1824 /*      ModestAccountMgr *mgr = NULL; */
1825 /*      TnyStoreAccount *store_account = NULL; */
1826 /*      TnyTransportAccount *transport_account = NULL; */
1827
1828 /*      g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE); */
1829 /*      g_return_val_if_fail (account_name, FALSE); */
1830
1831 /*      /\* Init mail operation. Set total and done to 0, and do not */
1832 /*         update them, this way the progress objects will know that */
1833 /*         we have no clue about the number of the objects *\/ */
1834 /*      priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self); */
1835 /*      priv->total = 0; */
1836 /*      priv->done  = 0; */
1837 /*      priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS; */
1838 /*      priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE; */
1839
1840 /*      /\* Get the store account *\/ */
1841 /*      store_account = (TnyStoreAccount *) */
1842 /*              modest_tny_account_store_get_server_account (modest_runtime_get_account_store (), */
1843 /*                                                                   account_name, */
1844 /*                                                                   TNY_ACCOUNT_TYPE_STORE); */
1845                                                                      
1846 /*      if (!store_account) { */
1847 /*              g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR, */
1848 /*                           MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND, */
1849 /*                           "cannot get tny store account for %s\n", account_name); */
1850 /*              goto error; */
1851 /*      } */
1852
1853 /*      priv->account = g_object_ref (store_account); */
1854         
1855 /*      /\* Get the transport account, we can not do it in the thread */
1856 /*         due to some problems with dbus *\/ */
1857 /*      transport_account = (TnyTransportAccount *) */
1858 /*              modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(), */
1859 /*                                                                                  account_name); */
1860 /*      if (!transport_account) { */
1861 /*              g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR, */
1862 /*                           MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND, */
1863 /*                           "cannot get tny transport account for %s\n", account_name); */
1864 /*              goto error; */
1865 /*      } */
1866
1867 /*      /\* Create the helper object *\/ */
1868 /*      info = g_slice_new (UpdateAccountInfo); */
1869 /*      info->mail_op = self; */
1870 /*      info->account = store_account; */
1871 /*      info->transport_account = transport_account; */
1872 /*      info->callback = callback; */
1873 /*      info->account_name = g_strdup (account_name); */
1874 /*      info->user_data = user_data; */
1875
1876 /*      /\* Get the message size limit *\/ */
1877 /*      info->max_size  = modest_conf_get_int (modest_runtime_get_conf (),  */
1878 /*                                             MODEST_CONF_MSG_SIZE_LIMIT, NULL); */
1879 /*      if (info->max_size == 0) */
1880 /*              info->max_size = G_MAXINT; */
1881 /*      else */
1882 /*              info->max_size = info->max_size * KB; */
1883
1884 /*      /\* Get per-account retrieval type *\/ */
1885 /*      mgr = modest_runtime_get_account_mgr (); */
1886 /*      info->retrieve_type = modest_account_mgr_get_retrieve_type (mgr, account_name); */
1887
1888 /*      /\* Get per-account message amount retrieval limit *\/ */
1889 /*      info->retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, account_name); */
1890 /*      if (info->retrieve_limit == 0) */
1891 /*              info->retrieve_limit = G_MAXINT; */
1892                 
1893 /*      /\* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); *\/ */
1894
1895 /*      /\* Set account busy *\/ */
1896 /*      modest_account_mgr_set_account_busy(mgr, account_name, TRUE); */
1897         
1898 /*      modest_mail_operation_notify_start (self); */
1899 /*      thread = g_thread_create (update_account_thread, info, FALSE, NULL); */
1900
1901 /*      return TRUE; */
1902
1903 /*  error: */
1904 /*      if (store_account) */
1905 /*              g_object_unref (store_account); */
1906 /*      if (transport_account) */
1907 /*              g_object_unref (transport_account); */
1908 /*      priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED; */
1909 /*      if (callback) { */
1910 /*              callback (self, NULL, user_data); */
1911 /*      } */
1912 /*      modest_mail_operation_notify_end (self); */
1913 /*      return FALSE; */
1914 /* } */
1915
1916 /* ******************************************************************* */
1917 /* ************************** STORE  ACTIONS ************************* */
1918 /* ******************************************************************* */
1919
1920
1921 TnyFolder *
1922 modest_mail_operation_create_folder (ModestMailOperation *self,
1923                                      TnyFolderStore *parent,
1924                                      const gchar *name)
1925 {
1926         ModestMailOperationPrivate *priv;
1927         TnyFolder *new_folder = NULL;
1928
1929         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1930         g_return_val_if_fail (name, NULL);
1931         
1932         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1933         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1934         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1935                 g_object_ref (parent) : 
1936                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1937
1938         /* Check for already existing folder */
1939         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1940                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1941                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1942                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1943                              _CS("ckdg_ib_folder_already_exists"));
1944         }
1945
1946         /* Check parent */
1947         if (TNY_IS_FOLDER (parent)) {
1948                 /* Check folder rules */
1949                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1950                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1951                         /* Set status failed and set an error */
1952                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1953                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1954                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1955                                      _("mail_in_ui_folder_create_error"));
1956                 }
1957         }
1958
1959         if (!strcmp (name, " ") || strchr (name, '/')) {
1960                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1961                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1962                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1963                              _("mail_in_ui_folder_create_error"));
1964         }
1965
1966         if (!priv->error) {
1967                 /* Create the folder */
1968                 modest_mail_operation_notify_start (self);
1969                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1970                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1971                 if (!priv->error)
1972                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1973         }
1974
1975         /* Notify about operation end */
1976         modest_mail_operation_notify_end (self);
1977
1978         return new_folder;
1979 }
1980
1981 void
1982 modest_mail_operation_remove_folder (ModestMailOperation *self,
1983                                      TnyFolder           *folder,
1984                                      gboolean             remove_to_trash)
1985 {
1986         TnyAccount *account;
1987         ModestMailOperationPrivate *priv;
1988         ModestTnyFolderRules rules;
1989
1990         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1991         g_return_if_fail (TNY_IS_FOLDER (folder));
1992         
1993         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1994         
1995         /* Check folder rules */
1996         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1997         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1998                 /* Set status failed and set an error */
1999                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2000                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2001                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2002                              _("mail_in_ui_folder_delete_error"));
2003                 goto end;
2004         }
2005
2006         /* Get the account */
2007         account = modest_tny_folder_get_account (folder);
2008         priv->account = g_object_ref(account);
2009         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2010
2011         /* Delete folder or move to trash */
2012         if (remove_to_trash) {
2013                 TnyFolder *trash_folder = NULL;
2014                 trash_folder = modest_tny_account_get_special_folder (account,
2015                                                                       TNY_FOLDER_TYPE_TRASH);
2016                 /* TODO: error_handling */
2017                 if (trash_folder) {
2018                         modest_mail_operation_notify_start (self);
2019                         modest_mail_operation_xfer_folder (self, folder,
2020                                                     TNY_FOLDER_STORE (trash_folder), 
2021                                                     TRUE, NULL, NULL);
2022                         g_object_unref (trash_folder);
2023                 }
2024         } else {
2025                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
2026                 if (parent) {
2027                         modest_mail_operation_notify_start (self);
2028                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
2029                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
2030                         
2031                         if (!priv->error)
2032                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2033
2034                         g_object_unref (parent);
2035                 } else
2036                         g_warning ("%s: could not get parent folder", __FUNCTION__);
2037         }
2038         g_object_unref (G_OBJECT (account));
2039
2040  end:
2041         /* Notify about operation end */
2042         modest_mail_operation_notify_end (self);
2043 }
2044
2045 static void
2046 transfer_folder_status_cb (GObject *obj,
2047                            TnyStatus *status,
2048                            gpointer user_data)
2049 {
2050         ModestMailOperation *self;
2051         ModestMailOperationPrivate *priv;
2052         ModestMailOperationState *state;
2053         XFerMsgAsyncHelper *helper;
2054
2055         g_return_if_fail (status != NULL);
2056         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
2057
2058         helper = (XFerMsgAsyncHelper *) user_data;
2059         g_return_if_fail (helper != NULL);
2060
2061         self = helper->mail_op;
2062         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2063
2064         priv->done = status->position;
2065         priv->total = status->of_total;
2066
2067         state = modest_mail_operation_clone_state (self);
2068
2069         /* This is not a GDK lock because we are a Tinymail callback
2070          * which is already GDK locked by Tinymail */
2071
2072         /* no gdk_threads_enter (), CHECKED */
2073
2074         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
2075
2076         /* no gdk_threads_leave (), CHECKED */
2077
2078         g_slice_free (ModestMailOperationState, state);
2079 }
2080
2081
2082 static void
2083 transfer_folder_cb (TnyFolder *folder, 
2084                     gboolean cancelled, 
2085                     TnyFolderStore *into, 
2086                     TnyFolder *new_folder, 
2087                     GError *err, 
2088                     gpointer user_data)
2089 {
2090         XFerMsgAsyncHelper *helper;
2091         ModestMailOperation *self = NULL;
2092         ModestMailOperationPrivate *priv = NULL;
2093
2094         helper = (XFerMsgAsyncHelper *) user_data;
2095         g_return_if_fail (helper != NULL);       
2096
2097         self = helper->mail_op;
2098         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2099
2100         if (err) {
2101                 priv->error = g_error_copy (err);
2102                 priv->done = 0;
2103                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2104         } else if (cancelled) {
2105                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2106                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2107                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2108                              _("Transference of %s was cancelled."),
2109                              tny_folder_get_name (folder));
2110         } else {
2111                 priv->done = 1;
2112                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2113         }
2114                 
2115         /* Notify about operation end */
2116         modest_mail_operation_notify_end (self);
2117
2118         /* If user defined callback function was defined, call it */
2119         if (helper->user_callback) {
2120
2121                 /* This is not a GDK lock because we are a Tinymail callback
2122                  * which is already GDK locked by Tinymail */
2123
2124                 /* no gdk_threads_enter (), CHECKED */
2125                 helper->user_callback (self, helper->user_data);
2126                 /* no gdk_threads_leave () , CHECKED */
2127         }
2128
2129         /* Free */
2130         g_object_unref (helper->mail_op);
2131         g_slice_free   (XFerMsgAsyncHelper, helper);
2132 }
2133
2134 /**
2135  *
2136  * This function checks if the new name is a valid name for our local
2137  * folders account. The new name could not be the same than then name
2138  * of any of the mandatory local folders
2139  *
2140  * We can not rely on tinymail because tinymail does not check the
2141  * name of the virtual folders that the account could have in the case
2142  * that we're doing a rename (because it directly calls Camel which
2143  * knows nothing about our virtual folders). 
2144  *
2145  * In the case of an actual copy/move (i.e. move/copy a folder between
2146  * accounts) tinymail uses the tny_folder_store_create_account which
2147  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2148  * checks the new name of the folder, so this call in that case
2149  * wouldn't be needed. *But* NOTE that if tinymail changes its
2150  * implementation (if folder transfers within the same account is no
2151  * longer implemented as a rename) this call will allow Modest to work
2152  * perfectly
2153  *
2154  * If the new name is not valid, this function will set the status to
2155  * failed and will set also an error in the mail operation
2156  */
2157 static gboolean
2158 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2159                                  TnyFolderStore *into,
2160                                  const gchar *new_name)
2161 {
2162         if (TNY_IS_ACCOUNT (into) && 
2163             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2164             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2165                                                                  new_name)) {
2166                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2167                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2168                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2169                              _CS("ckdg_ib_folder_already_exists"));
2170                 return FALSE;
2171         } else
2172                 return TRUE;
2173 }
2174
2175 void
2176 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2177                                    TnyFolder *folder,
2178                                    TnyFolderStore *parent,
2179                                    gboolean delete_original,
2180                                    XferAsyncUserCallback user_callback,
2181                                    gpointer user_data)
2182 {
2183         ModestMailOperationPrivate *priv = NULL;
2184         ModestTnyFolderRules parent_rules = 0, rules; 
2185         XFerMsgAsyncHelper *helper = NULL;
2186         const gchar *folder_name = NULL;
2187         const gchar *error_msg;
2188
2189         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2190         g_return_if_fail (TNY_IS_FOLDER (folder));
2191         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2192
2193         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2194         folder_name = tny_folder_get_name (folder);
2195
2196         /* Set the error msg */
2197         error_msg = _("mail_in_ui_folder_move_target_error");
2198
2199         /* Get account and set it into mail_operation */
2200         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2201         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2202         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2203
2204         /* Get folder rules */
2205         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2206         if (TNY_IS_FOLDER (parent))
2207                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2208         
2209         /* Apply operation constraints */
2210         if ((gpointer) parent == (gpointer) folder ||
2211             (!TNY_IS_FOLDER_STORE (parent)) || 
2212             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2213                 /* Folder rules */
2214                 goto error;
2215         } else if (TNY_IS_FOLDER (parent) && 
2216                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2217                 /* Folder rules */
2218                 goto error;
2219
2220         } else if (TNY_IS_FOLDER (parent) &&
2221                    TNY_IS_FOLDER_STORE (folder) &&
2222                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2223                                                   TNY_FOLDER_STORE (folder))) {
2224                 /* Do not move a parent into a child */
2225                 goto error;
2226         } else if (TNY_IS_FOLDER_STORE (parent) &&
2227                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2228                 /* Check that the new folder name is not used by any
2229                    parent subfolder */
2230                 goto error;     
2231         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2232                 /* Check that the new folder name is not used by any
2233                    special local folder */
2234                 goto error;
2235         } else {
2236                 /* Create the helper */
2237                 helper = g_slice_new0 (XFerMsgAsyncHelper);
2238                 helper->mail_op = g_object_ref (self);
2239                 helper->dest_folder = NULL;
2240                 helper->headers = NULL;
2241                 helper->user_callback = user_callback;
2242                 helper->user_data = user_data;
2243                 
2244                 /* Move/Copy folder */
2245                 modest_mail_operation_notify_start (self);
2246                 tny_folder_copy_async (folder,
2247                                        parent,
2248                                        tny_folder_get_name (folder),
2249                                        delete_original,
2250                                        transfer_folder_cb,
2251                                        transfer_folder_status_cb,
2252                                        helper);
2253                 return;
2254         }
2255
2256  error:
2257         /* Set status failed and set an error */
2258         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2259         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2260                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2261                      error_msg);
2262
2263         /* Call the user callback if exists */
2264         if (user_callback)
2265                 user_callback (self, user_data);
2266
2267         /* Notify the queue */
2268         modest_mail_operation_notify_end (self);
2269 }
2270
2271 void
2272 modest_mail_operation_rename_folder (ModestMailOperation *self,
2273                                      TnyFolder *folder,
2274                                      const gchar *name)
2275 {
2276         ModestMailOperationPrivate *priv;
2277         ModestTnyFolderRules rules;
2278         XFerMsgAsyncHelper *helper;
2279
2280         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2281         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2282         g_return_if_fail (name);
2283         
2284         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2285
2286         /* Get account and set it into mail_operation */
2287         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2288         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2289
2290         /* Check folder rules */
2291         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2292         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2293                 /* Set status failed and set an error */
2294                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2295                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2296                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2297                              _("FIXME: unable to rename"));
2298
2299                 /* Notify about operation end */
2300                 modest_mail_operation_notify_end (self);
2301         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2302                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2303                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2304                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2305                              _("FIXME: unable to rename"));
2306                 /* Notify about operation end */
2307                 modest_mail_operation_notify_end (self);
2308         } else {
2309                 TnyFolderStore *into;
2310
2311                 into = tny_folder_get_folder_store (folder);    
2312
2313                 /* Check that the new folder name is not used by any
2314                    special local folder */
2315                 if (new_name_valid_if_local_account (priv, into, name)) {
2316                         /* Create the helper */
2317                         helper = g_slice_new0 (XFerMsgAsyncHelper);
2318                         helper->mail_op = g_object_ref(self);
2319                         helper->dest_folder = NULL;
2320                         helper->headers = NULL;
2321                         helper->user_callback = NULL;
2322                         helper->user_data = NULL;
2323                 
2324                         /* Rename. Camel handles folder subscription/unsubscription */
2325                         modest_mail_operation_notify_start (self);
2326                         tny_folder_copy_async (folder, into, name, TRUE,
2327                                                transfer_folder_cb,
2328                                                transfer_folder_status_cb,
2329                                                helper);
2330                 } else {
2331                         modest_mail_operation_notify_end (self);
2332                 }
2333                 g_object_unref (into);
2334         }
2335 }
2336
2337 /* ******************************************************************* */
2338 /* **************************  MSG  ACTIONS  ************************* */
2339 /* ******************************************************************* */
2340
2341 void 
2342 modest_mail_operation_get_msg (ModestMailOperation *self,
2343                                TnyHeader *header,
2344                                GetMsgAsyncUserCallback user_callback,
2345                                gpointer user_data)
2346 {
2347         GetMsgInfo *helper = NULL;
2348         TnyFolder *folder;
2349         ModestMailOperationPrivate *priv;
2350         
2351         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2352         g_return_if_fail (TNY_IS_HEADER (header));
2353         
2354         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2355         folder = tny_header_get_folder (header);
2356
2357         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2358         priv->total = 1;
2359         priv->done = 0;
2360
2361         /* Get account and set it into mail_operation */
2362         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2363         
2364         /* Check for cached messages */
2365         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2366                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2367         else 
2368                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2369         
2370         /* Create the helper */
2371         helper = g_slice_new0 (GetMsgInfo);
2372         helper->header = g_object_ref (header);
2373         helper->mail_op = g_object_ref (self);
2374         helper->user_callback = user_callback;
2375         helper->user_data = user_data;
2376         helper->destroy_notify = NULL;
2377         helper->last_total_bytes = 0;
2378         helper->sum_total_bytes = 0;
2379         helper->total_bytes = tny_header_get_message_size (header);
2380
2381         modest_mail_operation_notify_start (self);
2382         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2383
2384         g_object_unref (G_OBJECT (folder));
2385 }
2386
2387 static void     
2388 get_msg_status_cb (GObject *obj,
2389                    TnyStatus *status,  
2390                    gpointer user_data)
2391 {
2392         GetMsgInfo *helper = NULL;
2393
2394         g_return_if_fail (status != NULL);
2395         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2396
2397         helper = (GetMsgInfo *) user_data;
2398         g_return_if_fail (helper != NULL);       
2399
2400         /* Notify progress */
2401         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2402                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2403 }
2404
2405 static void
2406 get_msg_async_cb (TnyFolder *folder, 
2407                   gboolean canceled, 
2408                   TnyMsg *msg, 
2409                   GError *err, 
2410                   gpointer user_data)
2411 {
2412         GetMsgInfo *info = NULL;
2413         ModestMailOperationPrivate *priv = NULL;
2414         gboolean finished;
2415
2416         info = (GetMsgInfo *) user_data;
2417
2418         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2419         priv->done++;
2420         finished = (priv->done == priv->total) ? TRUE : FALSE;
2421
2422         /* Check errors */
2423         if (canceled || err) {
2424                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2425                 if (!priv->error)
2426                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2427                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2428                                      err->message);
2429         } else {
2430                 /* Set the success status before calling the user callback */
2431                 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2432                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2433         }
2434
2435
2436         /* Call the user callback */
2437         if (info->user_callback)
2438                 info->user_callback (info->mail_op, info->header, canceled, 
2439                                      msg, err, info->user_data);
2440
2441         /* Notify about operation end if this is the last callback */
2442         if (finished) {
2443                 /* Free user data */
2444                 if (info->destroy_notify)
2445                         info->destroy_notify (info->user_data);
2446
2447                 /* Notify about operation end */
2448                 modest_mail_operation_notify_end (info->mail_op);
2449         }
2450
2451         /* Clean */
2452         g_object_unref (info->header);
2453         g_object_unref (info->mail_op);
2454         g_slice_free (GetMsgInfo, info);
2455 }
2456
2457 void 
2458 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2459                                      TnyList *header_list, 
2460                                      GetMsgAsyncUserCallback user_callback,
2461                                      gpointer user_data,
2462                                      GDestroyNotify notify)
2463 {
2464         ModestMailOperationPrivate *priv = NULL;
2465         gboolean size_ok = TRUE;
2466         gint max_size;
2467         TnyIterator *iter = NULL;
2468         
2469         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2470         
2471         /* Init mail operation */
2472         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2473         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2474         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2475         priv->done = 0;
2476         priv->total = tny_list_get_length(header_list);
2477
2478         /* Get account and set it into mail_operation */
2479         if (tny_list_get_length (header_list) >= 1) {
2480                 iter = tny_list_create_iterator (header_list);
2481                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2482                 if (header) {
2483                         TnyFolder *folder = tny_header_get_folder (header);
2484                         if (folder) {           
2485                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2486                                 g_object_unref (folder);
2487                         }
2488                         g_object_unref (header);
2489                 }
2490
2491                 if (tny_list_get_length (header_list) == 1) {
2492                         g_object_unref (iter);
2493                         iter = NULL;
2494                 }
2495         }
2496
2497         /* Get msg size limit */
2498         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2499                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2500                                          &(priv->error));
2501         if (priv->error) {
2502                 g_clear_error (&(priv->error));
2503                 max_size = G_MAXINT;
2504         } else {
2505                 max_size = max_size * KB;
2506         }
2507
2508         /* Check message size limits. If there is only one message
2509            always retrieve it */
2510         if (iter != NULL) {
2511                 while (!tny_iterator_is_done (iter) && size_ok) {
2512                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2513                         if (header) {
2514                                 if (tny_header_get_message_size (header) >= max_size)
2515                                         size_ok = FALSE;
2516                                 g_object_unref (header);
2517                         }
2518
2519                         tny_iterator_next (iter);
2520                 }
2521                 g_object_unref (iter);
2522         }
2523
2524         if (size_ok) {
2525                 const gint msg_list_size = compute_message_list_size (header_list);
2526
2527                 modest_mail_operation_notify_start (self);
2528                 iter = tny_list_create_iterator (header_list);
2529                 while (!tny_iterator_is_done (iter)) { 
2530                         GetMsgInfo *msg_info = NULL;
2531                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2532                         TnyFolder *folder = tny_header_get_folder (header);
2533                         
2534                         /* Create the message info */
2535                         msg_info = g_slice_new0 (GetMsgInfo);
2536                         msg_info->mail_op = g_object_ref (self);
2537                         msg_info->header = g_object_ref (header);
2538                         msg_info->user_callback = user_callback;
2539                         msg_info->user_data = user_data;
2540                         msg_info->destroy_notify = notify;
2541                         msg_info->last_total_bytes = 0;
2542                         msg_info->sum_total_bytes = 0;
2543                         msg_info->total_bytes = msg_list_size;
2544                         
2545                         /* The callback will call it per each header */
2546                         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2547                         
2548                         /* Free and go on */
2549                         g_object_unref (header);
2550                         g_object_unref (folder);
2551                         tny_iterator_next (iter);
2552                 }
2553                 g_object_unref (iter);
2554         } else {
2555                 /* Set status failed and set an error */
2556                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2557                 /* FIXME: the error msg is different for pop */
2558                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2559                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2560                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2561                 /* Remove from queue and free resources */
2562                 modest_mail_operation_notify_end (self);
2563                 if (notify)
2564                         notify (user_data);
2565         }
2566 }
2567
2568
2569 void 
2570 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2571                                   TnyHeader *header,
2572                                   gboolean remove_to_trash /*ignored*/)
2573 {
2574         TnyFolder *folder;
2575         ModestMailOperationPrivate *priv;
2576
2577         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2578         g_return_if_fail (TNY_IS_HEADER (header));
2579
2580         if (remove_to_trash)
2581                 g_warning ("remove to trash is not implemented");
2582
2583         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2584         folder = tny_header_get_folder (header);
2585
2586         /* Get account and set it into mail_operation */
2587         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2588         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2589         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2590
2591         /* remove message from folder */
2592         tny_folder_remove_msg (folder, header, &(priv->error));
2593         if (!priv->error) {
2594                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2595                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2596
2597                 modest_mail_operation_notify_start (self);
2598
2599                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2600                     TNY_IS_CAMEL_POP_FOLDER (folder))
2601                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> dont expunge */
2602                 else
2603                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2604         }
2605         
2606         
2607         /* Set status */
2608         if (!priv->error)
2609                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2610         else
2611                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2612
2613         /* Free */
2614         g_object_unref (G_OBJECT (folder));
2615
2616         /* Notify about operation end */
2617         modest_mail_operation_notify_end (self);
2618 }
2619
2620 void 
2621 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2622                                    TnyList *headers,
2623                                   gboolean remove_to_trash /*ignored*/)
2624 {
2625         TnyFolder *folder;
2626         ModestMailOperationPrivate *priv;
2627         TnyIterator *iter = NULL;
2628         TnyHeader *header = NULL;
2629         TnyList *remove_headers = NULL;
2630         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2631
2632         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2633         g_return_if_fail (TNY_IS_LIST (headers));
2634
2635         if (remove_to_trash)
2636                 g_warning ("remove to trash is not implemented");
2637
2638         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2639
2640         remove_headers = g_object_ref(headers);
2641
2642         /* Get folder from first header and sync it */
2643         iter = tny_list_create_iterator (headers);
2644         header = TNY_HEADER (tny_iterator_get_current (iter));
2645         folder = tny_header_get_folder (header);
2646
2647         /* Don't remove messages that are being sent */
2648         if (modest_tny_folder_is_local_folder (folder)) {
2649                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2650         }
2651         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2652                 TnyTransportAccount *traccount = NULL;
2653                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2654                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2655                 if (traccount) {
2656                         ModestTnySendQueueStatus status;
2657                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2658                         TnyIterator *iter = tny_list_create_iterator(headers);
2659                         g_object_unref(remove_headers);
2660                         remove_headers = TNY_LIST(tny_simple_list_new());
2661                         while (!tny_iterator_is_done(iter)) {
2662                                 char *msg_id;
2663                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2664                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2665                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2666                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2667                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2668                                 }
2669                                 g_object_unref(hdr);
2670                                 g_free(msg_id);
2671                                 tny_iterator_next(iter);
2672                         }
2673                         g_object_unref(iter);
2674                         g_object_unref(traccount);
2675                 }
2676         }
2677
2678         /* Get account and set it into mail_operation */
2679         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2680         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2681         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2682
2683         /* remove message from folder */
2684         modest_mail_operation_notify_start (self);
2685
2686         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2687         if (!priv->error) {
2688                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) || 
2689                     TNY_IS_CAMEL_POP_FOLDER (folder))
2690                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */ 
2691                 else
2692                         /* local folders */
2693                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2694         }
2695         
2696         
2697         /* Set status */
2698         if (!priv->error)
2699                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2700         else
2701                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2702
2703         /* Free */
2704         g_object_unref (remove_headers);
2705         g_object_unref (header);
2706         g_object_unref (iter);
2707         g_object_unref (G_OBJECT (folder));
2708
2709         /* Notify about operation end */
2710         modest_mail_operation_notify_end (self);
2711 }
2712
2713 static void
2714 notify_progress_of_multiple_messages (ModestMailOperation *self,
2715                                       TnyStatus *status,
2716                                       gint *last_total_bytes,
2717                                       gint *sum_total_bytes,
2718                                       gint total_bytes, 
2719                                       gboolean increment_done)
2720 {
2721         ModestMailOperationPrivate *priv;
2722         ModestMailOperationState *state;
2723         gboolean is_num_bytes;
2724
2725         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2726
2727         /* We know that tinymail sends us information about
2728            transferred bytes with this particular message */
2729         is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2730
2731         state = modest_mail_operation_clone_state (self);
2732         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2733                 /* We know that we're in a different message when the
2734                    total number of bytes to transfer is different. Of
2735                    course it could fail if we're transferring messages
2736                    of the same size, but this is a workarround */
2737                 if (status->of_total != *last_total_bytes) {
2738                         /* We need to increment the done when there is
2739                            no information about each individual
2740                            message, we need to do this in message
2741                            transfers, and we don't do it for getting
2742                            messages */
2743                         if (increment_done)
2744                                 priv->done++;
2745                         *sum_total_bytes += *last_total_bytes;
2746                         *last_total_bytes = status->of_total;
2747                 }
2748                 state->bytes_done += status->position + *sum_total_bytes;
2749                 state->bytes_total = total_bytes;
2750
2751                 /* Notify the status change. Only notify about changes
2752                    referred to bytes */
2753                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2754                                0, state, NULL);
2755         }
2756
2757         g_slice_free (ModestMailOperationState, state);
2758 }
2759
2760 static void
2761 transfer_msgs_status_cb (GObject *obj,
2762                          TnyStatus *status,  
2763                          gpointer user_data)
2764 {
2765         XFerMsgAsyncHelper *helper;
2766
2767         g_return_if_fail (status != NULL);
2768         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2769
2770         helper = (XFerMsgAsyncHelper *) user_data;
2771         g_return_if_fail (helper != NULL);       
2772
2773         /* Notify progress */
2774         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2775                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2776 }
2777
2778
2779 static void
2780 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2781 {
2782         XFerMsgAsyncHelper *helper;
2783         ModestMailOperation *self;
2784         ModestMailOperationPrivate *priv;
2785         TnyIterator *iter = NULL;
2786         TnyHeader *header = NULL;
2787
2788         helper = (XFerMsgAsyncHelper *) user_data;
2789         self = helper->mail_op;
2790
2791         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2792
2793         if (err) {
2794                 priv->error = g_error_copy (err);
2795                 priv->done = 0;
2796                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2797         } else if (cancelled) {
2798                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2799                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2800                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2801                              _("Error trying to refresh the contents of %s"),
2802                              tny_folder_get_name (folder));
2803         } else {
2804                 priv->done = 1;
2805                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2806
2807                 /* Update folder counts */
2808                 tny_folder_poke_status (folder);                
2809                 tny_folder_poke_status (helper->dest_folder);           
2810         }
2811
2812         
2813         /* Mark headers as deleted and seen */
2814         if ((helper->delete) && 
2815             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2816                 iter = tny_list_create_iterator (helper->headers);
2817                 while (!tny_iterator_is_done (iter)) {
2818                         header = TNY_HEADER (tny_iterator_get_current (iter));
2819                         tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2820                         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2821                         g_object_unref (header);
2822
2823                         tny_iterator_next (iter);
2824                 }
2825
2826         }
2827                 
2828
2829         /* Notify about operation end */
2830         modest_mail_operation_notify_end (self);
2831
2832         /* If user defined callback function was defined, call it */
2833         if (helper->user_callback) {
2834                 /* This is not a GDK lock because we are a Tinymail callback and
2835                  * Tinymail already acquires the Gdk lock */
2836
2837                 /* no gdk_threads_enter (), CHECKED */
2838                 helper->user_callback (self, helper->user_data);
2839                 /* no gdk_threads_leave (), CHECKED */
2840         }
2841
2842         /* Free */
2843         if (helper->headers)
2844                 g_object_unref (helper->headers);
2845         if (helper->dest_folder)
2846                 g_object_unref (helper->dest_folder);
2847         if (helper->mail_op)
2848                 g_object_unref (helper->mail_op);
2849         if (folder)
2850                 g_object_unref (folder);
2851         if (iter)
2852                 g_object_unref (iter);
2853         g_slice_free (XFerMsgAsyncHelper, helper);
2854 }
2855
2856 static guint
2857 compute_message_list_size (TnyList *headers)
2858 {
2859         TnyIterator *iter;
2860         guint size = 0;
2861
2862         iter = tny_list_create_iterator (headers);
2863         while (!tny_iterator_is_done (iter)) {
2864                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2865                 size += tny_header_get_message_size (header);
2866                 g_object_unref (header);
2867                 tny_iterator_next (iter);
2868         }
2869         g_object_unref (iter);
2870
2871         return size;
2872 }
2873
2874 static guint
2875 compute_message_array_size (GPtrArray *headers)
2876 {
2877         guint size = 0;
2878         gint i;
2879
2880         for (i = 0; i < headers->len; i++) {
2881                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2882                 size += tny_header_get_message_size (header);
2883         }
2884
2885         return size;
2886 }
2887
2888
2889 void
2890 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2891                                  TnyList *headers, 
2892                                  TnyFolder *folder, 
2893                                  gboolean delete_original,
2894                                  XferAsyncUserCallback user_callback,
2895                                  gpointer user_data)
2896 {
2897         ModestMailOperationPrivate *priv = NULL;
2898         TnyIterator *iter = NULL;
2899         TnyFolder *src_folder = NULL;
2900         XFerMsgAsyncHelper *helper = NULL;
2901         TnyHeader *header = NULL;
2902         ModestTnyFolderRules rules = 0;
2903
2904         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2905         g_return_if_fail (headers && TNY_IS_LIST (headers));
2906         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2907
2908         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2909         priv->total = tny_list_get_length (headers);
2910         priv->done = 0;
2911         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2912         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2913
2914         /* Apply folder rules */
2915         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2916         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2917                 /* Set status failed and set an error */
2918                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2919                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2920                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2921                              _CS("ckct_ib_unable_to_paste_here"));
2922                 /* Notify the queue */
2923                 modest_mail_operation_notify_end (self);
2924                 return;
2925         }
2926                 
2927         /* Get source folder */
2928         iter = tny_list_create_iterator (headers);
2929         header = TNY_HEADER (tny_iterator_get_current (iter));
2930         if (header) {
2931                 src_folder = tny_header_get_folder (header);
2932                 g_object_unref (header);
2933         }
2934         g_object_unref (iter);
2935
2936         if (src_folder == NULL) {
2937                 /* Notify the queue */
2938                 modest_mail_operation_notify_end (self);
2939
2940                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2941                 return;
2942         }
2943
2944         
2945         /* Check folder source and destination */
2946         if (src_folder == folder) {
2947                 /* Set status failed and set an error */
2948                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2949                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2950                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2951                              _("mail_in_ui_folder_copy_target_error"));
2952                 
2953                 /* Notify the queue */
2954                 modest_mail_operation_notify_end (self);
2955                 
2956                 /* Free */
2957                 g_object_unref (src_folder);            
2958                 return;
2959         }
2960
2961         /* Create the helper */
2962         helper = g_slice_new0 (XFerMsgAsyncHelper);
2963         helper->mail_op = g_object_ref(self);
2964         helper->dest_folder = g_object_ref(folder);
2965         helper->headers = g_object_ref(headers);
2966         helper->user_callback = user_callback;
2967         helper->user_data = user_data;
2968         helper->delete = delete_original;
2969         helper->last_total_bytes = 0;
2970         helper->sum_total_bytes = 0;
2971         helper->total_bytes = compute_message_list_size (headers);
2972
2973         /* Get account and set it into mail_operation */
2974         priv->account = modest_tny_folder_get_account (src_folder);
2975
2976         /* Transfer messages */
2977         modest_mail_operation_notify_start (self);
2978         tny_folder_transfer_msgs_async (src_folder, 
2979                                         headers, 
2980                                         folder, 
2981                                         delete_original, 
2982                                         transfer_msgs_cb, 
2983                                         transfer_msgs_status_cb,
2984                                         helper);
2985 }
2986
2987
2988 static void
2989 on_refresh_folder (TnyFolder   *folder, 
2990                    gboolean     cancelled, 
2991                    GError     *error,
2992                    gpointer     user_data)
2993 {
2994         RefreshAsyncHelper *helper = NULL;
2995         ModestMailOperation *self = NULL;
2996         ModestMailOperationPrivate *priv = NULL;
2997
2998         helper = (RefreshAsyncHelper *) user_data;
2999         self = helper->mail_op;
3000         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3001
3002         g_return_if_fail(priv!=NULL);
3003
3004         if (error) {
3005                 priv->error = g_error_copy (error);
3006                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3007                 goto out;
3008         }
3009
3010         if (cancelled) {
3011                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3012                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3013                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3014                              _("Error trying to refresh the contents of %s"),
3015                              tny_folder_get_name (folder));
3016                 goto out;
3017         }
3018
3019         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3020  out:
3021
3022         /* Call user defined callback, if it exists */
3023         if (helper->user_callback) {
3024
3025                 /* This is not a GDK lock because we are a Tinymail callback and
3026                  * Tinymail already acquires the Gdk lock */
3027                 helper->user_callback (self, folder, helper->user_data);
3028         }
3029
3030         /* Free */
3031         g_slice_free (RefreshAsyncHelper, helper);
3032
3033         /* Notify about operation end */
3034         modest_mail_operation_notify_end (self);
3035         g_object_unref(self);
3036 }
3037
3038 static void
3039 on_refresh_folder_status_update (GObject *obj,
3040                                  TnyStatus *status,
3041                                  gpointer user_data)
3042 {
3043         RefreshAsyncHelper *helper = NULL;
3044         ModestMailOperation *self = NULL;
3045         ModestMailOperationPrivate *priv = NULL;
3046         ModestMailOperationState *state;
3047
3048         g_return_if_fail (user_data != NULL);
3049         g_return_if_fail (status != NULL);
3050         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
3051
3052         helper = (RefreshAsyncHelper *) user_data;
3053         self = helper->mail_op;
3054         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3055
3056         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3057
3058         priv->done = status->position;
3059         priv->total = status->of_total;
3060
3061         state = modest_mail_operation_clone_state (self);
3062
3063         /* This is not a GDK lock because we are a Tinymail callback and
3064          * Tinymail already acquires the Gdk lock */
3065         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3066
3067         g_slice_free (ModestMailOperationState, state);
3068 }
3069
3070 void 
3071 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3072                                        TnyFolder *folder,
3073                                        RefreshAsyncUserCallback user_callback,
3074                                        gpointer user_data)
3075 {
3076         ModestMailOperationPrivate *priv = NULL;
3077         RefreshAsyncHelper *helper = NULL;
3078
3079         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3080
3081         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3082
3083         /* Get account and set it into mail_operation */
3084         priv->account = modest_tny_folder_get_account  (folder);
3085         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3086
3087         /* Create the helper */
3088         helper = g_slice_new0 (RefreshAsyncHelper);
3089         helper->mail_op = g_object_ref(self);
3090         helper->user_callback = user_callback;
3091         helper->user_data = user_data;
3092
3093         /* Refresh the folder. TODO: tinymail could issue a status
3094            updates before the callback call then this could happen. We
3095            must review the design */
3096         modest_mail_operation_notify_start (self);
3097         tny_folder_refresh_async (folder,
3098                                   on_refresh_folder,
3099                                   on_refresh_folder_status_update,
3100                                   helper);
3101 }
3102
3103
3104 static void
3105 modest_mail_operation_notify_start (ModestMailOperation *self)
3106 {
3107         ModestMailOperationPrivate *priv = NULL;
3108
3109         g_return_if_fail (self);
3110
3111         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3112
3113         /* Ensure that all the fields are filled correctly */
3114         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3115
3116         /* Notify the observers about the mail operation. We do not
3117            wrapp this emission because we assume that this function is
3118            always called from within the main lock */
3119         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3120 }
3121
3122 /**
3123  *
3124  * It's used by the mail operation queue to notify the observers
3125  * attached to that signal that the operation finished. We need to use
3126  * that because tinymail does not give us the progress of a given
3127  * operation when it finishes (it directly calls the operation
3128  * callback).
3129  */
3130 static void
3131 modest_mail_operation_notify_end (ModestMailOperation *self)
3132 {
3133         ModestMailOperationPrivate *priv = NULL;
3134
3135         g_return_if_fail (self);
3136
3137         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3138
3139         /* Notify the observers about the mail operation end. We do
3140            not wrapp this emission because we assume that this
3141            function is always called from within the main lock */
3142         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3143
3144         /* Remove the error user data */
3145         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3146                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3147 }
3148
3149 TnyAccount *
3150 modest_mail_operation_get_account (ModestMailOperation *self)
3151 {
3152         ModestMailOperationPrivate *priv = NULL;
3153
3154         g_return_val_if_fail (self, NULL);
3155
3156         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3157
3158         return (priv->account) ? g_object_ref (priv->account) : NULL;
3159 }
3160
3161 void
3162 modest_mail_operation_noop (ModestMailOperation *self)
3163 {
3164         ModestMailOperationPrivate *priv = NULL;
3165
3166         g_return_if_fail (self);
3167
3168         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3169         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3170         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3171         priv->done = 0;
3172         priv->total = 0;
3173
3174         /* This mail operation does nothing actually */
3175         modest_mail_operation_notify_start (self);
3176         modest_mail_operation_notify_end (self);
3177 }