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