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