This patch includes a lot of work to refactor and reorganize the
[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-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include "modest-account-mgr-helpers.h"
49 #include <modest-tny-account.h>
50 #include <modest-tny-send-queue.h>
51 #include <modest-runtime.h>
52 #include "modest-text-utils.h"
53 #include "modest-tny-msg.h"
54 #include "modest-tny-folder.h"
55 #include "modest-tny-account-store.h"
56 #include "modest-tny-platform-factory.h"
57 #include "modest-marshal.h"
58 #include "modest-error.h"
59 #include "modest-mail-operation.h"
60
61 #define KB 1024
62
63 /* 
64  * Remove all these #ifdef stuff when the tinymail's idle calls become
65  * locked
66  */
67 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
68
69 /* 'private'/'protected' functions */
70 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
71 static void modest_mail_operation_init       (ModestMailOperation *obj);
72 static void modest_mail_operation_finalize   (GObject *obj);
73
74 static void     get_msg_async_cb (TnyFolder *folder, 
75                                   gboolean cancelled, 
76                                   TnyMsg *msg, 
77                                   GError *rr, 
78                                   gpointer user_data);
79
80 static void     get_msg_status_cb (GObject *obj,
81                                    TnyStatus *status,  
82                                    gpointer user_data);
83
84 static void     modest_mail_operation_notify_start (ModestMailOperation *self);
85 static void     modest_mail_operation_notify_end (ModestMailOperation *self);
86
87 static void     notify_progress_of_multiple_messages (ModestMailOperation *self,
88                                                       TnyStatus *status,
89                                                       gint *last_total_bytes,
90                                                       gint *sum_total_bytes,
91                                                       gint total_bytes,
92                                                       gboolean increment_done);
93
94 static guint    compute_message_list_size (TnyList *headers);
95
96 static guint    compute_message_array_size (GPtrArray *headers);
97
98 static int      compare_headers_by_date   (gconstpointer a,
99                                            gconstpointer b);
100
101 enum _ModestMailOperationSignals 
102 {
103         PROGRESS_CHANGED_SIGNAL,
104         OPERATION_STARTED_SIGNAL,
105         OPERATION_FINISHED_SIGNAL,
106         NUM_SIGNALS
107 };
108
109 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
110 struct _ModestMailOperationPrivate {
111         TnyAccount                *account;
112         guint                      done;
113         guint                      total;
114         GObject                   *source;
115         GError                    *error;
116         ErrorCheckingUserCallback  error_checking;
117         gpointer                   error_checking_user_data;
118         ErrorCheckingUserDataDestroyer error_checking_user_data_destroyer;
119         ModestMailOperationStatus  status;      
120         ModestMailOperationTypeOperation op_type;
121 };
122
123 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
124                                                    MODEST_TYPE_MAIL_OPERATION, \
125                                                    ModestMailOperationPrivate))
126
127 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
128                                                    priv->status = new_status;\
129                                                }
130
131
132 typedef struct {
133         GetMsgAsyncUserCallback user_callback;
134         TnyHeader *header;
135         gpointer user_data;
136         ModestMailOperation *mail_op;
137         GDestroyNotify destroy_notify;
138         gint last_total_bytes;
139         gint sum_total_bytes;
140         gint total_bytes;
141 } GetMsgInfo;
142
143 typedef struct {
144         ModestMailOperation *mail_op;
145         TnyMsg *msg;
146         gulong msg_sent_handler;
147         gulong error_happened_handler;
148 } SendMsgInfo;
149
150 static void     send_mail_msg_sent_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
151                                             guint nth, guint total, gpointer userdata);
152 static void     send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
153                                                   GError *error, gpointer userdata);
154 static void     common_send_mail_operation_end (TnySendQueue *queue, TnyMsg *msg, 
155                                                 SendMsgInfo *info);
156
157 typedef struct _RefreshAsyncHelper {    
158         ModestMailOperation *mail_op;
159         RefreshAsyncUserCallback user_callback; 
160         gpointer user_data;
161 } RefreshAsyncHelper;
162
163 typedef struct _XFerMsgAsyncHelper
164 {
165         ModestMailOperation *mail_op;
166         TnyList *headers;
167         TnyFolder *dest_folder;
168         XferAsyncUserCallback user_callback;    
169         gboolean delete;
170         gpointer user_data;
171         gint last_total_bytes;
172         gint sum_total_bytes;
173         gint total_bytes;
174 } XFerMsgAsyncHelper;
175
176 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
177                                                       TnyMsg *msg,
178                                                       gpointer userdata);
179
180 static void          modest_mail_operation_create_msg (ModestMailOperation *self,
181                                                        const gchar *from, const gchar *to,
182                                                        const gchar *cc, const gchar *bcc,
183                                                        const gchar *subject, const gchar *plain_body,
184                                                        const gchar *html_body, const GList *attachments_list,
185                                                        const GList *images_list,
186                                                        TnyHeaderFlags priority_flags,
187                                                        ModestMailOperationCreateMsgCallback callback,
188                                                        gpointer userdata);
189
190 static gboolean      idle_notify_queue (gpointer data);
191 typedef struct
192 {
193         ModestMailOperation *mail_op;
194         gchar *from;
195         gchar *to;
196         gchar *cc;
197         gchar *bcc;
198         gchar *subject;
199         gchar *plain_body;
200         gchar *html_body;
201         GList *attachments_list;
202         GList *images_list;
203         TnyHeaderFlags priority_flags;
204         ModestMailOperationCreateMsgCallback callback;
205         gpointer userdata;
206 } CreateMsgInfo;
207
208 typedef struct
209 {
210         ModestMailOperation *mail_op;
211         TnyMsg *msg;
212         ModestMailOperationCreateMsgCallback callback;
213         gpointer userdata;
214 } CreateMsgIdleInfo;
215
216 /* globals */
217 static GObjectClass *parent_class = NULL;
218
219 static guint signals[NUM_SIGNALS] = {0};
220
221 GType
222 modest_mail_operation_get_type (void)
223 {
224         static GType my_type = 0;
225         if (!my_type) {
226                 static const GTypeInfo my_info = {
227                         sizeof(ModestMailOperationClass),
228                         NULL,           /* base init */
229                         NULL,           /* base finalize */
230                         (GClassInitFunc) modest_mail_operation_class_init,
231                         NULL,           /* class finalize */
232                         NULL,           /* class data */
233                         sizeof(ModestMailOperation),
234                         1,              /* n_preallocs */
235                         (GInstanceInitFunc) modest_mail_operation_init,
236                         NULL
237                 };
238                 my_type = g_type_register_static (G_TYPE_OBJECT,
239                                                   "ModestMailOperation",
240                                                   &my_info, 0);
241         }
242         return my_type;
243 }
244
245 static void
246 modest_mail_operation_class_init (ModestMailOperationClass *klass)
247 {
248         GObjectClass *gobject_class;
249         gobject_class = (GObjectClass*) klass;
250
251         parent_class            = g_type_class_peek_parent (klass);
252         gobject_class->finalize = modest_mail_operation_finalize;
253
254         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
255
256         /**
257          * ModestMailOperation::progress-changed
258          * @self: the #MailOperation that emits the signal
259          * @user_data: user data set when the signal handler was connected
260          *
261          * Emitted when the progress of a mail operation changes
262          */
263         signals[PROGRESS_CHANGED_SIGNAL] = 
264                 g_signal_new ("progress-changed",
265                               G_TYPE_FROM_CLASS (gobject_class),
266                               G_SIGNAL_RUN_FIRST,
267                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
268                               NULL, NULL,
269                               g_cclosure_marshal_VOID__POINTER,
270                               G_TYPE_NONE, 1, G_TYPE_POINTER);
271         /**
272          * operation-started
273          *
274          * This signal is issued whenever a mail operation starts, and
275          * starts mean when the tinymail operation is issued. This
276          * means that it could happen that something wrong happens and
277          * the tinymail function is never called. In this situation a
278          * operation-finished will be issued without any
279          * operation-started
280          */
281         signals[OPERATION_STARTED_SIGNAL] =
282                 g_signal_new ("operation-started",
283                               G_TYPE_FROM_CLASS (gobject_class),
284                               G_SIGNAL_RUN_FIRST,
285                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_started),
286                               NULL, NULL,
287                               g_cclosure_marshal_VOID__VOID,
288                               G_TYPE_NONE, 0);
289         /**
290          * operation-started
291          *
292          * This signal is issued whenever a mail operation
293          * finishes. Note that this signal could be issued without any
294          * previous "operation-started" signal, because this last one
295          * is only issued when the tinymail operation is successfully
296          * started
297          */
298         signals[OPERATION_FINISHED_SIGNAL] =
299                 g_signal_new ("operation-finished",
300                               G_TYPE_FROM_CLASS (gobject_class),
301                               G_SIGNAL_RUN_FIRST,
302                               G_STRUCT_OFFSET (ModestMailOperationClass, operation_finished),
303                               NULL, NULL,
304                               g_cclosure_marshal_VOID__VOID,
305                               G_TYPE_NONE, 0);
306 }
307
308 static void
309 modest_mail_operation_init (ModestMailOperation *obj)
310 {
311         ModestMailOperationPrivate *priv;
312
313         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
314
315         priv->account        = NULL;
316         priv->status         = MODEST_MAIL_OPERATION_STATUS_INVALID;
317         priv->op_type        = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
318         priv->error          = NULL;
319         priv->done           = 0;
320         priv->total          = 0;
321         priv->source         = NULL;
322         priv->error_checking = NULL;
323         priv->error_checking_user_data = NULL;
324 }
325
326 static void
327 modest_mail_operation_finalize (GObject *obj)
328 {
329         ModestMailOperationPrivate *priv;
330
331         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
332
333         
334         
335         if (priv->error) {
336                 g_error_free (priv->error);
337                 priv->error = NULL;
338         }
339         if (priv->source) {
340                 g_object_unref (priv->source);
341                 priv->source = NULL;
342         }
343         if (priv->account) {
344                 g_object_unref (priv->account);
345                 priv->account = NULL;
346         }
347
348
349         G_OBJECT_CLASS(parent_class)->finalize (obj);
350 }
351
352 ModestMailOperation*
353 modest_mail_operation_new (GObject *source)
354 {
355         ModestMailOperation *obj;
356         ModestMailOperationPrivate *priv;
357                 
358         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
359         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
360
361         if (source != NULL)
362                 priv->source = g_object_ref(source);
363
364         return obj;
365 }
366
367 ModestMailOperation*
368 modest_mail_operation_new_with_error_handling (GObject *source,
369                                                ErrorCheckingUserCallback error_handler,
370                                                gpointer user_data,
371                                                ErrorCheckingUserDataDestroyer error_handler_destroyer)
372 {
373         ModestMailOperation *obj;
374         ModestMailOperationPrivate *priv;
375                 
376         obj = modest_mail_operation_new (source);
377         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
378         
379         g_return_val_if_fail (error_handler != NULL, obj);
380         priv->error_checking = error_handler;
381         priv->error_checking_user_data = user_data;
382         priv->error_checking_user_data_destroyer = error_handler_destroyer;
383
384         return obj;
385 }
386
387 void
388 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
389 {
390         ModestMailOperationPrivate *priv;
391
392         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION(self));
393         
394         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
395         g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);     
396
397         /* Call the user callback */
398         if (priv->error_checking != NULL)
399                 priv->error_checking (self, priv->error_checking_user_data);
400 }
401
402
403 ModestMailOperationTypeOperation
404 modest_mail_operation_get_type_operation (ModestMailOperation *self)
405 {
406         ModestMailOperationPrivate *priv;
407
408         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
409                               MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
410         
411         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
412         
413         return priv->op_type;
414 }
415
416 gboolean 
417 modest_mail_operation_is_mine (ModestMailOperation *self, 
418                                GObject *me)
419 {
420         ModestMailOperationPrivate *priv;
421
422         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
423                               FALSE);
424         
425         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
426         if (priv->source == NULL) return FALSE;
427
428         return priv->source == me;
429 }
430
431 GObject *
432 modest_mail_operation_get_source (ModestMailOperation *self)
433 {
434         ModestMailOperationPrivate *priv;
435
436         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
437                               NULL);
438         
439         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
440         if (!priv) {
441                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
442                 return NULL;
443         }
444         
445         return (priv->source) ? g_object_ref (priv->source) : NULL;
446 }
447
448 ModestMailOperationStatus
449 modest_mail_operation_get_status (ModestMailOperation *self)
450 {
451         ModestMailOperationPrivate *priv;
452
453         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
454         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
455                               MODEST_MAIL_OPERATION_STATUS_INVALID);
456
457         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
458         if (!priv) {
459                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
460                 return MODEST_MAIL_OPERATION_STATUS_INVALID;
461         }
462         
463         return priv->status;
464 }
465
466 const GError *
467 modest_mail_operation_get_error (ModestMailOperation *self)
468 {
469         ModestMailOperationPrivate *priv;
470
471         g_return_val_if_fail (self, NULL);
472         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
473
474         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
475
476         if (!priv) {
477                 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
478                 return NULL;
479         }
480
481         return priv->error;
482 }
483
484 gboolean 
485 modest_mail_operation_cancel (ModestMailOperation *self)
486 {
487         ModestMailOperationPrivate *priv;
488         gboolean canceled = FALSE;
489         
490         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION (self), FALSE);
491
492         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
493
494         /* Note that if we call cancel with an already canceled mail
495            operation the progress changed signal won't be emitted */
496         if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
497                 return FALSE;
498
499         /* Set new status */
500         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
501         
502         /* Cancel the mail operation. We need to wrap it between this
503            start/stop operations to allow following calls to the
504            account */
505         g_return_val_if_fail (priv->account, FALSE);
506
507         if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SEND) {
508                 ModestTnySendQueue *queue;
509                 queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (priv->account));
510                 /* Cancel sending without removing the item */
511                 tny_send_queue_cancel (TNY_SEND_QUEUE (queue), FALSE, NULL);
512         } else {
513                 /* Cancel operation */
514                 tny_account_cancel (priv->account);
515         }
516
517         return canceled;
518 }
519
520 guint 
521 modest_mail_operation_get_task_done (ModestMailOperation *self)
522 {
523         ModestMailOperationPrivate *priv;
524
525         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
526                               0);
527         
528         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
529         return priv->done;
530 }
531
532 guint 
533 modest_mail_operation_get_task_total (ModestMailOperation *self)
534 {
535         ModestMailOperationPrivate *priv;
536
537         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
538                               0);
539
540         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
541         return priv->total;
542 }
543
544 gboolean
545 modest_mail_operation_is_finished (ModestMailOperation *self)
546 {
547         ModestMailOperationPrivate *priv;
548         gboolean retval = FALSE;
549
550         g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
551                               FALSE);
552         
553         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
554
555         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
556             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
557             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
558             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
559                 retval = TRUE;
560         } else {
561                 retval = FALSE;
562         }
563
564         return retval;
565 }
566
567 /*
568  * Creates an image of the current state of a mail operation, the
569  * caller must free it
570  */
571 static ModestMailOperationState *
572 modest_mail_operation_clone_state (ModestMailOperation *self)
573 {
574         ModestMailOperationState *state;
575         ModestMailOperationPrivate *priv;
576
577         /* FIXME: this should be fixed properly
578          * 
579          * in some cases, priv was NULL, so checking here to
580          * make sure.
581          */
582         g_return_val_if_fail (self, NULL);
583         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
584         g_return_val_if_fail (priv, NULL);
585
586         if (!priv)
587                 return NULL;
588
589         state = g_slice_new (ModestMailOperationState);
590
591         state->status = priv->status;
592         state->op_type = priv->op_type;
593         state->done = priv->done;
594         state->total = priv->total;
595         state->finished = modest_mail_operation_is_finished (self);
596         state->bytes_done = 0;
597         state->bytes_total = 0;
598
599         return state;
600 }
601
602 /* ******************************************************************* */
603 /* ************************** SEND   ACTIONS ************************* */
604 /* ******************************************************************* */
605
606 void
607 modest_mail_operation_send_mail (ModestMailOperation *self,
608                                  TnyTransportAccount *transport_account,
609                                  TnyMsg* msg)
610 {
611         TnySendQueue *send_queue = NULL;
612         ModestMailOperationPrivate *priv;
613         SendMsgInfo *info;
614         
615         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
616         g_return_if_fail (transport_account && TNY_IS_TRANSPORT_ACCOUNT (transport_account));
617         g_return_if_fail (msg && TNY_IS_MSG (msg));
618         
619         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
620
621         /* Get account and set it into mail_operation */
622         priv->account = g_object_ref (transport_account);
623         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
624         priv->done = 1;
625         priv->total = 1;
626
627         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
628         if (!TNY_IS_SEND_QUEUE(send_queue)) {
629                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
630                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
631                              "modest: could not find send queue for account\n");
632                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
633                 modest_mail_operation_notify_end (self);
634
635         } else {
636                 /* Add the msg to the queue */
637                 modest_mail_operation_notify_start (self);
638                 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue), 
639                                            msg, 
640                                            &(priv->error));
641
642                 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
643
644                 info = g_slice_new0 (SendMsgInfo);
645
646                 info->mail_op = g_object_ref (self);
647                 info->msg = g_object_ref (msg);
648                 info->msg_sent_handler = g_signal_connect (G_OBJECT (send_queue), "msg-sent",
649                                                            G_CALLBACK (send_mail_msg_sent_handler), info);
650                 info->error_happened_handler = g_signal_connect (G_OBJECT (send_queue), "error-happened",
651                                                                  G_CALLBACK (send_mail_error_happened_handler), info);
652         }
653
654 }
655
656 static void
657 common_send_mail_operation_end (TnySendQueue *queue, TnyMsg *msg,
658                                 SendMsgInfo *info)
659 {
660         g_signal_handler_disconnect (queue, info->msg_sent_handler);
661         g_signal_handler_disconnect (queue, info->error_happened_handler);
662
663         g_object_unref (info->msg);
664         modest_mail_operation_notify_end (info->mail_op);
665         g_object_unref (info->mail_op);
666
667         g_slice_free (SendMsgInfo, info);
668 }
669
670 static void     
671 send_mail_msg_sent_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
672                             guint nth, guint total, gpointer userdata)
673 {
674         SendMsgInfo *info = (SendMsgInfo *) userdata;
675
676         if (!strcmp (tny_msg_get_url_string (msg),
677                      tny_msg_get_url_string (info->msg))) {
678                 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
679                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
680
681                 common_send_mail_operation_end (queue, msg, info);
682         }
683 }
684
685 static void     
686 send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
687                                   GError *error, gpointer userdata)
688 {
689         SendMsgInfo *info = (SendMsgInfo *) userdata;
690
691         if (!strcmp (tny_msg_get_url_string (msg),
692                      tny_msg_get_url_string (info->msg))) {
693                 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
694                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
695                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
696                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
697                              "modest: send mail failed\n");
698
699                 common_send_mail_operation_end (queue, msg, info);
700         }
701 }
702
703
704 static gboolean
705 idle_create_msg_cb (gpointer idle_data)
706 {
707         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
708
709         /* This is a GDK lock because we are an idle callback and
710          * info->callback can contain Gtk+ code */
711
712         gdk_threads_enter (); /* CHECKED */
713         info->callback (info->mail_op, info->msg, info->userdata);
714
715         g_object_unref (info->mail_op);
716         if (info->msg)
717                 g_object_unref (info->msg);
718         g_slice_free (CreateMsgIdleInfo, info);
719         gdk_threads_leave (); /* CHECKED */
720
721         return FALSE;
722 }
723
724 static gpointer 
725 create_msg_thread (gpointer thread_data)
726 {
727         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
728         TnyMsg *new_msg = NULL;
729         ModestMailOperationPrivate *priv;
730
731         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
732         if (info->html_body == NULL) {
733                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
734                                               info->bcc, info->subject, info->plain_body, 
735                                               info->attachments_list);
736         } else {
737                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
738                                                          info->bcc, info->subject, info->html_body,
739                                                          info->plain_body, info->attachments_list,
740                                                          info->images_list);
741         }
742
743         if (new_msg) {
744                 TnyHeader *header;
745
746                 /* Set priority flags in message */
747                 header = tny_msg_get_header (new_msg);
748                 tny_header_set_flag (header, info->priority_flags);
749
750                 /* Set attachment flags in message */
751                 if (info->attachments_list != NULL)
752                         tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
753
754                 g_object_unref (G_OBJECT(header));
755         } else {
756                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
757                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
758                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
759                              "modest: failed to create a new msg\n");
760         }
761
762
763         g_free (info->to);
764         g_free (info->from);
765         g_free (info->cc);
766         g_free (info->bcc);
767         g_free (info->plain_body);
768         g_free (info->html_body);
769         g_free (info->subject);
770         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
771         g_list_free (info->attachments_list);
772         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
773         g_list_free (info->images_list);
774
775         if (info->callback) {
776                 CreateMsgIdleInfo *idle_info;
777                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
778                 idle_info->mail_op = g_object_ref (info->mail_op);
779                 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
780                 idle_info->callback = info->callback;
781                 idle_info->userdata = info->userdata;
782                 g_idle_add (idle_create_msg_cb, idle_info);
783         } else {
784                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
785         }
786
787         g_object_unref (info->mail_op);
788         g_slice_free (CreateMsgInfo, info);
789         return NULL;
790 }
791
792 void
793 modest_mail_operation_create_msg (ModestMailOperation *self,
794                                   const gchar *from, const gchar *to,
795                                   const gchar *cc, const gchar *bcc,
796                                   const gchar *subject, const gchar *plain_body,
797                                   const gchar *html_body,
798                                   const GList *attachments_list,
799                                   const GList *images_list,
800                                   TnyHeaderFlags priority_flags,
801                                   ModestMailOperationCreateMsgCallback callback,
802                                   gpointer userdata)
803 {
804         CreateMsgInfo *info = NULL;
805
806         info = g_slice_new0 (CreateMsgInfo);
807         info->mail_op = g_object_ref (self);
808
809         info->from = g_strdup (from);
810         info->to = g_strdup (to);
811         info->cc = g_strdup (cc);
812         info->bcc  = g_strdup (bcc);
813         info->subject = g_strdup (subject);
814         info->plain_body = g_strdup (plain_body);
815         info->html_body = g_strdup (html_body);
816         info->attachments_list = g_list_copy ((GList *) attachments_list);
817         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
818         info->images_list = g_list_copy ((GList *) images_list);
819         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
820         info->priority_flags = priority_flags;
821
822         info->callback = callback;
823         info->userdata = userdata;
824
825         g_thread_create (create_msg_thread, info, FALSE, NULL);
826 }
827
828 typedef struct
829 {
830         TnyTransportAccount *transport_account;
831         TnyMsg *draft_msg;
832 } SendNewMailInfo;
833
834 static void
835 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
836                                         TnyMsg *msg,
837                                         gpointer userdata)
838 {
839         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
840         TnyFolder *draft_folder = NULL;
841         TnyFolder *outbox_folder = NULL;
842         TnyHeader *header;
843         GError *err = NULL;
844
845         if (!msg) {
846                 goto end;
847         }
848
849         /* Call mail operation */
850         modest_mail_operation_send_mail (self, info->transport_account, msg);
851
852         /* Remove old mail from its source folder */
853         draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
854                                                               TNY_FOLDER_TYPE_DRAFTS);
855         outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
856                                                                TNY_FOLDER_TYPE_OUTBOX);
857         if (info->draft_msg != NULL) {
858                 TnyFolder *folder = NULL;
859                 TnyFolder *src_folder = NULL;
860                 TnyFolderType folder_type;              
861                 folder = tny_msg_get_folder (info->draft_msg);          
862                 if (folder == NULL) goto end;
863                 folder_type = modest_tny_folder_guess_folder_type (folder);
864
865                 if (folder_type == TNY_FOLDER_TYPE_INVALID)
866                         g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
867                 
868                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
869                         src_folder = outbox_folder;
870                 else 
871                         src_folder = draft_folder;
872
873                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
874                  * because this function requires it to have a UID. */          
875                 header = tny_msg_get_header (info->draft_msg);
876                 tny_folder_remove_msg (src_folder, header, NULL);
877
878                 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
879 /*              tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);  /\* expunge *\/ */
880                 
881                 g_object_unref (header);
882                 g_object_unref (folder);
883         }
884
885 end:
886         if (err != NULL)
887                 g_error_free(err);      
888         if (info->draft_msg)
889                 g_object_unref (info->draft_msg);
890         if (draft_folder)
891                 g_object_unref (draft_folder);
892         if (outbox_folder)
893                 g_object_unref (outbox_folder);
894         if (info->transport_account)
895                 g_object_unref (info->transport_account);
896         g_slice_free (SendNewMailInfo, info);
897 }
898
899 void
900 modest_mail_operation_send_new_mail (ModestMailOperation *self,
901                                      TnyTransportAccount *transport_account,
902                                      TnyMsg *draft_msg,
903                                      const gchar *from,  const gchar *to,
904                                      const gchar *cc,  const gchar *bcc,
905                                      const gchar *subject, const gchar *plain_body,
906                                      const gchar *html_body,
907                                      const GList *attachments_list,
908                                      const GList *images_list,
909                                      TnyHeaderFlags priority_flags)
910 {
911         ModestMailOperationPrivate *priv = NULL;
912         SendNewMailInfo *info;
913
914         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
915         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
916
917         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
918         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
919         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
920         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
921         
922         /* Check parametters */
923         if (to == NULL) {
924                 /* Set status failed and set an error */
925                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
926                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
927                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
928                              _("Error trying to send a mail. You need to set at least one recipient"));
929                 return;
930         }
931         info = g_slice_new0 (SendNewMailInfo);
932         info->transport_account = transport_account;
933         if (transport_account)
934                 g_object_ref (transport_account);
935         info->draft_msg = draft_msg;
936         if (draft_msg)
937                 g_object_ref (draft_msg);
938
939
940         modest_mail_operation_notify_start (self);
941         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
942                                           attachments_list, images_list, priority_flags,
943                                           modest_mail_operation_send_new_mail_cb, info);
944
945 }
946
947 typedef struct
948 {
949         TnyTransportAccount *transport_account;
950         TnyMsg *draft_msg;
951         SaveToDraftstCallback callback;
952         gpointer user_data;
953         TnyFolder *drafts;
954         TnyMsg *msg;
955         ModestMailOperation *mailop;
956 } SaveToDraftsAddMsgInfo;
957
958 static void
959 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
960                                                 gboolean canceled,
961                                                 GError *err,
962                                                 gpointer userdata)
963 {
964         ModestMailOperationPrivate *priv = NULL;
965         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
966
967         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
968
969         if (priv->error) {
970                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
971                 g_error_free(priv->error);
972         }
973
974         priv->error = (err == NULL) ? NULL : g_error_copy(err);
975
976         if ((!priv->error) && (info->draft_msg != NULL)) {
977                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
978                 TnyFolder *src_folder = tny_header_get_folder (header);
979
980                 /* Remove the old draft */
981                 tny_folder_remove_msg (src_folder, header, NULL);
982
983                 /* Synchronize to expunge and to update the msg counts */
984                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
985                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
986
987                 g_object_unref (G_OBJECT(header));
988                 g_object_unref (G_OBJECT(src_folder));
989         }
990
991         if (!priv->error)
992                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
993         else
994                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
995
996         /* Call the user callback */
997         if (info->callback)
998                 info->callback (info->mailop, info->msg, info->user_data);
999
1000         if (info->transport_account)
1001                 g_object_unref (G_OBJECT(info->transport_account));
1002         if (info->draft_msg)
1003                 g_object_unref (G_OBJECT (info->draft_msg));
1004         if (info->drafts)
1005                 g_object_unref (G_OBJECT(info->drafts));
1006         if (info->msg)
1007                 g_object_unref (G_OBJECT (info->msg));
1008         g_slice_free (SaveToDraftsAddMsgInfo, info);
1009
1010         modest_mail_operation_notify_end (info->mailop);
1011         g_object_unref(info->mailop);
1012 }
1013
1014 typedef struct
1015 {
1016         TnyTransportAccount *transport_account;
1017         TnyMsg *draft_msg;
1018         SaveToDraftstCallback callback;
1019         gpointer user_data;
1020 } SaveToDraftsInfo;
1021
1022 static void
1023 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1024                                          TnyMsg *msg,
1025                                          gpointer userdata)
1026 {
1027         TnyFolder *drafts = NULL;
1028         ModestMailOperationPrivate *priv = NULL;
1029         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1030
1031         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1032
1033         if (!msg) {
1034                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1035                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1036                              "modest: failed to create a new msg\n");
1037         } else {
1038                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1039                                                                 TNY_FOLDER_TYPE_DRAFTS);
1040                 if (!drafts) {
1041                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1042                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1043                                      "modest: failed to create a new msg\n");
1044                 }
1045         }
1046
1047         if (!priv->error) {
1048                 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1049                 cb_info->transport_account = g_object_ref(info->transport_account);
1050                 cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1051                 cb_info->callback = info->callback;
1052                 cb_info->user_data = info->user_data;
1053                 cb_info->drafts = g_object_ref(drafts);
1054                 cb_info->msg = g_object_ref(msg);
1055                 cb_info->mailop = g_object_ref(self);
1056                 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1057                                          NULL, cb_info);
1058         } else {
1059                 /* Call the user callback */
1060                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1061                 if (info->callback)
1062                         info->callback (self, msg, info->user_data);
1063                 modest_mail_operation_notify_end (self);
1064         }
1065
1066         if (drafts)
1067                 g_object_unref (G_OBJECT(drafts));
1068         if (info->draft_msg)
1069                 g_object_unref (G_OBJECT (info->draft_msg));
1070         if (info->transport_account)
1071                 g_object_unref (G_OBJECT(info->transport_account));
1072         g_slice_free (SaveToDraftsInfo, info);
1073 }
1074
1075 void
1076 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1077                                       TnyTransportAccount *transport_account,
1078                                       TnyMsg *draft_msg,
1079                                       const gchar *from,  const gchar *to,
1080                                       const gchar *cc,  const gchar *bcc,
1081                                       const gchar *subject, const gchar *plain_body,
1082                                       const gchar *html_body,
1083                                       const GList *attachments_list,
1084                                       const GList *images_list,
1085                                       TnyHeaderFlags priority_flags,
1086                                       SaveToDraftstCallback callback,
1087                                       gpointer user_data)
1088 {
1089         ModestMailOperationPrivate *priv = NULL;
1090         SaveToDraftsInfo *info = NULL;
1091
1092         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1093         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1094
1095         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1096
1097         /* Get account and set it into mail_operation */
1098         priv->account = g_object_ref (transport_account);
1099         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1100
1101         info = g_slice_new0 (SaveToDraftsInfo);
1102         info->transport_account = g_object_ref (transport_account);
1103         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1104         info->callback = callback;
1105         info->user_data = user_data;
1106
1107         modest_mail_operation_notify_start (self);
1108         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1109                                           attachments_list, images_list, priority_flags,
1110                                           modest_mail_operation_save_to_drafts_cb, info);
1111 }
1112
1113 typedef struct
1114 {
1115         ModestMailOperation *mail_op;
1116         TnyMimePart *mime_part;
1117         gssize size;
1118         GetMimePartSizeCallback callback;
1119         gpointer userdata;
1120 } GetMimePartSizeInfo;
1121
1122 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1123 /* We use this folder observer to track the headers that have been
1124  * added to a folder */
1125 typedef struct {
1126         GObject parent;
1127         TnyList *new_headers;
1128 } InternalFolderObserver;
1129
1130 typedef struct {
1131         GObjectClass parent;
1132 } InternalFolderObserverClass;
1133
1134 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1135
1136 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1137                          internal_folder_observer,
1138                          G_TYPE_OBJECT,
1139                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1140
1141
1142 static void
1143 foreach_add_item (gpointer header, gpointer user_data)
1144 {
1145         tny_list_prepend (TNY_LIST (user_data), 
1146                           g_object_ref (G_OBJECT (header)));
1147 }
1148
1149 /* This is the method that looks for new messages in a folder */
1150 static void
1151 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1152 {
1153         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1154         
1155         TnyFolderChangeChanged changed;
1156
1157         changed = tny_folder_change_get_changed (change);
1158
1159         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1160                 TnyList *list;
1161
1162                 /* Get added headers */
1163                 list = tny_simple_list_new ();
1164                 tny_folder_change_get_added_headers (change, list);
1165
1166                 /* Add them to the folder observer */
1167                 tny_list_foreach (list, foreach_add_item, 
1168                                   derived->new_headers);
1169
1170                 g_object_unref (G_OBJECT (list));
1171         }
1172 }
1173
1174 static void
1175 internal_folder_observer_init (InternalFolderObserver *self) 
1176 {
1177         self->new_headers = tny_simple_list_new ();
1178 }
1179 static void
1180 internal_folder_observer_finalize (GObject *object) 
1181 {
1182         InternalFolderObserver *self;
1183
1184         self = (InternalFolderObserver *) object;
1185         g_object_unref (self->new_headers);
1186
1187         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1188 }
1189 static void
1190 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1191 {
1192         iface->update_func = internal_folder_observer_update;
1193 }
1194 static void
1195 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1196 {
1197         GObjectClass *object_class;
1198
1199         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1200         object_class = (GObjectClass*) klass;
1201         object_class->finalize = internal_folder_observer_finalize;
1202 }
1203
1204 typedef struct 
1205 {
1206         ModestMailOperation *mail_op;
1207         gchar *account_name;
1208         UpdateAccountCallback callback;
1209         gpointer user_data;
1210         TnyList *folders;
1211         gint pending_calls;
1212         gboolean poke_all;
1213         TnyFolderObserver *inbox_observer;
1214         guint update_timeout;
1215 } UpdateAccountInfo;
1216
1217
1218 static void
1219 destroy_update_account_info (UpdateAccountInfo *info)
1220 {
1221         if (info->update_timeout) {
1222                 g_source_remove (info->update_timeout);
1223                 info->update_timeout = 0;
1224         }
1225
1226         g_free (info->account_name);
1227         g_object_unref (info->folders);
1228         g_object_unref (info->mail_op);
1229         g_slice_free (UpdateAccountInfo, info);
1230 }
1231
1232 static void
1233 update_account_get_msg_async_cb (TnyFolder *folder, 
1234                                  gboolean canceled, 
1235                                  TnyMsg *msg, 
1236                                  GError *err, 
1237                                  gpointer user_data)
1238 {
1239         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1240
1241         /* Just delete the helper. Don't do anything with the new
1242            msg. There is also no need to check for errors */
1243         g_object_unref (msg_info->mail_op);
1244         g_object_unref (msg_info->header);
1245         g_slice_free (GetMsgInfo, msg_info);
1246 }
1247
1248
1249 static void
1250 inbox_refreshed_cb (TnyFolder *inbox, 
1251                     gboolean canceled, 
1252                     GError *err, 
1253                     gpointer user_data)
1254 {       
1255         UpdateAccountInfo *info;
1256         ModestMailOperationPrivate *priv;
1257         TnyIterator *new_headers_iter;
1258         GPtrArray *new_headers_array = NULL;   
1259         gint max_size, retrieve_limit, i;
1260         ModestAccountMgr *mgr;
1261         ModestAccountRetrieveType retrieve_type;
1262         TnyList *new_headers = NULL;
1263         gboolean headers_only;
1264         TnyTransportAccount *transport_account;
1265         ModestTnySendQueue *send_queue;
1266
1267         info = (UpdateAccountInfo *) user_data;
1268         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1269         mgr = modest_runtime_get_account_mgr ();
1270
1271         if (canceled || err || !inbox) {
1272                 /* Try to send anyway */
1273                 goto send_mail;
1274         }
1275
1276         /* Get the message max size */
1277         max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1278                                          MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1279         if (max_size == 0)
1280                 max_size = G_MAXINT;
1281         else
1282                 max_size = max_size * KB;
1283
1284         /* Create the new headers array. We need it to sort the
1285            new headers by date */
1286         new_headers_array = g_ptr_array_new ();
1287         new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1288         while (!tny_iterator_is_done (new_headers_iter)) {
1289                 TnyHeader *header = NULL;
1290
1291                 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1292                 /* Apply per-message size limits */
1293                 if (tny_header_get_message_size (header) < max_size)
1294                         g_ptr_array_add (new_headers_array, g_object_ref (header));
1295                                 
1296                 g_object_unref (header);
1297                 tny_iterator_next (new_headers_iter);
1298         }
1299         g_object_unref (new_headers_iter);
1300         tny_folder_remove_observer (inbox, info->inbox_observer);
1301         g_object_unref (info->inbox_observer);
1302         info->inbox_observer = NULL;
1303
1304         /* Update the last updated key, even if we don't have to get new headers */
1305         modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1306         
1307         if (new_headers_array->len == 0)
1308                 goto send_mail;
1309
1310         /* Get per-account message amount retrieval limit */
1311         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1312         if (retrieve_limit == 0)
1313                 retrieve_limit = G_MAXINT;
1314         
1315         /* Get per-account retrieval type */
1316         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name); 
1317         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1318
1319         /* Order by date */
1320         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1321         
1322         /* TODO: Ask the user, instead of just failing,
1323          * showing mail_nc_msg_count_limit_exceeded, with 'Get
1324          * all' and 'Newest only' buttons. */
1325         if (new_headers_array->len > retrieve_limit) {
1326                 /* TODO */
1327         }
1328         
1329         if (!headers_only) {
1330                 gint msg_num = 0;
1331                 const gint msg_list_size = compute_message_array_size (new_headers_array);
1332
1333                 priv->done = 0;
1334                 priv->total = MIN (new_headers_array->len, retrieve_limit);
1335                 while (msg_num < priv->total) {         
1336                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1337                         TnyFolder *folder = tny_header_get_folder (header);
1338                         GetMsgInfo *msg_info;
1339
1340                         /* Create the message info */
1341                         msg_info = g_slice_new0 (GetMsgInfo);
1342                         msg_info->mail_op = g_object_ref (info->mail_op);
1343                         msg_info->header = g_object_ref (header);
1344                         msg_info->total_bytes = msg_list_size;
1345
1346                         /* Get message in an async way */
1347                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb, 
1348                                                   get_msg_status_cb, msg_info);
1349
1350                         g_object_unref (folder);
1351                         
1352                         msg_num++;
1353                 }
1354         }
1355
1356         /* Copy the headers to a list and free the array */
1357         new_headers = tny_simple_list_new ();
1358         for (i=0; i < new_headers_array->len; i++) {
1359                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1360                 tny_list_append (new_headers, G_OBJECT (header));
1361         }
1362         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1363         g_ptr_array_free (new_headers_array, FALSE);
1364
1365  send_mail:
1366         /* Send mails */
1367         priv->done = 0;
1368         priv->total = 0;
1369
1370         /* Get the transport account */
1371         transport_account = (TnyTransportAccount *)
1372                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1373                                                                                     info->account_name);
1374         
1375         /* Try to send */
1376         send_queue = modest_runtime_get_send_queue (transport_account);
1377         modest_tny_send_queue_try_to_send (send_queue);
1378
1379         /* Check if the operation was a success */
1380         if (!priv->error)
1381                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1382
1383         /* Set the account back to not busy */
1384         modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1385
1386         /* Call the user callback */
1387         if (info->callback)
1388                 info->callback (info->mail_op, new_headers, info->user_data);
1389
1390         /* Notify about operation end */
1391         modest_mail_operation_notify_end (info->mail_op);
1392
1393         /* Frees */
1394         if (new_headers)
1395                 g_object_unref (new_headers);
1396         destroy_update_account_info (info);
1397 }
1398
1399 static void 
1400 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1401                           gboolean canceled,
1402                           TnyList *list, 
1403                           GError *err, 
1404                           gpointer user_data)
1405 {
1406         UpdateAccountInfo *info;
1407         ModestMailOperationPrivate *priv;
1408     
1409         info = (UpdateAccountInfo *) user_data;
1410         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1411
1412         if (err || canceled) {
1413                 /* Try to continue anyway */
1414         } else {
1415                 TnyIterator *iter = tny_list_create_iterator (list);
1416                 while (!tny_iterator_is_done (iter)) {
1417                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1418                         TnyList *folders = tny_simple_list_new ();
1419
1420                         /* Add to the list of all folders */
1421                         tny_list_append (info->folders, (GObject *) folder);
1422                         
1423                         /* Add pending call */
1424                         info->pending_calls++;
1425                         
1426                         tny_folder_store_get_folders_async (folder, folders, recurse_folders_async_cb, 
1427                                                             NULL, NULL, info);
1428                         
1429                         g_object_unref (G_OBJECT (folder));
1430                         
1431                         tny_iterator_next (iter);           
1432                 }
1433                 g_object_unref (G_OBJECT (iter));
1434                 g_object_unref (G_OBJECT (list));
1435         }
1436
1437         /* Remove my own pending call */
1438         info->pending_calls--;
1439
1440         /* This means that we have all the folders */
1441         if (info->pending_calls == 0) {
1442                 TnyIterator *iter_all_folders;
1443                 TnyFolder *inbox = NULL;
1444
1445                 iter_all_folders = tny_list_create_iterator (info->folders);
1446
1447                 /* Do a poke status over all folders */
1448                 while (!tny_iterator_is_done (iter_all_folders) &&
1449                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1450                         TnyFolder *folder = NULL;
1451
1452                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1453
1454                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1455                                 /* Get a reference to the INBOX */
1456                                 inbox = g_object_ref (folder);
1457                         } else {
1458                                 /* Issue a poke status over the folder */
1459                                 if (info->poke_all)
1460                                         tny_folder_poke_status (folder);
1461                         }
1462
1463                         /* Free and go to next */
1464                         g_object_unref (folder);
1465                         tny_iterator_next (iter_all_folders);
1466                 }
1467                 g_object_unref (iter_all_folders);
1468
1469                 /* Stop the progress notification */
1470                 g_source_remove (info->update_timeout);
1471                 info->update_timeout = 0;
1472
1473                 /* Refresh the INBOX */
1474                 if (inbox) {
1475                         /* Refresh the folder. Our observer receives
1476                          * the new emails during folder refreshes, so
1477                          * we can use observer->new_headers
1478                          */
1479                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1480                         tny_folder_add_observer (inbox, info->inbox_observer);
1481
1482                         /* Refresh the INBOX */
1483                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1484                         g_object_unref (inbox);
1485                 } else {
1486                         /* We could not perform the inbox refresh but
1487                            we'll try to send mails anyway */
1488                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1489                 }
1490         }
1491 }
1492
1493 /* 
1494  * Issues the "progress-changed" signal. The timer won't be removed,
1495  * so you must call g_source_remove to stop the signal emission
1496  */
1497 static gboolean
1498 timeout_notify_progress (gpointer data)
1499 {
1500         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1501         ModestMailOperationState *state;
1502
1503         state = modest_mail_operation_clone_state (mail_op);
1504
1505         /* This is a GDK lock because we are an idle callback and
1506          * the handlers of this signal can contain Gtk+ code */
1507
1508         gdk_threads_enter (); /* CHECKED */
1509         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1510         gdk_threads_leave (); /* CHECKED */
1511
1512         g_slice_free (ModestMailOperationState, state);
1513         
1514         return TRUE;
1515 }
1516
1517 void
1518 modest_mail_operation_update_account (ModestMailOperation *self,
1519                                       const gchar *account_name,
1520                                       gboolean poke_all,
1521                                       UpdateAccountCallback callback,
1522                                       gpointer user_data)
1523 {
1524         UpdateAccountInfo *info = NULL;
1525         ModestMailOperationPrivate *priv = NULL;
1526         ModestTnyAccountStore *account_store = NULL;
1527         TnyStoreAccount *store_account = NULL;
1528         TnyList *folders;
1529
1530         /* Init mail operation */
1531         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1532         priv->total = 0;
1533         priv->done  = 0;
1534         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1535         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1536
1537         /* Get the store account */
1538         account_store = modest_runtime_get_account_store ();
1539         store_account = (TnyStoreAccount *)
1540                 modest_tny_account_store_get_server_account (account_store,
1541                                                              account_name,
1542                                                              TNY_ACCOUNT_TYPE_STORE);
1543         priv->account = g_object_ref (store_account);
1544
1545         /* Create the helper object */
1546         info = g_slice_new0 (UpdateAccountInfo);
1547         info->pending_calls = 1;
1548         info->folders = tny_simple_list_new ();
1549         info->mail_op = g_object_ref (self);
1550         info->poke_all = poke_all;
1551         info->account_name = g_strdup (account_name);
1552         info->callback = callback;
1553         info->user_data = user_data;
1554         info->update_timeout = g_timeout_add (250, timeout_notify_progress, self);
1555
1556         /* Set account busy */
1557         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1558         modest_mail_operation_notify_start (self);
1559
1560         /* Get all folders and continue in the callback */    
1561         folders = tny_simple_list_new ();
1562         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
1563                                 folders, recurse_folders_async_cb, 
1564                                 NULL, NULL, info);
1565 }
1566
1567 /*
1568  * Used to notify the queue from the main
1569  * loop. We call it inside an idle call to achieve that
1570  */
1571 static gboolean
1572 idle_notify_queue (gpointer data)
1573 {
1574         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1575
1576         gdk_threads_enter ();
1577         modest_mail_operation_notify_end (mail_op);
1578         gdk_threads_leave ();
1579         g_object_unref (mail_op);
1580
1581         return FALSE;
1582 }
1583
1584 static int
1585 compare_headers_by_date (gconstpointer a,
1586                          gconstpointer b)
1587 {
1588         TnyHeader **header1, **header2;
1589         time_t sent1, sent2;
1590
1591         header1 = (TnyHeader **) a;
1592         header2 = (TnyHeader **) b;
1593
1594         sent1 = tny_header_get_date_sent (*header1);
1595         sent2 = tny_header_get_date_sent (*header2);
1596
1597         /* We want the most recent ones (greater time_t) at the
1598            beginning */
1599         if (sent1 < sent2)
1600                 return 1;
1601         else
1602                 return -1;
1603 }
1604
1605
1606 /* ******************************************************************* */
1607 /* ************************** STORE  ACTIONS ************************* */
1608 /* ******************************************************************* */
1609
1610
1611 TnyFolder *
1612 modest_mail_operation_create_folder (ModestMailOperation *self,
1613                                      TnyFolderStore *parent,
1614                                      const gchar *name)
1615 {
1616         ModestMailOperationPrivate *priv;
1617         TnyFolder *new_folder = NULL;
1618
1619         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1620         g_return_val_if_fail (name, NULL);
1621         
1622         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1623         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1624         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1625                 g_object_ref (parent) : 
1626                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1627
1628         /* Check for already existing folder */
1629         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1630                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1631                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1632                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1633                              _CS("ckdg_ib_folder_already_exists"));
1634         }
1635
1636         /* Check parent */
1637         if (TNY_IS_FOLDER (parent)) {
1638                 /* Check folder rules */
1639                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1640                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1641                         /* Set status failed and set an error */
1642                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1643                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1644                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1645                                      _("mail_in_ui_folder_create_error"));
1646                 }
1647         }
1648
1649         if (!strcmp (name, " ") || strchr (name, '/')) {
1650                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1651                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1652                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1653                              _("mail_in_ui_folder_create_error"));
1654         }
1655
1656         if (!priv->error) {
1657                 /* Create the folder */
1658                 modest_mail_operation_notify_start (self);
1659                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1660                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1661                 if (!priv->error)
1662                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1663         }
1664
1665         /* Notify about operation end */
1666         modest_mail_operation_notify_end (self);
1667
1668         return new_folder;
1669 }
1670
1671 void
1672 modest_mail_operation_remove_folder (ModestMailOperation *self,
1673                                      TnyFolder           *folder,
1674                                      gboolean             remove_to_trash)
1675 {
1676         TnyAccount *account;
1677         ModestMailOperationPrivate *priv;
1678         ModestTnyFolderRules rules;
1679
1680         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1681         g_return_if_fail (TNY_IS_FOLDER (folder));
1682         
1683         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1684         
1685         /* Check folder rules */
1686         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1687         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1688                 /* Set status failed and set an error */
1689                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1690                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1691                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1692                              _("mail_in_ui_folder_delete_error"));
1693                 goto end;
1694         }
1695
1696         /* Get the account */
1697         account = modest_tny_folder_get_account (folder);
1698         priv->account = g_object_ref(account);
1699         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1700
1701         /* Delete folder or move to trash */
1702         if (remove_to_trash) {
1703                 TnyFolder *trash_folder = NULL;
1704                 trash_folder = modest_tny_account_get_special_folder (account,
1705                                                                       TNY_FOLDER_TYPE_TRASH);
1706                 /* TODO: error_handling */
1707                 if (trash_folder) {
1708                         modest_mail_operation_notify_start (self);
1709                         modest_mail_operation_xfer_folder (self, folder,
1710                                                     TNY_FOLDER_STORE (trash_folder), 
1711                                                     TRUE, NULL, NULL);
1712                         g_object_unref (trash_folder);
1713                 }
1714         } else {
1715                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1716                 if (parent) {
1717                         modest_mail_operation_notify_start (self);
1718                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
1719                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1720                         
1721                         if (!priv->error)
1722                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1723
1724                         g_object_unref (parent);
1725                 } else
1726                         g_warning ("%s: could not get parent folder", __FUNCTION__);
1727         }
1728         g_object_unref (G_OBJECT (account));
1729
1730  end:
1731         /* Notify about operation end */
1732         modest_mail_operation_notify_end (self);
1733 }
1734
1735 static void
1736 transfer_folder_status_cb (GObject *obj,
1737                            TnyStatus *status,
1738                            gpointer user_data)
1739 {
1740         ModestMailOperation *self;
1741         ModestMailOperationPrivate *priv;
1742         ModestMailOperationState *state;
1743         XFerMsgAsyncHelper *helper;
1744
1745         g_return_if_fail (status != NULL);
1746         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1747
1748         helper = (XFerMsgAsyncHelper *) user_data;
1749         g_return_if_fail (helper != NULL);
1750
1751         self = helper->mail_op;
1752         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1753
1754         priv->done = status->position;
1755         priv->total = status->of_total;
1756
1757         state = modest_mail_operation_clone_state (self);
1758
1759         /* This is not a GDK lock because we are a Tinymail callback
1760          * which is already GDK locked by Tinymail */
1761
1762         /* no gdk_threads_enter (), CHECKED */
1763
1764         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1765
1766         /* no gdk_threads_leave (), CHECKED */
1767
1768         g_slice_free (ModestMailOperationState, state);
1769 }
1770
1771
1772 static void
1773 transfer_folder_cb (TnyFolder *folder, 
1774                     gboolean cancelled, 
1775                     TnyFolderStore *into, 
1776                     TnyFolder *new_folder, 
1777                     GError *err, 
1778                     gpointer user_data)
1779 {
1780         XFerMsgAsyncHelper *helper;
1781         ModestMailOperation *self = NULL;
1782         ModestMailOperationPrivate *priv = NULL;
1783
1784         helper = (XFerMsgAsyncHelper *) user_data;
1785         g_return_if_fail (helper != NULL);       
1786
1787         self = helper->mail_op;
1788         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1789
1790         if (err) {
1791                 priv->error = g_error_copy (err);
1792                 priv->done = 0;
1793                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1794         } else if (cancelled) {
1795                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1796                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1797                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1798                              _("Transference of %s was cancelled."),
1799                              tny_folder_get_name (folder));
1800         } else {
1801                 priv->done = 1;
1802                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1803         }
1804                 
1805         /* Notify about operation end */
1806         modest_mail_operation_notify_end (self);
1807
1808         /* If user defined callback function was defined, call it */
1809         if (helper->user_callback) {
1810
1811                 /* This is not a GDK lock because we are a Tinymail callback
1812                  * which is already GDK locked by Tinymail */
1813
1814                 /* no gdk_threads_enter (), CHECKED */
1815                 helper->user_callback (self, helper->user_data);
1816                 /* no gdk_threads_leave () , CHECKED */
1817         }
1818
1819         /* Free */
1820         g_object_unref (helper->mail_op);
1821         g_slice_free   (XFerMsgAsyncHelper, helper);
1822 }
1823
1824 /**
1825  *
1826  * This function checks if the new name is a valid name for our local
1827  * folders account. The new name could not be the same than then name
1828  * of any of the mandatory local folders
1829  *
1830  * We can not rely on tinymail because tinymail does not check the
1831  * name of the virtual folders that the account could have in the case
1832  * that we're doing a rename (because it directly calls Camel which
1833  * knows nothing about our virtual folders). 
1834  *
1835  * In the case of an actual copy/move (i.e. move/copy a folder between
1836  * accounts) tinymail uses the tny_folder_store_create_account which
1837  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1838  * checks the new name of the folder, so this call in that case
1839  * wouldn't be needed. *But* NOTE that if tinymail changes its
1840  * implementation (if folder transfers within the same account is no
1841  * longer implemented as a rename) this call will allow Modest to work
1842  * perfectly
1843  *
1844  * If the new name is not valid, this function will set the status to
1845  * failed and will set also an error in the mail operation
1846  */
1847 static gboolean
1848 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1849                                  TnyFolderStore *into,
1850                                  const gchar *new_name)
1851 {
1852         if (TNY_IS_ACCOUNT (into) && 
1853             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1854             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1855                                                                  new_name)) {
1856                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1857                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1858                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1859                              _CS("ckdg_ib_folder_already_exists"));
1860                 return FALSE;
1861         } else
1862                 return TRUE;
1863 }
1864
1865 void
1866 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1867                                    TnyFolder *folder,
1868                                    TnyFolderStore *parent,
1869                                    gboolean delete_original,
1870                                    XferAsyncUserCallback user_callback,
1871                                    gpointer user_data)
1872 {
1873         ModestMailOperationPrivate *priv = NULL;
1874         ModestTnyFolderRules parent_rules = 0, rules; 
1875         XFerMsgAsyncHelper *helper = NULL;
1876         const gchar *folder_name = NULL;
1877         const gchar *error_msg;
1878
1879         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1880         g_return_if_fail (TNY_IS_FOLDER (folder));
1881         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1882
1883         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1884         folder_name = tny_folder_get_name (folder);
1885
1886         /* Set the error msg */
1887         error_msg = _("mail_in_ui_folder_move_target_error");
1888
1889         /* Get account and set it into mail_operation */
1890         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1891         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1892         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1893
1894         /* Get folder rules */
1895         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1896         if (TNY_IS_FOLDER (parent))
1897                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1898         
1899         /* Apply operation constraints */
1900         if ((gpointer) parent == (gpointer) folder ||
1901             (!TNY_IS_FOLDER_STORE (parent)) || 
1902             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1903                 /* Folder rules */
1904                 goto error;
1905         } else if (TNY_IS_FOLDER (parent) && 
1906                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1907                 /* Folder rules */
1908                 goto error;
1909
1910         } else if (TNY_IS_FOLDER (parent) &&
1911                    TNY_IS_FOLDER_STORE (folder) &&
1912                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
1913                                                   TNY_FOLDER_STORE (folder))) {
1914                 /* Do not move a parent into a child */
1915                 goto error;
1916         } else if (TNY_IS_FOLDER_STORE (parent) &&
1917                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1918                 /* Check that the new folder name is not used by any
1919                    parent subfolder */
1920                 goto error;     
1921         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1922                 /* Check that the new folder name is not used by any
1923                    special local folder */
1924                 goto error;
1925         } else {
1926                 /* Create the helper */
1927                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1928                 helper->mail_op = g_object_ref (self);
1929                 helper->dest_folder = NULL;
1930                 helper->headers = NULL;
1931                 helper->user_callback = user_callback;
1932                 helper->user_data = user_data;
1933                 
1934                 /* Move/Copy folder */
1935                 modest_mail_operation_notify_start (self);
1936                 tny_folder_copy_async (folder,
1937                                        parent,
1938                                        tny_folder_get_name (folder),
1939                                        delete_original,
1940                                        transfer_folder_cb,
1941                                        transfer_folder_status_cb,
1942                                        helper);
1943                 return;
1944         }
1945
1946  error:
1947         /* Set status failed and set an error */
1948         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1949         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1950                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1951                      error_msg);
1952
1953         /* Call the user callback if exists */
1954         if (user_callback)
1955                 user_callback (self, user_data);
1956
1957         /* Notify the queue */
1958         modest_mail_operation_notify_end (self);
1959 }
1960
1961 void
1962 modest_mail_operation_rename_folder (ModestMailOperation *self,
1963                                      TnyFolder *folder,
1964                                      const gchar *name)
1965 {
1966         ModestMailOperationPrivate *priv;
1967         ModestTnyFolderRules rules;
1968         XFerMsgAsyncHelper *helper;
1969
1970         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1971         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1972         g_return_if_fail (name);
1973         
1974         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1975
1976         /* Get account and set it into mail_operation */
1977         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1978         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1979
1980         /* Check folder rules */
1981         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1982         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1983                 /* Set status failed and set an error */
1984                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1985                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1986                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1987                              _("FIXME: unable to rename"));
1988
1989                 /* Notify about operation end */
1990                 modest_mail_operation_notify_end (self);
1991         } else if (!strcmp (name, " ") || strchr (name, '/')) {
1992                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1993                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1994                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1995                              _("FIXME: unable to rename"));
1996                 /* Notify about operation end */
1997                 modest_mail_operation_notify_end (self);
1998         } else {
1999                 TnyFolderStore *into;
2000
2001                 into = tny_folder_get_folder_store (folder);    
2002
2003                 /* Check that the new folder name is not used by any
2004                    special local folder */
2005                 if (new_name_valid_if_local_account (priv, into, name)) {
2006                         /* Create the helper */
2007                         helper = g_slice_new0 (XFerMsgAsyncHelper);
2008                         helper->mail_op = g_object_ref(self);
2009                         helper->dest_folder = NULL;
2010                         helper->headers = NULL;
2011                         helper->user_callback = NULL;
2012                         helper->user_data = NULL;
2013                 
2014                         /* Rename. Camel handles folder subscription/unsubscription */
2015                         modest_mail_operation_notify_start (self);
2016                         tny_folder_copy_async (folder, into, name, TRUE,
2017                                                transfer_folder_cb,
2018                                                transfer_folder_status_cb,
2019                                                helper);
2020                 } else {
2021                         modest_mail_operation_notify_end (self);
2022                 }
2023                 g_object_unref (into);
2024         }
2025 }
2026
2027 /* ******************************************************************* */
2028 /* **************************  MSG  ACTIONS  ************************* */
2029 /* ******************************************************************* */
2030
2031 void 
2032 modest_mail_operation_get_msg (ModestMailOperation *self,
2033                                TnyHeader *header,
2034                                GetMsgAsyncUserCallback user_callback,
2035                                gpointer user_data)
2036 {
2037         GetMsgInfo *helper = NULL;
2038         TnyFolder *folder;
2039         ModestMailOperationPrivate *priv;
2040         
2041         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2042         g_return_if_fail (TNY_IS_HEADER (header));
2043         
2044         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2045         folder = tny_header_get_folder (header);
2046
2047         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2048         priv->total = 1;
2049         priv->done = 0;
2050
2051         /* Get account and set it into mail_operation */
2052         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2053         
2054         /* Check for cached messages */
2055         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2056                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2057         else 
2058                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2059         
2060         /* Create the helper */
2061         helper = g_slice_new0 (GetMsgInfo);
2062         helper->header = g_object_ref (header);
2063         helper->mail_op = g_object_ref (self);
2064         helper->user_callback = user_callback;
2065         helper->user_data = user_data;
2066         helper->destroy_notify = NULL;
2067         helper->last_total_bytes = 0;
2068         helper->sum_total_bytes = 0;
2069         helper->total_bytes = tny_header_get_message_size (header);
2070
2071         modest_mail_operation_notify_start (self);
2072         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2073
2074         g_object_unref (G_OBJECT (folder));
2075 }
2076
2077 static void     
2078 get_msg_status_cb (GObject *obj,
2079                    TnyStatus *status,  
2080                    gpointer user_data)
2081 {
2082         GetMsgInfo *helper = NULL;
2083
2084         g_return_if_fail (status != NULL);
2085         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2086
2087         helper = (GetMsgInfo *) user_data;
2088         g_return_if_fail (helper != NULL);       
2089
2090         /* Notify progress */
2091         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2092                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2093 }
2094
2095 static void
2096 get_msg_async_cb (TnyFolder *folder, 
2097                   gboolean canceled, 
2098                   TnyMsg *msg, 
2099                   GError *err, 
2100                   gpointer user_data)
2101 {
2102         GetMsgInfo *info = NULL;
2103         ModestMailOperationPrivate *priv = NULL;
2104         gboolean finished;
2105
2106         info = (GetMsgInfo *) user_data;
2107
2108         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2109         priv->done++;
2110         finished = (priv->done == priv->total) ? TRUE : FALSE;
2111
2112         /* Check errors */
2113         if (canceled || err) {
2114                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2115                 if (!priv->error)
2116                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2117                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2118                                      err->message);
2119         } else {
2120                 /* Set the success status before calling the user callback */
2121                 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2122                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2123         }
2124
2125
2126         /* Call the user callback */
2127         if (info->user_callback)
2128                 info->user_callback (info->mail_op, info->header, canceled, 
2129                                      msg, err, info->user_data);
2130
2131         /* Notify about operation end if this is the last callback */
2132         if (finished) {
2133                 /* Free user data */
2134                 if (info->destroy_notify)
2135                         info->destroy_notify (info->user_data);
2136
2137                 /* Notify about operation end */
2138                 modest_mail_operation_notify_end (info->mail_op);
2139         }
2140
2141         /* Clean */
2142         g_object_unref (info->header);
2143         g_object_unref (info->mail_op);
2144         g_slice_free (GetMsgInfo, info);
2145 }
2146
2147 void 
2148 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2149                                      TnyList *header_list, 
2150                                      GetMsgAsyncUserCallback user_callback,
2151                                      gpointer user_data,
2152                                      GDestroyNotify notify)
2153 {
2154         ModestMailOperationPrivate *priv = NULL;
2155         gboolean size_ok = TRUE;
2156         gint max_size;
2157         TnyIterator *iter = NULL;
2158         
2159         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2160         
2161         /* Init mail operation */
2162         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2163         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2164         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2165         priv->done = 0;
2166         priv->total = tny_list_get_length(header_list);
2167
2168         /* Get account and set it into mail_operation */
2169         if (tny_list_get_length (header_list) >= 1) {
2170                 iter = tny_list_create_iterator (header_list);
2171                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2172                 if (header) {
2173                         TnyFolder *folder = tny_header_get_folder (header);
2174                         if (folder) {           
2175                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2176                                 g_object_unref (folder);
2177                         }
2178                         g_object_unref (header);
2179                 }
2180
2181                 if (tny_list_get_length (header_list) == 1) {
2182                         g_object_unref (iter);
2183                         iter = NULL;
2184                 }
2185         }
2186
2187         /* Get msg size limit */
2188         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2189                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2190                                          &(priv->error));
2191         if (priv->error) {
2192                 g_clear_error (&(priv->error));
2193                 max_size = G_MAXINT;
2194         } else {
2195                 max_size = max_size * KB;
2196         }
2197
2198         /* Check message size limits. If there is only one message
2199            always retrieve it */
2200         if (iter != NULL) {
2201                 while (!tny_iterator_is_done (iter) && size_ok) {
2202                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2203                         if (header) {
2204                                 if (tny_header_get_message_size (header) >= max_size)
2205                                         size_ok = FALSE;
2206                                 g_object_unref (header);
2207                         }
2208
2209                         tny_iterator_next (iter);
2210                 }
2211                 g_object_unref (iter);
2212         }
2213
2214         if (size_ok) {
2215                 const gint msg_list_size = compute_message_list_size (header_list);
2216
2217                 modest_mail_operation_notify_start (self);
2218                 iter = tny_list_create_iterator (header_list);
2219                 while (!tny_iterator_is_done (iter)) { 
2220                         GetMsgInfo *msg_info = NULL;
2221                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2222                         TnyFolder *folder = tny_header_get_folder (header);
2223                         
2224                         /* Create the message info */
2225                         msg_info = g_slice_new0 (GetMsgInfo);
2226                         msg_info->mail_op = g_object_ref (self);
2227                         msg_info->header = g_object_ref (header);
2228                         msg_info->user_callback = user_callback;
2229                         msg_info->user_data = user_data;
2230                         msg_info->destroy_notify = notify;
2231                         msg_info->last_total_bytes = 0;
2232                         msg_info->sum_total_bytes = 0;
2233                         msg_info->total_bytes = msg_list_size;
2234                         
2235                         /* The callback will call it per each header */
2236                         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2237                         
2238                         /* Free and go on */
2239                         g_object_unref (header);
2240                         g_object_unref (folder);
2241                         tny_iterator_next (iter);
2242                 }
2243                 g_object_unref (iter);
2244         } else {
2245                 /* Set status failed and set an error */
2246                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2247                 /* FIXME: the error msg is different for pop */
2248                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2249                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2250                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2251                 /* Remove from queue and free resources */
2252                 modest_mail_operation_notify_end (self);
2253                 if (notify)
2254                         notify (user_data);
2255         }
2256 }
2257
2258
2259 void 
2260 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2261                                   TnyHeader *header,
2262                                   gboolean remove_to_trash /*ignored*/)
2263 {
2264         TnyFolder *folder;
2265         ModestMailOperationPrivate *priv;
2266
2267         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2268         g_return_if_fail (TNY_IS_HEADER (header));
2269
2270         if (remove_to_trash)
2271                 g_warning ("remove to trash is not implemented");
2272
2273         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2274         folder = tny_header_get_folder (header);
2275
2276         /* Get account and set it into mail_operation */
2277         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2278         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2279         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2280
2281         /* remove message from folder */
2282         tny_folder_remove_msg (folder, header, &(priv->error));
2283         if (!priv->error) {
2284                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2285                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2286
2287                 modest_mail_operation_notify_start (self);
2288
2289                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2290                     TNY_IS_CAMEL_POP_FOLDER (folder))
2291                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> dont expunge */
2292                 else
2293                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2294         }
2295         
2296         
2297         /* Set status */
2298         if (!priv->error)
2299                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2300         else
2301                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2302
2303         /* Free */
2304         g_object_unref (G_OBJECT (folder));
2305
2306         /* Notify about operation end */
2307         modest_mail_operation_notify_end (self);
2308 }
2309
2310 void 
2311 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2312                                    TnyList *headers,
2313                                   gboolean remove_to_trash /*ignored*/)
2314 {
2315         TnyFolder *folder;
2316         ModestMailOperationPrivate *priv;
2317         TnyIterator *iter = NULL;
2318         TnyHeader *header = NULL;
2319         TnyList *remove_headers = NULL;
2320         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2321
2322         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2323         g_return_if_fail (TNY_IS_LIST (headers));
2324
2325         if (remove_to_trash)
2326                 g_warning ("remove to trash is not implemented");
2327
2328         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2329
2330         remove_headers = g_object_ref(headers);
2331
2332         /* Get folder from first header and sync it */
2333         iter = tny_list_create_iterator (headers);
2334         header = TNY_HEADER (tny_iterator_get_current (iter));
2335         folder = tny_header_get_folder (header);
2336
2337         /* Don't remove messages that are being sent */
2338         if (modest_tny_folder_is_local_folder (folder)) {
2339                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2340         }
2341         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2342                 TnyTransportAccount *traccount = NULL;
2343                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2344                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2345                 if (traccount) {
2346                         ModestTnySendQueueStatus status;
2347                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2348                         TnyIterator *iter = tny_list_create_iterator(headers);
2349                         g_object_unref(remove_headers);
2350                         remove_headers = TNY_LIST(tny_simple_list_new());
2351                         while (!tny_iterator_is_done(iter)) {
2352                                 char *msg_id;
2353                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2354                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2355                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2356                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2357                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2358                                 }
2359                                 g_object_unref(hdr);
2360                                 g_free(msg_id);
2361                                 tny_iterator_next(iter);
2362                         }
2363                         g_object_unref(iter);
2364                         g_object_unref(traccount);
2365                 }
2366         }
2367
2368         /* Get account and set it into mail_operation */
2369         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2370         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2371         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2372
2373         /* remove message from folder */
2374         modest_mail_operation_notify_start (self);
2375
2376         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2377         if (!priv->error) {
2378                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) || 
2379                     TNY_IS_CAMEL_POP_FOLDER (folder))
2380                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */ 
2381                 else
2382                         /* local folders */
2383                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2384         }
2385         
2386         
2387         /* Set status */
2388         if (!priv->error)
2389                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2390         else
2391                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2392
2393         /* Free */
2394         g_object_unref (remove_headers);
2395         g_object_unref (header);
2396         g_object_unref (iter);
2397         g_object_unref (G_OBJECT (folder));
2398
2399         /* Notify about operation end */
2400         modest_mail_operation_notify_end (self);
2401 }
2402
2403 static void
2404 notify_progress_of_multiple_messages (ModestMailOperation *self,
2405                                       TnyStatus *status,
2406                                       gint *last_total_bytes,
2407                                       gint *sum_total_bytes,
2408                                       gint total_bytes, 
2409                                       gboolean increment_done)
2410 {
2411         ModestMailOperationPrivate *priv;
2412         ModestMailOperationState *state;
2413         gboolean is_num_bytes;
2414
2415         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2416
2417         /* We know that tinymail sends us information about
2418            transferred bytes with this particular message */
2419         is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2420
2421         state = modest_mail_operation_clone_state (self);
2422         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2423                 /* We know that we're in a different message when the
2424                    total number of bytes to transfer is different. Of
2425                    course it could fail if we're transferring messages
2426                    of the same size, but this is a workarround */
2427                 if (status->of_total != *last_total_bytes) {
2428                         /* We need to increment the done when there is
2429                            no information about each individual
2430                            message, we need to do this in message
2431                            transfers, and we don't do it for getting
2432                            messages */
2433                         if (increment_done)
2434                                 priv->done++;
2435                         *sum_total_bytes += *last_total_bytes;
2436                         *last_total_bytes = status->of_total;
2437                 }
2438                 state->bytes_done += status->position + *sum_total_bytes;
2439                 state->bytes_total = total_bytes;
2440
2441                 /* Notify the status change. Only notify about changes
2442                    referred to bytes */
2443                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2444                                0, state, NULL);
2445         }
2446
2447         g_slice_free (ModestMailOperationState, state);
2448 }
2449
2450 static void
2451 transfer_msgs_status_cb (GObject *obj,
2452                          TnyStatus *status,  
2453                          gpointer user_data)
2454 {
2455         XFerMsgAsyncHelper *helper;
2456
2457         g_return_if_fail (status != NULL);
2458         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2459
2460         helper = (XFerMsgAsyncHelper *) user_data;
2461         g_return_if_fail (helper != NULL);       
2462
2463         /* Notify progress */
2464         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2465                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2466 }
2467
2468
2469 static void
2470 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2471 {
2472         XFerMsgAsyncHelper *helper;
2473         ModestMailOperation *self;
2474         ModestMailOperationPrivate *priv;
2475         TnyIterator *iter = NULL;
2476         TnyHeader *header = NULL;
2477
2478         helper = (XFerMsgAsyncHelper *) user_data;
2479         self = helper->mail_op;
2480
2481         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2482
2483         if (err) {
2484                 priv->error = g_error_copy (err);
2485                 priv->done = 0;
2486                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2487         } else if (cancelled) {
2488                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2489                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2490                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2491                              _("Error trying to refresh the contents of %s"),
2492                              tny_folder_get_name (folder));
2493         } else {
2494                 priv->done = 1;
2495                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2496
2497                 /* Update folder counts */
2498                 tny_folder_poke_status (folder);                
2499                 tny_folder_poke_status (helper->dest_folder);           
2500         }
2501
2502         
2503         /* Mark headers as deleted and seen */
2504         if ((helper->delete) && 
2505             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2506                 iter = tny_list_create_iterator (helper->headers);
2507                 while (!tny_iterator_is_done (iter)) {
2508                         header = TNY_HEADER (tny_iterator_get_current (iter));
2509                         tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2510                         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2511                         g_object_unref (header);
2512
2513                         tny_iterator_next (iter);
2514                 }
2515
2516         }
2517                 
2518
2519         /* Notify about operation end */
2520         modest_mail_operation_notify_end (self);
2521
2522         /* If user defined callback function was defined, call it */
2523         if (helper->user_callback) {
2524                 /* This is not a GDK lock because we are a Tinymail callback and
2525                  * Tinymail already acquires the Gdk lock */
2526
2527                 /* no gdk_threads_enter (), CHECKED */
2528                 helper->user_callback (self, helper->user_data);
2529                 /* no gdk_threads_leave (), CHECKED */
2530         }
2531
2532         /* Free */
2533         if (helper->headers)
2534                 g_object_unref (helper->headers);
2535         if (helper->dest_folder)
2536                 g_object_unref (helper->dest_folder);
2537         if (helper->mail_op)
2538                 g_object_unref (helper->mail_op);
2539         if (folder)
2540                 g_object_unref (folder);
2541         if (iter)
2542                 g_object_unref (iter);
2543         g_slice_free (XFerMsgAsyncHelper, helper);
2544 }
2545
2546 static guint
2547 compute_message_list_size (TnyList *headers)
2548 {
2549         TnyIterator *iter;
2550         guint size = 0;
2551
2552         iter = tny_list_create_iterator (headers);
2553         while (!tny_iterator_is_done (iter)) {
2554                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2555                 size += tny_header_get_message_size (header);
2556                 g_object_unref (header);
2557                 tny_iterator_next (iter);
2558         }
2559         g_object_unref (iter);
2560
2561         return size;
2562 }
2563
2564 static guint
2565 compute_message_array_size (GPtrArray *headers)
2566 {
2567         guint size = 0;
2568         gint i;
2569
2570         for (i = 0; i < headers->len; i++) {
2571                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2572                 size += tny_header_get_message_size (header);
2573         }
2574
2575         return size;
2576 }
2577
2578
2579 void
2580 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2581                                  TnyList *headers, 
2582                                  TnyFolder *folder, 
2583                                  gboolean delete_original,
2584                                  XferAsyncUserCallback user_callback,
2585                                  gpointer user_data)
2586 {
2587         ModestMailOperationPrivate *priv = NULL;
2588         TnyIterator *iter = NULL;
2589         TnyFolder *src_folder = NULL;
2590         XFerMsgAsyncHelper *helper = NULL;
2591         TnyHeader *header = NULL;
2592         ModestTnyFolderRules rules = 0;
2593
2594         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2595         g_return_if_fail (headers && TNY_IS_LIST (headers));
2596         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2597
2598         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2599         priv->total = tny_list_get_length (headers);
2600         priv->done = 0;
2601         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2602         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2603
2604         /* Apply folder rules */
2605         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2606         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2607                 /* Set status failed and set an error */
2608                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2609                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2610                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2611                              _CS("ckct_ib_unable_to_paste_here"));
2612                 /* Notify the queue */
2613                 modest_mail_operation_notify_end (self);
2614                 return;
2615         }
2616                 
2617         /* Get source folder */
2618         iter = tny_list_create_iterator (headers);
2619         header = TNY_HEADER (tny_iterator_get_current (iter));
2620         if (header) {
2621                 src_folder = tny_header_get_folder (header);
2622                 g_object_unref (header);
2623         }
2624         g_object_unref (iter);
2625
2626         if (src_folder == NULL) {
2627                 /* Notify the queue */
2628                 modest_mail_operation_notify_end (self);
2629
2630                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2631                 return;
2632         }
2633
2634         
2635         /* Check folder source and destination */
2636         if (src_folder == folder) {
2637                 /* Set status failed and set an error */
2638                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2639                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2640                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2641                              _("mail_in_ui_folder_copy_target_error"));
2642                 
2643                 /* Notify the queue */
2644                 modest_mail_operation_notify_end (self);
2645                 
2646                 /* Free */
2647                 g_object_unref (src_folder);            
2648                 return;
2649         }
2650
2651         /* Create the helper */
2652         helper = g_slice_new0 (XFerMsgAsyncHelper);
2653         helper->mail_op = g_object_ref(self);
2654         helper->dest_folder = g_object_ref(folder);
2655         helper->headers = g_object_ref(headers);
2656         helper->user_callback = user_callback;
2657         helper->user_data = user_data;
2658         helper->delete = delete_original;
2659         helper->last_total_bytes = 0;
2660         helper->sum_total_bytes = 0;
2661         helper->total_bytes = compute_message_list_size (headers);
2662
2663         /* Get account and set it into mail_operation */
2664         priv->account = modest_tny_folder_get_account (src_folder);
2665
2666         /* Transfer messages */
2667         modest_mail_operation_notify_start (self);
2668         tny_folder_transfer_msgs_async (src_folder, 
2669                                         headers, 
2670                                         folder, 
2671                                         delete_original, 
2672                                         transfer_msgs_cb, 
2673                                         transfer_msgs_status_cb,
2674                                         helper);
2675 }
2676
2677
2678 static void
2679 on_refresh_folder (TnyFolder   *folder, 
2680                    gboolean     cancelled, 
2681                    GError     *error,
2682                    gpointer     user_data)
2683 {
2684         RefreshAsyncHelper *helper = NULL;
2685         ModestMailOperation *self = NULL;
2686         ModestMailOperationPrivate *priv = NULL;
2687
2688         helper = (RefreshAsyncHelper *) user_data;
2689         self = helper->mail_op;
2690         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2691
2692         g_return_if_fail(priv!=NULL);
2693
2694         if (error) {
2695                 priv->error = g_error_copy (error);
2696                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2697                 goto out;
2698         }
2699
2700         if (cancelled) {
2701                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2702                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2703                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2704                              _("Error trying to refresh the contents of %s"),
2705                              tny_folder_get_name (folder));
2706                 goto out;
2707         }
2708
2709         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2710  out:
2711
2712         /* Call user defined callback, if it exists */
2713         if (helper->user_callback) {
2714
2715                 /* This is not a GDK lock because we are a Tinymail callback and
2716                  * Tinymail already acquires the Gdk lock */
2717                 helper->user_callback (self, folder, helper->user_data);
2718         }
2719
2720         /* Free */
2721         g_slice_free (RefreshAsyncHelper, helper);
2722
2723         /* Notify about operation end */
2724         modest_mail_operation_notify_end (self);
2725         g_object_unref(self);
2726 }
2727
2728 static void
2729 on_refresh_folder_status_update (GObject *obj,
2730                                  TnyStatus *status,
2731                                  gpointer user_data)
2732 {
2733         RefreshAsyncHelper *helper = NULL;
2734         ModestMailOperation *self = NULL;
2735         ModestMailOperationPrivate *priv = NULL;
2736         ModestMailOperationState *state;
2737
2738         g_return_if_fail (user_data != NULL);
2739         g_return_if_fail (status != NULL);
2740         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2741
2742         helper = (RefreshAsyncHelper *) user_data;
2743         self = helper->mail_op;
2744         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2745
2746         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2747
2748         priv->done = status->position;
2749         priv->total = status->of_total;
2750
2751         state = modest_mail_operation_clone_state (self);
2752
2753         /* This is not a GDK lock because we are a Tinymail callback and
2754          * Tinymail already acquires the Gdk lock */
2755         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2756
2757         g_slice_free (ModestMailOperationState, state);
2758 }
2759
2760 void 
2761 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2762                                        TnyFolder *folder,
2763                                        RefreshAsyncUserCallback user_callback,
2764                                        gpointer user_data)
2765 {
2766         ModestMailOperationPrivate *priv = NULL;
2767         RefreshAsyncHelper *helper = NULL;
2768
2769         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2770
2771         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2772
2773         /* Get account and set it into mail_operation */
2774         priv->account = modest_tny_folder_get_account  (folder);
2775         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2776
2777         /* Create the helper */
2778         helper = g_slice_new0 (RefreshAsyncHelper);
2779         helper->mail_op = g_object_ref(self);
2780         helper->user_callback = user_callback;
2781         helper->user_data = user_data;
2782
2783         /* Refresh the folder. TODO: tinymail could issue a status
2784            updates before the callback call then this could happen. We
2785            must review the design */
2786         modest_mail_operation_notify_start (self);
2787         tny_folder_refresh_async (folder,
2788                                   on_refresh_folder,
2789                                   on_refresh_folder_status_update,
2790                                   helper);
2791 }
2792
2793
2794 static void
2795 modest_mail_operation_notify_start (ModestMailOperation *self)
2796 {
2797         ModestMailOperationPrivate *priv = NULL;
2798
2799         g_return_if_fail (self);
2800
2801         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2802
2803         /* Ensure that all the fields are filled correctly */
2804         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2805
2806         /* Notify the observers about the mail operation. We do not
2807            wrapp this emission because we assume that this function is
2808            always called from within the main lock */
2809         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2810 }
2811
2812 /**
2813  *
2814  * It's used by the mail operation queue to notify the observers
2815  * attached to that signal that the operation finished. We need to use
2816  * that because tinymail does not give us the progress of a given
2817  * operation when it finishes (it directly calls the operation
2818  * callback).
2819  */
2820 static void
2821 modest_mail_operation_notify_end (ModestMailOperation *self)
2822 {
2823         ModestMailOperationPrivate *priv = NULL;
2824
2825         g_return_if_fail (self);
2826
2827         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2828
2829         /* Notify the observers about the mail operation end. We do
2830            not wrapp this emission because we assume that this
2831            function is always called from within the main lock */
2832         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2833
2834         /* Remove the error user data */
2835         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2836                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2837 }
2838
2839 TnyAccount *
2840 modest_mail_operation_get_account (ModestMailOperation *self)
2841 {
2842         ModestMailOperationPrivate *priv = NULL;
2843
2844         g_return_val_if_fail (self, NULL);
2845
2846         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2847
2848         return (priv->account) ? g_object_ref (priv->account) : NULL;
2849 }
2850
2851 void
2852 modest_mail_operation_noop (ModestMailOperation *self)
2853 {
2854         ModestMailOperationPrivate *priv = NULL;
2855
2856         g_return_if_fail (self);
2857
2858         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2859         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2860         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2861         priv->done = 0;
2862         priv->total = 0;
2863
2864         /* This mail operation does nothing actually */
2865         modest_mail_operation_notify_start (self);
2866         modest_mail_operation_notify_end (self);
2867 }