* src/maemo/modest-msg-edit-window.c:
[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         gboolean size_ok = TRUE;
2250         gint max_size;
2251         TnyIterator *iter = NULL;
2252         
2253         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2254
2255         /* Init mail operation */
2256         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2257         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2258         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2259         priv->done = 0;
2260         priv->total = tny_list_get_length(header_list);
2261
2262         /* Get account and set it into mail_operation */
2263         if (tny_list_get_length (header_list) >= 1) {
2264                 iter = tny_list_create_iterator (header_list);
2265                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2266                 if (header) {
2267                         TnyFolder *folder = tny_header_get_folder (header);
2268                         if (folder) {           
2269                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2270                                 g_object_unref (folder);
2271                         }
2272                         g_object_unref (header);
2273                 }
2274
2275                 if (tny_list_get_length (header_list) == 1) {
2276                         g_object_unref (iter);
2277                         iter = NULL;
2278                 }
2279         }
2280
2281         /* Get msg size limit */
2282         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2283                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2284                                          &(priv->error));
2285         if (priv->error) {
2286                 g_clear_error (&(priv->error));
2287                 max_size = G_MAXINT;
2288         } else {
2289                 max_size = max_size * KB;
2290         }
2291
2292         /* Check message size limits. If there is only one message
2293            always retrieve it */
2294         if (iter != NULL) {
2295                 while (!tny_iterator_is_done (iter) && size_ok) {
2296                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2297                         if (header) {
2298                                 if (tny_header_get_message_size (header) >= max_size)
2299                                         size_ok = FALSE;
2300                                 g_object_unref (header);
2301                         }
2302
2303                         tny_iterator_next (iter);
2304                 }
2305                 g_object_unref (iter);
2306         }
2307
2308         if (size_ok) {
2309                 const gint msg_list_size = compute_message_list_size (header_list);
2310
2311                 modest_mail_operation_notify_start (self);
2312                 iter = tny_list_create_iterator (header_list);
2313                 while (!tny_iterator_is_done (iter)) { 
2314                         /* notify about the start of the operation */ 
2315                         ModestMailOperationState *state;
2316                         state = modest_mail_operation_clone_state (self);
2317                         state->done = 0;
2318                         state->total = 0;
2319                         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2320                                         0, state, NULL);
2321                         
2322                         GetMsgInfo *msg_info = NULL;
2323                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2324                         TnyFolder *folder = tny_header_get_folder (header);
2325                         
2326                         /* Create the message info */
2327                         msg_info = g_slice_new0 (GetMsgInfo);
2328                         msg_info->mail_op = g_object_ref (self);
2329                         msg_info->header = g_object_ref (header);
2330                         msg_info->user_callback = user_callback;
2331                         msg_info->user_data = user_data;
2332                         msg_info->destroy_notify = notify;
2333                         msg_info->last_total_bytes = 0;
2334                         msg_info->sum_total_bytes = 0;
2335                         msg_info->total_bytes = msg_list_size;
2336                         
2337                         /* The callback will call it per each header */
2338                         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2339                         
2340                         /* Free and go on */
2341                         g_object_unref (header);
2342                         g_object_unref (folder);
2343                         tny_iterator_next (iter);
2344                 }
2345                 g_object_unref (iter);
2346         } else {
2347                 /* Set status failed and set an error */
2348                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2349                 /* FIXME: the error msg is different for pop */
2350                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2351                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2352                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2353                 /* Remove from queue and free resources */
2354                 modest_mail_operation_notify_end (self);
2355                 if (notify)
2356                         notify (user_data);
2357         }
2358
2359 }
2360
2361
2362 void 
2363 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2364                                   TnyHeader *header,
2365                                   gboolean remove_to_trash /*ignored*/)
2366 {
2367         TnyFolder *folder;
2368         ModestMailOperationPrivate *priv;
2369
2370         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2371         g_return_if_fail (TNY_IS_HEADER (header));
2372
2373         if (remove_to_trash)
2374                 g_warning ("remove to trash is not implemented");
2375
2376         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2377         folder = tny_header_get_folder (header);
2378
2379         /* Get account and set it into mail_operation */
2380         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2381         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2382         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2383
2384         /* remove message from folder */
2385         tny_folder_remove_msg (folder, header, &(priv->error));
2386         if (!priv->error) {
2387                 gboolean expunge, leave_on_server;
2388                 const gchar *account_name;
2389                 TnyAccount *account;
2390                 ModestTransportStoreProtocol account_proto;
2391
2392                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2393                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2394
2395                 modest_mail_operation_notify_start (self);
2396
2397                 /* Get leave on server setting */
2398                 account = tny_folder_get_account (folder);
2399                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2400                 leave_on_server =
2401                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2402                                                                 account_name);
2403
2404                 account_proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
2405
2406                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2407                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2408                         expunge = TRUE;
2409                 else
2410                         expunge = FALSE;
2411
2412                 /* Sync folder */
2413                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2414
2415                 /* Unref */
2416                 g_object_unref (account);
2417         }
2418         
2419         
2420         /* Set status */
2421         if (!priv->error)
2422                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2423         else
2424                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2425
2426         /* Free */
2427         g_object_unref (G_OBJECT (folder));
2428
2429         /* Notify about operation end */
2430         modest_mail_operation_notify_end (self);
2431 }
2432
2433 void 
2434 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2435                                    TnyList *headers,
2436                                   gboolean remove_to_trash /*ignored*/)
2437 {
2438         TnyFolder *folder;
2439         ModestMailOperationPrivate *priv;
2440         TnyIterator *iter = NULL;
2441         TnyHeader *header = NULL;
2442         TnyList *remove_headers = NULL;
2443         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2444
2445         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2446         g_return_if_fail (TNY_IS_LIST (headers));
2447
2448         if (remove_to_trash)
2449                 g_warning ("remove to trash is not implemented");
2450
2451         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2452
2453         remove_headers = g_object_ref(headers);
2454
2455         /* Get folder from first header and sync it */
2456         iter = tny_list_create_iterator (headers);
2457         header = TNY_HEADER (tny_iterator_get_current (iter));
2458         folder = tny_header_get_folder (header);
2459
2460         /* Don't remove messages that are being sent */
2461         if (modest_tny_folder_is_local_folder (folder)) {
2462                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2463         }
2464         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2465                 TnyTransportAccount *traccount = NULL;
2466                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2467                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2468                 if (traccount) {
2469                         ModestTnySendQueueStatus status;
2470                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2471                         TnyIterator *iter = tny_list_create_iterator(headers);
2472                         g_object_unref(remove_headers);
2473                         remove_headers = TNY_LIST(tny_simple_list_new());
2474                         while (!tny_iterator_is_done(iter)) {
2475                                 char *msg_id;
2476                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2477                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2478                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2479                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2480                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2481                                 }
2482                                 g_object_unref(hdr);
2483                                 g_free(msg_id);
2484                                 tny_iterator_next(iter);
2485                         }
2486                         g_object_unref(iter);
2487                         g_object_unref(traccount);
2488                 }
2489         }
2490
2491         /* Get account and set it into mail_operation */
2492         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2493         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2494         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2495
2496         /* remove message from folder */
2497         modest_mail_operation_notify_start (self);
2498
2499         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2500         if (!priv->error) {
2501                 gboolean expunge, leave_on_server;
2502                 const gchar *account_name;
2503                 const gchar *proto;
2504                 TnyAccount *account;
2505                 ModestTransportStoreProtocol account_proto = MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
2506                 
2507                 account = tny_folder_get_account (folder);
2508                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2509                 leave_on_server =
2510                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2511                                         account_name);
2512
2513                 proto = tny_account_get_proto (account);
2514                 if (proto) {
2515                         account_proto = modest_protocol_info_get_transport_store_protocol (proto);
2516                 }
2517
2518                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2519                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2520                         expunge = TRUE;
2521                 else
2522                         expunge = FALSE;
2523
2524                 /* Sync folder */
2525                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2526                 
2527                 g_object_unref (account);
2528         }
2529         
2530         
2531         /* Set status */
2532         if (!priv->error)
2533                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2534         else
2535                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2536
2537         /* Free */
2538         g_object_unref (remove_headers);
2539         g_object_unref (header);
2540         g_object_unref (iter);
2541         g_object_unref (G_OBJECT (folder));
2542
2543         /* Notify about operation end */
2544         modest_mail_operation_notify_end (self);
2545 }
2546
2547 static void
2548 notify_progress_of_multiple_messages (ModestMailOperation *self,
2549                                       TnyStatus *status,
2550                                       gint *last_total_bytes,
2551                                       gint *sum_total_bytes,
2552                                       gint total_bytes, 
2553                                       gboolean increment_done)
2554 {
2555         ModestMailOperationPrivate *priv;
2556         ModestMailOperationState *state;
2557         gboolean is_num_bytes = FALSE;
2558
2559         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2560
2561         /* We know that tinymail sends us information about
2562          *  transferred bytes with this particular message
2563          *  
2564          *  (FIXME: this is very ugly, and no I (djcb) didn't write this code,
2565          *  I just added the 'if' so we don't get runtime warning)
2566          */
2567         if (status->message)
2568                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2569
2570         state = modest_mail_operation_clone_state (self);
2571         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2572                 /* We know that we're in a different message when the
2573                    total number of bytes to transfer is different. Of
2574                    course it could fail if we're transferring messages
2575                    of the same size, but this is a workarround */
2576                 if (status->of_total != *last_total_bytes) {
2577                         /* We need to increment the done when there is
2578                            no information about each individual
2579                            message, we need to do this in message
2580                            transfers, and we don't do it for getting
2581                            messages */
2582                         if (increment_done)
2583                                 priv->done++;
2584                         *sum_total_bytes += *last_total_bytes;
2585                         *last_total_bytes = status->of_total;
2586                 }
2587                 state->bytes_done += status->position + *sum_total_bytes;
2588                 state->bytes_total = total_bytes;
2589
2590                 /* Notify the status change. Only notify about changes
2591                    referred to bytes */
2592                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2593                                0, state, NULL);
2594         }
2595
2596         g_slice_free (ModestMailOperationState, state);
2597 }
2598
2599 static void
2600 transfer_msgs_status_cb (GObject *obj,
2601                          TnyStatus *status,  
2602                          gpointer user_data)
2603 {
2604         XFerMsgsAsyncHelper *helper;
2605
2606         g_return_if_fail (status != NULL);
2607         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2608
2609         helper = (XFerMsgsAsyncHelper *) user_data;
2610         g_return_if_fail (helper != NULL);       
2611
2612         /* Notify progress */
2613         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2614                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2615 }
2616
2617
2618 static void
2619 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2620 {
2621         XFerMsgsAsyncHelper *helper;
2622         ModestMailOperation *self;
2623         ModestMailOperationPrivate *priv;
2624         TnyIterator *iter = NULL;
2625         TnyHeader *header = NULL;
2626
2627         helper = (XFerMsgsAsyncHelper *) user_data;
2628         self = helper->mail_op;
2629
2630         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2631
2632         if (err) {
2633                 priv->error = g_error_copy (err);
2634                 priv->done = 0;
2635                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2636         } else if (cancelled) {
2637                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2638                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2639                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2640                              _("Error trying to refresh the contents of %s"),
2641                              tny_folder_get_name (folder));
2642         } else {
2643                 priv->done = 1;
2644                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2645
2646                 /* Update folder counts */
2647                 tny_folder_poke_status (folder);                
2648                 tny_folder_poke_status (helper->dest_folder);           
2649         }
2650
2651         
2652         /* Mark headers as deleted and seen */
2653         if ((helper->delete) && 
2654             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2655                 iter = tny_list_create_iterator (helper->headers);
2656                 while (!tny_iterator_is_done (iter)) {
2657                         header = TNY_HEADER (tny_iterator_get_current (iter));
2658                         tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2659                         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2660                         g_object_unref (header);
2661
2662                         tny_iterator_next (iter);
2663                 }
2664         }
2665                 
2666
2667         /* Notify about operation end */
2668         modest_mail_operation_notify_end (self);
2669
2670         /* If user defined callback function was defined, call it */
2671         if (helper->user_callback) {
2672                 /* This is not a GDK lock because we are a Tinymail callback and
2673                  * Tinymail already acquires the Gdk lock */
2674
2675                 /* no gdk_threads_enter (), CHECKED */
2676                 helper->user_callback (self, helper->user_data);
2677                 /* no gdk_threads_leave (), CHECKED */
2678         }
2679
2680         /* Free */
2681         if (helper->headers)
2682                 g_object_unref (helper->headers);
2683         if (helper->dest_folder)
2684                 g_object_unref (helper->dest_folder);
2685         if (helper->mail_op)
2686                 g_object_unref (helper->mail_op);
2687         if (folder)
2688                 g_object_unref (folder);
2689         if (iter)
2690                 g_object_unref (iter);
2691         g_slice_free (XFerMsgsAsyncHelper, helper);
2692 }
2693
2694 static guint
2695 compute_message_list_size (TnyList *headers)
2696 {
2697         TnyIterator *iter;
2698         guint size = 0;
2699
2700         iter = tny_list_create_iterator (headers);
2701         while (!tny_iterator_is_done (iter)) {
2702                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2703                 size += tny_header_get_message_size (header);
2704                 g_object_unref (header);
2705                 tny_iterator_next (iter);
2706         }
2707         g_object_unref (iter);
2708
2709         return size;
2710 }
2711
2712 static guint
2713 compute_message_array_size (GPtrArray *headers)
2714 {
2715         guint size = 0;
2716         gint i;
2717
2718         for (i = 0; i < headers->len; i++) {
2719                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2720                 size += tny_header_get_message_size (header);
2721         }
2722
2723         return size;
2724 }
2725
2726
2727 void
2728 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2729                                  TnyList *headers, 
2730                                  TnyFolder *folder, 
2731                                  gboolean delete_original,
2732                                  XferMsgsAsyncUserCallback user_callback,
2733                                  gpointer user_data)
2734 {
2735         ModestMailOperationPrivate *priv = NULL;
2736         TnyIterator *iter = NULL;
2737         TnyFolder *src_folder = NULL;
2738         XFerMsgsAsyncHelper *helper = NULL;
2739         TnyHeader *header = NULL;
2740         ModestTnyFolderRules rules = 0;
2741
2742         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2743         g_return_if_fail (headers && TNY_IS_LIST (headers));
2744         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2745
2746         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2747         priv->total = tny_list_get_length (headers);
2748         priv->done = 0;
2749         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2750         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2751
2752         /* Apply folder rules */
2753         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2754         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2755                 /* Set status failed and set an error */
2756                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2757                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2758                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2759                              _CS("ckct_ib_unable_to_paste_here"));
2760                 /* Notify the queue */
2761                 modest_mail_operation_notify_end (self);
2762                 return;
2763         }
2764                 
2765         /* Get source folder */
2766         iter = tny_list_create_iterator (headers);
2767         header = TNY_HEADER (tny_iterator_get_current (iter));
2768         if (header) {
2769                 src_folder = tny_header_get_folder (header);
2770                 g_object_unref (header);
2771         }
2772         g_object_unref (iter);
2773
2774         if (src_folder == NULL) {
2775                 /* Notify the queue */
2776                 modest_mail_operation_notify_end (self);
2777
2778                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2779                 return;
2780         }
2781
2782         
2783         /* Check folder source and destination */
2784         if (src_folder == folder) {
2785                 /* Set status failed and set an error */
2786                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2787                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2788                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2789                              _("mail_in_ui_folder_copy_target_error"));
2790                 
2791                 /* Notify the queue */
2792                 modest_mail_operation_notify_end (self);
2793                 
2794                 /* Free */
2795                 g_object_unref (src_folder);            
2796                 return;
2797         }
2798
2799         /* Create the helper */
2800         helper = g_slice_new0 (XFerMsgsAsyncHelper);
2801         helper->mail_op = g_object_ref(self);
2802         helper->dest_folder = g_object_ref(folder);
2803         helper->headers = g_object_ref(headers);
2804         helper->user_callback = user_callback;
2805         helper->user_data = user_data;
2806         helper->delete = delete_original;
2807         helper->last_total_bytes = 0;
2808         helper->sum_total_bytes = 0;
2809         helper->total_bytes = compute_message_list_size (headers);
2810
2811         /* Get account and set it into mail_operation */
2812         priv->account = modest_tny_folder_get_account (src_folder);
2813
2814         /* Transfer messages */
2815         modest_mail_operation_notify_start (self);
2816         tny_folder_transfer_msgs_async (src_folder, 
2817                                         headers, 
2818                                         folder, 
2819                                         delete_original, 
2820                                         transfer_msgs_cb, 
2821                                         transfer_msgs_status_cb,
2822                                         helper);
2823 }
2824
2825
2826 static void
2827 on_refresh_folder (TnyFolder   *folder, 
2828                    gboolean     cancelled, 
2829                    GError     *error,
2830                    gpointer     user_data)
2831 {
2832         RefreshAsyncHelper *helper = NULL;
2833         ModestMailOperation *self = NULL;
2834         ModestMailOperationPrivate *priv = NULL;
2835
2836         helper = (RefreshAsyncHelper *) user_data;
2837         self = helper->mail_op;
2838         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2839
2840         g_return_if_fail(priv!=NULL);
2841
2842         if (error) {
2843                 priv->error = g_error_copy (error);
2844                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2845                 goto out;
2846         }
2847
2848         if (cancelled) {
2849                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2850                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2851                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2852                              _("Error trying to refresh the contents of %s"),
2853                              tny_folder_get_name (folder));
2854                 goto out;
2855         }
2856
2857         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2858  out:
2859
2860         /* Call user defined callback, if it exists */
2861         if (helper->user_callback) {
2862
2863                 /* This is not a GDK lock because we are a Tinymail callback and
2864                  * Tinymail already acquires the Gdk lock */
2865                 helper->user_callback (self, folder, helper->user_data);
2866         }
2867
2868         /* Free */
2869         g_slice_free (RefreshAsyncHelper, helper);
2870
2871         /* Notify about operation end */
2872         modest_mail_operation_notify_end (self);
2873         g_object_unref(self);
2874 }
2875
2876 static void
2877 on_refresh_folder_status_update (GObject *obj,
2878                                  TnyStatus *status,
2879                                  gpointer user_data)
2880 {
2881         RefreshAsyncHelper *helper = NULL;
2882         ModestMailOperation *self = NULL;
2883         ModestMailOperationPrivate *priv = NULL;
2884         ModestMailOperationState *state;
2885
2886         g_return_if_fail (user_data != NULL);
2887         g_return_if_fail (status != NULL);
2888         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2889
2890         helper = (RefreshAsyncHelper *) user_data;
2891         self = helper->mail_op;
2892         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2893
2894         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2895
2896         priv->done = status->position;
2897         priv->total = status->of_total;
2898
2899         state = modest_mail_operation_clone_state (self);
2900
2901         /* This is not a GDK lock because we are a Tinymail callback and
2902          * Tinymail already acquires the Gdk lock */
2903         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2904
2905         g_slice_free (ModestMailOperationState, state);
2906 }
2907
2908 void 
2909 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2910                                        TnyFolder *folder,
2911                                        RefreshAsyncUserCallback user_callback,
2912                                        gpointer user_data)
2913 {
2914         ModestMailOperationPrivate *priv = NULL;
2915         RefreshAsyncHelper *helper = NULL;
2916
2917         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2918
2919         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2920
2921         /* Get account and set it into mail_operation */
2922         priv->account = modest_tny_folder_get_account  (folder);
2923         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2924
2925         /* Create the helper */
2926         helper = g_slice_new0 (RefreshAsyncHelper);
2927         helper->mail_op = g_object_ref(self);
2928         helper->user_callback = user_callback;
2929         helper->user_data = user_data;
2930
2931         /* Refresh the folder. TODO: tinymail could issue a status
2932            updates before the callback call then this could happen. We
2933            must review the design */
2934         modest_mail_operation_notify_start (self);
2935         
2936         /* notify that the operation was started */
2937         ModestMailOperationState *state;
2938         state = modest_mail_operation_clone_state (self);
2939         state->done = 0;
2940         state->total = 0;
2941         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2942                         0, state, NULL);
2943         
2944         tny_folder_refresh_async (folder,
2945                                   on_refresh_folder,
2946                                   on_refresh_folder_status_update,
2947                                   helper);
2948 }
2949
2950
2951 static void
2952 modest_mail_operation_notify_start (ModestMailOperation *self)
2953 {
2954         ModestMailOperationPrivate *priv = NULL;
2955
2956         g_return_if_fail (self);
2957
2958         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2959
2960         /* Ensure that all the fields are filled correctly */
2961         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2962
2963         /* Notify the observers about the mail operation. We do not
2964            wrapp this emission because we assume that this function is
2965            always called from within the main lock */
2966         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2967 }
2968
2969 /**
2970  *
2971  * It's used by the mail operation queue to notify the observers
2972  * attached to that signal that the operation finished. We need to use
2973  * that because tinymail does not give us the progress of a given
2974  * operation when it finishes (it directly calls the operation
2975  * callback).
2976  */
2977 static void
2978 modest_mail_operation_notify_end (ModestMailOperation *self)
2979 {
2980         ModestMailOperationPrivate *priv = NULL;
2981
2982         g_return_if_fail (self);
2983
2984         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2985
2986         /* Notify the observers about the mail operation end. We do
2987            not wrapp this emission because we assume that this
2988            function is always called from within the main lock */
2989         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2990
2991         /* Remove the error user data */
2992         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2993                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2994 }
2995
2996 TnyAccount *
2997 modest_mail_operation_get_account (ModestMailOperation *self)
2998 {
2999         ModestMailOperationPrivate *priv = NULL;
3000
3001         g_return_val_if_fail (self, NULL);
3002
3003         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3004
3005         return (priv->account) ? g_object_ref (priv->account) : NULL;
3006 }
3007
3008 void
3009 modest_mail_operation_noop (ModestMailOperation *self)
3010 {
3011         ModestMailOperationPrivate *priv = NULL;
3012
3013         g_return_if_fail (self);
3014
3015         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3016         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3017         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3018         priv->done = 0;
3019         priv->total = 0;
3020
3021         /* This mail operation does nothing actually */
3022         modest_mail_operation_notify_start (self);
3023         modest_mail_operation_notify_end (self);
3024 }
3025
3026
3027 gchar*
3028 modest_mail_operation_to_string (ModestMailOperation *self)
3029 {
3030         const gchar *type, *status, *account_id;
3031         ModestMailOperationPrivate *priv = NULL;
3032         
3033         g_return_val_if_fail (self, NULL);
3034
3035         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3036
3037         /* new operations don't have anything interesting */
3038         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3039                 return g_strdup_printf ("%p <new operation>", self);
3040         
3041         switch (priv->op_type) {
3042         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3043         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3044         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3045         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3046         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3047         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3048         default: type = "UNEXPECTED"; break;
3049         }
3050
3051         switch (priv->status) {
3052         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3053         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3054         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3055         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3056         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3057         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3058         default:                                                status= "UNEXPECTED"; break;
3059         } 
3060
3061         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3062
3063         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3064                                 priv->done, priv->total,
3065                                 priv->error && priv->error->message ? priv->error->message : "");
3066 }