51a48473f11835f07aa4e2c0cd769f09bba9141c
[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
1597 /* ******************************************************************* */
1598 /* ************************** STORE  ACTIONS ************************* */
1599 /* ******************************************************************* */
1600
1601
1602 TnyFolder *
1603 modest_mail_operation_create_folder (ModestMailOperation *self,
1604                                      TnyFolderStore *parent,
1605                                      const gchar *name)
1606 {
1607         ModestMailOperationPrivate *priv;
1608         TnyFolder *new_folder = NULL;
1609
1610         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1611         g_return_val_if_fail (name, NULL);
1612         
1613         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1614         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1615         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1616                 g_object_ref (parent) : 
1617                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1618
1619         /* Check for already existing folder */
1620         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1621                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1622                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1623                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1624                              _CS("ckdg_ib_folder_already_exists"));
1625         }
1626
1627         /* Check parent */
1628         if (TNY_IS_FOLDER (parent)) {
1629                 /* Check folder rules */
1630                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1631                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1632                         /* Set status failed and set an error */
1633                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1634                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1635                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1636                                      _("mail_in_ui_folder_create_error"));
1637                 }
1638         }
1639
1640         if (!strcmp (name, " ") || strchr (name, '/')) {
1641                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1642                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1643                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1644                              _("mail_in_ui_folder_create_error"));
1645         }
1646
1647         if (!priv->error) {
1648                 /* Create the folder */
1649                 modest_mail_operation_notify_start (self);
1650                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1651                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1652                 if (!priv->error)
1653                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1654         }
1655
1656         /* Notify about operation end */
1657         modest_mail_operation_notify_end (self);
1658
1659         return new_folder;
1660 }
1661
1662 void
1663 modest_mail_operation_remove_folder (ModestMailOperation *self,
1664                                      TnyFolder           *folder,
1665                                      gboolean             remove_to_trash)
1666 {
1667         TnyAccount *account;
1668         ModestMailOperationPrivate *priv;
1669         ModestTnyFolderRules rules;
1670
1671         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1672         g_return_if_fail (TNY_IS_FOLDER (folder));
1673         
1674         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1675         
1676         /* Check folder rules */
1677         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1678         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1679                 /* Set status failed and set an error */
1680                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1681                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1682                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1683                              _("mail_in_ui_folder_delete_error"));
1684                 goto end;
1685         }
1686
1687         /* Get the account */
1688         account = modest_tny_folder_get_account (folder);
1689         priv->account = g_object_ref(account);
1690         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1691
1692         /* Delete folder or move to trash */
1693         if (remove_to_trash) {
1694                 TnyFolder *trash_folder = NULL;
1695                 trash_folder = modest_tny_account_get_special_folder (account,
1696                                                                       TNY_FOLDER_TYPE_TRASH);
1697                 /* TODO: error_handling */
1698                 if (trash_folder) {
1699                         modest_mail_operation_notify_start (self);
1700                         modest_mail_operation_xfer_folder (self, folder,
1701                                                     TNY_FOLDER_STORE (trash_folder), 
1702                                                     TRUE, NULL, NULL);
1703                         g_object_unref (trash_folder);
1704                 }
1705         } else {
1706                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1707                 if (parent) {
1708                         modest_mail_operation_notify_start (self);
1709                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
1710                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1711                         
1712                         if (!priv->error)
1713                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1714
1715                         g_object_unref (parent);
1716                 } else
1717                         g_warning ("%s: could not get parent folder", __FUNCTION__);
1718         }
1719         g_object_unref (G_OBJECT (account));
1720
1721  end:
1722         /* Notify about operation end */
1723         modest_mail_operation_notify_end (self);
1724 }
1725
1726 static void
1727 transfer_folder_status_cb (GObject *obj,
1728                            TnyStatus *status,
1729                            gpointer user_data)
1730 {
1731         ModestMailOperation *self;
1732         ModestMailOperationPrivate *priv;
1733         ModestMailOperationState *state;
1734         XFerMsgAsyncHelper *helper;
1735
1736         g_return_if_fail (status != NULL);
1737         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1738
1739         helper = (XFerMsgAsyncHelper *) user_data;
1740         g_return_if_fail (helper != NULL);
1741
1742         self = helper->mail_op;
1743         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1744
1745         priv->done = status->position;
1746         priv->total = status->of_total;
1747
1748         state = modest_mail_operation_clone_state (self);
1749
1750         /* This is not a GDK lock because we are a Tinymail callback
1751          * which is already GDK locked by Tinymail */
1752
1753         /* no gdk_threads_enter (), CHECKED */
1754
1755         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1756
1757         /* no gdk_threads_leave (), CHECKED */
1758
1759         g_slice_free (ModestMailOperationState, state);
1760 }
1761
1762
1763 static void
1764 transfer_folder_cb (TnyFolder *folder, 
1765                     gboolean cancelled, 
1766                     TnyFolderStore *into, 
1767                     TnyFolder *new_folder, 
1768                     GError *err, 
1769                     gpointer user_data)
1770 {
1771         XFerMsgAsyncHelper *helper;
1772         ModestMailOperation *self = NULL;
1773         ModestMailOperationPrivate *priv = NULL;
1774
1775         helper = (XFerMsgAsyncHelper *) user_data;
1776         g_return_if_fail (helper != NULL);       
1777
1778         self = helper->mail_op;
1779         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1780
1781         if (err) {
1782                 priv->error = g_error_copy (err);
1783                 priv->done = 0;
1784                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1785         } else if (cancelled) {
1786                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1787                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1788                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1789                              _("Transference of %s was cancelled."),
1790                              tny_folder_get_name (folder));
1791         } else {
1792                 priv->done = 1;
1793                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1794         }
1795                 
1796         /* Notify about operation end */
1797         modest_mail_operation_notify_end (self);
1798
1799         /* If user defined callback function was defined, call it */
1800         if (helper->user_callback) {
1801
1802                 /* This is not a GDK lock because we are a Tinymail callback
1803                  * which is already GDK locked by Tinymail */
1804
1805                 /* no gdk_threads_enter (), CHECKED */
1806                 helper->user_callback (self, helper->user_data);
1807                 /* no gdk_threads_leave () , CHECKED */
1808         }
1809
1810         /* Free */
1811         g_object_unref (helper->mail_op);
1812         g_slice_free   (XFerMsgAsyncHelper, helper);
1813 }
1814
1815 /**
1816  *
1817  * This function checks if the new name is a valid name for our local
1818  * folders account. The new name could not be the same than then name
1819  * of any of the mandatory local folders
1820  *
1821  * We can not rely on tinymail because tinymail does not check the
1822  * name of the virtual folders that the account could have in the case
1823  * that we're doing a rename (because it directly calls Camel which
1824  * knows nothing about our virtual folders). 
1825  *
1826  * In the case of an actual copy/move (i.e. move/copy a folder between
1827  * accounts) tinymail uses the tny_folder_store_create_account which
1828  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1829  * checks the new name of the folder, so this call in that case
1830  * wouldn't be needed. *But* NOTE that if tinymail changes its
1831  * implementation (if folder transfers within the same account is no
1832  * longer implemented as a rename) this call will allow Modest to work
1833  * perfectly
1834  *
1835  * If the new name is not valid, this function will set the status to
1836  * failed and will set also an error in the mail operation
1837  */
1838 static gboolean
1839 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1840                                  TnyFolderStore *into,
1841                                  const gchar *new_name)
1842 {
1843         if (TNY_IS_ACCOUNT (into) && 
1844             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1845             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1846                                                                  new_name)) {
1847                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1848                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1849                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1850                              _CS("ckdg_ib_folder_already_exists"));
1851                 return FALSE;
1852         } else
1853                 return TRUE;
1854 }
1855
1856 void
1857 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1858                                    TnyFolder *folder,
1859                                    TnyFolderStore *parent,
1860                                    gboolean delete_original,
1861                                    XferAsyncUserCallback user_callback,
1862                                    gpointer user_data)
1863 {
1864         ModestMailOperationPrivate *priv = NULL;
1865         ModestTnyFolderRules parent_rules = 0, rules; 
1866         XFerMsgAsyncHelper *helper = NULL;
1867         const gchar *folder_name = NULL;
1868         const gchar *error_msg;
1869
1870         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1871         g_return_if_fail (TNY_IS_FOLDER (folder));
1872         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1873
1874         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1875         folder_name = tny_folder_get_name (folder);
1876
1877         /* Set the error msg */
1878         error_msg = _("mail_in_ui_folder_move_target_error");
1879
1880         /* Get account and set it into mail_operation */
1881         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1882         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1883         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1884
1885         /* Get folder rules */
1886         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1887         if (TNY_IS_FOLDER (parent))
1888                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1889         
1890         /* Apply operation constraints */
1891         if ((gpointer) parent == (gpointer) folder ||
1892             (!TNY_IS_FOLDER_STORE (parent)) || 
1893             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1894                 /* Folder rules */
1895                 goto error;
1896         } else if (TNY_IS_FOLDER (parent) && 
1897                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1898                 /* Folder rules */
1899                 goto error;
1900
1901         } else if (TNY_IS_FOLDER (parent) &&
1902                    TNY_IS_FOLDER_STORE (folder) &&
1903                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
1904                                                   TNY_FOLDER_STORE (folder))) {
1905                 /* Do not move a parent into a child */
1906                 goto error;
1907         } else if (TNY_IS_FOLDER_STORE (parent) &&
1908                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1909                 /* Check that the new folder name is not used by any
1910                    parent subfolder */
1911                 goto error;     
1912         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1913                 /* Check that the new folder name is not used by any
1914                    special local folder */
1915                 goto error;
1916         } else {
1917                 /* Create the helper */
1918                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1919                 helper->mail_op = g_object_ref (self);
1920                 helper->dest_folder = NULL;
1921                 helper->headers = NULL;
1922                 helper->user_callback = user_callback;
1923                 helper->user_data = user_data;
1924                 
1925                 /* Move/Copy folder */
1926                 modest_mail_operation_notify_start (self);
1927                 tny_folder_copy_async (folder,
1928                                        parent,
1929                                        tny_folder_get_name (folder),
1930                                        delete_original,
1931                                        transfer_folder_cb,
1932                                        transfer_folder_status_cb,
1933                                        helper);
1934                 return;
1935         }
1936
1937  error:
1938         /* Set status failed and set an error */
1939         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1940         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1941                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1942                      error_msg);
1943
1944         /* Call the user callback if exists */
1945         if (user_callback)
1946                 user_callback (self, user_data);
1947
1948         /* Notify the queue */
1949         modest_mail_operation_notify_end (self);
1950 }
1951
1952 void
1953 modest_mail_operation_rename_folder (ModestMailOperation *self,
1954                                      TnyFolder *folder,
1955                                      const gchar *name)
1956 {
1957         ModestMailOperationPrivate *priv;
1958         ModestTnyFolderRules rules;
1959         XFerMsgAsyncHelper *helper;
1960
1961         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1962         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1963         g_return_if_fail (name);
1964         
1965         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1966
1967         /* Get account and set it into mail_operation */
1968         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1969         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1970
1971         /* Check folder rules */
1972         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1973         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1974                 /* Set status failed and set an error */
1975                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1976                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1977                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1978                              _("FIXME: unable to rename"));
1979
1980                 /* Notify about operation end */
1981                 modest_mail_operation_notify_end (self);
1982         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1983                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1984                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1985                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1986                              _("FIXME: unable to rename"));
1987                 /* Notify about operation end */
1988                 modest_mail_operation_notify_end (self);
1989         } else {
1990                 TnyFolderStore *into;
1991
1992                 into = tny_folder_get_folder_store (folder);    
1993
1994                 /* Check that the new folder name is not used by any
1995                    special local folder */
1996                 if (new_name_valid_if_local_account (priv, into, name)) {
1997                         /* Create the helper */
1998                         helper = g_slice_new0 (XFerMsgAsyncHelper);
1999                         helper->mail_op = g_object_ref(self);
2000                         helper->dest_folder = NULL;
2001                         helper->headers = NULL;
2002                         helper->user_callback = NULL;
2003                         helper->user_data = NULL;
2004                 
2005                         /* Rename. Camel handles folder subscription/unsubscription */
2006                         modest_mail_operation_notify_start (self);
2007                         tny_folder_copy_async (folder, into, name, TRUE,
2008                                                transfer_folder_cb,
2009                                                transfer_folder_status_cb,
2010                                                helper);
2011                 } else {
2012                         modest_mail_operation_notify_end (self);
2013                 }
2014                 g_object_unref (into);
2015         }
2016 }
2017
2018 /* ******************************************************************* */
2019 /* **************************  MSG  ACTIONS  ************************* */
2020 /* ******************************************************************* */
2021
2022 void 
2023 modest_mail_operation_get_msg (ModestMailOperation *self,
2024                                TnyHeader *header,
2025                                GetMsgAsyncUserCallback user_callback,
2026                                gpointer user_data)
2027 {
2028         GetMsgInfo *helper = NULL;
2029         TnyFolder *folder;
2030         ModestMailOperationPrivate *priv;
2031         
2032         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2033         g_return_if_fail (TNY_IS_HEADER (header));
2034         
2035         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2036         folder = tny_header_get_folder (header);
2037
2038         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2039         priv->total = 1;
2040         priv->done = 0;
2041
2042         /* Get account and set it into mail_operation */
2043         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2044         
2045         /* Check for cached messages */
2046         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2047                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2048         else 
2049                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2050         
2051         /* Create the helper */
2052         helper = g_slice_new0 (GetMsgInfo);
2053         helper->header = g_object_ref (header);
2054         helper->mail_op = g_object_ref (self);
2055         helper->user_callback = user_callback;
2056         helper->user_data = user_data;
2057         helper->destroy_notify = NULL;
2058         helper->last_total_bytes = 0;
2059         helper->sum_total_bytes = 0;
2060         helper->total_bytes = tny_header_get_message_size (header);
2061
2062         modest_mail_operation_notify_start (self);
2063         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2064
2065         g_object_unref (G_OBJECT (folder));
2066 }
2067
2068 static void     
2069 get_msg_status_cb (GObject *obj,
2070                    TnyStatus *status,  
2071                    gpointer user_data)
2072 {
2073         GetMsgInfo *helper = NULL;
2074
2075         g_return_if_fail (status != NULL);
2076         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2077
2078         helper = (GetMsgInfo *) user_data;
2079         g_return_if_fail (helper != NULL);       
2080
2081         /* Notify progress */
2082         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2083                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2084 }
2085
2086 static void
2087 get_msg_async_cb (TnyFolder *folder, 
2088                   gboolean canceled, 
2089                   TnyMsg *msg, 
2090                   GError *err, 
2091                   gpointer user_data)
2092 {
2093         GetMsgInfo *info = NULL;
2094         ModestMailOperationPrivate *priv = NULL;
2095         gboolean finished;
2096
2097         info = (GetMsgInfo *) user_data;
2098
2099         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2100         priv->done++;
2101         finished = (priv->done == priv->total) ? TRUE : FALSE;
2102
2103         /* Check errors */
2104         if (canceled || err) {
2105                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2106                 if (!priv->error)
2107                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2108                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2109                                      err->message);
2110         } else {
2111                 /* Set the success status before calling the user callback */
2112                 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2113                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2114         }
2115
2116
2117         /* Call the user callback */
2118         if (info->user_callback)
2119                 info->user_callback (info->mail_op, info->header, canceled, 
2120                                      msg, err, info->user_data);
2121
2122         /* Notify about operation end if this is the last callback */
2123         if (finished) {
2124                 /* Free user data */
2125                 if (info->destroy_notify)
2126                         info->destroy_notify (info->user_data);
2127
2128                 /* Notify about operation end */
2129                 modest_mail_operation_notify_end (info->mail_op);
2130         }
2131
2132         /* Clean */
2133         g_object_unref (info->header);
2134         g_object_unref (info->mail_op);
2135         g_slice_free (GetMsgInfo, info);
2136 }
2137
2138 void 
2139 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2140                                      TnyList *header_list, 
2141                                      GetMsgAsyncUserCallback user_callback,
2142                                      gpointer user_data,
2143                                      GDestroyNotify notify)
2144 {
2145         ModestMailOperationPrivate *priv = NULL;
2146         gboolean size_ok = TRUE;
2147         gint max_size;
2148         TnyIterator *iter = NULL;
2149         
2150         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2151         
2152         /* Init mail operation */
2153         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2154         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2155         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2156         priv->done = 0;
2157         priv->total = tny_list_get_length(header_list);
2158
2159         /* Get account and set it into mail_operation */
2160         if (tny_list_get_length (header_list) >= 1) {
2161                 iter = tny_list_create_iterator (header_list);
2162                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2163                 if (header) {
2164                         TnyFolder *folder = tny_header_get_folder (header);
2165                         if (folder) {           
2166                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2167                                 g_object_unref (folder);
2168                         }
2169                         g_object_unref (header);
2170                 }
2171
2172                 if (tny_list_get_length (header_list) == 1) {
2173                         g_object_unref (iter);
2174                         iter = NULL;
2175                 }
2176         }
2177
2178         /* Get msg size limit */
2179         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2180                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2181                                          &(priv->error));
2182         if (priv->error) {
2183                 g_clear_error (&(priv->error));
2184                 max_size = G_MAXINT;
2185         } else {
2186                 max_size = max_size * KB;
2187         }
2188
2189         /* Check message size limits. If there is only one message
2190            always retrieve it */
2191         if (iter != NULL) {
2192                 while (!tny_iterator_is_done (iter) && size_ok) {
2193                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2194                         if (header) {
2195                                 if (tny_header_get_message_size (header) >= max_size)
2196                                         size_ok = FALSE;
2197                                 g_object_unref (header);
2198                         }
2199
2200                         tny_iterator_next (iter);
2201                 }
2202                 g_object_unref (iter);
2203         }
2204
2205         if (size_ok) {
2206                 const gint msg_list_size = compute_message_list_size (header_list);
2207
2208                 modest_mail_operation_notify_start (self);
2209                 iter = tny_list_create_iterator (header_list);
2210                 while (!tny_iterator_is_done (iter)) { 
2211                         GetMsgInfo *msg_info = NULL;
2212                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2213                         TnyFolder *folder = tny_header_get_folder (header);
2214                         
2215                         /* Create the message info */
2216                         msg_info = g_slice_new0 (GetMsgInfo);
2217                         msg_info->mail_op = g_object_ref (self);
2218                         msg_info->header = g_object_ref (header);
2219                         msg_info->user_callback = user_callback;
2220                         msg_info->user_data = user_data;
2221                         msg_info->destroy_notify = notify;
2222                         msg_info->last_total_bytes = 0;
2223                         msg_info->sum_total_bytes = 0;
2224                         msg_info->total_bytes = msg_list_size;
2225                         
2226                         /* The callback will call it per each header */
2227                         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2228                         
2229                         /* Free and go on */
2230                         g_object_unref (header);
2231                         g_object_unref (folder);
2232                         tny_iterator_next (iter);
2233                 }
2234                 g_object_unref (iter);
2235         } else {
2236                 /* Set status failed and set an error */
2237                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2238                 /* FIXME: the error msg is different for pop */
2239                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2240                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2241                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2242                 /* Remove from queue and free resources */
2243                 modest_mail_operation_notify_end (self);
2244                 if (notify)
2245                         notify (user_data);
2246         }
2247 }
2248
2249
2250 void 
2251 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2252                                   TnyHeader *header,
2253                                   gboolean remove_to_trash /*ignored*/)
2254 {
2255         TnyFolder *folder;
2256         ModestMailOperationPrivate *priv;
2257
2258         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2259         g_return_if_fail (TNY_IS_HEADER (header));
2260
2261         if (remove_to_trash)
2262                 g_warning ("remove to trash is not implemented");
2263
2264         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2265         folder = tny_header_get_folder (header);
2266
2267         /* Get account and set it into mail_operation */
2268         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2269         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2270         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2271
2272         /* remove message from folder */
2273         tny_folder_remove_msg (folder, header, &(priv->error));
2274         if (!priv->error) {
2275                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2276                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2277
2278                 modest_mail_operation_notify_start (self);
2279
2280                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2281                     TNY_IS_CAMEL_POP_FOLDER (folder))
2282                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> dont expunge */
2283                 else
2284                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2285         }
2286         
2287         
2288         /* Set status */
2289         if (!priv->error)
2290                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2291         else
2292                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2293
2294         /* Free */
2295         g_object_unref (G_OBJECT (folder));
2296
2297         /* Notify about operation end */
2298         modest_mail_operation_notify_end (self);
2299 }
2300
2301 void 
2302 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2303                                    TnyList *headers,
2304                                   gboolean remove_to_trash /*ignored*/)
2305 {
2306         TnyFolder *folder;
2307         ModestMailOperationPrivate *priv;
2308         TnyIterator *iter = NULL;
2309         TnyHeader *header = NULL;
2310         TnyList *remove_headers = NULL;
2311         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2312
2313         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2314         g_return_if_fail (TNY_IS_LIST (headers));
2315
2316         if (remove_to_trash)
2317                 g_warning ("remove to trash is not implemented");
2318
2319         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2320
2321         remove_headers = g_object_ref(headers);
2322
2323         /* Get folder from first header and sync it */
2324         iter = tny_list_create_iterator (headers);
2325         header = TNY_HEADER (tny_iterator_get_current (iter));
2326         folder = tny_header_get_folder (header);
2327
2328         /* Don't remove messages that are being sent */
2329         if (modest_tny_folder_is_local_folder (folder)) {
2330                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2331         }
2332         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2333                 TnyTransportAccount *traccount = NULL;
2334                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2335                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2336                 if (traccount) {
2337                         ModestTnySendQueueStatus status;
2338                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2339                         TnyIterator *iter = tny_list_create_iterator(headers);
2340                         g_object_unref(remove_headers);
2341                         remove_headers = TNY_LIST(tny_simple_list_new());
2342                         while (!tny_iterator_is_done(iter)) {
2343                                 char *msg_id;
2344                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2345                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2346                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2347                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2348                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2349                                 }
2350                                 g_object_unref(hdr);
2351                                 g_free(msg_id);
2352                                 tny_iterator_next(iter);
2353                         }
2354                         g_object_unref(iter);
2355                         g_object_unref(traccount);
2356                 }
2357         }
2358
2359         /* Get account and set it into mail_operation */
2360         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2361         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2362         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2363
2364         /* remove message from folder */
2365         modest_mail_operation_notify_start (self);
2366
2367         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2368         if (!priv->error) {
2369                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) || 
2370                     TNY_IS_CAMEL_POP_FOLDER (folder))
2371                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */ 
2372                 else
2373                         /* local folders */
2374                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2375         }
2376         
2377         
2378         /* Set status */
2379         if (!priv->error)
2380                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2381         else
2382                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2383
2384         /* Free */
2385         g_object_unref (remove_headers);
2386         g_object_unref (header);
2387         g_object_unref (iter);
2388         g_object_unref (G_OBJECT (folder));
2389
2390         /* Notify about operation end */
2391         modest_mail_operation_notify_end (self);
2392 }
2393
2394 static void
2395 notify_progress_of_multiple_messages (ModestMailOperation *self,
2396                                       TnyStatus *status,
2397                                       gint *last_total_bytes,
2398                                       gint *sum_total_bytes,
2399                                       gint total_bytes, 
2400                                       gboolean increment_done)
2401 {
2402         ModestMailOperationPrivate *priv;
2403         ModestMailOperationState *state;
2404         gboolean is_num_bytes;
2405
2406         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2407
2408         /* We know that tinymail sends us information about
2409            transferred bytes with this particular message */
2410         is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2411
2412         state = modest_mail_operation_clone_state (self);
2413         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2414                 /* We know that we're in a different message when the
2415                    total number of bytes to transfer is different. Of
2416                    course it could fail if we're transferring messages
2417                    of the same size, but this is a workarround */
2418                 if (status->of_total != *last_total_bytes) {
2419                         /* We need to increment the done when there is
2420                            no information about each individual
2421                            message, we need to do this in message
2422                            transfers, and we don't do it for getting
2423                            messages */
2424                         if (increment_done)
2425                                 priv->done++;
2426                         *sum_total_bytes += *last_total_bytes;
2427                         *last_total_bytes = status->of_total;
2428                 }
2429                 state->bytes_done += status->position + *sum_total_bytes;
2430                 state->bytes_total = total_bytes;
2431
2432                 /* Notify the status change. Only notify about changes
2433                    referred to bytes */
2434                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2435                                0, state, NULL);
2436         }
2437
2438         g_slice_free (ModestMailOperationState, state);
2439 }
2440
2441 static void
2442 transfer_msgs_status_cb (GObject *obj,
2443                          TnyStatus *status,  
2444                          gpointer user_data)
2445 {
2446         XFerMsgAsyncHelper *helper;
2447
2448         g_return_if_fail (status != NULL);
2449         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2450
2451         helper = (XFerMsgAsyncHelper *) user_data;
2452         g_return_if_fail (helper != NULL);       
2453
2454         /* Notify progress */
2455         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2456                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2457 }
2458
2459
2460 static void
2461 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2462 {
2463         XFerMsgAsyncHelper *helper;
2464         ModestMailOperation *self;
2465         ModestMailOperationPrivate *priv;
2466         TnyIterator *iter = NULL;
2467         TnyHeader *header = NULL;
2468
2469         helper = (XFerMsgAsyncHelper *) user_data;
2470         self = helper->mail_op;
2471
2472         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2473
2474         if (err) {
2475                 priv->error = g_error_copy (err);
2476                 priv->done = 0;
2477                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2478         } else if (cancelled) {
2479                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2480                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2481                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2482                              _("Error trying to refresh the contents of %s"),
2483                              tny_folder_get_name (folder));
2484         } else {
2485                 priv->done = 1;
2486                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2487
2488                 /* Update folder counts */
2489                 tny_folder_poke_status (folder);                
2490                 tny_folder_poke_status (helper->dest_folder);           
2491         }
2492
2493         
2494         /* Mark headers as deleted and seen */
2495         if ((helper->delete) && 
2496             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2497                 iter = tny_list_create_iterator (helper->headers);
2498                 while (!tny_iterator_is_done (iter)) {
2499                         header = TNY_HEADER (tny_iterator_get_current (iter));
2500                         tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2501                         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2502                         g_object_unref (header);
2503
2504                         tny_iterator_next (iter);
2505                 }
2506
2507         }
2508                 
2509
2510         /* Notify about operation end */
2511         modest_mail_operation_notify_end (self);
2512
2513         /* If user defined callback function was defined, call it */
2514         if (helper->user_callback) {
2515                 /* This is not a GDK lock because we are a Tinymail callback and
2516                  * Tinymail already acquires the Gdk lock */
2517
2518                 /* no gdk_threads_enter (), CHECKED */
2519                 helper->user_callback (self, helper->user_data);
2520                 /* no gdk_threads_leave (), CHECKED */
2521         }
2522
2523         /* Free */
2524         if (helper->headers)
2525                 g_object_unref (helper->headers);
2526         if (helper->dest_folder)
2527                 g_object_unref (helper->dest_folder);
2528         if (helper->mail_op)
2529                 g_object_unref (helper->mail_op);
2530         if (folder)
2531                 g_object_unref (folder);
2532         if (iter)
2533                 g_object_unref (iter);
2534         g_slice_free (XFerMsgAsyncHelper, helper);
2535 }
2536
2537 static guint
2538 compute_message_list_size (TnyList *headers)
2539 {
2540         TnyIterator *iter;
2541         guint size = 0;
2542
2543         iter = tny_list_create_iterator (headers);
2544         while (!tny_iterator_is_done (iter)) {
2545                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2546                 size += tny_header_get_message_size (header);
2547                 g_object_unref (header);
2548                 tny_iterator_next (iter);
2549         }
2550         g_object_unref (iter);
2551
2552         return size;
2553 }
2554
2555 static guint
2556 compute_message_array_size (GPtrArray *headers)
2557 {
2558         guint size = 0;
2559         gint i;
2560
2561         for (i = 0; i < headers->len; i++) {
2562                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2563                 size += tny_header_get_message_size (header);
2564         }
2565
2566         return size;
2567 }
2568
2569
2570 void
2571 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2572                                  TnyList *headers, 
2573                                  TnyFolder *folder, 
2574                                  gboolean delete_original,
2575                                  XferAsyncUserCallback user_callback,
2576                                  gpointer user_data)
2577 {
2578         ModestMailOperationPrivate *priv = NULL;
2579         TnyIterator *iter = NULL;
2580         TnyFolder *src_folder = NULL;
2581         XFerMsgAsyncHelper *helper = NULL;
2582         TnyHeader *header = NULL;
2583         ModestTnyFolderRules rules = 0;
2584
2585         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2586         g_return_if_fail (headers && TNY_IS_LIST (headers));
2587         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2588
2589         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2590         priv->total = tny_list_get_length (headers);
2591         priv->done = 0;
2592         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2593         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2594
2595         /* Apply folder rules */
2596         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2597         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2598                 /* Set status failed and set an error */
2599                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2600                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2601                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2602                              _CS("ckct_ib_unable_to_paste_here"));
2603                 /* Notify the queue */
2604                 modest_mail_operation_notify_end (self);
2605                 return;
2606         }
2607                 
2608         /* Get source folder */
2609         iter = tny_list_create_iterator (headers);
2610         header = TNY_HEADER (tny_iterator_get_current (iter));
2611         if (header) {
2612                 src_folder = tny_header_get_folder (header);
2613                 g_object_unref (header);
2614         }
2615         g_object_unref (iter);
2616
2617         if (src_folder == NULL) {
2618                 /* Notify the queue */
2619                 modest_mail_operation_notify_end (self);
2620
2621                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2622                 return;
2623         }
2624
2625         
2626         /* Check folder source and destination */
2627         if (src_folder == folder) {
2628                 /* Set status failed and set an error */
2629                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2630                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2631                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2632                              _("mail_in_ui_folder_copy_target_error"));
2633                 
2634                 /* Notify the queue */
2635                 modest_mail_operation_notify_end (self);
2636                 
2637                 /* Free */
2638                 g_object_unref (src_folder);            
2639                 return;
2640         }
2641
2642         /* Create the helper */
2643         helper = g_slice_new0 (XFerMsgAsyncHelper);
2644         helper->mail_op = g_object_ref(self);
2645         helper->dest_folder = g_object_ref(folder);
2646         helper->headers = g_object_ref(headers);
2647         helper->user_callback = user_callback;
2648         helper->user_data = user_data;
2649         helper->delete = delete_original;
2650         helper->last_total_bytes = 0;
2651         helper->sum_total_bytes = 0;
2652         helper->total_bytes = compute_message_list_size (headers);
2653
2654         /* Get account and set it into mail_operation */
2655         priv->account = modest_tny_folder_get_account (src_folder);
2656
2657         /* Transfer messages */
2658         modest_mail_operation_notify_start (self);
2659         tny_folder_transfer_msgs_async (src_folder, 
2660                                         headers, 
2661                                         folder, 
2662                                         delete_original, 
2663                                         transfer_msgs_cb, 
2664                                         transfer_msgs_status_cb,
2665                                         helper);
2666 }
2667
2668
2669 static void
2670 on_refresh_folder (TnyFolder   *folder, 
2671                    gboolean     cancelled, 
2672                    GError     *error,
2673                    gpointer     user_data)
2674 {
2675         RefreshAsyncHelper *helper = NULL;
2676         ModestMailOperation *self = NULL;
2677         ModestMailOperationPrivate *priv = NULL;
2678
2679         helper = (RefreshAsyncHelper *) user_data;
2680         self = helper->mail_op;
2681         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2682
2683         g_return_if_fail(priv!=NULL);
2684
2685         if (error) {
2686                 priv->error = g_error_copy (error);
2687                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2688                 goto out;
2689         }
2690
2691         if (cancelled) {
2692                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2693                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2694                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2695                              _("Error trying to refresh the contents of %s"),
2696                              tny_folder_get_name (folder));
2697                 goto out;
2698         }
2699
2700         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2701  out:
2702
2703         /* Call user defined callback, if it exists */
2704         if (helper->user_callback) {
2705
2706                 /* This is not a GDK lock because we are a Tinymail callback and
2707                  * Tinymail already acquires the Gdk lock */
2708                 helper->user_callback (self, folder, helper->user_data);
2709         }
2710
2711         /* Free */
2712         g_slice_free (RefreshAsyncHelper, helper);
2713
2714         /* Notify about operation end */
2715         modest_mail_operation_notify_end (self);
2716         g_object_unref(self);
2717 }
2718
2719 static void
2720 on_refresh_folder_status_update (GObject *obj,
2721                                  TnyStatus *status,
2722                                  gpointer user_data)
2723 {
2724         RefreshAsyncHelper *helper = NULL;
2725         ModestMailOperation *self = NULL;
2726         ModestMailOperationPrivate *priv = NULL;
2727         ModestMailOperationState *state;
2728
2729         g_return_if_fail (user_data != NULL);
2730         g_return_if_fail (status != NULL);
2731         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2732
2733         helper = (RefreshAsyncHelper *) user_data;
2734         self = helper->mail_op;
2735         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2736
2737         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2738
2739         priv->done = status->position;
2740         priv->total = status->of_total;
2741
2742         state = modest_mail_operation_clone_state (self);
2743
2744         /* This is not a GDK lock because we are a Tinymail callback and
2745          * Tinymail already acquires the Gdk lock */
2746         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2747
2748         g_slice_free (ModestMailOperationState, state);
2749 }
2750
2751 void 
2752 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2753                                        TnyFolder *folder,
2754                                        RefreshAsyncUserCallback user_callback,
2755                                        gpointer user_data)
2756 {
2757         ModestMailOperationPrivate *priv = NULL;
2758         RefreshAsyncHelper *helper = NULL;
2759
2760         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2761
2762         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2763
2764         /* Get account and set it into mail_operation */
2765         priv->account = modest_tny_folder_get_account  (folder);
2766         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2767
2768         /* Create the helper */
2769         helper = g_slice_new0 (RefreshAsyncHelper);
2770         helper->mail_op = g_object_ref(self);
2771         helper->user_callback = user_callback;
2772         helper->user_data = user_data;
2773
2774         /* Refresh the folder. TODO: tinymail could issue a status
2775            updates before the callback call then this could happen. We
2776            must review the design */
2777         modest_mail_operation_notify_start (self);
2778         tny_folder_refresh_async (folder,
2779                                   on_refresh_folder,
2780                                   on_refresh_folder_status_update,
2781                                   helper);
2782 }
2783
2784
2785 static void
2786 modest_mail_operation_notify_start (ModestMailOperation *self)
2787 {
2788         ModestMailOperationPrivate *priv = NULL;
2789
2790         g_return_if_fail (self);
2791
2792         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2793
2794         /* Ensure that all the fields are filled correctly */
2795         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2796
2797         /* Notify the observers about the mail operation. We do not
2798            wrapp this emission because we assume that this function is
2799            always called from within the main lock */
2800         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2801 }
2802
2803 /**
2804  *
2805  * It's used by the mail operation queue to notify the observers
2806  * attached to that signal that the operation finished. We need to use
2807  * that because tinymail does not give us the progress of a given
2808  * operation when it finishes (it directly calls the operation
2809  * callback).
2810  */
2811 static void
2812 modest_mail_operation_notify_end (ModestMailOperation *self)
2813 {
2814         ModestMailOperationPrivate *priv = NULL;
2815
2816         g_return_if_fail (self);
2817
2818         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2819
2820         /* Notify the observers about the mail operation end. We do
2821            not wrapp this emission because we assume that this
2822            function is always called from within the main lock */
2823         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2824
2825         /* Remove the error user data */
2826         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2827                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2828 }
2829
2830 TnyAccount *
2831 modest_mail_operation_get_account (ModestMailOperation *self)
2832 {
2833         ModestMailOperationPrivate *priv = NULL;
2834
2835         g_return_val_if_fail (self, NULL);
2836
2837         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2838
2839         return (priv->account) ? g_object_ref (priv->account) : NULL;
2840 }
2841
2842 void
2843 modest_mail_operation_noop (ModestMailOperation *self)
2844 {
2845         ModestMailOperationPrivate *priv = NULL;
2846
2847         g_return_if_fail (self);
2848
2849         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2850         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2851         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2852         priv->done = 0;
2853         priv->total = 0;
2854
2855         /* This mail operation does nothing actually */
2856         modest_mail_operation_notify_start (self);
2857         modest_mail_operation_notify_end (self);
2858 }