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