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