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