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