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