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