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