Work to fix NB#82451.
[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 if (info->poke_all) {
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                         TnyList *folders = tny_simple_list_new ();
1455
1456                         /* Add to the list of all folders */
1457                         tny_list_append (info->folders, (GObject *) folder);
1458                         
1459                         /* Add pending call */
1460                         info->pending_calls++;
1461                         
1462                         tny_folder_store_get_folders_async (folder, folders, NULL,
1463                                                             recurse_folders_async_cb, 
1464                                                             NULL, info);
1465                         
1466                         g_object_unref (G_OBJECT (folder));
1467                         
1468                         tny_iterator_next (iter);           
1469                 }
1470                 g_object_unref (G_OBJECT (iter));
1471                 g_object_unref (G_OBJECT (list));
1472         }
1473
1474         /* Remove my own pending call */
1475         info->pending_calls--;
1476
1477         /* This means that we have all the folders */
1478         if (info->pending_calls == 0) {
1479                 TnyIterator *iter_all_folders;
1480                 TnyFolder *inbox = NULL;
1481
1482                 /* If there was any error do not continue */
1483                 if (priv->error) {
1484                         update_account_notify_user_and_free (info, NULL);
1485                         return;
1486                 }
1487
1488                 iter_all_folders = tny_list_create_iterator (info->folders);
1489
1490                 /* Do a poke status over all folders */
1491                 while (!tny_iterator_is_done (iter_all_folders) &&
1492                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1493                         TnyFolder *folder = NULL;
1494
1495                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1496
1497                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1498                                 /* Get a reference to the INBOX */
1499                                 inbox = g_object_ref (folder);
1500                         } else {
1501                                 /* Issue a poke status over the folder */
1502                                 if (info->poke_all)
1503                                         tny_folder_poke_status (folder);
1504                         }
1505
1506                         /* Free and go to next */
1507                         g_object_unref (folder);
1508                         tny_iterator_next (iter_all_folders);
1509                 }
1510                 g_object_unref (iter_all_folders);
1511
1512                 /* Refresh the INBOX */
1513                 if (inbox) {
1514                         /* Refresh the folder. Our observer receives
1515                          * the new emails during folder refreshes, so
1516                          * we can use observer->new_headers
1517                          */
1518                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1519                         tny_folder_add_observer (inbox, info->inbox_observer);
1520
1521                         /* Refresh the INBOX */
1522                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1523                         g_object_unref (inbox);
1524                 } else {
1525                         /* We could not perform the inbox refresh but
1526                            we'll try to send mails anyway */
1527                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1528                 }
1529         }
1530 }
1531
1532 void
1533 modest_mail_operation_update_account (ModestMailOperation *self,
1534                                       const gchar *account_name,
1535                                       gboolean poke_all,
1536                                       gboolean interactive,
1537                                       RetrieveAllCallback retrieve_all_cb,
1538                                       UpdateAccountCallback callback,
1539                                       gpointer user_data)
1540 {
1541         UpdateAccountInfo *info = NULL;
1542         ModestMailOperationPrivate *priv = NULL;
1543         ModestTnyAccountStore *account_store = NULL;
1544         TnyList *folders;
1545         ModestMailOperationState *state;
1546
1547         /* Init mail operation */
1548         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1549         priv->total = 0;
1550         priv->done  = 0;
1551         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1552         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
1553
1554         /* Get the store account */
1555         account_store = modest_runtime_get_account_store ();
1556         priv->account =
1557                 modest_tny_account_store_get_server_account (account_store,
1558                                                              account_name,
1559                                                              TNY_ACCOUNT_TYPE_STORE);
1560
1561         /* The above function could return NULL */
1562         if (!priv->account) {
1563                 /* Check if the operation was a success */
1564                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1565                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1566                              "no account");
1567                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1568
1569                 /* Call the user callback */
1570                 if (callback)
1571                         callback (self, NULL, user_data);
1572
1573                 /* Notify about operation end */
1574                 modest_mail_operation_notify_end (self);
1575
1576                 return;
1577         }
1578
1579         /* Create the helper object */
1580         info = g_slice_new0 (UpdateAccountInfo);
1581         info->pending_calls = 1;
1582         info->folders = tny_simple_list_new ();
1583         info->mail_op = g_object_ref (self);
1584         info->poke_all = poke_all;
1585         info->interactive = interactive;
1586         info->account_name = g_strdup (account_name);
1587         info->callback = callback;
1588         info->user_data = user_data;
1589         info->retrieve_all_cb = retrieve_all_cb;
1590
1591         /* Set account busy */
1592         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1593         modest_mail_operation_notify_start (self);
1594
1595         /* notify about the start of the operation */ 
1596         state = modest_mail_operation_clone_state (self);
1597         state->done = 0;
1598         state->total = 0;
1599
1600         /* Start notifying progress */
1601         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1602         g_slice_free (ModestMailOperationState, state);
1603         
1604         /* Get all folders and continue in the callback */ 
1605         folders = tny_simple_list_new ();
1606         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
1607                                             folders, NULL,
1608                                             recurse_folders_async_cb, 
1609                                             NULL, info);
1610 }
1611
1612 /*
1613  * Used to notify the queue from the main
1614  * loop. We call it inside an idle call to achieve that
1615  */
1616 static gboolean
1617 idle_notify_queue (gpointer data)
1618 {
1619         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1620
1621         gdk_threads_enter ();
1622         modest_mail_operation_notify_end (mail_op);
1623         gdk_threads_leave ();
1624         g_object_unref (mail_op);
1625
1626         return FALSE;
1627 }
1628
1629 static int
1630 compare_headers_by_date (gconstpointer a,
1631                          gconstpointer b)
1632 {
1633         TnyHeader **header1, **header2;
1634         time_t sent1, sent2;
1635
1636         header1 = (TnyHeader **) a;
1637         header2 = (TnyHeader **) b;
1638
1639         sent1 = tny_header_get_date_sent (*header1);
1640         sent2 = tny_header_get_date_sent (*header2);
1641
1642         /* We want the most recent ones (greater time_t) at the
1643            beginning */
1644         if (sent1 < sent2)
1645                 return 1;
1646         else
1647                 return -1;
1648 }
1649
1650
1651 /* ******************************************************************* */
1652 /* ************************** STORE  ACTIONS ************************* */
1653 /* ******************************************************************* */
1654
1655 typedef struct {
1656         ModestMailOperation *mail_op;
1657         CreateFolderUserCallback callback;
1658         gpointer user_data;
1659 } CreateFolderInfo;
1660
1661
1662 static void
1663 create_folder_cb (TnyFolderStore *parent_folder, 
1664                   gboolean canceled, 
1665                   TnyFolder *new_folder, 
1666                   GError *err, 
1667                   gpointer user_data)
1668 {
1669         ModestMailOperationPrivate *priv;
1670         CreateFolderInfo *info;
1671
1672         info = (CreateFolderInfo *) user_data;
1673         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1674
1675         if (canceled || err) {
1676                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1677                 if (err)
1678                         priv->error = g_error_copy (err);
1679                 else
1680                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1681                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1682                                      "canceled");               
1683         } else {
1684                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1685         }
1686
1687         /* The user will unref the new_folder */
1688         if (info->callback)
1689                 info->callback (info->mail_op, parent_folder, 
1690                                 new_folder, info->user_data);
1691         
1692         /* Notify about operation end */
1693         modest_mail_operation_notify_end (info->mail_op);
1694
1695         /* Frees */
1696         g_object_unref (info->mail_op);
1697         g_slice_free (CreateFolderInfo, info);
1698 }
1699
1700 void
1701 modest_mail_operation_create_folder (ModestMailOperation *self,
1702                                      TnyFolderStore *parent,
1703                                      const gchar *name,
1704                                      CreateFolderUserCallback callback,
1705                                      gpointer user_data)
1706 {
1707         ModestMailOperationPrivate *priv;
1708
1709         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1710         g_return_if_fail (name);
1711         
1712         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1713         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1714         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1715                 g_object_ref (parent) : 
1716                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1717
1718         /* Check for already existing folder */
1719         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1720                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1721                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1722                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1723                              _CS("ckdg_ib_folder_already_exists"));
1724         }
1725
1726         /* Check parent */
1727         if (TNY_IS_FOLDER (parent)) {
1728                 /* Check folder rules */
1729                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1730                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1731                         /* Set status failed and set an error */
1732                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1733                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1734                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1735                                      _("mail_in_ui_folder_create_error"));
1736                 }
1737         }
1738
1739         if (!strcmp (name, " ") || strchr (name, '/')) {
1740                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1741                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1742                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1743                              _("mail_in_ui_folder_create_error"));
1744         }
1745
1746         if (!priv->error) {
1747                 CreateFolderInfo *info;
1748
1749                 info = g_slice_new0 (CreateFolderInfo);
1750                 info->mail_op = g_object_ref (self);
1751                 info->callback = callback;
1752                 info->user_data = user_data;
1753
1754                 modest_mail_operation_notify_start (self);
1755
1756                 /* Create the folder */
1757                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
1758                                                       NULL, info);
1759         } else {
1760                 /* Call the user callback anyway */
1761                 if (callback)
1762                         callback (self, parent, NULL, user_data);
1763                 /* Notify about operation end */
1764                 modest_mail_operation_notify_end (self);
1765         }
1766 }
1767
1768 void
1769 modest_mail_operation_remove_folder (ModestMailOperation *self,
1770                                      TnyFolder           *folder,
1771                                      gboolean             remove_to_trash)
1772 {
1773         ModestMailOperationPrivate *priv;
1774         ModestTnyFolderRules rules;
1775
1776         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1777         g_return_if_fail (TNY_IS_FOLDER (folder));
1778         
1779         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1780         
1781         /* Check folder rules */
1782         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1783         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1784                 /* Set status failed and set an error */
1785                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1786                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1787                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1788                              _("mail_in_ui_folder_delete_error"));
1789                 goto end;
1790         }
1791
1792         /* Get the account */
1793         priv->account = modest_tny_folder_get_account (folder);
1794         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1795
1796         /* Delete folder or move to trash */
1797         if (remove_to_trash) {
1798                 TnyFolder *trash_folder = NULL;
1799                 trash_folder = modest_tny_account_get_special_folder (priv->account,
1800                                                                       TNY_FOLDER_TYPE_TRASH);
1801                 /* TODO: error_handling */
1802                 if (trash_folder) {
1803                         modest_mail_operation_notify_start (self);
1804                         modest_mail_operation_xfer_folder (self, folder,
1805                                                     TNY_FOLDER_STORE (trash_folder), 
1806                                                     TRUE, NULL, NULL);
1807                         g_object_unref (trash_folder);
1808                 } else {
1809                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
1810                 }
1811         } else {
1812                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1813                 if (parent) {
1814                         modest_mail_operation_notify_start (self);
1815                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
1816                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1817                         
1818                         if (!priv->error)
1819                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1820
1821                         g_object_unref (parent);
1822                 } else
1823                         g_warning ("%s: could not get parent folder", __FUNCTION__);
1824         }
1825
1826  end:
1827         /* Notify about operation end */
1828         modest_mail_operation_notify_end (self);
1829 }
1830
1831 static void
1832 transfer_folder_status_cb (GObject *obj,
1833                            TnyStatus *status,
1834                            gpointer user_data)
1835 {
1836         ModestMailOperation *self;
1837         ModestMailOperationPrivate *priv;
1838         ModestMailOperationState *state;
1839         XFerFolderAsyncHelper *helper;
1840
1841         g_return_if_fail (status != NULL);
1842         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1843
1844         helper = (XFerFolderAsyncHelper *) user_data;
1845         g_return_if_fail (helper != NULL);
1846
1847         self = helper->mail_op;
1848         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1849
1850         priv->done = status->position;
1851         priv->total = status->of_total;
1852
1853         state = modest_mail_operation_clone_state (self);
1854
1855         /* This is not a GDK lock because we are a Tinymail callback
1856          * which is already GDK locked by Tinymail */
1857
1858         /* no gdk_threads_enter (), CHECKED */
1859
1860         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1861
1862         /* no gdk_threads_leave (), CHECKED */
1863
1864         g_slice_free (ModestMailOperationState, state);
1865 }
1866
1867
1868 static void
1869 transfer_folder_cb (TnyFolder *folder, 
1870                     gboolean cancelled, 
1871                     TnyFolderStore *into, 
1872                     TnyFolder *new_folder, 
1873                     GError *err, 
1874                     gpointer user_data)
1875 {
1876         XFerFolderAsyncHelper *helper;
1877         ModestMailOperation *self = NULL;
1878         ModestMailOperationPrivate *priv = NULL;
1879
1880         helper = (XFerFolderAsyncHelper *) user_data;
1881         g_return_if_fail (helper != NULL);       
1882
1883         self = helper->mail_op;
1884         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1885
1886         if (err) {
1887                 priv->error = g_error_copy (err);
1888                 priv->done = 0;
1889                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1890         } else if (cancelled) {
1891                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1892                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1893                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1894                              _("Transference of %s was cancelled."),
1895                              tny_folder_get_name (folder));
1896         } else {
1897                 priv->done = 1;
1898                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1899         }
1900                 
1901         /* Notify about operation end */
1902         modest_mail_operation_notify_end (self);
1903
1904         /* If user defined callback function was defined, call it */
1905         if (helper->user_callback) {
1906
1907                 /* This is not a GDK lock because we are a Tinymail callback
1908                  * which is already GDK locked by Tinymail */
1909
1910                 /* no gdk_threads_enter (), CHECKED */
1911                 helper->user_callback (self, new_folder, helper->user_data);
1912                 /* no gdk_threads_leave () , CHECKED */
1913         }
1914
1915         /* Free */
1916         g_object_unref (helper->mail_op);
1917         g_slice_free   (XFerFolderAsyncHelper, helper);
1918 }
1919
1920 /**
1921  *
1922  * This function checks if the new name is a valid name for our local
1923  * folders account. The new name could not be the same than then name
1924  * of any of the mandatory local folders
1925  *
1926  * We can not rely on tinymail because tinymail does not check the
1927  * name of the virtual folders that the account could have in the case
1928  * that we're doing a rename (because it directly calls Camel which
1929  * knows nothing about our virtual folders). 
1930  *
1931  * In the case of an actual copy/move (i.e. move/copy a folder between
1932  * accounts) tinymail uses the tny_folder_store_create_account which
1933  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1934  * checks the new name of the folder, so this call in that case
1935  * wouldn't be needed. *But* NOTE that if tinymail changes its
1936  * implementation (if folder transfers within the same account is no
1937  * longer implemented as a rename) this call will allow Modest to work
1938  * perfectly
1939  *
1940  * If the new name is not valid, this function will set the status to
1941  * failed and will set also an error in the mail operation
1942  */
1943 static gboolean
1944 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1945                                  TnyFolderStore *into,
1946                                  const gchar *new_name)
1947 {
1948         if (TNY_IS_ACCOUNT (into) && 
1949             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1950             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1951                                                                  new_name)) {
1952                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1953                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1954                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1955                              _CS("ckdg_ib_folder_already_exists"));
1956                 return FALSE;
1957         } else
1958                 return TRUE;
1959 }
1960
1961 void
1962 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1963                                    TnyFolder *folder,
1964                                    TnyFolderStore *parent,
1965                                    gboolean delete_original,
1966                                    XferFolderAsyncUserCallback user_callback,
1967                                    gpointer user_data)
1968 {
1969         ModestMailOperationPrivate *priv = NULL;
1970         ModestTnyFolderRules parent_rules = 0, rules; 
1971         XFerFolderAsyncHelper *helper = NULL;
1972         const gchar *folder_name = NULL;
1973         const gchar *error_msg;
1974
1975         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1976         g_return_if_fail (TNY_IS_FOLDER (folder));
1977         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1978
1979         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1980         folder_name = tny_folder_get_name (folder);
1981
1982         /* Set the error msg */
1983         error_msg = _("mail_in_ui_folder_move_target_error");
1984
1985         /* Get account and set it into mail_operation */
1986         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1987         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1988         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1989
1990         /* Get folder rules */
1991         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1992         if (TNY_IS_FOLDER (parent))
1993                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1994         
1995         /* Apply operation constraints */
1996         if ((gpointer) parent == (gpointer) folder ||
1997             (!TNY_IS_FOLDER_STORE (parent)) || 
1998             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1999                 /* Folder rules */
2000                 goto error;
2001         } else if (TNY_IS_FOLDER (parent) && 
2002                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2003                 /* Folder rules */
2004                 goto error;
2005
2006         } else if (TNY_IS_FOLDER (parent) &&
2007                    TNY_IS_FOLDER_STORE (folder) &&
2008                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2009                                                   TNY_FOLDER_STORE (folder))) {
2010                 /* Do not move a parent into a child */
2011                 goto error;
2012         } else if (TNY_IS_FOLDER_STORE (parent) &&
2013                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2014                 /* Check that the new folder name is not used by any
2015                    parent subfolder */
2016                 goto error;     
2017         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2018                 /* Check that the new folder name is not used by any
2019                    special local folder */
2020                 goto error;
2021         } else {
2022                 /* Create the helper */
2023                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2024                 helper->mail_op = g_object_ref (self);
2025                 helper->user_callback = user_callback;
2026                 helper->user_data = user_data;
2027                 
2028                 /* Move/Copy folder */
2029                 modest_mail_operation_notify_start (self);
2030                 tny_folder_copy_async (folder,
2031                                        parent,
2032                                        tny_folder_get_name (folder),
2033                                        delete_original,
2034                                        transfer_folder_cb,
2035                                        transfer_folder_status_cb,
2036                                        helper);
2037                 return;
2038         }
2039
2040  error:
2041         /* Set status failed and set an error */
2042         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2043         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2044                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2045                      error_msg);
2046
2047         /* Call the user callback if exists */
2048         if (user_callback)
2049                 user_callback (self, NULL, user_data);
2050
2051         /* Notify the queue */
2052         modest_mail_operation_notify_end (self);
2053 }
2054
2055 void
2056 modest_mail_operation_rename_folder (ModestMailOperation *self,
2057                                      TnyFolder *folder,
2058                                      const gchar *name,
2059                                      XferFolderAsyncUserCallback user_callback,
2060                                      gpointer user_data)
2061 {
2062         ModestMailOperationPrivate *priv;
2063         ModestTnyFolderRules rules;
2064         XFerFolderAsyncHelper *helper;
2065
2066         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2067         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2068         g_return_if_fail (name);
2069         
2070         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2071
2072         /* Get account and set it into mail_operation */
2073         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2074         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2075
2076         /* Check folder rules */
2077         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2078         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2079                 goto error;
2080         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2081                 goto error;
2082         } else {
2083                 TnyFolderStore *into;
2084
2085                 into = tny_folder_get_folder_store (folder);    
2086
2087                 /* Check that the new folder name is not used by any
2088                    special local folder */
2089                 if (new_name_valid_if_local_account (priv, into, name)) {
2090                         /* Create the helper */
2091                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2092                         helper->mail_op = g_object_ref(self);
2093                         helper->user_callback = user_callback;
2094                         helper->user_data = user_data;
2095                 
2096                         /* Rename. Camel handles folder subscription/unsubscription */
2097                         modest_mail_operation_notify_start (self);
2098                         tny_folder_copy_async (folder, into, name, TRUE,
2099                                                transfer_folder_cb,
2100                                                transfer_folder_status_cb,
2101                                                helper);
2102                 } else {
2103                         goto error;
2104                 }
2105                 g_object_unref (into);
2106
2107                 return;
2108         }
2109  error:
2110         /* Set status failed and set an error */
2111         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2112         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2113                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2114                      _("FIXME: unable to rename"));
2115         
2116         if (user_callback)
2117                 user_callback (self, NULL, user_data);
2118
2119         /* Notify about operation end */
2120         modest_mail_operation_notify_end (self);
2121 }
2122
2123 /* ******************************************************************* */
2124 /* **************************  MSG  ACTIONS  ************************* */
2125 /* ******************************************************************* */
2126
2127 void 
2128 modest_mail_operation_get_msg (ModestMailOperation *self,
2129                                TnyHeader *header,
2130                                GetMsgAsyncUserCallback user_callback,
2131                                gpointer user_data)
2132 {
2133         GetMsgInfo *helper = NULL;
2134         TnyFolder *folder;
2135         ModestMailOperationPrivate *priv;
2136         
2137         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2138         g_return_if_fail (TNY_IS_HEADER (header));
2139         
2140         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2141         folder = tny_header_get_folder (header);
2142
2143         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2144         priv->total = 1;
2145         priv->done = 0;
2146
2147         /* Get account and set it into mail_operation */
2148         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2149         
2150         /* Check for cached messages */
2151         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2152                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2153         else 
2154                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2155         
2156         /* Create the helper */
2157         helper = g_slice_new0 (GetMsgInfo);
2158         helper->header = g_object_ref (header);
2159         helper->mail_op = g_object_ref (self);
2160         helper->user_callback = user_callback;
2161         helper->user_data = user_data;
2162         helper->destroy_notify = NULL;
2163         helper->last_total_bytes = 0;
2164         helper->sum_total_bytes = 0;
2165         helper->total_bytes = tny_header_get_message_size (header);
2166
2167         modest_mail_operation_notify_start (self);
2168         
2169         /* notify about the start of the operation */ 
2170         ModestMailOperationState *state;
2171         state = modest_mail_operation_clone_state (self);
2172         state->done = 0;
2173         state->total = 0;
2174         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2175                                 0, state, NULL);
2176         
2177         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2178
2179         g_object_unref (G_OBJECT (folder));
2180 }
2181
2182 static void     
2183 get_msg_status_cb (GObject *obj,
2184                    TnyStatus *status,  
2185                    gpointer user_data)
2186 {
2187         GetMsgInfo *helper = NULL;
2188
2189         g_return_if_fail (status != NULL);
2190         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2191
2192         helper = (GetMsgInfo *) user_data;
2193         g_return_if_fail (helper != NULL);       
2194
2195         /* Notify progress */
2196         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2197                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2198 }
2199
2200 static void
2201 get_msg_async_cb (TnyFolder *folder, 
2202                   gboolean canceled, 
2203                   TnyMsg *msg, 
2204                   GError *err, 
2205                   gpointer user_data)
2206 {
2207         GetMsgInfo *info = NULL;
2208         ModestMailOperationPrivate *priv = NULL;
2209         gboolean finished;
2210
2211         info = (GetMsgInfo *) user_data;
2212
2213         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2214         priv->done++;
2215
2216         if (info->more_msgs) {
2217                 tny_iterator_next (info->more_msgs);
2218                 finished = (tny_iterator_is_done (info->more_msgs));
2219         } else {
2220                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2221         }
2222
2223         /* If canceled by the user, ignore the error given by Tinymail */
2224         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED || canceled) {
2225                 canceled = TRUE;
2226                 finished = TRUE;
2227         } else if (err) {
2228                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2229                 if (err) {
2230                         priv->error = g_error_copy ((const GError *) err);
2231                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2232                 }
2233                 if (!priv->error) {
2234                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2235                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2236                                      err->message);
2237                 }
2238         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2239                 /* Set the success status before calling the user callback */
2240                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2241         }
2242
2243
2244         /* Call the user callback */
2245         if (info->user_callback)
2246                 info->user_callback (info->mail_op, info->header, canceled, 
2247                                      msg, err, info->user_data);
2248
2249         /* Notify about operation end if this is the last callback */
2250         if (finished) {
2251                 /* Free user data */
2252                 if (info->destroy_notify)
2253                         info->destroy_notify (info->user_data);
2254
2255                 /* Notify about operation end */
2256                 modest_mail_operation_notify_end (info->mail_op);
2257
2258                 /* Clean */
2259                 if (info->more_msgs)
2260                         g_object_unref (info->more_msgs);
2261                 g_object_unref (info->header);
2262                 g_object_unref (info->mail_op);
2263                 g_slice_free (GetMsgInfo, info);
2264         } else if (info->more_msgs) {
2265                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
2266                 TnyFolder *folder = tny_header_get_folder (header);
2267
2268                 g_object_unref (info->header);
2269                 info->header = g_object_ref (header);
2270
2271                 /* Retrieve the next message */
2272                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
2273
2274                 g_object_unref (header);
2275                 g_object_unref (folder);
2276         } else {
2277                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
2278         }
2279 }
2280
2281 void 
2282 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2283                                      TnyList *header_list, 
2284                                      GetMsgAsyncUserCallback user_callback,
2285                                      gpointer user_data,
2286                                      GDestroyNotify notify)
2287 {
2288         ModestMailOperationPrivate *priv = NULL;
2289         gint msg_list_size;
2290         TnyIterator *iter = NULL;
2291         gboolean has_uncached_messages;
2292         
2293         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2294
2295         /* Init mail operation */
2296         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2297         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2298         priv->done = 0;
2299         priv->total = tny_list_get_length(header_list);
2300
2301         /* Check uncached messages */
2302         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
2303              !has_uncached_messages && !tny_iterator_is_done (iter); 
2304              tny_iterator_next (iter)) {
2305                 TnyHeader *header;
2306
2307                 header = (TnyHeader *) tny_iterator_get_current (iter);
2308                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
2309                         has_uncached_messages = TRUE;
2310                 g_object_unref (header);
2311         }       
2312         g_object_unref (iter);
2313         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
2314
2315         /* Get account and set it into mail_operation */
2316         if (tny_list_get_length (header_list) >= 1) {
2317                 TnyIterator *iterator = tny_list_create_iterator (header_list);
2318                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
2319                 if (header) {
2320                         TnyFolder *folder = tny_header_get_folder (header);
2321                         if (folder) {           
2322                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2323                                 g_object_unref (folder);
2324                         }
2325                         g_object_unref (header);
2326                 }
2327                 g_object_unref (iterator);
2328         }
2329
2330         msg_list_size = compute_message_list_size (header_list);
2331
2332         modest_mail_operation_notify_start (self);
2333         iter = tny_list_create_iterator (header_list);
2334         if (!tny_iterator_is_done (iter)) {
2335                 /* notify about the start of the operation */
2336                 ModestMailOperationState *state;
2337                 state = modest_mail_operation_clone_state (self);
2338                 state->done = 0;
2339                 state->total = 0;
2340                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2341                                0, state, NULL);
2342
2343                 GetMsgInfo *msg_info = NULL;
2344                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2345                 TnyFolder *folder = tny_header_get_folder (header);
2346
2347                 /* Create the message info */
2348                 msg_info = g_slice_new0 (GetMsgInfo);
2349                 msg_info->mail_op = g_object_ref (self);
2350                 msg_info->header = g_object_ref (header);
2351                 msg_info->more_msgs = g_object_ref (iter);
2352                 msg_info->user_callback = user_callback;
2353                 msg_info->user_data = user_data;
2354                 msg_info->destroy_notify = notify;
2355                 msg_info->last_total_bytes = 0;
2356                 msg_info->sum_total_bytes = 0;
2357                 msg_info->total_bytes = msg_list_size;
2358
2359                 /* The callback will call it per each header */
2360                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2361
2362                 /* Free and go on */
2363                 g_object_unref (header);
2364                 g_object_unref (folder);
2365                 g_slice_free (ModestMailOperationState, state);
2366         }
2367         g_object_unref (iter);
2368 }
2369
2370
2371 void 
2372 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2373                                   TnyHeader *header,
2374                                   gboolean remove_to_trash /*ignored*/)
2375 {
2376         TnyFolder *folder;
2377         ModestMailOperationPrivate *priv;
2378
2379         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2380         g_return_if_fail (TNY_IS_HEADER (header));
2381
2382         if (remove_to_trash)
2383                 g_warning ("remove to trash is not implemented");
2384
2385         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2386         folder = tny_header_get_folder (header);
2387
2388         /* Get account and set it into mail_operation */
2389         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2390         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2391         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2392
2393         /* remove message from folder */
2394         tny_folder_remove_msg (folder, header, &(priv->error));
2395         if (!priv->error) {
2396                 gboolean expunge, leave_on_server;
2397                 const gchar *account_name;
2398                 TnyAccount *account;
2399                 ModestTransportStoreProtocol account_proto;
2400
2401                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2402                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2403
2404                 modest_mail_operation_notify_start (self);
2405
2406                 /* Get leave on server setting */
2407                 account = tny_folder_get_account (folder);
2408                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2409                 leave_on_server =
2410                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2411                                                                 account_name);
2412
2413                 account_proto = modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (account));
2414
2415                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2416                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2417                         expunge = TRUE;
2418                 else
2419                         expunge = FALSE;
2420
2421                 /* Sync folder */
2422                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2423
2424                 /* Unref */
2425                 g_object_unref (account);
2426         }
2427         
2428         
2429         /* Set status */
2430         if (!priv->error)
2431                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2432         else
2433                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2434
2435         /* Free */
2436         g_object_unref (G_OBJECT (folder));
2437
2438         /* Notify about operation end */
2439         modest_mail_operation_notify_end (self);
2440 }
2441
2442 void 
2443 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2444                                    TnyList *headers,
2445                                   gboolean remove_to_trash /*ignored*/)
2446 {
2447         TnyFolder *folder = NULL;
2448         ModestMailOperationPrivate *priv;
2449         TnyIterator *iter = NULL;
2450         TnyHeader *header = NULL;
2451         TnyList *remove_headers = NULL;
2452         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2453
2454         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2455         g_return_if_fail (TNY_IS_LIST (headers));
2456
2457         if (remove_to_trash)
2458                 g_warning ("remove to trash is not implemented");
2459
2460         if (tny_list_get_length(headers) == 0) {
2461                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
2462                 goto cleanup; /* nothing to do */
2463         }
2464         
2465         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2466         remove_headers = g_object_ref(headers);
2467
2468         /* Get folder from first header and sync it */
2469         iter = tny_list_create_iterator (headers);      
2470         header = TNY_HEADER (tny_iterator_get_current (iter));
2471
2472         folder = tny_header_get_folder (header);        
2473         if (!TNY_IS_FOLDER(folder)) {
2474                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
2475                 goto cleanup;
2476         }
2477
2478         /* Don't remove messages that are being sent */
2479         if (modest_tny_folder_is_local_folder (folder)) {
2480                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2481         }
2482         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2483                 TnyTransportAccount *traccount = NULL;
2484                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2485                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2486                 if (traccount) {
2487                         ModestTnySendQueueStatus status;
2488                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2489                         TnyIterator *iter = tny_list_create_iterator(headers);
2490                         g_object_unref(remove_headers);
2491                         remove_headers = TNY_LIST(tny_simple_list_new());
2492                         while (!tny_iterator_is_done(iter)) {
2493                                 char *msg_id;
2494                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2495                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2496                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2497                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2498                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2499                                 }
2500                                 g_object_unref(hdr);
2501                                 g_free(msg_id);
2502                                 tny_iterator_next(iter);
2503                         }
2504                         g_object_unref(iter);
2505                         g_object_unref(traccount);
2506                 }
2507         }
2508
2509         /* Get account and set it into mail_operation */
2510         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2511         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2512         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2513
2514         /* remove message from folder */
2515         modest_mail_operation_notify_start (self);
2516
2517         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2518         if (!priv->error) {
2519                 gboolean expunge, leave_on_server;
2520                 const gchar *account_name;
2521                 const gchar *proto;
2522                 TnyAccount *account;
2523                 ModestTransportStoreProtocol account_proto = MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN;
2524                 
2525                 account = tny_folder_get_account (folder);
2526                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2527                 leave_on_server =
2528                         modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2529                                                                 account_name);
2530                 
2531                 proto = tny_account_get_proto (account);
2532                 if (proto) {
2533                         account_proto = modest_protocol_info_get_transport_store_protocol (proto);
2534                 }
2535                 
2536                 if (((account_proto == MODEST_PROTOCOL_STORE_POP) && !leave_on_server) ||
2537                     modest_tny_folder_is_remote_folder (folder) == FALSE)
2538                         expunge = TRUE;
2539                 else
2540                         expunge = FALSE;
2541
2542                 /* Sync folder */
2543                 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2544                 
2545                 g_object_unref (account);
2546         }
2547         
2548         
2549         /* Set status */
2550         if (!priv->error)
2551                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2552         else
2553                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2554
2555         /* Free */
2556 cleanup:
2557         if (remove_headers)
2558                 g_object_unref (remove_headers);
2559         if (header)
2560                 g_object_unref (header);
2561         if (iter)
2562                 g_object_unref (iter);
2563         if (folder)
2564                 g_object_unref (folder);
2565
2566         /* Notify about operation end */
2567         modest_mail_operation_notify_end (self);
2568 }
2569
2570 static void
2571 notify_progress_of_multiple_messages (ModestMailOperation *self,
2572                                       TnyStatus *status,
2573                                       gint *last_total_bytes,
2574                                       gint *sum_total_bytes,
2575                                       gint total_bytes, 
2576                                       gboolean increment_done)
2577 {
2578         ModestMailOperationPrivate *priv;
2579         ModestMailOperationState *state;
2580         gboolean is_num_bytes = FALSE;
2581
2582         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2583
2584         /* We know that tinymail sends us information about
2585          *  transferred bytes with this particular message
2586          *  
2587          *  (FIXME: this is very ugly, and no I (djcb) didn't write this code,
2588          *  I just added the 'if' so we don't get runtime warning)
2589          */
2590         if (status->message)
2591                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2592
2593         state = modest_mail_operation_clone_state (self);
2594         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2595                 /* We know that we're in a different message when the
2596                    total number of bytes to transfer is different. Of
2597                    course it could fail if we're transferring messages
2598                    of the same size, but this is a workarround */
2599                 if (status->of_total != *last_total_bytes) {
2600                         /* We need to increment the done when there is
2601                            no information about each individual
2602                            message, we need to do this in message
2603                            transfers, and we don't do it for getting
2604                            messages */
2605                         if (increment_done)
2606                                 priv->done++;
2607                         *sum_total_bytes += *last_total_bytes;
2608                         *last_total_bytes = status->of_total;
2609                 }
2610                 state->bytes_done += status->position + *sum_total_bytes;
2611                 state->bytes_total = total_bytes;
2612
2613                 /* Notify the status change. Only notify about changes
2614                    referred to bytes */
2615                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2616                                0, state, NULL);
2617         }
2618
2619         g_slice_free (ModestMailOperationState, state);
2620 }
2621
2622 static void
2623 transfer_msgs_status_cb (GObject *obj,
2624                          TnyStatus *status,  
2625                          gpointer user_data)
2626 {
2627         XFerMsgsAsyncHelper *helper;
2628
2629         g_return_if_fail (status != NULL);
2630         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2631
2632         helper = (XFerMsgsAsyncHelper *) user_data;
2633         g_return_if_fail (helper != NULL);       
2634
2635         /* Notify progress */
2636         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2637                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2638 }
2639
2640
2641 static void
2642 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2643 {
2644         XFerMsgsAsyncHelper *helper;
2645         ModestMailOperation *self;
2646         ModestMailOperationPrivate *priv;
2647         gboolean finished = TRUE;
2648
2649         helper = (XFerMsgsAsyncHelper *) user_data;
2650         self = helper->mail_op;
2651
2652         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2653
2654         if (err) {
2655                 priv->error = g_error_copy (err);
2656                 priv->done = 0;
2657                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2658         } else if (cancelled) {
2659                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2660                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2661                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2662                              _("Error trying to refresh the contents of %s"),
2663                              tny_folder_get_name (folder));
2664         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
2665                 if (helper->more_msgs) {
2666                         /* We'll transfer the next message in the list */
2667                         tny_iterator_next (helper->more_msgs);
2668                         if (!tny_iterator_is_done (helper->more_msgs)) {
2669                                 GObject *next_header;
2670                                 g_object_unref (helper->headers);
2671                                 helper->headers = tny_simple_list_new ();
2672                                 next_header = tny_iterator_get_current (helper->more_msgs);
2673                                 tny_list_append (helper->headers, next_header);
2674                                 g_object_unref (next_header);
2675                                 finished = FALSE;
2676                         }
2677                 }
2678
2679                 if (finished) {
2680                         priv->done = 1;
2681                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2682                 }
2683         }
2684
2685         if (finished) {
2686
2687                 /* Update folder counts */
2688                 tny_folder_poke_status (folder);
2689                 tny_folder_poke_status (helper->dest_folder);
2690
2691                 /* Notify about operation end */
2692                 modest_mail_operation_notify_end (self);
2693
2694                 /* If user defined callback function was defined, call it */
2695                 if (helper->user_callback) {
2696                         /* This is not a GDK lock because we are a Tinymail callback and
2697                          * Tinymail already acquires the Gdk lock */
2698
2699                         /* no gdk_threads_enter (), CHECKED */
2700                         helper->user_callback (self, helper->user_data);
2701                         /* no gdk_threads_leave (), CHECKED */
2702                 }
2703
2704                 /* Free */
2705                 if (helper->more_msgs)
2706                         g_object_unref (helper->more_msgs);
2707                 if (helper->headers)
2708                         g_object_unref (helper->headers);
2709                 if (helper->dest_folder)
2710                         g_object_unref (helper->dest_folder);
2711                 if (helper->mail_op)
2712                         g_object_unref (helper->mail_op);
2713                 g_slice_free (XFerMsgsAsyncHelper, helper);
2714         } else {
2715                 /* Transfer more messages */
2716                 tny_folder_transfer_msgs_async (folder,
2717                                                 helper->headers,
2718                                                 helper->dest_folder,
2719                                                 helper->delete,
2720                                                 transfer_msgs_cb,
2721                                                 transfer_msgs_status_cb,
2722                                                 helper);
2723         }
2724 }
2725
2726 static guint
2727 compute_message_list_size (TnyList *headers)
2728 {
2729         TnyIterator *iter;
2730         guint size = 0;
2731
2732         iter = tny_list_create_iterator (headers);
2733         while (!tny_iterator_is_done (iter)) {
2734                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2735                 size += tny_header_get_message_size (header);
2736                 g_object_unref (header);
2737                 tny_iterator_next (iter);
2738         }
2739         g_object_unref (iter);
2740
2741         return size;
2742 }
2743
2744 static guint
2745 compute_message_array_size (GPtrArray *headers)
2746 {
2747         guint size = 0;
2748         gint i;
2749
2750         for (i = 0; i < headers->len; i++) {
2751                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2752                 size += tny_header_get_message_size (header);
2753         }
2754
2755         return size;
2756 }
2757
2758
2759 void
2760 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2761                                  TnyList *headers, 
2762                                  TnyFolder *folder, 
2763                                  gboolean delete_original,
2764                                  XferMsgsAsyncUserCallback user_callback,
2765                                  gpointer user_data)
2766 {
2767         ModestMailOperationPrivate *priv = NULL;
2768         TnyIterator *iter = NULL;
2769         TnyFolder *src_folder = NULL;
2770         XFerMsgsAsyncHelper *helper = NULL;
2771         TnyHeader *header = NULL;
2772         ModestTnyFolderRules rules = 0;
2773         TnyAccount *dst_account = NULL;
2774         gboolean leave_on_server;
2775
2776         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2777         g_return_if_fail (headers && TNY_IS_LIST (headers));
2778         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2779
2780         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2781         priv->total = tny_list_get_length (headers);
2782         priv->done = 0;
2783         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2784         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2785
2786         /* Apply folder rules */
2787         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2788         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2789                 /* Set status failed and set an error */
2790                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2791                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2792                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2793                              _CS("ckct_ib_unable_to_paste_here"));
2794                 /* Notify the queue */
2795                 modest_mail_operation_notify_end (self);
2796                 return;
2797         }
2798                 
2799         /* Get source folder */
2800         iter = tny_list_create_iterator (headers);
2801         header = TNY_HEADER (tny_iterator_get_current (iter));
2802         if (header) {
2803                 src_folder = tny_header_get_folder (header);
2804                 g_object_unref (header);
2805         }
2806         g_object_unref (iter);
2807
2808         if (src_folder == NULL) {
2809                 /* Notify the queue */
2810                 modest_mail_operation_notify_end (self);
2811
2812                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2813                 return;
2814         }
2815
2816         
2817         /* Check folder source and destination */
2818         if (src_folder == folder) {
2819                 /* Set status failed and set an error */
2820                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2821                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2822                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2823                              _("mail_in_ui_folder_copy_target_error"));
2824                 
2825                 /* Notify the queue */
2826                 modest_mail_operation_notify_end (self);
2827                 
2828                 /* Free */
2829                 g_object_unref (src_folder);            
2830                 return;
2831         }
2832
2833         /* Create the helper */
2834         helper = g_slice_new0 (XFerMsgsAsyncHelper);
2835         helper->mail_op = g_object_ref(self);
2836         helper->dest_folder = g_object_ref(folder);
2837         helper->user_callback = user_callback;
2838         helper->user_data = user_data;
2839         helper->delete = delete_original;
2840         helper->last_total_bytes = 0;
2841         helper->sum_total_bytes = 0;
2842         helper->total_bytes = compute_message_list_size (headers);
2843
2844         /* Get account and set it into mail_operation */
2845         priv->account = modest_tny_folder_get_account (src_folder);
2846         dst_account = modest_tny_folder_get_account (folder);
2847
2848         if (priv->account == dst_account) {
2849                 /* Transfer all messages at once using the fast
2850                  * method. Note that depending on the server this
2851                  * might not be that fast, and might not be
2852                  * user-cancellable either */
2853                 helper->headers = g_object_ref (headers);
2854                 helper->more_msgs = NULL;
2855         } else {
2856                 /* Transfer messages one by one so the user can cancel
2857                  * the operation */
2858                 GObject *hdr;
2859                 helper->headers = tny_simple_list_new ();
2860                 helper->more_msgs = tny_list_create_iterator (headers);
2861                 hdr = tny_iterator_get_current (helper->more_msgs);
2862                 tny_list_append (helper->headers, hdr);
2863                 g_object_unref (hdr);
2864         }
2865
2866         /* If leave_on_server is set to TRUE then don't use
2867            delete_original, we always pass FALSE. This is because
2868            otherwise tinymail will try to sync the source folder and
2869            this could cause an error if we're offline while
2870            transferring an already downloaded message from a POP
2871            account */
2872         if (modest_protocol_info_get_transport_store_protocol (tny_account_get_proto (priv->account)) == 
2873             MODEST_PROTOCOL_STORE_POP) {
2874                 const gchar *account_name;
2875
2876                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
2877                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2878                                                                           account_name);
2879         } else {
2880                 leave_on_server = FALSE;
2881         }
2882
2883         modest_mail_operation_notify_start (self);
2884         tny_folder_transfer_msgs_async (src_folder, 
2885                                         helper->headers, 
2886                                         folder, 
2887                                         (leave_on_server) ? FALSE : delete_original, 
2888                                         transfer_msgs_cb, 
2889                                         transfer_msgs_status_cb,
2890                                         helper);
2891         g_object_unref (src_folder);
2892         g_object_unref (dst_account);
2893 }
2894
2895
2896 static void
2897 on_refresh_folder (TnyFolder   *folder, 
2898                    gboolean     cancelled, 
2899                    GError     *error,
2900                    gpointer     user_data)
2901 {
2902         RefreshAsyncHelper *helper = NULL;
2903         ModestMailOperation *self = NULL;
2904         ModestMailOperationPrivate *priv = NULL;
2905
2906         helper = (RefreshAsyncHelper *) user_data;
2907         self = helper->mail_op;
2908         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2909
2910         g_return_if_fail(priv!=NULL);
2911
2912         if (error) {
2913                 priv->error = g_error_copy (error);
2914                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2915                 goto out;
2916         }
2917
2918         if (cancelled) {
2919                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2920                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2921                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2922                              _("Error trying to refresh the contents of %s"),
2923                              tny_folder_get_name (folder));
2924                 goto out;
2925         }
2926
2927         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2928  out:
2929
2930         /* Call user defined callback, if it exists */
2931         if (helper->user_callback) {
2932
2933                 /* This is not a GDK lock because we are a Tinymail callback and
2934                  * Tinymail already acquires the Gdk lock */
2935                 helper->user_callback (self, folder, helper->user_data);
2936         }
2937
2938         /* Free */
2939         g_slice_free (RefreshAsyncHelper, helper);
2940
2941         /* Notify about operation end */
2942         modest_mail_operation_notify_end (self);
2943         g_object_unref(self);
2944 }
2945
2946 static void
2947 on_refresh_folder_status_update (GObject *obj,
2948                                  TnyStatus *status,
2949                                  gpointer user_data)
2950 {
2951         RefreshAsyncHelper *helper = NULL;
2952         ModestMailOperation *self = NULL;
2953         ModestMailOperationPrivate *priv = NULL;
2954         ModestMailOperationState *state;
2955
2956         g_return_if_fail (user_data != NULL);
2957         g_return_if_fail (status != NULL);
2958         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2959
2960         helper = (RefreshAsyncHelper *) user_data;
2961         self = helper->mail_op;
2962         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2963
2964         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2965
2966         priv->done = status->position;
2967         priv->total = status->of_total;
2968
2969         state = modest_mail_operation_clone_state (self);
2970
2971         /* This is not a GDK lock because we are a Tinymail callback and
2972          * Tinymail already acquires the Gdk lock */
2973         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2974
2975         g_slice_free (ModestMailOperationState, state);
2976 }
2977
2978 void 
2979 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2980                                        TnyFolder *folder,
2981                                        RefreshAsyncUserCallback user_callback,
2982                                        gpointer user_data)
2983 {
2984         ModestMailOperationPrivate *priv = NULL;
2985         RefreshAsyncHelper *helper = NULL;
2986
2987         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2988
2989         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2990
2991         /* Get account and set it into mail_operation */
2992         priv->account = modest_tny_folder_get_account  (folder);
2993         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2994
2995         /* Create the helper */
2996         helper = g_slice_new0 (RefreshAsyncHelper);
2997         helper->mail_op = g_object_ref(self);
2998         helper->user_callback = user_callback;
2999         helper->user_data = user_data;
3000
3001         modest_mail_operation_notify_start (self);
3002         
3003         /* notify that the operation was started */
3004         ModestMailOperationState *state;
3005         state = modest_mail_operation_clone_state (self);
3006         state->done = 0;
3007         state->total = 0;
3008         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3009                         0, state, NULL);
3010
3011         /* FIXME: we're leaking the state here, or? valgrind thinks so */
3012         
3013         tny_folder_refresh_async (folder,
3014                                   on_refresh_folder,
3015                                   on_refresh_folder_status_update,
3016                                   helper);
3017 }
3018
3019 static void
3020 run_queue_stop (ModestTnySendQueue *queue,
3021                 ModestMailOperation *self)
3022 {
3023         ModestMailOperationPrivate *priv;
3024
3025         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3026         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3027         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3028
3029         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3030
3031         modest_mail_operation_notify_end (self);
3032         g_signal_handlers_disconnect_by_func (queue, run_queue_stop, self);
3033         g_object_unref (self);
3034 }
3035 void
3036 modest_mail_operation_run_queue (ModestMailOperation *self,
3037                                  ModestTnySendQueue *queue)
3038 {
3039         ModestMailOperationPrivate *priv;
3040
3041         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3042         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3043         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3044
3045         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3046         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3047         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3048
3049         modest_mail_operation_notify_start (self);
3050         g_object_ref (self);
3051         g_signal_connect ((gpointer) queue, "queue-stop", G_CALLBACK (run_queue_stop), (gpointer) self);
3052 }
3053
3054 static void
3055 sync_folder_finish_callback (TnyFolder *self, gboolean cancelled, GError *err, ModestMailOperation *mail_op)
3056 {
3057         ModestMailOperationPrivate *priv;
3058
3059         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3060         if (err != NULL) {
3061                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3062                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
3063                              err->message);
3064                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3065         } else {
3066                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3067         }
3068         modest_mail_operation_notify_end (mail_op);
3069         g_object_unref (mail_op);
3070 }
3071
3072 void
3073 modest_mail_operation_sync_folder (ModestMailOperation *self,
3074                                    TnyFolder *folder, gboolean expunge)
3075 {
3076         ModestMailOperationPrivate *priv;
3077
3078         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3079         g_return_if_fail (TNY_IS_FOLDER (folder));
3080         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3081
3082         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3083         priv->account = TNY_ACCOUNT (tny_folder_get_account (folder));
3084         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3085
3086         modest_mail_operation_notify_start (self);
3087         g_object_ref (self);
3088         tny_folder_sync_async (folder, expunge, (TnyFolderCallback) sync_folder_finish_callback, NULL, self);
3089 }
3090
3091 static void
3092 modest_mail_operation_notify_start (ModestMailOperation *self)
3093 {
3094         ModestMailOperationPrivate *priv = NULL;
3095
3096         g_return_if_fail (self);
3097
3098         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3099
3100         /* Ensure that all the fields are filled correctly */
3101         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3102
3103         /* Notify the observers about the mail operation. We do not
3104            wrapp this emission because we assume that this function is
3105            always called from within the main lock */
3106         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
3107 }
3108
3109 /**
3110  *
3111  * It's used by the mail operation queue to notify the observers
3112  * attached to that signal that the operation finished. We need to use
3113  * that because tinymail does not give us the progress of a given
3114  * operation when it finishes (it directly calls the operation
3115  * callback).
3116  */
3117 static void
3118 modest_mail_operation_notify_end (ModestMailOperation *self)
3119 {
3120         ModestMailOperationPrivate *priv = NULL;
3121
3122         g_return_if_fail (self);
3123
3124         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3125
3126         /* Notify the observers about the mail operation end. We do
3127            not wrapp this emission because we assume that this
3128            function is always called from within the main lock */
3129         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
3130
3131         /* Remove the error user data */
3132         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
3133                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
3134 }
3135
3136 TnyAccount *
3137 modest_mail_operation_get_account (ModestMailOperation *self)
3138 {
3139         ModestMailOperationPrivate *priv = NULL;
3140
3141         g_return_val_if_fail (self, NULL);
3142
3143         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3144
3145         return (priv->account) ? g_object_ref (priv->account) : NULL;
3146 }
3147
3148 void
3149 modest_mail_operation_noop (ModestMailOperation *self)
3150 {
3151         ModestMailOperationPrivate *priv = NULL;
3152
3153         g_return_if_fail (self);
3154
3155         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3156         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3157         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
3158         priv->done = 0;
3159         priv->total = 0;
3160
3161         /* This mail operation does nothing actually */
3162         modest_mail_operation_notify_start (self);
3163         modest_mail_operation_notify_end (self);
3164 }
3165
3166
3167 gchar*
3168 modest_mail_operation_to_string (ModestMailOperation *self)
3169 {
3170         const gchar *type, *status, *account_id;
3171         ModestMailOperationPrivate *priv = NULL;
3172         
3173         g_return_val_if_fail (self, NULL);
3174
3175         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3176
3177         /* new operations don't have anything interesting */
3178         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
3179                 return g_strdup_printf ("%p <new operation>", self);
3180         
3181         switch (priv->op_type) {
3182         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
3183         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
3184         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
3185         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
3186         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
3187         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
3188         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
3189         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
3190         default: type = "UNEXPECTED"; break;
3191         }
3192
3193         switch (priv->status) {
3194         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
3195         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
3196         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
3197         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
3198         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
3199         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
3200         default:                                                status= "UNEXPECTED"; break;
3201         } 
3202
3203         account_id = priv->account ? tny_account_get_id (priv->account) : "";
3204
3205         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
3206                                 priv->done, priv->total,
3207                                 priv->error && priv->error->message ? priv->error->message : "");
3208 }