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