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