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