Add emision of signals for account created and removed through dbus
[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-error.h>
45 #include <tny-folder-observer.h>
46 #include <camel/camel-stream-mem.h>
47 #include <glib/gi18n.h>
48 #include <modest-defs.h>
49 #include "modest-platform.h"
50 #include "modest-account-mgr-helpers.h"
51 #include <modest-tny-account.h>
52 #include <modest-tny-send-queue.h>
53 #include <modest-runtime.h>
54 #include "modest-text-utils.h"
55 #include "modest-tny-msg.h"
56 #include "modest-tny-folder.h"
57 #include "modest-tny-account-store.h"
58 #include "modest-tny-platform-factory.h"
59 #include "modest-marshal.h"
60 #include "modest-error.h"
61 #include "modest-mail-operation.h"
62 #include <modest-count-stream.h>
63 #include <libgnomevfs/gnome-vfs.h>
64 #include "modest-utils.h"
65 #include "modest-debug.h"
66 #ifdef MODEST_USE_LIBTIME
67 #include <clockd/libtime.h>
68 #endif
69 #include "modest-account-protocol.h"
70 #include <camel/camel-stream-null.h>
71 #include <widgets/modest-msg-view-window.h>
72
73 #define KB 1024
74
75 /* 
76  * Remove all these #ifdef stuff when the tinymail's idle calls become
77  * locked
78  */
79 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
80
81 /* 'private'/'protected' functions */
82 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
83 static void modest_mail_operation_init       (ModestMailOperation *obj);
84 static void modest_mail_operation_finalize   (GObject *obj);
85
86 static void     get_msg_async_cb (TnyFolder *folder, 
87                                   gboolean cancelled, 
88                                   TnyMsg *msg, 
89                                   GError *rr, 
90                                   gpointer user_data);
91
92 static void     get_msg_status_cb (GObject *obj,
93                                    TnyStatus *status,  
94                                    gpointer user_data);
95
96 static void     modest_mail_operation_notify_start (ModestMailOperation *self);
97 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
98
99 static void     notify_progress_of_multiple_messages (ModestMailOperation *self,
100                                                       TnyStatus *status,
101                                                       gint *last_total_bytes,
102                                                       gint *sum_total_bytes,
103                                                       gint total_bytes,
104                                                       gboolean increment_done);
105
106 static guint    compute_message_list_size (TnyList *headers, guint num_elements);
107
108 static int      compare_headers_by_date   (gconstpointer a,
109                                            gconstpointer b);
110
111 static void     sync_folder_finish_callback (TnyFolder *self, 
112                                              gboolean cancelled, 
113                                              GError *err, 
114                                              gpointer user_data);
115
116 static gboolean _check_memory_low         (ModestMailOperation *mail_op);
117
118
119 typedef struct {
120         ModestTnySendQueue *queue;
121         ModestMailOperation *self;
122         guint error_handler;
123         guint start_handler;
124         guint stop_handler;
125 } RunQueueHelper;
126
127 static void run_queue_notify_and_destroy (RunQueueHelper *helper,
128                                           ModestMailOperationStatus status);
129
130 /* Helpers for the update account operation (send & receive)*/
131 typedef struct 
132 {
133         ModestMailOperation *mail_op;
134         gchar *account_name;
135         UpdateAccountCallback callback;
136         gpointer user_data;
137         TnyList *folders;
138         gint pending_calls;
139         gboolean poke_all;
140         TnyFolderObserver *inbox_observer;
141         gboolean interactive;
142         gboolean msg_readed;
143         gboolean update_folder_counts;
144 } UpdateAccountInfo;
145
146 static void destroy_update_account_info         (UpdateAccountInfo *info);
147
148 static void update_account_send_mail            (UpdateAccountInfo *info);
149
150 static void update_account_get_msg_async_cb     (TnyFolder *folder, 
151                                                  gboolean canceled, 
152                                                  TnyMsg *msg, 
153                                                  GError *err, 
154                                                  gpointer user_data);
155
156 static void update_account_notify_user_and_free (UpdateAccountInfo *info, 
157                                                  TnyList *new_headers);
158
159 enum _ModestMailOperationSignals 
160 {
161         PROGRESS_CHANGED_SIGNAL,
162         OPERATION_STARTED_SIGNAL,
163         OPERATION_FINISHED_SIGNAL,
164         NUM_SIGNALS
165 };
166
167 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
168 struct _ModestMailOperationPrivate {
169         TnyAccount                *account;
170         guint                      done;
171         guint                      total;
172         GObject                   *source;
173         GError                    *error;
174         ErrorCheckingUserCallback  error_checking;
175         gpointer                   error_checking_user_data;
176         ErrorCheckingUserDataDestroyer error_checking_user_data_destroyer;
177         ModestMailOperationStatus  status;      
178         ModestMailOperationTypeOperation op_type;
179 };
180
181 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
182                                                    MODEST_TYPE_MAIL_OPERATION, \
183                                                    ModestMailOperationPrivate))
184
185 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
186                                                    priv->status = new_status;\
187                                                }
188
189
190 typedef struct {
191         GetMsgAsyncUserCallback user_callback;
192         TnyHeader *header;
193         TnyIterator *more_msgs;
194         gpointer user_data;
195         ModestMailOperation *mail_op;
196         GDestroyNotify destroy_notify;
197         gint last_total_bytes;
198         gint sum_total_bytes;
199         gint total_bytes;
200         TnyIterator *get_parts;
201         TnyMsg *msg;
202 } GetMsgInfo;
203
204 typedef struct _RefreshAsyncHelper {    
205         ModestMailOperation *mail_op;
206         RefreshAsyncUserCallback user_callback; 
207         gpointer user_data;
208 } RefreshAsyncHelper;
209
210 typedef struct _XFerMsgsAsyncHelper
211 {
212         ModestMailOperation *mail_op;
213         TnyList *headers;
214         TnyIterator *more_msgs;
215         TnyFolder *dest_folder;
216         XferMsgsAsyncUserCallback user_callback;        
217         gboolean delete;
218         gpointer user_data;
219         gint last_total_bytes;
220         gint sum_total_bytes;
221         gint total_bytes;
222 } XFerMsgsAsyncHelper;
223
224 typedef struct _XFerFolderAsyncHelper
225 {
226         ModestMailOperation *mail_op;
227         XferFolderAsyncUserCallback user_callback;      
228         gpointer user_data;
229 } XFerFolderAsyncHelper;
230
231 typedef struct _SyncFolderHelper {
232         ModestMailOperation *mail_op;
233         SyncFolderCallback user_callback;
234         gpointer user_data;
235 } SyncFolderHelper;
236
237 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
238                                                       TnyMsg *msg,
239                                                       gpointer userdata);
240
241 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
242                                                        const gchar *from, const gchar *to,
243                                                        const gchar *cc, const gchar *bcc,
244                                                        const gchar *subject, const gchar *plain_body,
245                                                        const gchar *html_body, const GList *attachments_list,
246                                                        const GList *images_list,
247                                                        TnyHeaderFlags priority_flags,
248                                                        const gchar *references, const gchar *in_reply_to,
249                                                        ModestMailOperationCreateMsgCallback callback,
250                                                        gpointer userdata);
251
252 static gboolean      idle_notify_queue (gpointer data);
253 typedef struct
254 {
255         ModestMailOperation *mail_op;
256         gchar *from;
257         gchar *to;
258         gchar *cc;
259         gchar *bcc;
260         gchar *subject;
261         gchar *references;
262         gchar *in_reply_to;
263         gchar *plain_body;
264         gchar *html_body;
265         GList *attachments_list;
266         GList *images_list;
267         TnyHeaderFlags priority_flags;
268         ModestMailOperationCreateMsgCallback callback;
269         gpointer userdata;
270 } CreateMsgInfo;
271
272 typedef struct
273 {
274         ModestMailOperation *mail_op;
275         TnyMsg *msg;
276         ModestMailOperationCreateMsgCallback callback;
277         gpointer userdata;
278 } CreateMsgIdleInfo;
279
280 /* globals */
281 static GObjectClass *parent_class = NULL;
282
283 static guint signals[NUM_SIGNALS] = {0};
284
285 GType
286 modest_mail_operation_get_type (void)
287 {
288         static GType my_type = 0;
289         if (!my_type) {
290                 static const GTypeInfo my_info = {
291                         sizeof(ModestMailOperationClass),
292                         NULL,           /* base init */
293                         NULL,           /* base finalize */
294                         (GClassInitFunc) modest_mail_operation_class_init,
295                         NULL,           /* class finalize */
296                         NULL,           /* class data */
297                         sizeof(ModestMailOperation),
298                         1,              /* n_preallocs */
299                         (GInstanceInitFunc) modest_mail_operation_init,
300                         NULL
301                 };
302                 my_type = g_type_register_static (G_TYPE_OBJECT,
303                                                   "ModestMailOperation",
304                                                   &my_info, 0);
305         }
306         return my_type;
307 }
308
309 static void
310 modest_mail_operation_class_init (ModestMailOperationClass *klass)
311 {
312         GObjectClass *gobject_class;
313         gobject_class = (GObjectClass*) klass;
314
315         parent_class            = g_type_class_peek_parent (klass);
316         gobject_class->finalize = modest_mail_operation_finalize;
317
318         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
319
320         /**
321          * ModestMailOperation::progress-changed
322          * @self: the #MailOperation that emits the signal
323          * @user_data: user data set when the signal handler was connected
324          *
325          * Emitted when the progress of a mail operation changes
326          */
327         signals[PROGRESS_CHANGED_SIGNAL] = 
328                 g_signal_new ("progress-changed",
329                               G_TYPE_FROM_CLASS (gobject_class),
330                               G_SIGNAL_RUN_FIRST,
331                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
332                               NULL, NULL,
333                               g_cclosure_marshal_VOID__POINTER,
334                               G_TYPE_NONE, 1, G_TYPE_POINTER);
335         /**
336          * operation-started
337          *
338          * This signal is issued whenever a mail operation starts, and
339          * starts mean when the tinymail operation is issued. This
340          * means that it could happen that something wrong happens and
341          * the tinymail function is never called. In this situation a
342          * operation-finished will be issued without any
343          * operation-started
344          */
345         signals[OPERATION_STARTED_SIGNAL] =
346                 g_signal_new ("operation-started",
347                               G_TYPE_FROM_CLASS (gobject_class),
348                               G_SIGNAL_RUN_FIRST,
349                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_started),
350                               NULL, NULL,
351                               g_cclosure_marshal_VOID__VOID,
352                               G_TYPE_NONE, 0);
353         /**
354          * operation-started
355          *
356          * This signal is issued whenever a mail operation
357          * finishes. Note that this signal could be issued without any
358          * previous "operation-started" signal, because this last one
359          * is only issued when the tinymail operation is successfully
360          * started
361          */
362         signals[OPERATION_FINISHED_SIGNAL] =
363                 g_signal_new ("operation-finished",
364                               G_TYPE_FROM_CLASS (gobject_class),
365                               G_SIGNAL_RUN_FIRST,
366                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_finished),
367                               NULL, NULL,
368                               g_cclosure_marshal_VOID__VOID,
369                               G_TYPE_NONE, 0);
370 }
371
372 static void
373 modest_mail_operation_init (ModestMailOperation *obj)
374 {
375         ModestMailOperationPrivate *priv;
376
377         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
378
379         priv->account        = NULL;
380         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
381         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
382         priv->error          = NULL;
383         priv->done           = 0;
384         priv->total          = 0;
385         priv->source         = NULL;
386         priv->error_checking = NULL;
387         priv->error_checking_user_data = NULL;
388 }
389
390 static void
391 modest_mail_operation_finalize (GObject *obj)
392 {
393         ModestMailOperationPrivate *priv;
394
395         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
396
397         
398         
399         if (priv->error) {
400                 g_error_free (priv->error);
401                 priv->error = NULL;
402         }
403         if (priv->source) {
404                 g_object_unref (priv->source);
405                 priv->source = NULL;
406         }
407         if (priv->account) {
408                 g_object_unref (priv->account);
409                 priv->account = NULL;
410         }
411
412
413         G_OBJECT_CLASS(parent_class)->finalize (obj);
414 }
415
416 ModestMailOperation*
417 modest_mail_operation_new (GObject *source)
418 {
419         ModestMailOperation *obj;
420         ModestMailOperationPrivate *priv;
421                 
422         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
423         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
424
425         if (source != NULL)
426                 priv->source = g_object_ref(source);
427
428         return obj;
429 }
430
431 ModestMailOperation*
432 modest_mail_operation_new_with_error_handling (GObject *source,
433                                                ErrorCheckingUserCallback error_handler,
434                                                gpointer user_data,
435                                                ErrorCheckingUserDataDestroyer error_handler_destroyer)
436 {
437         ModestMailOperation *obj;
438         ModestMailOperationPrivate *priv;
439                 
440         obj = modest_mail_operation_new (source);
441         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
442         
443         g_return_val_if_fail (error_handler != NULL, obj);
444         priv->error_checking = error_handler;
445         priv->error_checking_user_data = user_data;
446         priv->error_checking_user_data_destroyer = error_handler_destroyer;
447
448         return obj;
449 }
450
451 void
452 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
453 {
454         ModestMailOperationPrivate *priv;
455
456         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION(self));
457         
458         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
459         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
460
461         /* Call the user callback */
462         if (priv->error_checking != NULL)
463                 priv->error_checking (self, priv->error_checking_user_data);
464 }
465
466
467 ModestMailOperationTypeOperation
468 modest_mail_operation_get_type_operation (ModestMailOperation *self)
469 {
470         ModestMailOperationPrivate *priv;
471
472         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
473                               MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
474         
475         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
476         
477         return priv->op_type;
478 }
479
480 gboolean 
481 modest_mail_operation_is_mine (ModestMailOperation *self, 
482                                GObject *me)
483 {
484         ModestMailOperationPrivate *priv;
485
486         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
487                               FALSE);
488         
489         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
490         if (priv->source == NULL) return FALSE;
491
492         return priv->source == me;
493 }
494
495 GObject *
496 modest_mail_operation_get_source (ModestMailOperation *self)
497 {
498         ModestMailOperationPrivate *priv;
499
500         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
501                               NULL);
502         
503         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
504         if (!priv) {
505                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
506                 return NULL;
507         }
508         
509         return (priv->source) ? g_object_ref (priv->source) : NULL;
510 }
511
512 ModestMailOperationStatus
513 modest_mail_operation_get_status (ModestMailOperation *self)
514 {
515         ModestMailOperationPrivate *priv;
516
517         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
518         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
519                               MODEST_MAIL_OPERATION_STATUS_INVALID);
520
521         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
522         if (!priv) {
523                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
524                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
525         }
526         
527         return priv->status;
528 }
529
530 const GError *
531 modest_mail_operation_get_error (ModestMailOperation *self)
532 {
533         ModestMailOperationPrivate *priv;
534
535         g_return_val_if_fail (self, NULL);
536         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
537
538         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
539
540         if (!priv) {
541                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
542                 return NULL;
543         }
544
545         return priv->error;
546 }
547
548 gboolean 
549 modest_mail_operation_cancel (ModestMailOperation *self)
550 {
551         ModestMailOperationPrivate *priv;
552         gboolean canceled = FALSE;
553         
554         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION (self), FALSE);
555
556         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
557
558         /* Set new status */
559         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
560         
561         /* Cancel the mail operation */
562         g_return_val_if_fail (priv->account, FALSE);
563         tny_account_cancel (priv->account);
564
565         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SEND) {
566                 ModestTnySendQueue *queue;
567                 queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (priv->account),
568                                                        TRUE);
569
570                 /* Cancel the sending of the following next messages */
571                 if (TNY_IS_SEND_QUEUE (queue))
572                         tny_send_queue_cancel (TNY_SEND_QUEUE (queue), TNY_SEND_QUEUE_CANCEL_ACTION_SUSPEND, NULL);
573         }
574         
575         return canceled;
576 }
577
578 guint 
579 modest_mail_operation_get_task_done (ModestMailOperation *self)
580 {
581         ModestMailOperationPrivate *priv;
582
583         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
584                               0);
585         
586         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
587         return priv->done;
588 }
589
590 guint 
591 modest_mail_operation_get_task_total (ModestMailOperation *self)
592 {
593         ModestMailOperationPrivate *priv;
594
595         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
596                               0);
597
598         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
599         return priv->total;
600 }
601
602 gboolean
603 modest_mail_operation_is_finished (ModestMailOperation *self)
604 {
605         ModestMailOperationPrivate *priv;
606         gboolean retval = FALSE;
607
608         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
609                               FALSE);
610         
611         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
612
613         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
614             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
615             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
616             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
617                 retval = TRUE;
618         } else {
619                 retval = FALSE;
620         }
621
622         return retval;
623 }
624
625 /*
626  * Creates an image of the current state of a mail operation, the
627  * caller must free it
628  */
629 static ModestMailOperationState *
630 modest_mail_operation_clone_state (ModestMailOperation *self)
631 {
632         ModestMailOperationState *state;
633         ModestMailOperationPrivate *priv;
634
635         g_return_val_if_fail (self, NULL);
636         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
637         g_return_val_if_fail (priv, NULL);
638
639         if (!priv)
640                 return NULL;
641
642         state = g_slice_new (ModestMailOperationState);
643
644         state->status = priv->status;
645         state->op_type = priv->op_type;
646         state->done = priv->done;
647         state->total = priv->total;
648         state->finished = modest_mail_operation_is_finished (self);
649         state->bytes_done = 0;
650         state->bytes_total = 0;
651
652         return state;
653 }
654
655 /* ******************************************************************* */
656 /* ************************** SEND   ACTIONS ************************* */
657 /* ******************************************************************* */
658
659 typedef struct 
660 {
661         ModestMailOperation *mail_op;
662         gboolean notify;
663 } SendNewMailHelper;
664
665 static void
666 send_mail_on_sync_async_cb (TnyFolder *folder, 
667                             gboolean cancelled, 
668                             GError *err, 
669                             gpointer user_data)
670 {
671         ModestMailOperationPrivate *priv;
672         ModestMailOperation *self;
673         SendNewMailHelper *helper;
674
675         helper = (SendNewMailHelper *) user_data;
676         self = helper->mail_op;
677         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
678
679         if (cancelled)
680                 goto end;
681
682         if (err) {
683                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
684                              MODEST_MAIL_OPERATION_ERROR_SEND_QUEUE_ADD_ERROR,
685                              "Error adding a msg to the send queue\n");
686                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
687         } else {
688                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
689         }
690
691  end:
692         if (helper->notify)
693                 modest_mail_operation_notify_end (self);
694
695         g_object_unref (helper->mail_op);
696         g_slice_free (SendNewMailHelper, helper);
697 }
698
699 static void
700 run_queue_start (TnySendQueue *self,
701                  gpointer user_data)
702 {
703         RunQueueHelper *helper = (RunQueueHelper *) user_data;
704         ModestMailOperation *mail_op;
705                         
706         g_debug ("%s sending queue successfully started", __FUNCTION__);
707
708         /* Wait for the message to be sent */
709         mail_op = modest_mail_operation_new (NULL);
710         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
711                                          mail_op);
712         modest_mail_operation_run_queue (mail_op, helper->queue);
713         g_object_unref (mail_op);
714
715         /* Free the helper and end operation */
716         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
717 }
718
719 static void 
720 run_queue_error_happened (TnySendQueue *queue, 
721                           TnyHeader *header, 
722                           TnyMsg *msg, 
723                           GError *error, 
724                           gpointer user_data)
725 {
726         RunQueueHelper *helper = (RunQueueHelper *) user_data;
727         ModestMailOperationPrivate *priv;
728
729         /* If we are here this means that the send queue could not
730            start to send emails. Shouldn't happen as this means that
731            we could not create the thread */
732         g_debug ("%s sending queue failed to create the thread", __FUNCTION__);
733
734         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
735         priv->error = g_error_copy ((const GError *) error);
736
737         if (error->code != TNY_SYSTEM_ERROR_UNKNOWN) {
738                 /* This code is here for safety reasons. It should
739                    never be called, because that would mean that we
740                    are not controlling some error case */
741                 g_warning ("%s Error %s should not happen", 
742                            __FUNCTION__, error->message);
743         }
744
745         /* Free helper and end operation */
746         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_FAILED);
747 }
748
749 static void
750 send_mail_on_added_to_outbox (TnySendQueue *send_queue, 
751                               gboolean cancelled, 
752                               TnyMsg *msg, 
753                               GError *err,
754                               gpointer user_data)
755 {
756         ModestMailOperationPrivate *priv;
757         ModestMailOperation *self;
758         SendNewMailHelper *helper;
759
760         helper = (SendNewMailHelper *) user_data;
761         self = helper->mail_op;
762         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
763
764         if (cancelled)
765                 goto end;
766
767         if (err) {
768                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
769                              MODEST_MAIL_OPERATION_ERROR_SEND_QUEUE_ADD_ERROR,
770                              "Error adding a msg to the send queue\n");
771                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
772         } else {
773                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
774         }
775
776  end:
777         if (helper->notify) {
778                 TnyTransportAccount *trans_account;
779                 ModestTnySendQueue *queue;
780
781                 trans_account = (TnyTransportAccount *) modest_mail_operation_get_account (self);
782                 if (trans_account) {
783                         queue = modest_runtime_get_send_queue (trans_account, TRUE);
784                         if (queue) {
785                                 RunQueueHelper *helper;
786
787                                 /* Create the helper */
788                                 helper = g_slice_new0 (RunQueueHelper);
789                                 helper->queue = g_object_ref (queue);
790                                 helper->self = g_object_ref (self);
791
792                                 /* if sending is ongoing wait for the queue to
793                                    stop. Otherwise wait for the queue-start
794                                    signal. It could happen that the queue
795                                    could not start, then check also the error
796                                    happened signal */
797                                 if (modest_tny_send_queue_sending_in_progress (queue)) {
798                                         run_queue_start (TNY_SEND_QUEUE (queue), helper);
799                                 } else {
800                                         helper->start_handler = g_signal_connect (queue, "queue-start", 
801                                                                                   G_CALLBACK (run_queue_start), 
802                                                                                   helper);
803                                         helper->error_handler = g_signal_connect (queue, "error-happened", 
804                                                                                   G_CALLBACK (run_queue_error_happened), 
805                                                                                   helper);
806                                 }
807                         } else {
808                                 /* Finalize this mail operation */
809                                 modest_mail_operation_notify_end (self);
810                         }
811                         g_object_unref (trans_account);
812                 } else {
813                         g_warning ("No transport account for the operation");
814                 }
815         }
816
817         g_object_unref (helper->mail_op);
818         g_slice_free (SendNewMailHelper, helper);
819 }
820
821 static gboolean
822 idle_create_msg_cb (gpointer idle_data)
823 {
824         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
825
826         /* This is a GDK lock because we are an idle callback and
827          * info->callback can contain Gtk+ code */
828
829         gdk_threads_enter (); /* CHECKED */
830         info->callback (info->mail_op, info->msg, info->userdata);
831
832         g_object_unref (info->mail_op);
833         if (info->msg)
834                 g_object_unref (info->msg);
835         g_slice_free (CreateMsgIdleInfo, info);
836         gdk_threads_leave (); /* CHECKED */
837
838         return FALSE;
839 }
840
841 static gpointer 
842 create_msg_thread (gpointer thread_data)
843 {
844         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
845         TnyMsg *new_msg = NULL;
846         ModestMailOperationPrivate *priv;
847         gint attached = 0;
848
849         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
850         if (info->html_body == NULL) {
851                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
852                                               info->bcc, info->subject, 
853                                               info->references, info->in_reply_to,
854                                               info->plain_body, 
855                                               info->attachments_list, &attached,
856                                               &(priv->error));
857         } else {
858                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
859                                                          info->bcc, info->subject, 
860                                                          info->references, info->in_reply_to,
861                                                          info->html_body,
862                                                          info->plain_body, info->attachments_list,
863                                                          info->images_list, &attached,
864                                                          &(priv->error));
865         }
866
867         if (new_msg) {
868                 TnyHeader *header = tny_msg_get_header (new_msg);
869
870                 /* Set priority flags in message */
871                 if (info->priority_flags != TNY_HEADER_FLAG_NORMAL_PRIORITY)
872                         tny_header_set_flag (header, info->priority_flags);
873
874                 /* Set attachment flags in message */
875                 if (info->attachments_list != NULL && attached > 0)
876                         tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
877
878                 g_object_unref (G_OBJECT(header));
879         } else {
880                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
881                 if (!priv->error)
882                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
883                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
884                                      "modest: failed to create a new msg\n");
885         }
886
887
888         g_free (info->to);
889         g_free (info->from);
890         g_free (info->cc);
891         g_free (info->bcc);
892         g_free (info->plain_body);
893         g_free (info->html_body);
894         g_free (info->subject);
895         g_free (info->references);
896         g_free (info->in_reply_to);
897         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
898         g_list_free (info->attachments_list);
899         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
900         g_list_free (info->images_list);
901
902         if (info->callback) {
903                 CreateMsgIdleInfo *idle_info;
904                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
905                 idle_info->mail_op = g_object_ref (info->mail_op);
906                 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
907                 idle_info->callback = info->callback;
908                 idle_info->userdata = info->userdata;
909                 g_idle_add (idle_create_msg_cb, idle_info);
910         } else {
911                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
912         }
913
914         g_object_unref (info->mail_op);
915         g_slice_free (CreateMsgInfo, info);
916         if (new_msg) g_object_unref(new_msg);
917         return NULL;
918 }
919
920
921 void
922 modest_mail_operation_create_msg (ModestMailOperation *self,
923                                   const gchar *from, const gchar *to,
924                                   const gchar *cc, const gchar *bcc,
925                                   const gchar *subject, const gchar *plain_body,
926                                   const gchar *html_body,
927                                   const GList *attachments_list,
928                                   const GList *images_list,
929                                   TnyHeaderFlags priority_flags,
930                                   const gchar *references,
931                                   const gchar *in_reply_to,
932                                   ModestMailOperationCreateMsgCallback callback,
933                                   gpointer userdata)
934 {
935         CreateMsgInfo *info = NULL;
936
937         info = g_slice_new0 (CreateMsgInfo);
938         info->mail_op = g_object_ref (self);
939
940         info->from = g_strdup (from);
941         info->to = g_strdup (to);
942         info->cc = g_strdup (cc);
943         info->bcc  = g_strdup (bcc);
944         info->subject = g_strdup (subject);
945         info->plain_body = g_strdup (plain_body);
946         info->html_body = g_strdup (html_body);
947         info->references = g_strdup (references);
948         info->in_reply_to = g_strdup (in_reply_to);
949         info->attachments_list = g_list_copy ((GList *) attachments_list);
950         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
951         info->images_list = g_list_copy ((GList *) images_list);
952         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
953         info->priority_flags = 0 | priority_flags;
954
955         info->callback = callback;
956         info->userdata = userdata;
957
958         g_thread_create (create_msg_thread, info, FALSE, NULL);
959 }
960
961 typedef struct
962 {
963         TnyTransportAccount *transport_account;
964         TnyMsg *draft_msg;
965 } SendNewMailInfo;
966
967 static void
968 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
969                                         TnyMsg *msg,
970                                         gpointer userdata)
971 {
972         TnySendQueue *send_queue = NULL;
973         ModestMailOperationPrivate *priv = NULL;
974         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
975         TnyFolder *draft_folder = NULL;
976         TnyFolder *outbox_folder = NULL;
977         TnyHeader *header = NULL;
978
979         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
980
981         if (!msg) {
982                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
983                 modest_mail_operation_notify_end (self);
984                 goto end;
985         }
986
987         if (priv->error && priv->error->code != MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
988                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
989                 modest_mail_operation_notify_end (self);
990                 goto end;
991         }
992
993         /* Add message to send queue */
994         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (info->transport_account, TRUE));
995         if (!TNY_IS_SEND_QUEUE(send_queue)) {
996                 if (priv->error) {
997                         g_error_free (priv->error);
998                         priv->error = NULL;
999                 }
1000                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1001                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1002                              "modest: could not find send queue for account\n");
1003                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1004                 modest_mail_operation_notify_end (self);
1005                 goto end;
1006         } else {
1007                 SendNewMailHelper *helper = g_slice_new (SendNewMailHelper);
1008                 helper->mail_op = g_object_ref (self);
1009                 helper->notify = (info->draft_msg == NULL);
1010
1011                 /* Add the msg to the queue. The callback will free
1012                    the helper */
1013                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1014                                                                   FALSE);
1015                 tny_send_queue_add_async (send_queue, msg, send_mail_on_added_to_outbox, 
1016                                           NULL, helper);
1017         }
1018
1019         if (info->draft_msg != NULL) {
1020                 TnyList *tmp_headers = NULL;
1021                 TnyFolder *folder = NULL;
1022                 TnyFolder *src_folder = NULL;
1023                 TnyFolderType folder_type;              
1024                 TnyTransportAccount *transport_account = NULL;
1025                 SendNewMailHelper *helper = NULL;
1026
1027                 /* To remove the old mail from its source folder, we need to get the
1028                  * transport account of the original draft message (the transport account
1029                  * might have been changed by the user) */
1030                 header = tny_msg_get_header (info->draft_msg);
1031                 transport_account = modest_tny_account_store_get_transport_account_from_outbox_header(
1032                         modest_runtime_get_account_store(), header);
1033                 if (transport_account == NULL)
1034                         transport_account = g_object_ref(info->transport_account);
1035                 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
1036                                                                       TNY_FOLDER_TYPE_DRAFTS);
1037                 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account),
1038                                                                        TNY_FOLDER_TYPE_OUTBOX);
1039                 g_object_unref(transport_account);
1040
1041                 if (!draft_folder) {
1042                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL drafts folder",
1043                                    __FUNCTION__);
1044                         modest_mail_operation_notify_end (self);
1045                         goto end;
1046                 }
1047                 if (!outbox_folder) {
1048                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL outbox folder",
1049                                    __FUNCTION__);
1050                         modest_mail_operation_notify_end (self);
1051                         goto end;
1052                 }
1053
1054                 folder = tny_msg_get_folder (info->draft_msg);          
1055                 if (folder == NULL) {
1056                         modest_mail_operation_notify_end (self);
1057                         goto end;
1058                 }
1059                 folder_type = modest_tny_folder_guess_folder_type (folder);
1060
1061                 if (folder_type == TNY_FOLDER_TYPE_INVALID)
1062                         g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1063                 
1064                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
1065                         src_folder = outbox_folder;
1066                 else 
1067                         src_folder = draft_folder;
1068
1069                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
1070                  * because this function requires it to have a UID. */
1071                 helper = g_slice_new (SendNewMailHelper);
1072                 helper->mail_op = g_object_ref (self);
1073                 helper->notify = TRUE;
1074
1075                 tmp_headers = tny_simple_list_new ();
1076                 tny_list_append (tmp_headers, (GObject*) header);
1077                 tny_folder_remove_msgs_async (src_folder, tmp_headers, NULL, NULL, NULL);
1078                 g_object_unref (tmp_headers);
1079                 tny_folder_sync_async (src_folder, TRUE, send_mail_on_sync_async_cb, 
1080                                        NULL, helper);
1081                 g_object_unref (folder);
1082         }
1083
1084 end:
1085         if (header)
1086                 g_object_unref (header);
1087         if (info->draft_msg)
1088                 g_object_unref (info->draft_msg);
1089         if (draft_folder)
1090                 g_object_unref (draft_folder);
1091         if (outbox_folder)
1092                 g_object_unref (outbox_folder);
1093         if (info->transport_account)
1094                 g_object_unref (info->transport_account);
1095         g_slice_free (SendNewMailInfo, info);
1096 }
1097
1098 void
1099 modest_mail_operation_send_new_mail (ModestMailOperation *self,
1100                                      TnyTransportAccount *transport_account,
1101                                      TnyMsg *draft_msg,
1102                                      const gchar *from,  const gchar *to,
1103                                      const gchar *cc,  const gchar *bcc,
1104                                      const gchar *subject, const gchar *plain_body,
1105                                      const gchar *html_body,
1106                                      const GList *attachments_list,
1107                                      const GList *images_list,
1108                                      const gchar *references,
1109                                      const gchar *in_reply_to,
1110                                      TnyHeaderFlags priority_flags)
1111 {
1112         ModestMailOperationPrivate *priv = NULL;
1113         SendNewMailInfo *info;
1114
1115         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1116         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1117
1118         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1119         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1120         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
1121         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1122
1123         modest_mail_operation_notify_start (self);
1124
1125         /* Check parametters */
1126         if (to == NULL) {
1127                 /* Set status failed and set an error */
1128                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1129                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1130                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1131                              _("Error trying to send a mail. You need to set at least one recipient"));
1132                 modest_mail_operation_notify_end (self);
1133                 return;
1134         }
1135         info = g_slice_new0 (SendNewMailInfo);
1136         info->transport_account = transport_account;
1137         if (transport_account)
1138                 g_object_ref (transport_account);
1139         info->draft_msg = draft_msg;
1140         if (draft_msg)
1141                 g_object_ref (draft_msg);
1142
1143
1144         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1145                                           attachments_list, images_list, priority_flags,
1146                                           references, in_reply_to,
1147                                           modest_mail_operation_send_new_mail_cb, info);
1148
1149 }
1150
1151 typedef struct
1152 {
1153         ModestMailOperation *mailop;
1154         TnyMsg *msg;
1155         SaveToDraftstCallback callback;
1156         gpointer userdata;
1157 } FinishSaveRemoteDraftInfo;
1158
1159 static void
1160 finish_save_remote_draft (ModestAccountProtocol *protocol,
1161                           GError *err,
1162                           const gchar *account_id,
1163                           TnyMsg *new_remote_msg,
1164                           TnyMsg *new_msg,
1165                           TnyMsg *old_msg,
1166                           gpointer userdata)
1167 {
1168         FinishSaveRemoteDraftInfo *info = (FinishSaveRemoteDraftInfo *) userdata;
1169         ModestMailOperationPrivate *priv = NULL;
1170
1171         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1172
1173         if (!priv->error && err != NULL) {
1174                 /* Priority for errors in save to local stage */
1175                 priv->error = g_error_copy (err);
1176                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1177         }
1178
1179         if (info->callback)
1180                 info->callback (info->mailop, info->msg, info->userdata);
1181
1182         if (info->msg)
1183                 g_object_unref (info->msg);
1184
1185         modest_mail_operation_notify_end (info->mailop);
1186         g_object_unref (info->mailop);
1187
1188         g_slice_free (FinishSaveRemoteDraftInfo, info);
1189 }
1190
1191 typedef struct
1192 {
1193         TnyTransportAccount *transport_account;
1194         TnyMsg *draft_msg;
1195         SaveToDraftstCallback callback;
1196         gpointer user_data;
1197         TnyFolder *drafts;
1198         TnyMsg *msg;
1199         ModestMailOperation *mailop;
1200 } SaveToDraftsAddMsgInfo;
1201
1202 static void
1203 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
1204                                                 gboolean canceled,
1205                                                 GError *err,
1206                                                 gpointer userdata)
1207 {
1208         ModestMailOperationPrivate *priv = NULL;
1209         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
1210         GError *io_error = NULL;
1211         gboolean callback_called = FALSE;
1212
1213         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
1214
1215         if (priv->error && priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1216                 io_error = priv->error;
1217                 priv->error = NULL;
1218         }
1219         if (priv->error) {
1220                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
1221                 g_error_free(priv->error);
1222         }
1223
1224         priv->error = (err == NULL) ? NULL : g_error_copy(err);
1225
1226         if ((!priv->error) && (info->draft_msg != NULL)) {
1227                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
1228                 TnyFolder *src_folder = tny_header_get_folder (header);
1229
1230                 g_debug ("--- REMOVE AND SYNC");
1231                 /* Remove the old draft */
1232                 tny_folder_remove_msg (src_folder, header, NULL);
1233
1234                 /* Synchronize to expunge and to update the msg counts */
1235                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1236                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1237                 g_debug ("--- REMOVED - SYNCED");
1238
1239                 g_object_unref (G_OBJECT(header));
1240                 g_object_unref (G_OBJECT(src_folder));
1241         }
1242
1243         if (priv->error) {
1244                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1245                 if (io_error) {
1246                         g_error_free (io_error);
1247                         io_error = NULL;
1248                 }
1249         } else if (io_error) {
1250                 priv->error = io_error;
1251                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
1252         } else {
1253                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1254         }
1255
1256         if (info->transport_account) {
1257                 ModestProtocolType transport_protocol_type;
1258                 ModestProtocol *transport_protocol;
1259
1260                 transport_protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (info->transport_account));
1261
1262                 transport_protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1263                                                                                     transport_protocol_type);
1264                 if (transport_protocol && MODEST_IS_ACCOUNT_PROTOCOL (transport_protocol)) {
1265                         FinishSaveRemoteDraftInfo *srd_info = g_slice_new (FinishSaveRemoteDraftInfo);
1266                         srd_info->mailop = info->mailop?g_object_ref (info->mailop):NULL;
1267                         srd_info->msg = info->msg?g_object_ref (info->msg):NULL;
1268                         srd_info->callback = info->callback;
1269                         srd_info->userdata = info->user_data;
1270                         modest_account_protocol_save_remote_draft (MODEST_ACCOUNT_PROTOCOL (transport_protocol), 
1271                                                                    tny_account_get_id (TNY_ACCOUNT (info->transport_account)),
1272                                                                    info->msg, info->draft_msg,
1273                                                                    finish_save_remote_draft,
1274                                                                    srd_info);
1275                                                                    
1276                         callback_called = TRUE;
1277                 }
1278         }
1279
1280         /* Call the user callback */
1281         if (!callback_called && info->callback)
1282                 info->callback (info->mailop, info->msg, info->user_data);
1283
1284         if (info->transport_account)
1285                 g_object_unref (G_OBJECT(info->transport_account));
1286         if (info->draft_msg)
1287                 g_object_unref (G_OBJECT (info->draft_msg));
1288         if (info->drafts)
1289                 g_object_unref (G_OBJECT(info->drafts));
1290         if (info->msg)
1291                 g_object_unref (G_OBJECT (info->msg));
1292
1293         if (!callback_called)
1294                 modest_mail_operation_notify_end (info->mailop);
1295         if (info->mailop)
1296                 g_object_unref(info->mailop);
1297         g_slice_free (SaveToDraftsAddMsgInfo, info);
1298 }
1299
1300 typedef struct
1301 {
1302         TnyTransportAccount *transport_account;
1303         TnyMsg *draft_msg;
1304         SaveToDraftstCallback callback;
1305         gpointer user_data;
1306 } SaveToDraftsInfo;
1307
1308 static void
1309 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1310                                          TnyMsg *msg,
1311                                          gpointer userdata)
1312 {
1313         TnyFolder *drafts = NULL;
1314         ModestMailOperationPrivate *priv = NULL;
1315         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1316
1317         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1318
1319         if (!msg) {
1320                 if (!(priv->error)) {
1321                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1322                                      MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1323                                      "modest: failed to create a new msg\n");
1324                 }
1325         } else {
1326                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1327                                                                 TNY_FOLDER_TYPE_DRAFTS);
1328                 if (!drafts && !(priv->error)) {
1329                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1330                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1331                                      "modest: failed to create a new msg\n");
1332                 }
1333         }
1334
1335         if (!priv->error || priv->error->code == MODEST_MAIL_OPERATION_ERROR_FILE_IO) {
1336                 if (drafts) {
1337                         SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1338                         cb_info->transport_account = g_object_ref(info->transport_account);
1339                         cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1340                         cb_info->callback = info->callback;
1341                         cb_info->user_data = info->user_data;
1342                         cb_info->drafts = g_object_ref(drafts);
1343                         cb_info->msg = g_object_ref(msg);
1344                         cb_info->mailop = g_object_ref(self);
1345                         tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1346                                                  NULL, cb_info);
1347                 }
1348         } else {
1349                 /* Call the user callback */
1350                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1351                 if (info->callback)
1352                         info->callback (self, msg, info->user_data);
1353                 modest_mail_operation_notify_end (self);
1354         }
1355
1356         if (drafts)
1357                 g_object_unref (G_OBJECT(drafts));
1358         if (info->draft_msg)
1359                 g_object_unref (G_OBJECT (info->draft_msg));
1360         if (info->transport_account)
1361                 g_object_unref (G_OBJECT(info->transport_account));
1362         g_slice_free (SaveToDraftsInfo, info);
1363 }
1364
1365 void
1366 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1367                                       TnyTransportAccount *transport_account,
1368                                       TnyMsg *draft_msg,
1369                                       const gchar *from,  const gchar *to,
1370                                       const gchar *cc,  const gchar *bcc,
1371                                       const gchar *subject, const gchar *plain_body,
1372                                       const gchar *html_body,
1373                                       const GList *attachments_list,
1374                                       const GList *images_list,
1375                                       TnyHeaderFlags priority_flags,
1376                                       const gchar *references,
1377                                       const gchar *in_reply_to,
1378                                       SaveToDraftstCallback callback,
1379                                       gpointer user_data)
1380 {
1381         ModestMailOperationPrivate *priv = NULL;
1382         SaveToDraftsInfo *info = NULL;
1383
1384         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1385         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1386
1387         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1388
1389         /* Get account and set it into mail_operation */
1390         priv->account = g_object_ref (transport_account);
1391         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1392
1393         info = g_slice_new0 (SaveToDraftsInfo);
1394         info->transport_account = g_object_ref (transport_account);
1395         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1396         info->callback = callback;
1397         info->user_data = user_data;
1398
1399         g_debug ("--- CREATE MESSAGE");
1400         modest_mail_operation_notify_start (self);
1401         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1402                                           attachments_list, images_list, priority_flags,
1403                                           references, in_reply_to,
1404                                           modest_mail_operation_save_to_drafts_cb, info);
1405 }
1406
1407 typedef struct
1408 {
1409         ModestMailOperation *mail_op;
1410         TnyMimePart *mime_part;
1411         gssize size;
1412         GetMimePartSizeCallback callback;
1413         gpointer userdata;
1414 } GetMimePartSizeInfo;
1415
1416 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1417 /* We use this folder observer to track the headers that have been
1418  * added to a folder */
1419 typedef struct {
1420         GObject parent;
1421         TnyList *new_headers;
1422 } InternalFolderObserver;
1423
1424 typedef struct {
1425         GObjectClass parent;
1426 } InternalFolderObserverClass;
1427
1428 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1429
1430 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1431                          internal_folder_observer,
1432                          G_TYPE_OBJECT,
1433                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1434
1435
1436 static void
1437 foreach_add_item (gpointer header, gpointer user_data)
1438 {
1439         tny_list_append (TNY_LIST (user_data), G_OBJECT (header));
1440 }
1441
1442 /* This is the method that looks for new messages in a folder */
1443 static void
1444 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1445 {
1446         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1447         
1448         TnyFolderChangeChanged changed;
1449
1450         changed = tny_folder_change_get_changed (change);
1451
1452         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1453                 TnyList *list;
1454
1455                 /* Get added headers */
1456                 list = tny_simple_list_new ();
1457                 tny_folder_change_get_added_headers (change, list);
1458
1459                 /* Add them to the folder observer */
1460                 tny_list_foreach (list, foreach_add_item, 
1461                                   derived->new_headers);
1462
1463                 g_object_unref (G_OBJECT (list));
1464         }
1465 }
1466
1467 static void
1468 internal_folder_observer_init (InternalFolderObserver *self) 
1469 {
1470         self->new_headers = tny_simple_list_new ();
1471 }
1472 static void
1473 internal_folder_observer_finalize (GObject *object) 
1474 {
1475         InternalFolderObserver *self;
1476
1477         self = (InternalFolderObserver *) object;
1478         g_object_unref (self->new_headers);
1479
1480         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1481 }
1482 static void
1483 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1484 {
1485         iface->update = internal_folder_observer_update;
1486 }
1487 static void
1488 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1489 {
1490         GObjectClass *object_class;
1491
1492         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1493         object_class = (GObjectClass*) klass;
1494         object_class->finalize = internal_folder_observer_finalize;
1495 }
1496
1497 static void
1498 destroy_update_account_info (UpdateAccountInfo *info)
1499 {
1500         g_free (info->account_name);
1501         g_object_unref (info->folders);
1502         g_object_unref (info->mail_op);
1503         g_slice_free (UpdateAccountInfo, info);
1504 }
1505
1506
1507 static void
1508 update_account_send_mail (UpdateAccountInfo *info)
1509 {
1510         TnyTransportAccount *transport_account = NULL;
1511         ModestTnyAccountStore *account_store;
1512
1513         if (info->update_folder_counts)
1514                 return;
1515
1516         account_store = modest_runtime_get_account_store ();
1517
1518         /* We don't try to send messages while sending mails is blocked */
1519         if (modest_tny_account_store_is_send_mail_blocked (account_store))
1520                 return;
1521
1522         /* Get the transport account */
1523         transport_account = (TnyTransportAccount *) 
1524                 modest_tny_account_store_get_server_account (account_store, info->account_name, 
1525                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1526
1527         if (transport_account) {
1528                 ModestTnySendQueue *send_queue;
1529                 TnyFolder *outbox;
1530                 guint num_messages;
1531
1532                 send_queue = modest_runtime_get_send_queue (transport_account, TRUE);
1533                 g_object_unref (transport_account);
1534
1535                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1536                         /* Get outbox folder */
1537                         outbox = tny_send_queue_get_outbox (TNY_SEND_QUEUE (send_queue));
1538                         if (outbox) { /* this could fail in some cases */
1539                                 num_messages = tny_folder_get_all_count (outbox);
1540                                 g_object_unref (outbox);
1541                         } else {
1542                                 g_warning ("%s: could not get outbox", __FUNCTION__);
1543                                 num_messages = 0;
1544                         }
1545
1546                         if (num_messages != 0) {
1547                                 ModestMailOperation *mail_op;
1548                                 /* Reenable suspended items */
1549                                 mail_op = modest_mail_operation_new (NULL);
1550                                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1551                                                                  mail_op);
1552                                 modest_mail_operation_queue_wakeup (mail_op, MODEST_TNY_SEND_QUEUE (send_queue));
1553
1554                                 /* Try to send */
1555                                 modest_tny_send_queue_set_requested_send_receive (MODEST_TNY_SEND_QUEUE (send_queue), 
1556                                                                                   info->interactive);
1557                         }
1558                 }
1559         }
1560 }
1561
1562 static void
1563 update_account_get_msg_async_cb (TnyFolder *folder, 
1564                                  gboolean canceled, 
1565                                  TnyMsg *msg, 
1566                                  GError *err, 
1567                                  gpointer user_data)
1568 {
1569         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1570         ModestMailOperationPrivate *priv;
1571
1572         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (msg_info->mail_op);
1573         priv->done++;
1574
1575         if (TNY_IS_MSG (msg)) {
1576                 TnyHeader *header = tny_msg_get_header (msg);
1577
1578                 if (header) {
1579                         ModestMailOperationState *state;
1580                         state = modest_mail_operation_clone_state (msg_info->mail_op);
1581                         msg_info->sum_total_bytes += tny_header_get_message_size (header);
1582                         state->bytes_done = msg_info->sum_total_bytes;
1583                         state->bytes_total = msg_info->total_bytes;
1584
1585                         /* Notify the status change. Only notify about changes
1586                            referred to bytes */
1587                         g_signal_emit (G_OBJECT (msg_info->mail_op), 
1588                                        signals[PROGRESS_CHANGED_SIGNAL], 
1589                                        0, state, NULL);
1590
1591                         g_object_unref (header);
1592                         g_slice_free (ModestMailOperationState, state);
1593                 }
1594         }
1595
1596         if (priv->done == priv->total) {
1597                 TnyList *new_headers;
1598                 UpdateAccountInfo *info;
1599
1600                 /* After getting all the messages send the ones in the
1601                    outboxes */
1602                 info = (UpdateAccountInfo *) msg_info->user_data;
1603                 update_account_send_mail (info);
1604
1605                 /* Check if the operation was a success */
1606                 if (!priv->error)
1607                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1608                 
1609                 /* Call the user callback and free */
1610                 new_headers = tny_iterator_get_list (msg_info->more_msgs);
1611                 update_account_notify_user_and_free (info, new_headers);
1612
1613                 /* Delete the helper */
1614                 if (msg_info->msg)
1615                         g_object_unref (msg_info->msg);
1616                 g_object_unref (msg_info->more_msgs);
1617                 g_object_unref (msg_info->mail_op);
1618                 g_slice_free (GetMsgInfo, msg_info);
1619         }
1620 }
1621
1622 static void
1623 update_account_notify_user_and_free (UpdateAccountInfo *info, 
1624                                      TnyList *new_headers)
1625 {
1626         /* Set the account back to not busy */
1627         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), 
1628                                              info->account_name, FALSE);
1629         
1630         /* User callback */
1631         if (info->callback)
1632                 info->callback (info->mail_op, new_headers, info->user_data);
1633         
1634         /* Mail operation end */
1635         modest_mail_operation_notify_end (info->mail_op);
1636
1637         /* Frees */
1638         if (new_headers)
1639                 g_object_unref (new_headers);
1640         destroy_update_account_info (info);
1641 }
1642
1643 static void
1644 inbox_refreshed_cb (TnyFolder *inbox, 
1645                     gboolean canceled, 
1646                     GError *err, 
1647                     gpointer user_data)
1648 {       
1649         UpdateAccountInfo *info;
1650         ModestMailOperationPrivate *priv;
1651         TnyIterator *new_headers_iter;
1652         GPtrArray *new_headers_array = NULL;
1653         gint max_size = G_MAXINT, retrieve_limit, i;
1654         ModestAccountMgr *mgr;
1655         ModestAccountRetrieveType retrieve_type;
1656         TnyList *new_headers = NULL;
1657         gboolean headers_only;
1658         time_t time_to_store;
1659
1660         info = (UpdateAccountInfo *) user_data;
1661         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1662         mgr = modest_runtime_get_account_mgr ();
1663
1664         if (canceled || err) {
1665                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1666                 if (err)
1667                         priv->error = g_error_copy (err);
1668                 else
1669                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1670                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1671                                      "canceled");
1672
1673                 if (inbox)
1674                         tny_folder_remove_observer (inbox, info->inbox_observer);
1675                 g_object_unref (info->inbox_observer);
1676                 info->inbox_observer = NULL;
1677
1678                 /* Notify the user about the error and then exit */
1679                 update_account_notify_user_and_free (info, NULL);
1680                 return;
1681         }
1682
1683         if (!inbox) {
1684                 /* Try to send anyway */
1685                 goto send_mail;
1686         }
1687
1688         if (!info->update_folder_counts) {
1689                 /* Set the last updated as the current time */
1690 #ifdef MODEST_USE_LIBTIME
1691                 struct tm utc_tm;
1692                 time_get_utc (&utc_tm);
1693                 time_to_store = time_mktime (&utc_tm, "GMT");
1694 #else
1695                 time_to_store = time (NULL);
1696 #endif
1697                 modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time_to_store);
1698
1699                 /* Get the message max size */
1700                 max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1701                                                  MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1702                 if (max_size == 0)
1703                         max_size = G_MAXINT;
1704                 else
1705                         max_size = max_size * KB;
1706         }
1707
1708         /* Create the new headers array. We need it to sort the
1709            new headers by date */
1710         new_headers_array = g_ptr_array_new ();
1711         if (info->inbox_observer) {
1712                 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1713                 if (!tny_iterator_is_done (new_headers_iter)) {
1714                         modest_platform_emit_folder_updated_signal (info->account_name, tny_folder_get_id (TNY_FOLDER (inbox)));
1715                 }
1716                 while (!tny_iterator_is_done (new_headers_iter)) {
1717                         TnyHeader *header = NULL;
1718
1719                         header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1720                         /* Apply per-message size limits */
1721                         if (tny_header_get_message_size (header) < max_size)
1722                                 g_ptr_array_add (new_headers_array, g_object_ref (header));
1723
1724                         g_object_unref (header);
1725                         tny_iterator_next (new_headers_iter);
1726                 }
1727                 g_object_unref (new_headers_iter);
1728
1729                 tny_folder_remove_observer (inbox, info->inbox_observer);
1730                 g_object_unref (info->inbox_observer);
1731                 info->inbox_observer = NULL;
1732         }
1733
1734         if (new_headers_array->len == 0) {
1735                 g_ptr_array_free (new_headers_array, FALSE);
1736                 goto send_mail;
1737         }
1738
1739         /* Get per-account message amount retrieval limit */
1740         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1741         if (retrieve_limit == 0)
1742                 retrieve_limit = G_MAXINT;
1743
1744         /* Get per-account retrieval type */
1745         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1746         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1747
1748         /* Order by date */
1749         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1750
1751         /* Copy the headers to a list and free the array */
1752         new_headers = tny_simple_list_new ();
1753         for (i=0; i < new_headers_array->len; i++) {
1754                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1755                 /* We want the first element to be the most recent
1756                    one, that's why we reverse the list */
1757                 tny_list_prepend (new_headers, G_OBJECT (header));
1758         }
1759         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1760         g_ptr_array_free (new_headers_array, FALSE);
1761
1762         if (!headers_only && (tny_list_get_length (new_headers) > 0)) {
1763                 gint msg_num = 0;
1764                 TnyIterator *iter;
1765                 GetMsgInfo *msg_info;
1766
1767                 priv->done = 0;
1768                 priv->total = MIN (tny_list_get_length (new_headers), retrieve_limit);
1769
1770                 iter = tny_list_create_iterator (new_headers);
1771
1772                 /* Create the message info */
1773                 msg_info = g_slice_new0 (GetMsgInfo);
1774                 msg_info->mail_op = g_object_ref (info->mail_op);
1775                 msg_info->total_bytes = compute_message_list_size (new_headers, priv->total);
1776                 msg_info->more_msgs = g_object_ref (iter);
1777                 msg_info->msg = NULL;
1778                 msg_info->user_data = info;
1779
1780                 while ((msg_num < priv->total ) && !tny_iterator_is_done (iter)) {
1781                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1782                         TnyFolder *folder = tny_header_get_folder (header);
1783
1784                         /* Get message in an async way */
1785                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb,
1786                                                   NULL, msg_info);
1787
1788                         g_object_unref (folder);
1789                         g_object_unref (header);
1790
1791                         msg_num++;
1792                         tny_iterator_next (iter);
1793                 }
1794                 g_object_unref (iter);
1795                 g_object_unref (new_headers);
1796
1797                 /* The mail operation will finish when the last
1798                    message is retrieved */
1799                 return;
1800         }
1801  send_mail:
1802         /* If we don't have to retrieve the new messages then
1803            simply send mail */
1804         update_account_send_mail (info);
1805
1806         /* Check if the operation was a success */
1807         if (!priv->error)
1808                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1809
1810         /* Call the user callback and free */
1811         update_account_notify_user_and_free (info, new_headers);
1812 }
1813
1814 static void
1815 inbox_refresh_status_update (GObject *obj,
1816                              TnyStatus *status,
1817                              gpointer user_data)
1818 {
1819         UpdateAccountInfo *info = NULL;
1820         ModestMailOperation *self = NULL;
1821         ModestMailOperationPrivate *priv = NULL;
1822         ModestMailOperationState *state;
1823
1824         g_return_if_fail (user_data != NULL);
1825         g_return_if_fail (status != NULL);
1826
1827         /* Show only the status information we want */
1828         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
1829                 return;
1830
1831         info = (UpdateAccountInfo *) user_data;
1832         self = info->mail_op;
1833         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1834
1835         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1836
1837         priv->done = status->position;
1838         priv->total = status->of_total;
1839
1840         state = modest_mail_operation_clone_state (self);
1841
1842         /* This is not a GDK lock because we are a Tinymail callback and
1843          * Tinymail already acquires the Gdk lock */
1844         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1845
1846         g_slice_free (ModestMailOperationState, state);
1847 }
1848
1849 static void 
1850 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1851                           gboolean canceled,
1852                           TnyList *list, 
1853                           GError *err, 
1854                           gpointer user_data)
1855 {
1856         UpdateAccountInfo *info;
1857         ModestMailOperationPrivate *priv;
1858     
1859         info = (UpdateAccountInfo *) user_data;
1860         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1861
1862         if (err || canceled) {
1863                 /* If the error was previosly set by another callback
1864                    don't set it again */
1865                 if (!priv->error) {
1866                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1867                         if (err)
1868                                 priv->error = g_error_copy (err);
1869                         else
1870                                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1871                                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1872                                              "canceled");
1873                 }
1874         } else { 
1875                 /* We're not getting INBOX children if we don't want to poke all */
1876                 TnyIterator *iter = tny_list_create_iterator (list);
1877                 while (!tny_iterator_is_done (iter)) {
1878                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1879
1880                         /* Add to the list of all folders */
1881                         tny_list_append (info->folders, (GObject *) folder);
1882                         
1883                         if (info->poke_all) {
1884                                 TnyList *folders = tny_simple_list_new ();
1885                                 /* Add pending call */
1886                                 info->pending_calls++;
1887                                 
1888                                 tny_folder_store_get_folders_async (folder, folders, NULL, FALSE,
1889                                                                     recurse_folders_async_cb, 
1890                                                                     NULL, info);
1891                                 g_object_unref (folders);
1892                         }
1893                         
1894                         g_object_unref (G_OBJECT (folder));
1895                         
1896                         tny_iterator_next (iter);           
1897                 }
1898                 g_object_unref (G_OBJECT (iter));
1899         }
1900
1901         /* Remove my own pending call */
1902         info->pending_calls--;
1903
1904         /* This means that we have all the folders */
1905         if (info->pending_calls == 0) {
1906                 TnyIterator *iter_all_folders;
1907                 TnyFolder *inbox = NULL;
1908
1909                 /* If there was any error do not continue */
1910                 if (priv->error) {
1911                         update_account_notify_user_and_free (info, NULL);
1912                         return;
1913                 }
1914
1915                 iter_all_folders = tny_list_create_iterator (info->folders);
1916
1917                 /* Do a poke status over all folders */
1918                 while (!tny_iterator_is_done (iter_all_folders) &&
1919                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1920                         TnyFolder *folder = NULL;
1921
1922                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1923
1924                         if (!info->update_folder_counts && tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1925                                 /* Get a reference to the INBOX */
1926                                 inbox = g_object_ref (folder);
1927                         } else {
1928                                 /* Issue a poke status over the folder */
1929                                 if (info->poke_all)
1930                                         tny_folder_poke_status (folder);
1931                         }
1932
1933                         /* Free and go to next */
1934                         g_object_unref (folder);
1935                         tny_iterator_next (iter_all_folders);
1936                 }
1937                 g_object_unref (iter_all_folders);
1938
1939                 /* Refresh the INBOX */
1940                 if (inbox) {
1941                         /* Refresh the folder. Our observer receives
1942                          * the new emails during folder refreshes, so
1943                          * we can use observer->new_headers
1944                          */
1945                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1946                         tny_folder_add_observer (inbox, info->inbox_observer);
1947
1948                         /* Refresh the INBOX */
1949                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, inbox_refresh_status_update, info);
1950                         g_object_unref (inbox);
1951                 } else {
1952                         /* We could not perform the inbox refresh but
1953                            we'll try to send mails anyway */
1954                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1955                 }
1956         }
1957 }
1958
1959 void
1960 modest_mail_operation_update_account (ModestMailOperation *self,
1961                                       const gchar *account_name,
1962                                       gboolean poke_all,
1963                                       gboolean interactive,
1964                                       UpdateAccountCallback callback,
1965                                       gpointer user_data)
1966 {
1967         UpdateAccountInfo *info = NULL;
1968         ModestMailOperationPrivate *priv = NULL;
1969         ModestTnyAccountStore *account_store = NULL;
1970         TnyList *folders;
1971         ModestMailOperationState *state;
1972
1973         /* Init mail operation */
1974         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1975         priv->total = 0;
1976         priv->done  = 0;
1977         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1978         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE;
1979
1980         /* Get the store account */
1981         account_store = modest_runtime_get_account_store ();
1982         priv->account =
1983                 modest_tny_account_store_get_server_account (account_store,
1984                                                              account_name,
1985                                                              TNY_ACCOUNT_TYPE_STORE);
1986
1987         /* The above function could return NULL */
1988         if (!priv->account) {
1989                 /* Check if the operation was a success */
1990                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1991                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1992                              "no account");
1993                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1994
1995                 /* Call the user callback */
1996                 if (callback)
1997                         callback (self, NULL, user_data);
1998
1999                 /* Notify about operation end */
2000                 modest_mail_operation_notify_end (self);
2001
2002                 return;
2003         }
2004         
2005         /* We have once seen priv->account getting finalized during this code,
2006          * therefore adding a reference (bug #82296) */
2007         
2008         g_object_ref (priv->account);
2009
2010         /* Create the helper object */
2011         info = g_slice_new0 (UpdateAccountInfo);
2012         info->pending_calls = 1;
2013         info->folders = tny_simple_list_new ();
2014         info->mail_op = g_object_ref (self);
2015         info->poke_all = poke_all;
2016         info->interactive = interactive;
2017         info->update_folder_counts = FALSE;
2018         info->account_name = g_strdup (account_name);
2019         info->callback = callback;
2020         info->user_data = user_data;
2021
2022         /* Set account busy */
2023         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2024         modest_mail_operation_notify_start (self);
2025
2026         /* notify about the start of the operation */ 
2027         state = modest_mail_operation_clone_state (self);
2028         state->done = 0;
2029         state->total = 0;
2030
2031         /* Start notifying progress */
2032         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2033         g_slice_free (ModestMailOperationState, state);
2034         
2035         /* Get all folders and continue in the callback */ 
2036         folders = tny_simple_list_new ();
2037         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2038                                             folders, NULL, TRUE,
2039                                             recurse_folders_async_cb, 
2040                                             NULL, info);
2041         g_object_unref (folders);
2042         
2043         g_object_unref (priv->account);
2044         
2045 }
2046
2047 void
2048 modest_mail_operation_update_folder_counts (ModestMailOperation *self,
2049                                       const gchar *account_name)
2050 {
2051         UpdateAccountInfo *info = NULL;
2052         ModestMailOperationPrivate *priv = NULL;
2053         ModestTnyAccountStore *account_store = NULL;
2054         TnyList *folders;
2055         ModestMailOperationState *state;
2056
2057         /* Init mail operation */
2058         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2059         priv->total = 0;
2060         priv->done  = 0;
2061         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2062         priv->op_type = MODEST_MAIL_OPERATION_TYPE_UPDATE_FOLDER_COUNTS;
2063
2064         /* Get the store account */
2065         account_store = modest_runtime_get_account_store ();
2066         priv->account =
2067                 modest_tny_account_store_get_server_account (account_store,
2068                                                              account_name,
2069                                                              TNY_ACCOUNT_TYPE_STORE);
2070
2071         /* The above function could return NULL */
2072         if (!priv->account) {
2073                 /* Check if the operation was a success */
2074                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2075                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2076                              "no account");
2077                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2078
2079                 /* Notify about operation end */
2080                 modest_mail_operation_notify_end (self);
2081
2082                 return;
2083         }
2084         
2085         /* We have once seen priv->account getting finalized during this code,
2086          * therefore adding a reference (bug #82296) */
2087         
2088         g_object_ref (priv->account);
2089
2090         /* Create the helper object */
2091         info = g_slice_new0 (UpdateAccountInfo);
2092         info->pending_calls = 1;
2093         info->folders = tny_simple_list_new ();
2094         info->mail_op = g_object_ref (self);
2095         info->poke_all = TRUE;
2096         info->interactive = FALSE;
2097         info->update_folder_counts = TRUE;
2098         info->account_name = g_strdup (account_name);
2099         info->callback = NULL;
2100         info->user_data = NULL;
2101
2102         /* Set account busy */
2103         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
2104         modest_mail_operation_notify_start (self);
2105
2106         /* notify about the start of the operation */ 
2107         state = modest_mail_operation_clone_state (self);
2108         state->done = 0;
2109         state->total = 0;
2110
2111         /* Start notifying progress */
2112         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2113         g_slice_free (ModestMailOperationState, state);
2114         
2115         /* Get all folders and continue in the callback */ 
2116         folders = tny_simple_list_new ();
2117         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (priv->account),
2118                                             folders, NULL, TRUE,
2119                                             recurse_folders_async_cb, 
2120                                             NULL, info);
2121         g_object_unref (folders);
2122         
2123         g_object_unref (priv->account);
2124         
2125 }
2126
2127 /*
2128  * Used to notify the queue from the main
2129  * loop. We call it inside an idle call to achieve that
2130  */
2131 static gboolean
2132 idle_notify_queue (gpointer data)
2133 {
2134         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
2135
2136         gdk_threads_enter ();
2137         modest_mail_operation_notify_end (mail_op);
2138         gdk_threads_leave ();
2139         g_object_unref (mail_op);
2140
2141         return FALSE;
2142 }
2143
2144 static int
2145 compare_headers_by_date (gconstpointer a,
2146                          gconstpointer b)
2147 {
2148         TnyHeader **header1, **header2;
2149         time_t sent1, sent2;
2150
2151         header1 = (TnyHeader **) a;
2152         header2 = (TnyHeader **) b;
2153
2154         sent1 = tny_header_get_date_sent (*header1);
2155         sent2 = tny_header_get_date_sent (*header2);
2156
2157         /* We want the most recent ones (greater time_t) at the
2158            beginning */
2159         if (sent1 < sent2)
2160                 return -1;
2161         else
2162                 return 1;
2163 }
2164
2165
2166 /* ******************************************************************* */
2167 /* ************************** STORE  ACTIONS ************************* */
2168 /* ******************************************************************* */
2169
2170 typedef struct {
2171         ModestMailOperation *mail_op;
2172         CreateFolderUserCallback callback;
2173         gpointer user_data;
2174 } CreateFolderInfo;
2175
2176
2177 static void
2178 create_folder_cb (TnyFolderStore *parent_folder, 
2179                   gboolean canceled, 
2180                   TnyFolder *new_folder, 
2181                   GError *err, 
2182                   gpointer user_data)
2183 {
2184         ModestMailOperationPrivate *priv;
2185         CreateFolderInfo *info;
2186
2187         info = (CreateFolderInfo *) user_data;
2188         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2189
2190         if (canceled || err) {
2191                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2192                 if (err)
2193                         priv->error = g_error_copy (err);
2194                 else
2195                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2196                                      MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2197                                      "canceled");               
2198         } else {
2199                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2200         }
2201
2202         /* The user will unref the new_folder */
2203         if (info->callback)
2204                 info->callback (info->mail_op, parent_folder, 
2205                                 new_folder, info->user_data);
2206         
2207         /* Notify about operation end */
2208         modest_mail_operation_notify_end (info->mail_op);
2209
2210         /* Frees */
2211         g_object_unref (info->mail_op);
2212         g_slice_free (CreateFolderInfo, info);
2213 }
2214
2215 void
2216 modest_mail_operation_create_folder (ModestMailOperation *self,
2217                                      TnyFolderStore *parent,
2218                                      const gchar *name,
2219                                      CreateFolderUserCallback callback,
2220                                      gpointer user_data)
2221 {
2222         ModestMailOperationPrivate *priv;
2223
2224         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2225         g_return_if_fail (name);
2226         
2227         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2228         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2229         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
2230                 g_object_ref (parent) : 
2231                 modest_tny_folder_get_account (TNY_FOLDER (parent));
2232
2233         /* Check for already existing folder */
2234         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
2235                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2236                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2237                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2238                              _CS("ckdg_ib_folder_already_exists"));
2239         }
2240
2241         /* Check parent */
2242         if (TNY_IS_FOLDER (parent)) {
2243                 /* Check folder rules */
2244                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2245                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2246                         /* Set status failed and set an error */
2247                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2248                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2249                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2250                                      _("mail_in_ui_folder_create_error"));
2251                 }
2252         }
2253
2254         if (!priv->error && (!strcmp (name, " ") || strchr (name, '/'))) {
2255                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2256                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2257                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2258                              _("mail_in_ui_folder_create_error"));
2259         }
2260
2261         if (!priv->error) {
2262                 CreateFolderInfo *info;
2263
2264                 info = g_slice_new0 (CreateFolderInfo);
2265                 info->mail_op = g_object_ref (self);
2266                 info->callback = callback;
2267                 info->user_data = user_data;
2268
2269                 modest_mail_operation_notify_start (self);
2270
2271                 /* Create the folder */
2272                 tny_folder_store_create_folder_async (parent, name, create_folder_cb, 
2273                                                       NULL, info);
2274         } else {
2275                 /* Call the user callback anyway */
2276                 if (callback)
2277                         callback (self, parent, NULL, user_data);
2278                 /* Notify about operation end */
2279                 modest_mail_operation_notify_end (self);
2280         }
2281 }
2282
2283 void
2284 modest_mail_operation_remove_folder (ModestMailOperation *self,
2285                                      TnyFolder           *folder,
2286                                      gboolean             remove_to_trash)
2287 {
2288         ModestMailOperationPrivate *priv;
2289         ModestTnyFolderRules rules;
2290
2291         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2292         g_return_if_fail (TNY_IS_FOLDER (folder));
2293         
2294         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2295         
2296         /* Check folder rules */
2297         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2298         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
2299                 /* Set status failed and set an error */
2300                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2301                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2302                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2303                              _("mail_in_ui_folder_delete_error"));
2304                 goto end;
2305         }
2306
2307         /* Get the account */
2308         priv->account = modest_tny_folder_get_account (folder);
2309         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2310
2311         /* Delete folder or move to trash */
2312         if (remove_to_trash) {
2313                 TnyFolder *trash_folder = NULL;
2314                 trash_folder = modest_tny_account_get_special_folder (priv->account,
2315                                                                       TNY_FOLDER_TYPE_TRASH);
2316                 /* TODO: error_handling */
2317                 if (trash_folder) {
2318                         modest_mail_operation_notify_start (self);
2319                         modest_mail_operation_xfer_folder (self, folder,
2320                                                     TNY_FOLDER_STORE (trash_folder), 
2321                                                     TRUE, NULL, NULL);
2322                         g_object_unref (trash_folder);
2323                 } else {
2324                         g_warning ("%s: modest_tny_account_get_special_folder(..) returned a NULL trash folder", __FUNCTION__);
2325                 }
2326         } else {
2327                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
2328                 if (parent) {
2329                         modest_mail_operation_notify_start (self);
2330                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
2331                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
2332                         
2333                         if (!priv->error)
2334                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2335
2336                         g_object_unref (parent);
2337                 } else
2338                         g_warning ("%s: could not get parent folder", __FUNCTION__);
2339         }
2340
2341  end:
2342         /* Notify about operation end */
2343         modest_mail_operation_notify_end (self);
2344 }
2345
2346 static void
2347 transfer_folder_status_cb (GObject *obj,
2348                            TnyStatus *status,
2349                            gpointer user_data)
2350 {
2351         ModestMailOperation *self;
2352         ModestMailOperationPrivate *priv;
2353         ModestMailOperationState *state;
2354         XFerFolderAsyncHelper *helper;
2355
2356         g_return_if_fail (status != NULL);
2357
2358         /* Show only the status information we want */
2359         if (status->code != TNY_FOLDER_STATUS_CODE_COPY_FOLDER)
2360                 return;
2361
2362         helper = (XFerFolderAsyncHelper *) user_data;
2363         g_return_if_fail (helper != NULL);
2364
2365         self = helper->mail_op;
2366         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2367
2368         priv->done = status->position;
2369         priv->total = status->of_total;
2370
2371         state = modest_mail_operation_clone_state (self);
2372
2373         /* This is not a GDK lock because we are a Tinymail callback
2374          * which is already GDK locked by Tinymail */
2375
2376         /* no gdk_threads_enter (), CHECKED */
2377
2378         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
2379
2380         /* no gdk_threads_leave (), CHECKED */
2381
2382         g_slice_free (ModestMailOperationState, state);
2383 }
2384
2385 static void
2386 transfer_folder_cb (TnyFolder *folder, 
2387                     gboolean cancelled, 
2388                     TnyFolderStore *into, 
2389                     TnyFolder *new_folder, 
2390                     GError *err, 
2391                     gpointer user_data)
2392 {
2393         XFerFolderAsyncHelper *helper;
2394         ModestMailOperation *self = NULL;
2395         ModestMailOperationPrivate *priv = NULL;
2396
2397         helper = (XFerFolderAsyncHelper *) user_data;
2398         g_return_if_fail (helper != NULL);
2399
2400         self = helper->mail_op;
2401         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2402
2403         if (err) {
2404                 priv->error = g_error_copy (err);
2405                 priv->done = 0;
2406                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2407         } else if (cancelled) {
2408                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2409                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2410                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
2411                              _("Transference of %s was cancelled."),
2412                              tny_folder_get_name (folder));
2413         } else {
2414                 priv->done = 1;
2415                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2416         }
2417
2418         /* Update state of new folder */
2419         if (new_folder) {
2420                 tny_folder_refresh_async (new_folder, NULL, NULL, NULL);
2421                 tny_folder_poke_status (new_folder);
2422         }
2423
2424         /* Notify about operation end */
2425         modest_mail_operation_notify_end (self);
2426
2427         /* If user defined callback function was defined, call it */
2428         if (helper->user_callback) {
2429
2430                 /* This is not a GDK lock because we are a Tinymail callback
2431                  * which is already GDK locked by Tinymail */
2432
2433                 /* no gdk_threads_enter (), CHECKED */
2434                 helper->user_callback (self, new_folder, helper->user_data);
2435                 /* no gdk_threads_leave () , CHECKED */
2436         }
2437
2438         /* Free */
2439         g_object_unref (helper->mail_op);
2440         g_slice_free   (XFerFolderAsyncHelper, helper);
2441 }
2442
2443 /**
2444  *
2445  * This function checks if the new name is a valid name for our local
2446  * folders account. The new name could not be the same than then name
2447  * of any of the mandatory local folders
2448  *
2449  * We can not rely on tinymail because tinymail does not check the
2450  * name of the virtual folders that the account could have in the case
2451  * that we're doing a rename (because it directly calls Camel which
2452  * knows nothing about our virtual folders). 
2453  *
2454  * In the case of an actual copy/move (i.e. move/copy a folder between
2455  * accounts) tinymail uses the tny_folder_store_create_account which
2456  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
2457  * checks the new name of the folder, so this call in that case
2458  * wouldn't be needed. *But* NOTE that if tinymail changes its
2459  * implementation (if folder transfers within the same account is no
2460  * longer implemented as a rename) this call will allow Modest to work
2461  * perfectly
2462  *
2463  * If the new name is not valid, this function will set the status to
2464  * failed and will set also an error in the mail operation
2465  */
2466 static gboolean
2467 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
2468                                  TnyFolderStore *into,
2469                                  const gchar *new_name)
2470 {
2471         if (TNY_IS_ACCOUNT (into) && 
2472             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
2473             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
2474                                                                  new_name)) {
2475                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2476                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2477                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
2478                              _CS("ckdg_ib_folder_already_exists"));
2479                 return FALSE;
2480         } else
2481                 return TRUE;
2482 }
2483
2484 void
2485 modest_mail_operation_xfer_folder (ModestMailOperation *self,
2486                                    TnyFolder *folder,
2487                                    TnyFolderStore *parent,
2488                                    gboolean delete_original,
2489                                    XferFolderAsyncUserCallback user_callback,
2490                                    gpointer user_data)
2491 {
2492         ModestMailOperationPrivate *priv = NULL;
2493         ModestTnyFolderRules parent_rules = 0, rules; 
2494         XFerFolderAsyncHelper *helper = NULL;
2495         const gchar *folder_name = NULL;
2496         const gchar *error_msg;
2497
2498         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2499         g_return_if_fail (TNY_IS_FOLDER (folder));
2500         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
2501
2502         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2503         folder_name = tny_folder_get_name (folder);
2504
2505         /* Set the error msg */
2506         error_msg = _("mail_in_ui_folder_move_target_error");
2507
2508         /* Get account and set it into mail_operation */
2509         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2510         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2511         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2512
2513         /* Get folder rules */
2514         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2515         if (TNY_IS_FOLDER (parent))
2516                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
2517         
2518         /* Apply operation constraints */
2519         if ((gpointer) parent == (gpointer) folder ||
2520             (!TNY_IS_FOLDER_STORE (parent)) || 
2521             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
2522                 /* Folder rules */
2523                 goto error;
2524         } else if (TNY_IS_FOLDER (parent) && 
2525                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
2526                 /* Folder rules */
2527                 goto error;
2528
2529         } else if (TNY_IS_FOLDER (parent) &&
2530                    TNY_IS_FOLDER_STORE (folder) &&
2531                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
2532                                                   TNY_FOLDER_STORE (folder))) {
2533                 /* Do not move a parent into a child */
2534                 goto error;
2535         } else if (TNY_IS_FOLDER_STORE (parent) &&
2536                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
2537                 /* Check that the new folder name is not used by any
2538                    parent subfolder */
2539                 goto error;     
2540         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
2541                 /* Check that the new folder name is not used by any
2542                    special local folder */
2543                 goto error;
2544         } else {
2545                 /* Create the helper */
2546                 helper = g_slice_new0 (XFerFolderAsyncHelper);
2547                 helper->mail_op = g_object_ref (self);
2548                 helper->user_callback = user_callback;
2549                 helper->user_data = user_data;
2550                 
2551                 /* Move/Copy folder */
2552                 modest_mail_operation_notify_start (self);
2553                 tny_folder_copy_async (folder,
2554                                        parent,
2555                                        tny_folder_get_name (folder),
2556                                        delete_original,
2557                                        transfer_folder_cb,
2558                                        transfer_folder_status_cb,
2559                                        helper);
2560                 return;
2561         }
2562
2563  error:
2564         /* Set status failed and set an error */
2565         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2566         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2567                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2568                      "%s", error_msg);
2569
2570         /* Call the user callback if exists */
2571         if (user_callback)
2572                 user_callback (self, NULL, user_data);
2573
2574         /* Notify the queue */
2575         modest_mail_operation_notify_end (self);
2576 }
2577
2578 void
2579 modest_mail_operation_rename_folder (ModestMailOperation *self,
2580                                      TnyFolder *folder,
2581                                      const gchar *name,
2582                                      XferFolderAsyncUserCallback user_callback,
2583                                      gpointer user_data)
2584 {
2585         ModestMailOperationPrivate *priv;
2586         ModestTnyFolderRules rules;
2587         XFerFolderAsyncHelper *helper;
2588
2589         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2590         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2591         g_return_if_fail (name);
2592         
2593         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2594
2595         /* Get account and set it into mail_operation */
2596         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2597         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2598
2599         /* Check folder rules */
2600         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2601         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2602                 goto error;
2603         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2604                 goto error;
2605         } else {
2606                 TnyFolderStore *into;
2607
2608                 into = tny_folder_get_folder_store (folder);    
2609
2610                 /* Check that the new folder name is not used by any
2611                    special local folder */
2612                 if (new_name_valid_if_local_account (priv, into, name)) {
2613                         /* Create the helper */
2614                         helper = g_slice_new0 (XFerFolderAsyncHelper);
2615                         helper->mail_op = g_object_ref(self);
2616                         helper->user_callback = user_callback;
2617                         helper->user_data = user_data;
2618                 
2619                         /* Rename. Camel handles folder subscription/unsubscription */
2620                         modest_mail_operation_notify_start (self);
2621                         tny_folder_copy_async (folder, into, name, TRUE,
2622                                                transfer_folder_cb,
2623                                                transfer_folder_status_cb,
2624                                                helper);
2625                         g_object_unref (into);
2626                 } else {
2627                         g_object_unref (into);
2628                         goto error;
2629                 }
2630
2631                 return;
2632         }
2633  error:
2634         /* Set status failed and set an error */
2635         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2636         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2637                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2638                      _("FIXME: unable to rename"));
2639         
2640         if (user_callback)
2641                 user_callback (self, NULL, user_data);
2642
2643         /* Notify about operation end */
2644         modest_mail_operation_notify_end (self);
2645 }
2646
2647 /* ******************************************************************* */
2648 /* **************************  MSG  ACTIONS  ************************* */
2649 /* ******************************************************************* */
2650
2651 void 
2652 modest_mail_operation_find_msg (ModestMailOperation *self,
2653                                 TnyFolder *folder,
2654                                 const gchar *msg_uid,
2655                                 gboolean progress_feedback,
2656                                 GetMsgAsyncUserCallback user_callback,
2657                                 gpointer user_data)
2658 {
2659         GetMsgInfo *helper = NULL;
2660         ModestMailOperationPrivate *priv;
2661         
2662         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2663         g_return_if_fail (msg_uid != NULL);
2664         
2665         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2666         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2667         priv->total = 1;
2668         priv->done = 0;
2669
2670         /* Check memory low */
2671         if (_check_memory_low (self)) {
2672                 if (user_callback)
2673                         user_callback (self, NULL, FALSE, NULL, priv->error, user_data);
2674                 modest_mail_operation_notify_end (self);
2675                 return;
2676         }
2677
2678         /* Get account and set it into mail_operation */
2679         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2680         
2681         /* Check for cached messages */
2682         if (progress_feedback) {
2683                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2684         } else {
2685                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2686         }
2687         
2688         /* Create the helper */
2689         helper = g_slice_new0 (GetMsgInfo);
2690         helper->header = NULL;
2691         helper->mail_op = g_object_ref (self);
2692         helper->user_callback = user_callback;
2693         helper->user_data = user_data;
2694         helper->destroy_notify = NULL;
2695         helper->last_total_bytes = 0;
2696         helper->sum_total_bytes = 0;
2697         helper->total_bytes = 0;
2698         helper->more_msgs = NULL;
2699         helper->get_parts = NULL;
2700         helper->msg = NULL;
2701
2702         modest_mail_operation_notify_start (self);
2703         
2704         /* notify about the start of the operation */ 
2705         ModestMailOperationState *state;
2706         state = modest_mail_operation_clone_state (self);
2707         state->done = 0;
2708         state->total = 0;
2709         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2710                                 0, state, NULL);
2711         g_slice_free (ModestMailOperationState, state);
2712         
2713         tny_folder_find_msg_async (folder, msg_uid, get_msg_async_cb, get_msg_status_cb, helper);
2714 }
2715
2716 void 
2717 modest_mail_operation_get_msg (ModestMailOperation *self,
2718                                TnyHeader *header,
2719                                gboolean progress_feedback,
2720                                GetMsgAsyncUserCallback user_callback,
2721                                gpointer user_data)
2722 {
2723         GetMsgInfo *helper = NULL;
2724         TnyFolder *folder;
2725         ModestMailOperationPrivate *priv;
2726         
2727         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2728         g_return_if_fail (TNY_IS_HEADER (header));
2729         
2730         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2731         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2732         priv->total = 1;
2733         priv->done = 0;
2734
2735         /* Check memory low */
2736         if (_check_memory_low (self)) {
2737                 if (user_callback)
2738                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2739                 modest_mail_operation_notify_end (self);
2740                 return;
2741         }
2742
2743         /* Get account and set it into mail_operation */
2744         folder = tny_header_get_folder (header);
2745         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2746         
2747         /* Check for cached messages */
2748         if (progress_feedback) {
2749                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2750                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2751                 else 
2752                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2753         } else {
2754                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2755         }
2756         
2757         /* Create the helper */
2758         helper = g_slice_new0 (GetMsgInfo);
2759         helper->header = g_object_ref (header);
2760         helper->mail_op = g_object_ref (self);
2761         helper->user_callback = user_callback;
2762         helper->user_data = user_data;
2763         helper->destroy_notify = NULL;
2764         helper->last_total_bytes = 0;
2765         helper->sum_total_bytes = 0;
2766         helper->total_bytes = tny_header_get_message_size (header);
2767         helper->more_msgs = NULL;
2768         helper->get_parts = NULL;
2769         helper->msg = NULL;
2770
2771         modest_mail_operation_notify_start (self);
2772         
2773         /* notify about the start of the operation */ 
2774         ModestMailOperationState *state;
2775         state = modest_mail_operation_clone_state (self);
2776         state->done = 0;
2777         state->total = 0;
2778         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2779                                 0, state, NULL);
2780         g_slice_free (ModestMailOperationState, state);
2781         
2782         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2783
2784         g_object_unref (G_OBJECT (folder));
2785 }
2786
2787 void 
2788 modest_mail_operation_get_msg_and_parts (ModestMailOperation *self,
2789                                          TnyHeader *header,
2790                                          TnyList *parts,
2791                                          gboolean progress_feedback,
2792                                          GetMsgAsyncUserCallback user_callback,
2793                                          gpointer user_data)
2794 {
2795         GetMsgInfo *helper = NULL;
2796         TnyFolder *folder;
2797         ModestMailOperationPrivate *priv;
2798         
2799         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2800         g_return_if_fail (TNY_IS_HEADER (header));
2801         
2802         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2803         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2804         priv->total = 1;
2805         priv->done = 0;
2806
2807         /* Check memory low */
2808         if (_check_memory_low (self)) {
2809                 if (user_callback)
2810                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
2811                 modest_mail_operation_notify_end (self);
2812                 return;
2813         }
2814
2815         /* Get account and set it into mail_operation */
2816         folder = tny_header_get_folder (header);
2817         if (folder == NULL && MODEST_IS_MSG_VIEW_WINDOW (priv->source)) {
2818                 const gchar *acc_name;
2819                 acc_name = modest_window_get_active_account (MODEST_WINDOW (priv->source));
2820                 priv->account = modest_tny_account_store_get_server_account
2821                         (modest_runtime_get_account_store (),
2822                          acc_name,
2823                          TNY_ACCOUNT_TYPE_STORE);
2824                 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (priv->account), 
2825                                                                        modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (priv->source)));
2826         } else {
2827                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2828         }
2829         
2830         /* Check for cached messages */
2831         if (progress_feedback) {
2832                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2833                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2834                 else 
2835                         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2836         } else {
2837                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
2838         }
2839         
2840         /* Create the helper */
2841         helper = g_slice_new0 (GetMsgInfo);
2842         helper->header = g_object_ref (header);
2843         helper->mail_op = g_object_ref (self);
2844         helper->user_callback = user_callback;
2845         helper->user_data = user_data;
2846         helper->destroy_notify = NULL;
2847         helper->last_total_bytes = 0;
2848         helper->sum_total_bytes = 0;
2849         helper->total_bytes = tny_header_get_message_size (header);
2850         helper->more_msgs = NULL;
2851         helper->get_parts = tny_list_create_iterator (parts);
2852         helper->msg = NULL;
2853
2854         modest_mail_operation_notify_start (self);
2855         
2856         /* notify about the start of the operation */ 
2857         ModestMailOperationState *state;
2858         state = modest_mail_operation_clone_state (self);
2859         state->done = 0;
2860         state->total = 0;
2861         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2862                                 0, state, NULL);
2863         g_slice_free (ModestMailOperationState, state);
2864         
2865         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2866
2867         g_object_unref (G_OBJECT (folder));
2868 }
2869
2870 static void     
2871 get_msg_status_cb (GObject *obj,
2872                    TnyStatus *status,  
2873                    gpointer user_data)
2874 {
2875         GetMsgInfo *helper = NULL;
2876
2877         g_return_if_fail (status != NULL);
2878
2879         /* Show only the status information we want */
2880         if (status->code != TNY_FOLDER_STATUS_CODE_GET_MSG)
2881                 return;
2882
2883         helper = (GetMsgInfo *) user_data;
2884         g_return_if_fail (helper != NULL);       
2885
2886         /* Notify progress */
2887         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2888                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2889 }
2890
2891 static void
2892 get_msg_async_get_part_cb (TnyMimePart *self, gboolean cancelled, TnyStream *stream, GError *err, gpointer user_data)
2893 {
2894         GetMsgInfo *helper;
2895         TnyFolder *folder = NULL;
2896
2897         helper = (GetMsgInfo *) user_data;
2898
2899         if (helper->header) {
2900                 folder = tny_header_get_folder (helper->header);
2901         }
2902
2903         get_msg_async_cb (folder, cancelled, helper->msg, err, user_data);
2904
2905         if (folder) g_object_unref (folder);
2906 }
2907
2908 static void
2909 get_msg_async_cb (TnyFolder *folder, 
2910                   gboolean canceled, 
2911                   TnyMsg *msg, 
2912                   GError *err, 
2913                   gpointer user_data)
2914 {
2915         GetMsgInfo *info = NULL;
2916         ModestMailOperationPrivate *priv = NULL;
2917         gboolean finished;
2918
2919         info = (GetMsgInfo *) user_data;
2920
2921         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2922         priv->done++;
2923
2924         if (info->more_msgs) {
2925                 tny_iterator_next (info->more_msgs);
2926                 finished = (tny_iterator_is_done (info->more_msgs));
2927         } else if (info->get_parts) {
2928                 tny_iterator_next (info->get_parts);
2929                 finished = (tny_iterator_is_done (info->get_parts));
2930         } else {
2931                 finished = (priv->done == priv->total) ? TRUE : FALSE;
2932         }
2933
2934         /* If canceled by the user, ignore the error given by Tinymail */
2935         if (canceled) {
2936                 canceled = TRUE;
2937                 finished = TRUE;
2938                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2939         } else if (err) {
2940                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2941                 priv->error = g_error_copy ((const GError *) err);
2942                 if (priv->error) {
2943                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
2944                 } else {
2945                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2946                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2947                                      "%s", err->message);
2948                 }
2949         } else if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
2950                 /* Set the success status before calling the user callback */
2951                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2952         }
2953
2954         if (info->header == NULL && msg)
2955                 info->header = tny_msg_get_header (msg);
2956
2957         /* Call the user callback */
2958         if (info->user_callback && (finished || (info->get_parts == NULL)))
2959                 info->user_callback (info->mail_op, info->header, canceled, 
2960                                      msg, err, info->user_data);
2961
2962         /* Notify about operation end if this is the last callback */
2963         if (finished) {
2964                 /* Free user data */
2965                 if (info->destroy_notify)
2966                         info->destroy_notify (info->user_data);
2967
2968                 /* Notify about operation end */
2969                 modest_mail_operation_notify_end (info->mail_op);
2970
2971                 /* Clean */
2972                 if (info->msg)
2973                         g_object_unref (info->msg);
2974                 if (info->more_msgs)
2975                         g_object_unref (info->more_msgs);
2976                 if (info->header)
2977                         g_object_unref (info->header);
2978                 g_object_unref (info->mail_op);
2979                 g_slice_free (GetMsgInfo, info);
2980         } else if (info->get_parts) {
2981                 CamelStream *null_stream;
2982                 TnyStream *tny_null_stream;
2983                 TnyMimePart *part;
2984
2985                 if (info->msg == NULL && msg != NULL)
2986                         info->msg = g_object_ref (msg);
2987
2988                 null_stream = camel_stream_null_new ();
2989                 tny_null_stream = tny_camel_stream_new (null_stream);
2990                 
2991                 part = TNY_MIME_PART (tny_iterator_get_current (info->get_parts));
2992                 tny_mime_part_decode_to_stream_async (part, tny_null_stream, 
2993                                                       get_msg_async_get_part_cb,
2994                                                       get_msg_status_cb,
2995                                                       info);
2996                 g_object_unref (tny_null_stream);
2997                 g_object_unref (part);
2998
2999         } else if (info->more_msgs) {
3000                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (info->more_msgs));
3001                 TnyFolder *folder = tny_header_get_folder (header);
3002
3003                 g_object_unref (info->header);
3004                 info->header = g_object_ref (header);
3005
3006                 /* Retrieve the next message */
3007                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, info);
3008
3009                 g_object_unref (header);
3010                 g_object_unref (folder);
3011         } else {
3012                 g_warning ("%s: finished != TRUE but no messages left", __FUNCTION__);
3013         }
3014 }
3015
3016 void 
3017 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
3018                                      TnyList *header_list, 
3019                                      GetMsgAsyncUserCallback user_callback,
3020                                      gpointer user_data,
3021                                      GDestroyNotify notify)
3022 {
3023         ModestMailOperationPrivate *priv = NULL;
3024         gint msg_list_size;
3025         TnyIterator *iter = NULL;
3026         gboolean has_uncached_messages;
3027         
3028         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3029
3030         /* Init mail operation */
3031         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3032         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3033         priv->done = 0;
3034         priv->total = tny_list_get_length(header_list);
3035
3036         /* Check memory low */
3037         if (_check_memory_low (self)) {
3038                 if (user_callback) {
3039                         TnyHeader *header = NULL;
3040                         TnyIterator *iter;
3041
3042                         if (tny_list_get_length (header_list) > 0) {
3043                                 iter = tny_list_create_iterator (header_list);
3044                                 header = (TnyHeader *) tny_iterator_get_current (iter);
3045                                 g_object_unref (iter);
3046                         }
3047                         user_callback (self, header, FALSE, NULL, priv->error, user_data);
3048                         if (header)
3049                                 g_object_unref (header);
3050                 }
3051                 if (notify)
3052                         notify (user_data);
3053                 /* Notify about operation end */
3054                 modest_mail_operation_notify_end (self);
3055                 return;
3056         }
3057
3058         /* Check uncached messages */
3059         for (iter = tny_list_create_iterator (header_list), has_uncached_messages = FALSE;
3060              !has_uncached_messages && !tny_iterator_is_done (iter); 
3061              tny_iterator_next (iter)) {
3062                 TnyHeader *header;
3063
3064                 header = (TnyHeader *) tny_iterator_get_current (iter);
3065                 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED))
3066                         has_uncached_messages = TRUE;
3067                 g_object_unref (header);
3068         }       
3069         g_object_unref (iter);
3070         priv->op_type = has_uncached_messages?MODEST_MAIL_OPERATION_TYPE_RECEIVE:MODEST_MAIL_OPERATION_TYPE_OPEN;
3071
3072         /* Get account and set it into mail_operation */
3073         if (tny_list_get_length (header_list) >= 1) {
3074                 TnyIterator *iterator = tny_list_create_iterator (header_list);
3075                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iterator));
3076                 if (header) {
3077                         TnyFolder *folder = tny_header_get_folder (header);
3078                         if (folder) {           
3079                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
3080                                 g_object_unref (folder);
3081                         }
3082                         g_object_unref (header);
3083                 }
3084                 g_object_unref (iterator);
3085         }
3086
3087         msg_list_size = compute_message_list_size (header_list, 0);
3088
3089         modest_mail_operation_notify_start (self);
3090         iter = tny_list_create_iterator (header_list);
3091         if (!tny_iterator_is_done (iter)) {
3092                 /* notify about the start of the operation */
3093                 ModestMailOperationState *state;
3094                 state = modest_mail_operation_clone_state (self);
3095                 state->done = 0;
3096                 state->total = 0;
3097                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
3098                                0, state, NULL);
3099
3100                 GetMsgInfo *msg_info = NULL;
3101                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3102                 TnyFolder *folder = tny_header_get_folder (header);
3103
3104                 /* Create the message info */
3105                 msg_info = g_slice_new0 (GetMsgInfo);
3106                 msg_info->mail_op = g_object_ref (self);
3107                 msg_info->header = g_object_ref (header);
3108                 msg_info->more_msgs = g_object_ref (iter);
3109                 msg_info->user_callback = user_callback;
3110                 msg_info->user_data = user_data;
3111                 msg_info->destroy_notify = notify;
3112                 msg_info->last_total_bytes = 0;
3113                 msg_info->sum_total_bytes = 0;
3114                 msg_info->total_bytes = msg_list_size;
3115                 msg_info->msg = NULL;
3116
3117                 /* The callback will call it per each header */
3118                 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
3119
3120                 /* Free and go on */
3121                 g_object_unref (header);
3122                 g_object_unref (folder);
3123                 g_slice_free (ModestMailOperationState, state);
3124         }
3125         g_object_unref (iter);
3126 }
3127
3128
3129 static void
3130 remove_msgs_async_cb (TnyFolder *folder, 
3131                       gboolean canceled, 
3132                       GError *err, 
3133                       gpointer user_data)
3134 {
3135         gboolean expunge;
3136         const gchar *account_name;
3137         TnyAccount *account;
3138         ModestProtocolType account_proto = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
3139         ModestMailOperation *self;
3140         ModestMailOperationPrivate *priv;
3141         ModestProtocolRegistry *protocol_registry;
3142         SyncFolderHelper *helper;
3143
3144         self = (ModestMailOperation *) user_data;
3145         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3146         protocol_registry = modest_runtime_get_protocol_registry ();
3147
3148         if (canceled || err) {
3149                 /* If canceled by the user, ignore the error given by Tinymail */
3150                 if (canceled) {
3151                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3152                 } else if (err) {
3153                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3154                         priv->error = g_error_copy ((const GError *) err);
3155                         priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3156                 }
3157                 /* Exit */
3158                 modest_mail_operation_notify_end (self);
3159                 g_object_unref (self);
3160                 return;
3161         }
3162
3163         account = modest_tny_folder_get_account (folder);
3164         account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
3165         account_proto = modest_tny_account_get_protocol_type (account);
3166         g_object_unref (account);
3167
3168         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_proto)) {
3169                 if (modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3170                                                             account_name))
3171                         expunge = FALSE;
3172                 else
3173                         expunge = TRUE;
3174         } else {
3175                 expunge = TRUE;
3176         }
3177
3178         /* Create helper */
3179         helper = g_slice_new0 (SyncFolderHelper);
3180         helper->mail_op = g_object_ref (self);
3181         helper->user_callback = NULL;
3182         helper->user_data = NULL;
3183
3184         /* Sync folder */
3185         tny_folder_sync_async(folder, expunge, sync_folder_finish_callback, NULL, helper);
3186
3187         /* Remove the extra reference */
3188         g_object_unref (self);
3189 }
3190
3191 void 
3192 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
3193                                    TnyList *headers,
3194                                    gboolean remove_to_trash /*ignored*/)
3195 {
3196         TnyFolder *folder = NULL;
3197         ModestMailOperationPrivate *priv;
3198         TnyIterator *iter = NULL;
3199         TnyHeader *header = NULL;
3200         TnyList *remove_headers = NULL;
3201         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
3202         ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
3203
3204         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3205         g_return_if_fail (TNY_IS_LIST (headers));
3206
3207         if (remove_to_trash)
3208                 g_warning ("remove to trash is not implemented");
3209
3210         if (tny_list_get_length(headers) == 0) {
3211                 g_warning ("%s: list of headers is empty\n", __FUNCTION__);
3212                 goto cleanup; /* nothing to do */
3213         }
3214
3215         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3216
3217         /* Get folder from first header and sync it */
3218         iter = tny_list_create_iterator (headers);
3219         header = TNY_HEADER (tny_iterator_get_current (iter));
3220         g_object_unref (iter);
3221
3222         folder = tny_header_get_folder (header);
3223         if (!TNY_IS_FOLDER(folder)) {
3224                 g_warning ("%s: could not get folder for header\n", __FUNCTION__);
3225                 goto cleanup;
3226         }
3227
3228         /* Use the merged folder if we're removing messages from outbox */
3229         if (modest_tny_folder_is_local_folder (folder)) {
3230                 ModestTnyLocalFoldersAccount *local_account;
3231
3232                 local_account = (ModestTnyLocalFoldersAccount *)
3233                         modest_tny_account_store_get_local_folders_account (accstore);
3234                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
3235                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
3236                         g_object_unref (folder);
3237                         folder = modest_tny_local_folders_account_get_merged_outbox (local_account);
3238                 }
3239                 g_object_unref (local_account);
3240         }
3241
3242         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
3243                 TnyIterator *headers_iter = tny_list_create_iterator (headers);
3244
3245                 while (!tny_iterator_is_done (headers_iter)) {
3246                         TnyTransportAccount *traccount = NULL;
3247                         TnyHeader *hdr = NULL;
3248
3249                         hdr = TNY_HEADER (tny_iterator_get_current (headers_iter));
3250                         traccount = modest_tny_account_store_get_transport_account_from_outbox_header (accstore,
3251                                                                                                        header);
3252                         if (traccount) {
3253                                 ModestTnySendQueueStatus status;
3254                                 ModestTnySendQueue *send_queue;
3255
3256                                 send_queue = modest_runtime_get_send_queue(traccount, TRUE);
3257                                 if (TNY_IS_SEND_QUEUE (send_queue)) {
3258                                         char *msg_id;
3259
3260                                         msg_id = modest_tny_send_queue_get_msg_id (hdr);
3261                                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
3262                                         if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
3263                                                 if (G_UNLIKELY (remove_headers == NULL))
3264                                                         remove_headers = tny_simple_list_new ();
3265                                                 tny_list_append(remove_headers, G_OBJECT(hdr));
3266                                         }
3267                                         g_free(msg_id);
3268                                 }
3269                                 g_object_unref(traccount);
3270                         }
3271                         g_object_unref(hdr);
3272                         tny_iterator_next (headers_iter);
3273                 }
3274                 g_object_unref(headers_iter);
3275         }
3276
3277         /* Get account and set it into mail_operation */
3278         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
3279         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
3280         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3281
3282         if (!remove_headers)
3283                 remove_headers = g_object_ref (headers);
3284
3285         /* remove message from folder */
3286         modest_mail_operation_notify_start (self);
3287         tny_folder_remove_msgs_async (folder, remove_headers, remove_msgs_async_cb, 
3288                                       NULL, g_object_ref (self));
3289
3290 cleanup:
3291         if (remove_headers)
3292                 g_object_unref (remove_headers);
3293         if (header)
3294                 g_object_unref (header);
3295         if (folder)
3296                 g_object_unref (folder);
3297 }
3298
3299 static void
3300 notify_progress_of_multiple_messages (ModestMailOperation *self,
3301                                       TnyStatus *status,
3302                                       gint *last_total_bytes,
3303                                       gint *sum_total_bytes,
3304                                       gint total_bytes, 
3305                                       gboolean increment_done)
3306 {
3307         ModestMailOperationPrivate *priv;
3308         ModestMailOperationState *state;
3309         gboolean is_num_bytes = FALSE;
3310
3311         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3312
3313         /* We know that tinymail sends us information about
3314          *  transferred bytes with this particular message
3315          */
3316         if (status->message)
3317                 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
3318
3319         state = modest_mail_operation_clone_state (self);
3320         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
3321                 /* We know that we're in a different message when the
3322                    total number of bytes to transfer is different. Of
3323                    course it could fail if we're transferring messages
3324                    of the same size, but this is a workarround */
3325                 if (status->of_total != *last_total_bytes) {
3326                         /* We need to increment the done when there is
3327                            no information about each individual
3328                            message, we need to do this in message
3329                            transfers, and we don't do it for getting
3330                            messages */
3331                         if (increment_done)
3332                                 priv->done++;
3333                         *sum_total_bytes += *last_total_bytes;
3334                         *last_total_bytes = status->of_total;
3335                 }
3336                 state->bytes_done += status->position + *sum_total_bytes;
3337                 state->bytes_total = total_bytes;
3338
3339                 /* Notify the status change. Only notify about changes
3340                    referred to bytes */
3341                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3342                                0, state, NULL);
3343         }
3344
3345         g_slice_free (ModestMailOperationState, state);
3346 }
3347
3348 static void
3349 transfer_msgs_status_cb (GObject *obj,
3350                          TnyStatus *status,  
3351                          gpointer user_data)
3352 {
3353         XFerMsgsAsyncHelper *helper;
3354
3355         g_return_if_fail (status != NULL);
3356
3357         /* Show only the status information we want */
3358         if (status->code != TNY_FOLDER_STATUS_CODE_XFER_MSGS)
3359                 return;
3360
3361         helper = (XFerMsgsAsyncHelper *) user_data;
3362         g_return_if_fail (helper != NULL);       
3363
3364         /* Notify progress */
3365         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
3366                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
3367 }
3368
3369 static void
3370 transfer_msgs_sync_folder_cb (TnyFolder *self, 
3371                               gboolean cancelled, 
3372                               GError *err, 
3373                               gpointer user_data)
3374 {
3375         XFerMsgsAsyncHelper *helper;
3376         /* We don't care here about the results of the
3377            synchronization */
3378         helper = (XFerMsgsAsyncHelper *) user_data;
3379
3380         /* Notify about operation end */
3381         modest_mail_operation_notify_end (helper->mail_op);
3382
3383         /* If user defined callback function was defined, call it */
3384         if (helper->user_callback)
3385                 helper->user_callback (helper->mail_op, helper->user_data);
3386         
3387         /* Free */
3388         if (helper->more_msgs)
3389                 g_object_unref (helper->more_msgs);
3390         if (helper->headers)
3391                 g_object_unref (helper->headers);
3392         if (helper->dest_folder)
3393                 g_object_unref (helper->dest_folder);
3394         if (helper->mail_op)
3395                 g_object_unref (helper->mail_op);
3396         g_slice_free (XFerMsgsAsyncHelper, helper);
3397 }
3398
3399 static void
3400 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
3401 {
3402         XFerMsgsAsyncHelper *helper;
3403         ModestMailOperation *self;
3404         ModestMailOperationPrivate *priv;
3405         gboolean finished = TRUE;
3406
3407         helper = (XFerMsgsAsyncHelper *) user_data;
3408         self = helper->mail_op;
3409
3410         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3411
3412         if (cancelled) {
3413                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3414         } else if (err) {
3415                 priv->error = g_error_copy (err);
3416                 priv->done = 0;
3417                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
3418         } else if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
3419                 if (helper->more_msgs) {
3420                         /* We'll transfer the next message in the list */
3421                         tny_iterator_next (helper->more_msgs);
3422                         if (!tny_iterator_is_done (helper->more_msgs)) {
3423                                 GObject *next_header;
3424                                 g_object_unref (helper->headers);
3425                                 helper->headers = tny_simple_list_new ();
3426                                 next_header = tny_iterator_get_current (helper->more_msgs);
3427                                 tny_list_append (helper->headers, next_header);
3428                                 g_object_unref (next_header);
3429                                 finished = FALSE;
3430                         }
3431                 }
3432                 if (finished) {
3433                         priv->done = 1;
3434                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3435                 }
3436         }
3437
3438         if (finished) {
3439                 /* Synchronize the source folder contents. This should
3440                    be done by tinymail but the camel_folder_sync it's
3441                    actually disabled in transfer_msgs_thread_clean
3442                    because it's supposed to cause hangs */
3443                 tny_folder_sync_async (folder, helper->delete, 
3444                                        transfer_msgs_sync_folder_cb, 
3445                                        NULL, helper);
3446         } else {
3447                 /* Transfer more messages */
3448                 tny_folder_transfer_msgs_async (folder,
3449                                                 helper->headers,
3450                                                 helper->dest_folder,
3451                                                 helper->delete,
3452                                                 transfer_msgs_cb,
3453                                                 transfer_msgs_status_cb,
3454                                                 helper);
3455         }
3456 }
3457
3458 /* Computes the size of the messages the headers in the list belongs
3459    to. If num_elements is different from 0 then it only takes into
3460    account the first num_elements for the calculation */
3461 static guint
3462 compute_message_list_size (TnyList *headers, 
3463                            guint num_elements)
3464 {
3465         TnyIterator *iter;
3466         guint size = 0, element = 0;
3467
3468         /* If num_elements is not valid then take all into account */
3469         if ((num_elements <= 0) || (num_elements > tny_list_get_length (headers)))
3470                 num_elements = tny_list_get_length (headers);
3471
3472         iter = tny_list_create_iterator (headers);
3473         while (!tny_iterator_is_done (iter) && element < num_elements) {
3474                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
3475                 size += tny_header_get_message_size (header);
3476                 g_object_unref (header);
3477                 tny_iterator_next (iter);
3478                 element++;
3479         }
3480         g_object_unref (iter);
3481
3482         return size;
3483 }
3484
3485 void
3486 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
3487                                  TnyList *headers, 
3488                                  TnyFolder *folder, 
3489                                  gboolean delete_original,
3490                                  XferMsgsAsyncUserCallback user_callback,
3491                                  gpointer user_data)
3492 {
3493         ModestMailOperationPrivate *priv = NULL;
3494         TnyIterator *iter = NULL;
3495         TnyFolder *src_folder = NULL;
3496         XFerMsgsAsyncHelper *helper = NULL;
3497         TnyHeader *header = NULL;
3498         ModestTnyFolderRules rules = 0;
3499         TnyAccount *dst_account = NULL;
3500         gboolean leave_on_server;
3501         ModestMailOperationState *state;
3502         ModestProtocolRegistry *protocol_registry;
3503         ModestProtocolType account_protocol;
3504
3505         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
3506         g_return_if_fail (headers && TNY_IS_LIST (headers));
3507         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
3508
3509         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3510         protocol_registry = modest_runtime_get_protocol_registry ();
3511
3512         priv->total = tny_list_get_length (headers);
3513         priv->done = 0;
3514         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3515         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3516
3517         /* Apply folder rules */
3518         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3519         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3520                 /* Set status failed and set an error */
3521                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3522                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3523                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
3524                              _CS("ckct_ib_unable_to_paste_here"));
3525                 /* Notify the queue */
3526                 modest_mail_operation_notify_end (self);
3527                 return;
3528         }
3529                 
3530         /* Get source folder */
3531         iter = tny_list_create_iterator (headers);
3532         header = TNY_HEADER (tny_iterator_get_current (iter));
3533         if (header) {
3534                 src_folder = tny_header_get_folder (header);
3535                 g_object_unref (header);
3536         }
3537         g_object_unref (iter);
3538
3539         if (src_folder == NULL) {
3540                 /* Notify the queue */
3541                 modest_mail_operation_notify_end (self);
3542
3543                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
3544                 return;
3545         }
3546
3547         
3548         /* Check folder source and destination */
3549         if (src_folder == folder) {
3550                 /* Set status failed and set an error */
3551                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3552                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3553                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
3554                              _("mail_in_ui_folder_copy_target_error"));
3555                 
3556                 /* Notify the queue */
3557                 modest_mail_operation_notify_end (self);
3558                 
3559                 /* Free */
3560                 g_object_unref (src_folder);            
3561                 return;
3562         }
3563
3564         /* Create the helper */
3565         helper = g_slice_new0 (XFerMsgsAsyncHelper);
3566         helper->mail_op = g_object_ref(self);
3567         helper->dest_folder = g_object_ref(folder);
3568         helper->user_callback = user_callback;
3569         helper->user_data = user_data;
3570         helper->last_total_bytes = 0;
3571         helper->sum_total_bytes = 0;
3572         helper->total_bytes = compute_message_list_size (headers, 0);
3573
3574         /* Get account and set it into mail_operation */
3575         priv->account = modest_tny_folder_get_account (src_folder);
3576         dst_account = modest_tny_folder_get_account (folder);
3577
3578         if (priv->account == dst_account) {
3579                 /* Transfer all messages at once using the fast
3580                  * method. Note that depending on the server this
3581                  * might not be that fast, and might not be
3582                  * user-cancellable either */
3583                 helper->headers = g_object_ref (headers);
3584                 helper->more_msgs = NULL;
3585         } else {
3586                 /* Transfer messages one by one so the user can cancel
3587                  * the operation */
3588                 GObject *hdr;
3589                 helper->headers = tny_simple_list_new ();
3590                 helper->more_msgs = tny_list_create_iterator (headers);
3591                 hdr = tny_iterator_get_current (helper->more_msgs);
3592                 tny_list_append (helper->headers, hdr);
3593                 g_object_unref (hdr);
3594         }
3595
3596         /* If leave_on_server is set to TRUE then don't use
3597            delete_original, we always pass FALSE. This is because
3598            otherwise tinymail will try to sync the source folder and
3599            this could cause an error if we're offline while
3600            transferring an already downloaded message from a POP
3601            account */
3602         account_protocol = modest_tny_account_get_protocol_type (priv->account);
3603         if (modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, account_protocol)) {
3604                 const gchar *account_name;
3605
3606                 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (priv->account);
3607                 leave_on_server = modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
3608                                                                           account_name);
3609         } else {
3610                 leave_on_server = FALSE;
3611         }
3612
3613         /* Do not delete messages if leave on server is TRUE */
3614         helper->delete = (leave_on_server) ? FALSE : delete_original;
3615
3616         modest_mail_operation_notify_start (self);
3617
3618         /* Start notifying progress */
3619         state = modest_mail_operation_clone_state (self);
3620         state->done = 0;
3621         state->total = 0;
3622         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3623         g_slice_free (ModestMailOperationState, state);
3624
3625         tny_folder_transfer_msgs_async (src_folder, 
3626                                         helper->headers, 
3627                                         folder, 
3628                                         helper->delete, 
3629                                         transfer_msgs_cb, 
3630                                         transfer_msgs_status_cb,
3631                                         helper);
3632         g_object_unref (src_folder);
3633         g_object_unref (dst_account);
3634 }
3635
3636
3637 static void
3638 on_refresh_folder (TnyFolder   *folder, 
3639                    gboolean     cancelled, 
3640                    GError     *error,
3641                    gpointer     user_data)
3642 {
3643         RefreshAsyncHelper *helper = NULL;
3644         ModestMailOperation *self = NULL;
3645         ModestMailOperationPrivate *priv = NULL;
3646
3647         helper = (RefreshAsyncHelper *) user_data;
3648         self = helper->mail_op;
3649         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3650
3651         g_return_if_fail(priv!=NULL);
3652
3653         if (error) {
3654                 priv->error = g_error_copy (error);
3655                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3656                 goto out;
3657         }
3658
3659         if (cancelled) {
3660                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3661                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
3662                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
3663                              _("Error trying to refresh the contents of %s"),
3664                              tny_folder_get_name (folder));
3665                 goto out;
3666         }
3667
3668         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3669  out:
3670
3671         /* Call user defined callback, if it exists */
3672         if (helper->user_callback) {
3673
3674                 /* This is not a GDK lock because we are a Tinymail callback and
3675                  * Tinymail already acquires the Gdk lock */
3676                 helper->user_callback (self, folder, helper->user_data);
3677         }
3678
3679         /* Free */
3680         g_slice_free (RefreshAsyncHelper, helper);
3681
3682         /* Notify about operation end */
3683         modest_mail_operation_notify_end (self);
3684         g_object_unref(self);
3685 }
3686
3687 static void
3688 on_refresh_folder_status_update (GObject *obj,
3689                                  TnyStatus *status,
3690                                  gpointer user_data)
3691 {
3692         RefreshAsyncHelper *helper = NULL;
3693         ModestMailOperation *self = NULL;
3694         ModestMailOperationPrivate *priv = NULL;
3695         ModestMailOperationState *state;
3696
3697         g_return_if_fail (user_data != NULL);
3698         g_return_if_fail (status != NULL);
3699
3700         /* Show only the status information we want */
3701         if (status->code != TNY_FOLDER_STATUS_CODE_REFRESH)
3702                 return;
3703
3704         helper = (RefreshAsyncHelper *) user_data;
3705         self = helper->mail_op;
3706         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
3707
3708         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3709
3710         priv->done = status->position;
3711         priv->total = status->of_total;
3712
3713         state = modest_mail_operation_clone_state (self);
3714
3715         /* This is not a GDK lock because we are a Tinymail callback and
3716          * Tinymail already acquires the Gdk lock */
3717         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
3718
3719         g_slice_free (ModestMailOperationState, state);
3720 }
3721
3722 void 
3723 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
3724                                        TnyFolder *folder,
3725                                        RefreshAsyncUserCallback user_callback,
3726                                        gpointer user_data)
3727 {
3728         ModestMailOperationPrivate *priv = NULL;
3729         RefreshAsyncHelper *helper = NULL;
3730
3731         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3732
3733         /* Check memory low */
3734         if (_check_memory_low (self)) {
3735                 if (user_callback)
3736                         user_callback (self, folder, user_data);
3737                 /* Notify about operation end */
3738                 modest_mail_operation_notify_end (self);
3739                 return;
3740         }
3741
3742         /* Get account and set it into mail_operation */
3743         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3744         priv->account = modest_tny_folder_get_account  (folder);
3745         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
3746
3747         /* Create the helper */
3748         helper = g_slice_new0 (RefreshAsyncHelper);
3749         helper->mail_op = g_object_ref(self);
3750         helper->user_callback = user_callback;
3751         helper->user_data = user_data;
3752
3753         modest_mail_operation_notify_start (self);
3754         
3755         /* notify that the operation was started */
3756         ModestMailOperationState *state;
3757         state = modest_mail_operation_clone_state (self);
3758         state->done = 0;
3759         state->total = 0;
3760         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
3761                         0, state, NULL);
3762         g_slice_free (ModestMailOperationState, state);
3763         
3764         tny_folder_refresh_async (folder,
3765                                   on_refresh_folder,
3766                                   on_refresh_folder_status_update,
3767                                   helper);
3768 }
3769
3770 static void
3771 run_queue_notify_and_destroy (RunQueueHelper *helper,
3772                               ModestMailOperationStatus status)
3773 {
3774         ModestMailOperationPrivate *priv;
3775
3776         /* Disconnect */
3777         if (helper->error_handler &&
3778             g_signal_handler_is_connected (helper->queue, helper->error_handler))
3779                 g_signal_handler_disconnect (helper->queue, helper->error_handler);
3780         if (helper->start_handler &&
3781             g_signal_handler_is_connected (helper->queue, helper->start_handler))
3782                 g_signal_handler_disconnect (helper->queue, helper->start_handler);
3783         if (helper->stop_handler &&
3784             g_signal_handler_is_connected (helper->queue, helper->stop_handler))
3785                 g_signal_handler_disconnect (helper->queue, helper->stop_handler);
3786
3787         /* Set status */
3788         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->self);
3789         priv->status = status;
3790
3791         /* Notify end */
3792         modest_mail_operation_notify_end (helper->self);
3793
3794         /* Free data */
3795         g_object_unref (helper->queue);
3796         g_object_unref (helper->self);
3797         g_slice_free (RunQueueHelper, helper);
3798 }
3799
3800 static void
3801 run_queue_stop (ModestTnySendQueue *queue,
3802                 gpointer user_data)
3803 {
3804         RunQueueHelper *helper;
3805
3806         g_debug ("%s sending queue stopped", __FUNCTION__);
3807
3808         helper = (RunQueueHelper *) user_data;
3809         run_queue_notify_and_destroy (helper, MODEST_MAIL_OPERATION_STATUS_SUCCESS);
3810 }
3811
3812 void
3813 modest_mail_operation_run_queue (ModestMailOperation *self,
3814                                  ModestTnySendQueue *queue)
3815 {
3816         ModestMailOperationPrivate *priv;
3817         RunQueueHelper *helper;
3818
3819         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3820         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3821         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3822
3823         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3824         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3825         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE;
3826
3827         /* Create the helper */
3828         helper = g_slice_new0 (RunQueueHelper);
3829         helper->queue = g_object_ref (queue);
3830         helper->self = g_object_ref (self);
3831         helper->stop_handler = g_signal_connect (queue, "queue-stop", 
3832                                                  G_CALLBACK (run_queue_stop), 
3833                                                  helper);
3834
3835         /* Notify operation has started */
3836         modest_mail_operation_notify_start (self);
3837         g_debug ("%s, run queue started", __FUNCTION__);
3838 }
3839
3840 static void
3841 queue_wakeup_callback (ModestTnySendQueue *queue,
3842                        gboolean cancelled,
3843                        GError *error,
3844                        gpointer userdata)
3845 {
3846         ModestMailOperation *mail_op;
3847         ModestMailOperationPrivate *priv;
3848
3849         mail_op = (ModestMailOperation *) userdata;
3850         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
3851
3852         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3853         tny_camel_send_queue_flush (TNY_CAMEL_SEND_QUEUE (queue));
3854
3855         /* Notify end */
3856         modest_mail_operation_notify_end (mail_op);
3857         g_object_unref (mail_op);
3858 }
3859
3860 void
3861 modest_mail_operation_queue_wakeup (ModestMailOperation *self,
3862                                     ModestTnySendQueue *queue)
3863 {
3864         ModestMailOperationPrivate *priv;
3865
3866         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3867         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (queue));
3868         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3869
3870         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3871         priv->account = TNY_ACCOUNT (tny_camel_send_queue_get_transport_account (TNY_CAMEL_SEND_QUEUE (queue)));
3872         priv->op_type = MODEST_MAIL_OPERATION_TYPE_QUEUE_WAKEUP;
3873
3874         g_object_ref (self);
3875
3876         modest_tny_send_queue_wakeup (queue, queue_wakeup_callback, self);
3877         modest_mail_operation_notify_start (self);
3878 }
3879
3880 static void
3881 shutdown_callback (ModestTnyAccountStore *account_store, gpointer userdata)
3882 {
3883         ModestMailOperation *self = (ModestMailOperation *) userdata;
3884         ModestMailOperationPrivate *priv;
3885
3886         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3887         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3888         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3889
3890         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3891
3892         modest_mail_operation_notify_end (self);
3893         g_object_unref (self);
3894 }
3895
3896 void
3897 modest_mail_operation_shutdown (ModestMailOperation *self, ModestTnyAccountStore *account_store)
3898 {
3899         ModestMailOperationPrivate *priv;
3900
3901         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3902         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (account_store));
3903         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3904
3905         modest_mail_operation_queue_set_running_shutdown (modest_runtime_get_mail_operation_queue ());
3906
3907         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3908         priv->account = NULL;
3909         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SHUTDOWN;
3910
3911         modest_mail_operation_notify_start (self);
3912         g_object_ref (self);
3913         modest_tny_account_store_shutdown (account_store, shutdown_callback, self);
3914 }
3915
3916 static void
3917 sync_folder_finish_callback (TnyFolder *self,
3918                              gboolean cancelled,
3919                              GError *err,
3920                              gpointer user_data)
3921
3922 {
3923         ModestMailOperationPrivate *priv;
3924         SyncFolderHelper *helper;
3925
3926         helper = (SyncFolderHelper *) user_data;
3927         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (helper->mail_op);
3928
3929         /* If canceled by the user, ignore the error given by Tinymail */
3930         if (cancelled) {
3931                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
3932         } else if (err) {
3933                 /* If the operation was a sync then the status is
3934                    failed, but if it's part of another operation then
3935                    just set it as finished with errors */
3936                 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER)
3937                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
3938                 else
3939                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
3940                 priv->error = g_error_copy ((const GError *) err);
3941                 priv->error->domain = MODEST_MAIL_OPERATION_ERROR;
3942         } else {
3943                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
3944         }
3945
3946         /* User callback */
3947         if (helper->user_callback)
3948                 helper->user_callback (helper->mail_op, self, helper->user_data);
3949
3950         modest_mail_operation_notify_end (helper->mail_op);
3951
3952         /* Frees */
3953         g_object_unref (helper->mail_op);
3954         g_slice_free (SyncFolderHelper, helper);
3955 }
3956
3957 void
3958 modest_mail_operation_sync_folder (ModestMailOperation *self,
3959                                    TnyFolder *folder,
3960                                    gboolean expunge,
3961                                    SyncFolderCallback callback,
3962                                    gpointer user_data)
3963 {
3964         ModestMailOperationPrivate *priv;
3965         SyncFolderHelper *helper;
3966
3967         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
3968         g_return_if_fail (TNY_IS_FOLDER (folder));
3969         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
3970
3971         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
3972         priv->account = modest_tny_folder_get_account (folder);
3973         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER;
3974
3975         /* Create helper */
3976         helper = g_slice_new0 (SyncFolderHelper);
3977         helper->mail_op = g_object_ref (self);
3978         helper->user_callback = callback;
3979         helper->user_data = user_data;
3980
3981         modest_mail_operation_notify_start (self);
3982         tny_folder_sync_async (folder, expunge,
3983                                (TnyFolderCallback) sync_folder_finish_callback,
3984                                NULL, helper);
3985 }
3986
3987 static void
3988 modest_mail_operation_notify_start (ModestMailOperation *self)
3989 {
3990         ModestMailOperationPrivate *priv = NULL;
3991
3992         g_return_if_fail (self);
3993
3994         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
3995
3996         /* Ensure that all the fields are filled correctly */
3997         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
3998
3999         /* Notify the observers about the mail operation. We do not
4000            wrapp this emission because we assume that this function is
4001            always called from within the main lock */
4002         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
4003 }
4004
4005 /**
4006  *
4007  * It's used by the mail operation queue to notify the observers
4008  * attached to that signal that the operation finished. We need to use
4009  * that because tinymail does not give us the progress of a given
4010  * operation when it finishes (it directly calls the operation
4011  * callback).
4012  */
4013 static void
4014 modest_mail_operation_notify_end (ModestMailOperation *self)
4015 {
4016         ModestMailOperationPrivate *priv = NULL;
4017
4018         g_return_if_fail (self);
4019
4020         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4021
4022         /* Notify the observers about the mail operation end. We do
4023            not wrapp this emission because we assume that this
4024            function is always called from within the main lock */
4025         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
4026
4027         /* Remove the error user data */
4028         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
4029                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
4030 }
4031
4032 TnyAccount *
4033 modest_mail_operation_get_account (ModestMailOperation *self)
4034 {
4035         ModestMailOperationPrivate *priv = NULL;
4036
4037         g_return_val_if_fail (self, NULL);
4038
4039         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4040
4041         return (priv->account) ? g_object_ref (priv->account) : NULL;
4042 }
4043
4044 void
4045 modest_mail_operation_noop (ModestMailOperation *self)
4046 {
4047         ModestMailOperationPrivate *priv = NULL;
4048
4049         g_return_if_fail (self);
4050
4051         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4052         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
4053         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
4054         priv->done = 0;
4055         priv->total = 0;
4056
4057         /* This mail operation does nothing actually */
4058         modest_mail_operation_notify_start (self);
4059         modest_mail_operation_notify_end (self);
4060 }
4061
4062
4063 gchar*
4064 modest_mail_operation_to_string (ModestMailOperation *self)
4065 {
4066         const gchar *type, *status, *account_id;
4067         ModestMailOperationPrivate *priv = NULL;
4068         
4069         g_return_val_if_fail (self, NULL);
4070
4071         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
4072
4073         /* new operations don't have anything interesting */
4074         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_UNKNOWN)
4075                 return g_strdup_printf ("%p <new operation>", self);
4076         
4077         switch (priv->op_type) {
4078         case MODEST_MAIL_OPERATION_TYPE_SEND:    type= "SEND";    break;
4079         case MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE:    type= "SEND-AND-RECEIVE";    break;
4080         case MODEST_MAIL_OPERATION_TYPE_RECEIVE: type= "RECEIVE"; break;
4081         case MODEST_MAIL_OPERATION_TYPE_OPEN:    type= "OPEN";    break;
4082         case MODEST_MAIL_OPERATION_TYPE_DELETE:  type= "DELETE";  break;
4083         case MODEST_MAIL_OPERATION_TYPE_INFO:    type= "INFO";    break;
4084         case MODEST_MAIL_OPERATION_TYPE_RUN_QUEUE: type= "RUN-QUEUE"; break;
4085         case MODEST_MAIL_OPERATION_TYPE_SYNC_FOLDER: type= "SYNC-FOLDER"; break;
4086         case MODEST_MAIL_OPERATION_TYPE_SHUTDOWN: type= "SHUTDOWN"; break;
4087         case MODEST_MAIL_OPERATION_TYPE_UNKNOWN: type= "UNKNOWN"; break;
4088         default: type = "UNEXPECTED"; break;
4089         }
4090
4091         switch (priv->status) {
4092         case MODEST_MAIL_OPERATION_STATUS_INVALID:              status= "INVALID"; break;
4093         case MODEST_MAIL_OPERATION_STATUS_SUCCESS:              status= "SUCCESS"; break;
4094         case MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS: status= "FINISHED-WITH-ERRORS"; break;
4095         case MODEST_MAIL_OPERATION_STATUS_FAILED:               status= "FAILED"; break;
4096         case MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS:          status= "IN-PROGRESS"; break;
4097         case MODEST_MAIL_OPERATION_STATUS_CANCELED:             status= "CANCELLED"; break;
4098         default:                                                status= "UNEXPECTED"; break;
4099         } 
4100
4101         account_id = priv->account ? tny_account_get_id (priv->account) : "";
4102
4103         return g_strdup_printf ("%p \"%s\" (%s) [%s] {%d/%d} '%s'", self, account_id,type, status,
4104                                 priv->done, priv->total,
4105                                 priv->error && priv->error->message ? priv->error->message : "");
4106 }
4107
4108 /* 
4109  * Once the mail operations were objects this will be no longer
4110  * needed. I don't like it, but we need it for the moment
4111  */
4112 static gboolean
4113 _check_memory_low (ModestMailOperation *mail_op)
4114 {
4115         if (modest_platform_check_memory_low (NULL, FALSE)) {
4116                 ModestMailOperationPrivate *priv;
4117
4118                 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
4119                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
4120                 g_set_error (&(priv->error),
4121                              MODEST_MAIL_OPERATION_ERROR,
4122                              MODEST_MAIL_OPERATION_ERROR_LOW_MEMORY,
4123                              "Not enough memory to complete the operation");
4124                 return TRUE;
4125         } else {
4126                 return FALSE;
4127         }
4128 }