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