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