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