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