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