45a545501926b510ded15872e5ab9192b470aa20
[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         TnyHeader *hdr1, *hdr2;
676         const char *msgid1, *msgid2;
677         hdr1 = tny_msg_get_header(msg);
678         hdr2 = tny_msg_get_header(info->msg);
679         msgid1 = tny_header_get_message_id(hdr1);
680         msgid2 = tny_header_get_message_id(hdr2);
681         if (msgid1 == NULL) msgid1 = "(null)";
682         if (msgid2 == NULL) msgid2 = "(null)";
683
684         if (!strcmp (msgid1, msgid2)) {
685                 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
686                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
687
688                 common_send_mail_operation_end (queue, msg, info);
689         }
690         g_object_unref(G_OBJECT(hdr1));
691         g_object_unref(G_OBJECT(hdr2));
692 }
693
694 static void     
695 send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
696                                   GError *error, gpointer userdata)
697 {
698         SendMsgInfo *info = (SendMsgInfo *) userdata;
699         TnyHeader *hdr1, *hdr2;
700         hdr1 = tny_msg_get_header(msg);
701         hdr2 = tny_msg_get_header(info->msg);
702         const char *msgid1, *msgid2;
703         hdr1 = tny_msg_get_header(msg);
704         hdr2 = tny_msg_get_header(info->msg);
705         msgid1 = tny_header_get_message_id(hdr1);
706         msgid2 = tny_header_get_message_id(hdr2);
707         if (msgid1 == NULL) msgid1 = "(null)";
708         if (msgid2 == NULL) msgid2 = "(null)";
709
710         if (!strcmp (msgid1, msgid2)) {
711                 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
712                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
713                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
714                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
715                              "modest: send mail failed\n");
716
717                 common_send_mail_operation_end (queue, msg, info);
718         }
719         g_object_unref(G_OBJECT(hdr1));
720         g_object_unref(G_OBJECT(hdr2));
721 }
722
723
724 static gboolean
725 idle_create_msg_cb (gpointer idle_data)
726 {
727         CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
728
729         /* This is a GDK lock because we are an idle callback and
730          * info->callback can contain Gtk+ code */
731
732         gdk_threads_enter (); /* CHECKED */
733         info->callback (info->mail_op, info->msg, info->userdata);
734
735         g_object_unref (info->mail_op);
736         if (info->msg)
737                 g_object_unref (info->msg);
738         g_slice_free (CreateMsgIdleInfo, info);
739         gdk_threads_leave (); /* CHECKED */
740
741         return FALSE;
742 }
743
744 static gpointer 
745 create_msg_thread (gpointer thread_data)
746 {
747         CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
748         TnyMsg *new_msg = NULL;
749         ModestMailOperationPrivate *priv;
750
751         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
752         if (info->html_body == NULL) {
753                 new_msg = modest_tny_msg_new (info->to, info->from, info->cc, 
754                                               info->bcc, info->subject, info->plain_body, 
755                                               info->attachments_list);
756         } else {
757                 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
758                                                          info->bcc, info->subject, info->html_body,
759                                                          info->plain_body, info->attachments_list,
760                                                          info->images_list);
761         }
762
763         if (new_msg) {
764                 TnyHeader *header;
765
766                 /* Set priority flags in message */
767                 header = tny_msg_get_header (new_msg);
768                 tny_header_set_flag (header, info->priority_flags);
769
770                 /* Set attachment flags in message */
771                 if (info->attachments_list != NULL)
772                         tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
773
774                 g_object_unref (G_OBJECT(header));
775         } else {
776                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
777                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
778                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
779                              "modest: failed to create a new msg\n");
780         }
781
782
783         g_free (info->to);
784         g_free (info->from);
785         g_free (info->cc);
786         g_free (info->bcc);
787         g_free (info->plain_body);
788         g_free (info->html_body);
789         g_free (info->subject);
790         g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
791         g_list_free (info->attachments_list);
792         g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
793         g_list_free (info->images_list);
794
795         if (info->callback) {
796                 CreateMsgIdleInfo *idle_info;
797                 idle_info = g_slice_new0 (CreateMsgIdleInfo);
798                 idle_info->mail_op = g_object_ref (info->mail_op);
799                 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
800                 idle_info->callback = info->callback;
801                 idle_info->userdata = info->userdata;
802                 g_idle_add (idle_create_msg_cb, idle_info);
803         } else {
804                 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
805         }
806
807         g_object_unref (info->mail_op);
808         g_slice_free (CreateMsgInfo, info);
809         return NULL;
810 }
811
812 void
813 modest_mail_operation_create_msg (ModestMailOperation *self,
814                                   const gchar *from, const gchar *to,
815                                   const gchar *cc, const gchar *bcc,
816                                   const gchar *subject, const gchar *plain_body,
817                                   const gchar *html_body,
818                                   const GList *attachments_list,
819                                   const GList *images_list,
820                                   TnyHeaderFlags priority_flags,
821                                   ModestMailOperationCreateMsgCallback callback,
822                                   gpointer userdata)
823 {
824         CreateMsgInfo *info = NULL;
825
826         info = g_slice_new0 (CreateMsgInfo);
827         info->mail_op = g_object_ref (self);
828
829         info->from = g_strdup (from);
830         info->to = g_strdup (to);
831         info->cc = g_strdup (cc);
832         info->bcc  = g_strdup (bcc);
833         info->subject = g_strdup (subject);
834         info->plain_body = g_strdup (plain_body);
835         info->html_body = g_strdup (html_body);
836         info->attachments_list = g_list_copy ((GList *) attachments_list);
837         g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
838         info->images_list = g_list_copy ((GList *) images_list);
839         g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
840         info->priority_flags = priority_flags;
841
842         info->callback = callback;
843         info->userdata = userdata;
844
845         g_thread_create (create_msg_thread, info, FALSE, NULL);
846 }
847
848 typedef struct
849 {
850         TnyTransportAccount *transport_account;
851         TnyMsg *draft_msg;
852 } SendNewMailInfo;
853
854 static void
855 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
856                                         TnyMsg *msg,
857                                         gpointer userdata)
858 {
859         SendNewMailInfo *info = (SendNewMailInfo *) userdata;
860         TnyFolder *draft_folder = NULL;
861         TnyFolder *outbox_folder = NULL;
862         TnyHeader *header;
863         GError *err = NULL;
864
865         if (!msg) {
866                 goto end;
867         }
868
869         /* Call mail operation */
870         modest_mail_operation_send_mail (self, info->transport_account, msg);
871
872         /* Remove old mail from its source folder */
873         draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
874                                                               TNY_FOLDER_TYPE_DRAFTS);
875         outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
876                                                                TNY_FOLDER_TYPE_OUTBOX);
877         if (info->draft_msg != NULL) {
878                 TnyFolder *folder = NULL;
879                 TnyFolder *src_folder = NULL;
880                 TnyFolderType folder_type;              
881                 folder = tny_msg_get_folder (info->draft_msg);          
882                 if (folder == NULL) goto end;
883                 folder_type = modest_tny_folder_guess_folder_type (folder);
884
885                 if (folder_type == TNY_FOLDER_TYPE_INVALID)
886                         g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
887                 
888                 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) 
889                         src_folder = outbox_folder;
890                 else 
891                         src_folder = draft_folder;
892
893                 /* Note: This can fail (with a warning) if the message is not really already in a folder,
894                  * because this function requires it to have a UID. */          
895                 header = tny_msg_get_header (info->draft_msg);
896                 tny_folder_remove_msg (src_folder, header, NULL);
897
898                 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
899 /*              tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);  /\* expunge *\/ */
900                 
901                 g_object_unref (header);
902                 g_object_unref (folder);
903         }
904
905 end:
906         if (err != NULL)
907                 g_error_free(err);      
908         if (info->draft_msg)
909                 g_object_unref (info->draft_msg);
910         if (draft_folder)
911                 g_object_unref (draft_folder);
912         if (outbox_folder)
913                 g_object_unref (outbox_folder);
914         if (info->transport_account)
915                 g_object_unref (info->transport_account);
916         g_slice_free (SendNewMailInfo, info);
917 }
918
919 void
920 modest_mail_operation_send_new_mail (ModestMailOperation *self,
921                                      TnyTransportAccount *transport_account,
922                                      TnyMsg *draft_msg,
923                                      const gchar *from,  const gchar *to,
924                                      const gchar *cc,  const gchar *bcc,
925                                      const gchar *subject, const gchar *plain_body,
926                                      const gchar *html_body,
927                                      const GList *attachments_list,
928                                      const GList *images_list,
929                                      TnyHeaderFlags priority_flags)
930 {
931         ModestMailOperationPrivate *priv = NULL;
932         SendNewMailInfo *info;
933
934         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
935         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
936
937         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
938         priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
939         priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
940         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
941         
942         /* Check parametters */
943         if (to == NULL) {
944                 /* Set status failed and set an error */
945                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
946                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
947                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
948                              _("Error trying to send a mail. You need to set at least one recipient"));
949                 return;
950         }
951         info = g_slice_new0 (SendNewMailInfo);
952         info->transport_account = transport_account;
953         if (transport_account)
954                 g_object_ref (transport_account);
955         info->draft_msg = draft_msg;
956         if (draft_msg)
957                 g_object_ref (draft_msg);
958
959
960         modest_mail_operation_notify_start (self);
961         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
962                                           attachments_list, images_list, priority_flags,
963                                           modest_mail_operation_send_new_mail_cb, info);
964
965 }
966
967 typedef struct
968 {
969         TnyTransportAccount *transport_account;
970         TnyMsg *draft_msg;
971         SaveToDraftstCallback callback;
972         gpointer user_data;
973         TnyFolder *drafts;
974         TnyMsg *msg;
975         ModestMailOperation *mailop;
976 } SaveToDraftsAddMsgInfo;
977
978 static void
979 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
980                                                 gboolean canceled,
981                                                 GError *err,
982                                                 gpointer userdata)
983 {
984         ModestMailOperationPrivate *priv = NULL;
985         SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
986
987         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
988
989         if (priv->error) {
990                 g_warning ("%s: priv->error != NULL", __FUNCTION__);
991                 g_error_free(priv->error);
992         }
993
994         priv->error = (err == NULL) ? NULL : g_error_copy(err);
995
996         if ((!priv->error) && (info->draft_msg != NULL)) {
997                 TnyHeader *header = tny_msg_get_header (info->draft_msg);
998                 TnyFolder *src_folder = tny_header_get_folder (header);
999
1000                 /* Remove the old draft */
1001                 tny_folder_remove_msg (src_folder, header, NULL);
1002
1003                 /* Synchronize to expunge and to update the msg counts */
1004                 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1005                 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1006
1007                 g_object_unref (G_OBJECT(header));
1008                 g_object_unref (G_OBJECT(src_folder));
1009         }
1010
1011         if (!priv->error)
1012                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1013         else
1014                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1015
1016         /* Call the user callback */
1017         if (info->callback)
1018                 info->callback (info->mailop, info->msg, info->user_data);
1019
1020         if (info->transport_account)
1021                 g_object_unref (G_OBJECT(info->transport_account));
1022         if (info->draft_msg)
1023                 g_object_unref (G_OBJECT (info->draft_msg));
1024         if (info->drafts)
1025                 g_object_unref (G_OBJECT(info->drafts));
1026         if (info->msg)
1027                 g_object_unref (G_OBJECT (info->msg));
1028         g_slice_free (SaveToDraftsAddMsgInfo, info);
1029
1030         modest_mail_operation_notify_end (info->mailop);
1031         g_object_unref(info->mailop);
1032 }
1033
1034 typedef struct
1035 {
1036         TnyTransportAccount *transport_account;
1037         TnyMsg *draft_msg;
1038         SaveToDraftstCallback callback;
1039         gpointer user_data;
1040 } SaveToDraftsInfo;
1041
1042 static void
1043 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1044                                          TnyMsg *msg,
1045                                          gpointer userdata)
1046 {
1047         TnyFolder *drafts = NULL;
1048         ModestMailOperationPrivate *priv = NULL;
1049         SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1050
1051         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1052
1053         if (!msg) {
1054                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1055                              MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1056                              "modest: failed to create a new msg\n");
1057         } else {
1058                 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1059                                                                 TNY_FOLDER_TYPE_DRAFTS);
1060                 if (!drafts) {
1061                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1062                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1063                                      "modest: failed to create a new msg\n");
1064                 }
1065         }
1066
1067         if (!priv->error) {
1068                 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1069                 cb_info->transport_account = g_object_ref(info->transport_account);
1070                 cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1071                 cb_info->callback = info->callback;
1072                 cb_info->user_data = info->user_data;
1073                 cb_info->drafts = g_object_ref(drafts);
1074                 cb_info->msg = g_object_ref(msg);
1075                 cb_info->mailop = g_object_ref(self);
1076                 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1077                                          NULL, cb_info);
1078         } else {
1079                 /* Call the user callback */
1080                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1081                 if (info->callback)
1082                         info->callback (self, msg, info->user_data);
1083                 modest_mail_operation_notify_end (self);
1084         }
1085
1086         if (drafts)
1087                 g_object_unref (G_OBJECT(drafts));
1088         if (info->draft_msg)
1089                 g_object_unref (G_OBJECT (info->draft_msg));
1090         if (info->transport_account)
1091                 g_object_unref (G_OBJECT(info->transport_account));
1092         g_slice_free (SaveToDraftsInfo, info);
1093 }
1094
1095 void
1096 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1097                                       TnyTransportAccount *transport_account,
1098                                       TnyMsg *draft_msg,
1099                                       const gchar *from,  const gchar *to,
1100                                       const gchar *cc,  const gchar *bcc,
1101                                       const gchar *subject, const gchar *plain_body,
1102                                       const gchar *html_body,
1103                                       const GList *attachments_list,
1104                                       const GList *images_list,
1105                                       TnyHeaderFlags priority_flags,
1106                                       SaveToDraftstCallback callback,
1107                                       gpointer user_data)
1108 {
1109         ModestMailOperationPrivate *priv = NULL;
1110         SaveToDraftsInfo *info = NULL;
1111
1112         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1113         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1114
1115         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1116
1117         /* Get account and set it into mail_operation */
1118         priv->account = g_object_ref (transport_account);
1119         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1120
1121         info = g_slice_new0 (SaveToDraftsInfo);
1122         info->transport_account = g_object_ref (transport_account);
1123         info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1124         info->callback = callback;
1125         info->user_data = user_data;
1126
1127         modest_mail_operation_notify_start (self);
1128         modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1129                                           attachments_list, images_list, priority_flags,
1130                                           modest_mail_operation_save_to_drafts_cb, info);
1131 }
1132
1133 typedef struct
1134 {
1135         ModestMailOperation *mail_op;
1136         TnyMimePart *mime_part;
1137         gssize size;
1138         GetMimePartSizeCallback callback;
1139         gpointer userdata;
1140 } GetMimePartSizeInfo;
1141
1142 /***** I N T E R N A L    F O L D E R    O B S E R V E R *****/
1143 /* We use this folder observer to track the headers that have been
1144  * added to a folder */
1145 typedef struct {
1146         GObject parent;
1147         TnyList *new_headers;
1148 } InternalFolderObserver;
1149
1150 typedef struct {
1151         GObjectClass parent;
1152 } InternalFolderObserverClass;
1153
1154 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1155
1156 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1157                          internal_folder_observer,
1158                          G_TYPE_OBJECT,
1159                          G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1160
1161
1162 static void
1163 foreach_add_item (gpointer header, gpointer user_data)
1164 {
1165         tny_list_prepend (TNY_LIST (user_data), 
1166                           g_object_ref (G_OBJECT (header)));
1167 }
1168
1169 /* This is the method that looks for new messages in a folder */
1170 static void
1171 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1172 {
1173         InternalFolderObserver *derived = (InternalFolderObserver *)self;
1174         
1175         TnyFolderChangeChanged changed;
1176
1177         changed = tny_folder_change_get_changed (change);
1178
1179         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1180                 TnyList *list;
1181
1182                 /* Get added headers */
1183                 list = tny_simple_list_new ();
1184                 tny_folder_change_get_added_headers (change, list);
1185
1186                 /* Add them to the folder observer */
1187                 tny_list_foreach (list, foreach_add_item, 
1188                                   derived->new_headers);
1189
1190                 g_object_unref (G_OBJECT (list));
1191         }
1192 }
1193
1194 static void
1195 internal_folder_observer_init (InternalFolderObserver *self) 
1196 {
1197         self->new_headers = tny_simple_list_new ();
1198 }
1199 static void
1200 internal_folder_observer_finalize (GObject *object) 
1201 {
1202         InternalFolderObserver *self;
1203
1204         self = (InternalFolderObserver *) object;
1205         g_object_unref (self->new_headers);
1206
1207         G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1208 }
1209 static void
1210 tny_folder_observer_init (TnyFolderObserverIface *iface) 
1211 {
1212         iface->update_func = internal_folder_observer_update;
1213 }
1214 static void
1215 internal_folder_observer_class_init (InternalFolderObserverClass *klass) 
1216 {
1217         GObjectClass *object_class;
1218
1219         internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1220         object_class = (GObjectClass*) klass;
1221         object_class->finalize = internal_folder_observer_finalize;
1222 }
1223
1224 typedef struct 
1225 {
1226         ModestMailOperation *mail_op;
1227         gchar *account_name;
1228         UpdateAccountCallback callback;
1229         gpointer user_data;
1230         TnyList *folders;
1231         gint pending_calls;
1232         gboolean poke_all;
1233         TnyFolderObserver *inbox_observer;
1234         guint update_timeout;
1235 } UpdateAccountInfo;
1236
1237
1238 static void
1239 destroy_update_account_info (UpdateAccountInfo *info)
1240 {
1241         if (info->update_timeout) {
1242                 g_source_remove (info->update_timeout);
1243                 info->update_timeout = 0;
1244         }
1245
1246         g_free (info->account_name);
1247         g_object_unref (info->folders);
1248         g_object_unref (info->mail_op);
1249         g_slice_free (UpdateAccountInfo, info);
1250 }
1251
1252 static void
1253 update_account_get_msg_async_cb (TnyFolder *folder, 
1254                                  gboolean canceled, 
1255                                  TnyMsg *msg, 
1256                                  GError *err, 
1257                                  gpointer user_data)
1258 {
1259         GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1260
1261         /* Just delete the helper. Don't do anything with the new
1262            msg. There is also no need to check for errors */
1263         g_object_unref (msg_info->mail_op);
1264         g_object_unref (msg_info->header);
1265         g_slice_free (GetMsgInfo, msg_info);
1266 }
1267
1268
1269 static void
1270 inbox_refreshed_cb (TnyFolder *inbox, 
1271                     gboolean canceled, 
1272                     GError *err, 
1273                     gpointer user_data)
1274 {       
1275         UpdateAccountInfo *info;
1276         ModestMailOperationPrivate *priv;
1277         TnyIterator *new_headers_iter;
1278         GPtrArray *new_headers_array = NULL;   
1279         gint max_size, retrieve_limit, i;
1280         ModestAccountMgr *mgr;
1281         ModestAccountRetrieveType retrieve_type;
1282         TnyList *new_headers = NULL;
1283         gboolean headers_only;
1284         TnyTransportAccount *transport_account;
1285         ModestTnySendQueue *send_queue;
1286
1287         info = (UpdateAccountInfo *) user_data;
1288         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1289         mgr = modest_runtime_get_account_mgr ();
1290
1291         if (canceled || err || !inbox) {
1292                 /* Try to send anyway */
1293                 goto send_mail;
1294         }
1295
1296         /* Get the message max size */
1297         max_size  = modest_conf_get_int (modest_runtime_get_conf (),
1298                                          MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1299         if (max_size == 0)
1300                 max_size = G_MAXINT;
1301         else
1302                 max_size = max_size * KB;
1303
1304         /* Create the new headers array. We need it to sort the
1305            new headers by date */
1306         new_headers_array = g_ptr_array_new ();
1307         new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1308         while (!tny_iterator_is_done (new_headers_iter)) {
1309                 TnyHeader *header = NULL;
1310
1311                 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1312                 /* Apply per-message size limits */
1313                 if (tny_header_get_message_size (header) < max_size)
1314                         g_ptr_array_add (new_headers_array, g_object_ref (header));
1315                                 
1316                 g_object_unref (header);
1317                 tny_iterator_next (new_headers_iter);
1318         }
1319         g_object_unref (new_headers_iter);
1320         tny_folder_remove_observer (inbox, info->inbox_observer);
1321         g_object_unref (info->inbox_observer);
1322         info->inbox_observer = NULL;
1323
1324         /* Update the last updated key, even if we don't have to get new headers */
1325         modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1326         
1327         if (new_headers_array->len == 0)
1328                 goto send_mail;
1329
1330         /* Get per-account message amount retrieval limit */
1331         retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1332         if (retrieve_limit == 0)
1333                 retrieve_limit = G_MAXINT;
1334         
1335         /* Get per-account retrieval type */
1336         retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name); 
1337         headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1338
1339         /* Order by date */
1340         g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1341         
1342         /* TODO: Ask the user, instead of just failing,
1343          * showing mail_nc_msg_count_limit_exceeded, with 'Get
1344          * all' and 'Newest only' buttons. */
1345         if (new_headers_array->len > retrieve_limit) {
1346                 /* TODO */
1347         }
1348         
1349         if (!headers_only) {
1350                 gint msg_num = 0;
1351                 const gint msg_list_size = compute_message_array_size (new_headers_array);
1352
1353                 priv->done = 0;
1354                 priv->total = MIN (new_headers_array->len, retrieve_limit);
1355                 while (msg_num < priv->total) {         
1356                         TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1357                         TnyFolder *folder = tny_header_get_folder (header);
1358                         GetMsgInfo *msg_info;
1359
1360                         /* Create the message info */
1361                         msg_info = g_slice_new0 (GetMsgInfo);
1362                         msg_info->mail_op = g_object_ref (info->mail_op);
1363                         msg_info->header = g_object_ref (header);
1364                         msg_info->total_bytes = msg_list_size;
1365
1366                         /* Get message in an async way */
1367                         tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb, 
1368                                                   get_msg_status_cb, msg_info);
1369
1370                         g_object_unref (folder);
1371                         
1372                         msg_num++;
1373                 }
1374         }
1375
1376         /* Copy the headers to a list and free the array */
1377         new_headers = tny_simple_list_new ();
1378         for (i=0; i < new_headers_array->len; i++) {
1379                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1380                 tny_list_append (new_headers, G_OBJECT (header));
1381         }
1382         g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1383         g_ptr_array_free (new_headers_array, FALSE);
1384
1385  send_mail:
1386         /* Send mails */
1387         priv->done = 0;
1388         priv->total = 0;
1389
1390         /* Get the transport account */
1391         transport_account = (TnyTransportAccount *)
1392                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1393                                                                                     info->account_name);
1394         
1395         /* Try to send */
1396         send_queue = modest_runtime_get_send_queue (transport_account);
1397         modest_tny_send_queue_try_to_send (send_queue);
1398
1399         /* Check if the operation was a success */
1400         if (!priv->error)
1401                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1402
1403         /* Set the account back to not busy */
1404         modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1405
1406         /* Call the user callback */
1407         if (info->callback)
1408                 info->callback (info->mail_op, new_headers, info->user_data);
1409
1410         /* Notify about operation end */
1411         modest_mail_operation_notify_end (info->mail_op);
1412
1413         /* Frees */
1414         if (new_headers)
1415                 g_object_unref (new_headers);
1416         destroy_update_account_info (info);
1417 }
1418
1419 static void 
1420 recurse_folders_async_cb (TnyFolderStore *folder_store, 
1421                           gboolean canceled,
1422                           TnyList *list, 
1423                           GError *err, 
1424                           gpointer user_data)
1425 {
1426         UpdateAccountInfo *info;
1427         ModestMailOperationPrivate *priv;
1428     
1429         info = (UpdateAccountInfo *) user_data;
1430         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1431
1432         if (err || canceled) {
1433                 /* Try to continue anyway */
1434         } else {
1435                 TnyIterator *iter = tny_list_create_iterator (list);
1436                 while (!tny_iterator_is_done (iter)) {
1437                         TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1438                         TnyList *folders = tny_simple_list_new ();
1439
1440                         /* Add to the list of all folders */
1441                         tny_list_append (info->folders, (GObject *) folder);
1442                         
1443                         /* Add pending call */
1444                         info->pending_calls++;
1445                         
1446                         tny_folder_store_get_folders_async (folder, folders, recurse_folders_async_cb, 
1447                                                             NULL, NULL, info);
1448                         
1449                         g_object_unref (G_OBJECT (folder));
1450                         
1451                         tny_iterator_next (iter);           
1452                 }
1453                 g_object_unref (G_OBJECT (iter));
1454                 g_object_unref (G_OBJECT (list));
1455         }
1456
1457         /* Remove my own pending call */
1458         info->pending_calls--;
1459
1460         /* This means that we have all the folders */
1461         if (info->pending_calls == 0) {
1462                 TnyIterator *iter_all_folders;
1463                 TnyFolder *inbox = NULL;
1464
1465                 iter_all_folders = tny_list_create_iterator (info->folders);
1466
1467                 /* Do a poke status over all folders */
1468                 while (!tny_iterator_is_done (iter_all_folders) &&
1469                        priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1470                         TnyFolder *folder = NULL;
1471
1472                         folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1473
1474                         if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1475                                 /* Get a reference to the INBOX */
1476                                 inbox = g_object_ref (folder);
1477                         } else {
1478                                 /* Issue a poke status over the folder */
1479                                 if (info->poke_all)
1480                                         tny_folder_poke_status (folder);
1481                         }
1482
1483                         /* Free and go to next */
1484                         g_object_unref (folder);
1485                         tny_iterator_next (iter_all_folders);
1486                 }
1487                 g_object_unref (iter_all_folders);
1488
1489                 /* Stop the progress notification */
1490                 g_source_remove (info->update_timeout);
1491                 info->update_timeout = 0;
1492
1493                 /* Refresh the INBOX */
1494                 if (inbox) {
1495                         /* Refresh the folder. Our observer receives
1496                          * the new emails during folder refreshes, so
1497                          * we can use observer->new_headers
1498                          */
1499                         info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1500                         tny_folder_add_observer (inbox, info->inbox_observer);
1501
1502                         /* Refresh the INBOX */
1503                         tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1504                         g_object_unref (inbox);
1505                 } else {
1506                         /* We could not perform the inbox refresh but
1507                            we'll try to send mails anyway */
1508                         inbox_refreshed_cb (inbox, FALSE, NULL, info);
1509                 }
1510         }
1511 }
1512
1513 /* 
1514  * Issues the "progress-changed" signal. The timer won't be removed,
1515  * so you must call g_source_remove to stop the signal emission
1516  */
1517 static gboolean
1518 timeout_notify_progress (gpointer data)
1519 {
1520         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1521         ModestMailOperationState *state;
1522
1523         state = modest_mail_operation_clone_state (mail_op);
1524
1525         /* This is a GDK lock because we are an idle callback and
1526          * the handlers of this signal can contain Gtk+ code */
1527
1528         gdk_threads_enter (); /* CHECKED */
1529         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1530         gdk_threads_leave (); /* CHECKED */
1531
1532         g_slice_free (ModestMailOperationState, state);
1533         
1534         return TRUE;
1535 }
1536
1537 void
1538 modest_mail_operation_update_account (ModestMailOperation *self,
1539                                       const gchar *account_name,
1540                                       gboolean poke_all,
1541                                       UpdateAccountCallback callback,
1542                                       gpointer user_data)
1543 {
1544         UpdateAccountInfo *info = NULL;
1545         ModestMailOperationPrivate *priv = NULL;
1546         ModestTnyAccountStore *account_store = NULL;
1547         TnyStoreAccount *store_account = NULL;
1548         TnyList *folders;
1549
1550         /* Init mail operation */
1551         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1552         priv->total = 0;
1553         priv->done  = 0;
1554         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1555         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1556
1557         /* Get the store account */
1558         account_store = modest_runtime_get_account_store ();
1559         store_account = (TnyStoreAccount *)
1560                 modest_tny_account_store_get_server_account (account_store,
1561                                                              account_name,
1562                                                              TNY_ACCOUNT_TYPE_STORE);
1563         priv->account = g_object_ref (store_account);
1564
1565         /* Create the helper object */
1566         info = g_slice_new0 (UpdateAccountInfo);
1567         info->pending_calls = 1;
1568         info->folders = tny_simple_list_new ();
1569         info->mail_op = g_object_ref (self);
1570         info->poke_all = poke_all;
1571         info->account_name = g_strdup (account_name);
1572         info->callback = callback;
1573         info->user_data = user_data;
1574         info->update_timeout = g_timeout_add (250, timeout_notify_progress, self);
1575
1576         /* Set account busy */
1577         modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1578         modest_mail_operation_notify_start (self);
1579
1580         /* Get all folders and continue in the callback */    
1581         folders = tny_simple_list_new ();
1582         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
1583                                 folders, recurse_folders_async_cb, 
1584                                 NULL, NULL, info);
1585 }
1586
1587 /*
1588  * Used to notify the queue from the main
1589  * loop. We call it inside an idle call to achieve that
1590  */
1591 static gboolean
1592 idle_notify_queue (gpointer data)
1593 {
1594         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1595
1596         gdk_threads_enter ();
1597         modest_mail_operation_notify_end (mail_op);
1598         gdk_threads_leave ();
1599         g_object_unref (mail_op);
1600
1601         return FALSE;
1602 }
1603
1604 static int
1605 compare_headers_by_date (gconstpointer a,
1606                          gconstpointer b)
1607 {
1608         TnyHeader **header1, **header2;
1609         time_t sent1, sent2;
1610
1611         header1 = (TnyHeader **) a;
1612         header2 = (TnyHeader **) b;
1613
1614         sent1 = tny_header_get_date_sent (*header1);
1615         sent2 = tny_header_get_date_sent (*header2);
1616
1617         /* We want the most recent ones (greater time_t) at the
1618            beginning */
1619         if (sent1 < sent2)
1620                 return 1;
1621         else
1622                 return -1;
1623 }
1624
1625
1626 /* ******************************************************************* */
1627 /* ************************** STORE  ACTIONS ************************* */
1628 /* ******************************************************************* */
1629
1630
1631 TnyFolder *
1632 modest_mail_operation_create_folder (ModestMailOperation *self,
1633                                      TnyFolderStore *parent,
1634                                      const gchar *name)
1635 {
1636         ModestMailOperationPrivate *priv;
1637         TnyFolder *new_folder = NULL;
1638
1639         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1640         g_return_val_if_fail (name, NULL);
1641         
1642         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1643         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1644         priv->account = (TNY_IS_ACCOUNT (parent)) ? 
1645                 g_object_ref (parent) : 
1646                 modest_tny_folder_get_account (TNY_FOLDER (parent));
1647
1648         /* Check for already existing folder */
1649         if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1650                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1651                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1652                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1653                              _CS("ckdg_ib_folder_already_exists"));
1654         }
1655
1656         /* Check parent */
1657         if (TNY_IS_FOLDER (parent)) {
1658                 /* Check folder rules */
1659                 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1660                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1661                         /* Set status failed and set an error */
1662                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1663                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1664                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1665                                      _("mail_in_ui_folder_create_error"));
1666                 }
1667         }
1668
1669         if (!strcmp (name, " ") || strchr (name, '/')) {
1670                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1671                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1672                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1673                              _("mail_in_ui_folder_create_error"));
1674         }
1675
1676         if (!priv->error) {
1677                 /* Create the folder */
1678                 modest_mail_operation_notify_start (self);
1679                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1680                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1681                 if (!priv->error)
1682                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1683         }
1684
1685         /* Notify about operation end */
1686         modest_mail_operation_notify_end (self);
1687
1688         return new_folder;
1689 }
1690
1691 void
1692 modest_mail_operation_remove_folder (ModestMailOperation *self,
1693                                      TnyFolder           *folder,
1694                                      gboolean             remove_to_trash)
1695 {
1696         TnyAccount *account;
1697         ModestMailOperationPrivate *priv;
1698         ModestTnyFolderRules rules;
1699
1700         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1701         g_return_if_fail (TNY_IS_FOLDER (folder));
1702         
1703         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1704         
1705         /* Check folder rules */
1706         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1707         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1708                 /* Set status failed and set an error */
1709                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1710                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1711                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1712                              _("mail_in_ui_folder_delete_error"));
1713                 goto end;
1714         }
1715
1716         /* Get the account */
1717         account = modest_tny_folder_get_account (folder);
1718         priv->account = g_object_ref(account);
1719         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1720
1721         /* Delete folder or move to trash */
1722         if (remove_to_trash) {
1723                 TnyFolder *trash_folder = NULL;
1724                 trash_folder = modest_tny_account_get_special_folder (account,
1725                                                                       TNY_FOLDER_TYPE_TRASH);
1726                 /* TODO: error_handling */
1727                 if (trash_folder) {
1728                         modest_mail_operation_notify_start (self);
1729                         modest_mail_operation_xfer_folder (self, folder,
1730                                                     TNY_FOLDER_STORE (trash_folder), 
1731                                                     TRUE, NULL, NULL);
1732                         g_object_unref (trash_folder);
1733                 }
1734         } else {
1735                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1736                 if (parent) {
1737                         modest_mail_operation_notify_start (self);
1738                         tny_folder_store_remove_folder (parent, folder, &(priv->error));
1739                         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1740                         
1741                         if (!priv->error)
1742                                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1743
1744                         g_object_unref (parent);
1745                 } else
1746                         g_warning ("%s: could not get parent folder", __FUNCTION__);
1747         }
1748         g_object_unref (G_OBJECT (account));
1749
1750  end:
1751         /* Notify about operation end */
1752         modest_mail_operation_notify_end (self);
1753 }
1754
1755 static void
1756 transfer_folder_status_cb (GObject *obj,
1757                            TnyStatus *status,
1758                            gpointer user_data)
1759 {
1760         ModestMailOperation *self;
1761         ModestMailOperationPrivate *priv;
1762         ModestMailOperationState *state;
1763         XFerMsgAsyncHelper *helper;
1764
1765         g_return_if_fail (status != NULL);
1766         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1767
1768         helper = (XFerMsgAsyncHelper *) user_data;
1769         g_return_if_fail (helper != NULL);
1770
1771         self = helper->mail_op;
1772         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1773
1774         priv->done = status->position;
1775         priv->total = status->of_total;
1776
1777         state = modest_mail_operation_clone_state (self);
1778
1779         /* This is not a GDK lock because we are a Tinymail callback
1780          * which is already GDK locked by Tinymail */
1781
1782         /* no gdk_threads_enter (), CHECKED */
1783
1784         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL); 
1785
1786         /* no gdk_threads_leave (), CHECKED */
1787
1788         g_slice_free (ModestMailOperationState, state);
1789 }
1790
1791
1792 static void
1793 transfer_folder_cb (TnyFolder *folder, 
1794                     gboolean cancelled, 
1795                     TnyFolderStore *into, 
1796                     TnyFolder *new_folder, 
1797                     GError *err, 
1798                     gpointer user_data)
1799 {
1800         XFerMsgAsyncHelper *helper;
1801         ModestMailOperation *self = NULL;
1802         ModestMailOperationPrivate *priv = NULL;
1803
1804         helper = (XFerMsgAsyncHelper *) user_data;
1805         g_return_if_fail (helper != NULL);       
1806
1807         self = helper->mail_op;
1808         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1809
1810         if (err) {
1811                 priv->error = g_error_copy (err);
1812                 priv->done = 0;
1813                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1814         } else if (cancelled) {
1815                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1816                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1817                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1818                              _("Transference of %s was cancelled."),
1819                              tny_folder_get_name (folder));
1820         } else {
1821                 priv->done = 1;
1822                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1823         }
1824                 
1825         /* Notify about operation end */
1826         modest_mail_operation_notify_end (self);
1827
1828         /* If user defined callback function was defined, call it */
1829         if (helper->user_callback) {
1830
1831                 /* This is not a GDK lock because we are a Tinymail callback
1832                  * which is already GDK locked by Tinymail */
1833
1834                 /* no gdk_threads_enter (), CHECKED */
1835                 helper->user_callback (self, helper->user_data);
1836                 /* no gdk_threads_leave () , CHECKED */
1837         }
1838
1839         /* Free */
1840         g_object_unref (helper->mail_op);
1841         g_slice_free   (XFerMsgAsyncHelper, helper);
1842 }
1843
1844 /**
1845  *
1846  * This function checks if the new name is a valid name for our local
1847  * folders account. The new name could not be the same than then name
1848  * of any of the mandatory local folders
1849  *
1850  * We can not rely on tinymail because tinymail does not check the
1851  * name of the virtual folders that the account could have in the case
1852  * that we're doing a rename (because it directly calls Camel which
1853  * knows nothing about our virtual folders). 
1854  *
1855  * In the case of an actual copy/move (i.e. move/copy a folder between
1856  * accounts) tinymail uses the tny_folder_store_create_account which
1857  * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1858  * checks the new name of the folder, so this call in that case
1859  * wouldn't be needed. *But* NOTE that if tinymail changes its
1860  * implementation (if folder transfers within the same account is no
1861  * longer implemented as a rename) this call will allow Modest to work
1862  * perfectly
1863  *
1864  * If the new name is not valid, this function will set the status to
1865  * failed and will set also an error in the mail operation
1866  */
1867 static gboolean
1868 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1869                                  TnyFolderStore *into,
1870                                  const gchar *new_name)
1871 {
1872         if (TNY_IS_ACCOUNT (into) && 
1873             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1874             modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1875                                                                  new_name)) {
1876                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1877                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1878                              MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1879                              _CS("ckdg_ib_folder_already_exists"));
1880                 return FALSE;
1881         } else
1882                 return TRUE;
1883 }
1884
1885 void
1886 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1887                                    TnyFolder *folder,
1888                                    TnyFolderStore *parent,
1889                                    gboolean delete_original,
1890                                    XferAsyncUserCallback user_callback,
1891                                    gpointer user_data)
1892 {
1893         ModestMailOperationPrivate *priv = NULL;
1894         ModestTnyFolderRules parent_rules = 0, rules; 
1895         XFerMsgAsyncHelper *helper = NULL;
1896         const gchar *folder_name = NULL;
1897         const gchar *error_msg;
1898
1899         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1900         g_return_if_fail (TNY_IS_FOLDER (folder));
1901         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1902
1903         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1904         folder_name = tny_folder_get_name (folder);
1905
1906         /* Set the error msg */
1907         error_msg = _("mail_in_ui_folder_move_target_error");
1908
1909         /* Get account and set it into mail_operation */
1910         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1911         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1912         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1913
1914         /* Get folder rules */
1915         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1916         if (TNY_IS_FOLDER (parent))
1917                 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1918         
1919         /* Apply operation constraints */
1920         if ((gpointer) parent == (gpointer) folder ||
1921             (!TNY_IS_FOLDER_STORE (parent)) || 
1922             (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1923                 /* Folder rules */
1924                 goto error;
1925         } else if (TNY_IS_FOLDER (parent) && 
1926                    (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1927                 /* Folder rules */
1928                 goto error;
1929
1930         } else if (TNY_IS_FOLDER (parent) &&
1931                    TNY_IS_FOLDER_STORE (folder) &&
1932                    modest_tny_folder_is_ancestor (TNY_FOLDER (parent), 
1933                                                   TNY_FOLDER_STORE (folder))) {
1934                 /* Do not move a parent into a child */
1935                 goto error;
1936         } else if (TNY_IS_FOLDER_STORE (parent) &&
1937                    modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1938                 /* Check that the new folder name is not used by any
1939                    parent subfolder */
1940                 goto error;     
1941         } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1942                 /* Check that the new folder name is not used by any
1943                    special local folder */
1944                 goto error;
1945         } else {
1946                 /* Create the helper */
1947                 helper = g_slice_new0 (XFerMsgAsyncHelper);
1948                 helper->mail_op = g_object_ref (self);
1949                 helper->dest_folder = NULL;
1950                 helper->headers = NULL;
1951                 helper->user_callback = user_callback;
1952                 helper->user_data = user_data;
1953                 
1954                 /* Move/Copy folder */
1955                 modest_mail_operation_notify_start (self);
1956                 tny_folder_copy_async (folder,
1957                                        parent,
1958                                        tny_folder_get_name (folder),
1959                                        delete_original,
1960                                        transfer_folder_cb,
1961                                        transfer_folder_status_cb,
1962                                        helper);
1963                 return;
1964         }
1965
1966  error:
1967         /* Set status failed and set an error */
1968         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1969         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1970                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1971                      error_msg);
1972
1973         /* Call the user callback if exists */
1974         if (user_callback)
1975                 user_callback (self, user_data);
1976
1977         /* Notify the queue */
1978         modest_mail_operation_notify_end (self);
1979 }
1980
1981 void
1982 modest_mail_operation_rename_folder (ModestMailOperation *self,
1983                                      TnyFolder *folder,
1984                                      const gchar *name)
1985 {
1986         ModestMailOperationPrivate *priv;
1987         ModestTnyFolderRules rules;
1988         XFerMsgAsyncHelper *helper;
1989
1990         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1991         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1992         g_return_if_fail (name);
1993         
1994         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1995
1996         /* Get account and set it into mail_operation */
1997         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1998         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1999
2000         /* Check folder rules */
2001         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2002         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2003                 /* Set status failed and set an error */
2004                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2005                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2006                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2007                              _("FIXME: unable to rename"));
2008
2009                 /* Notify about operation end */
2010                 modest_mail_operation_notify_end (self);
2011         } else if (!strcmp (name, " ") || strchr (name, '/')) {
2012                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2013                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2014                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2015                              _("FIXME: unable to rename"));
2016                 /* Notify about operation end */
2017                 modest_mail_operation_notify_end (self);
2018         } else {
2019                 TnyFolderStore *into;
2020
2021                 into = tny_folder_get_folder_store (folder);    
2022
2023                 /* Check that the new folder name is not used by any
2024                    special local folder */
2025                 if (new_name_valid_if_local_account (priv, into, name)) {
2026                         /* Create the helper */
2027                         helper = g_slice_new0 (XFerMsgAsyncHelper);
2028                         helper->mail_op = g_object_ref(self);
2029                         helper->dest_folder = NULL;
2030                         helper->headers = NULL;
2031                         helper->user_callback = NULL;
2032                         helper->user_data = NULL;
2033                 
2034                         /* Rename. Camel handles folder subscription/unsubscription */
2035                         modest_mail_operation_notify_start (self);
2036                         tny_folder_copy_async (folder, into, name, TRUE,
2037                                                transfer_folder_cb,
2038                                                transfer_folder_status_cb,
2039                                                helper);
2040                 } else {
2041                         modest_mail_operation_notify_end (self);
2042                 }
2043                 g_object_unref (into);
2044         }
2045 }
2046
2047 /* ******************************************************************* */
2048 /* **************************  MSG  ACTIONS  ************************* */
2049 /* ******************************************************************* */
2050
2051 void 
2052 modest_mail_operation_get_msg (ModestMailOperation *self,
2053                                TnyHeader *header,
2054                                GetMsgAsyncUserCallback user_callback,
2055                                gpointer user_data)
2056 {
2057         GetMsgInfo *helper = NULL;
2058         TnyFolder *folder;
2059         ModestMailOperationPrivate *priv;
2060         
2061         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2062         g_return_if_fail (TNY_IS_HEADER (header));
2063         
2064         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2065         folder = tny_header_get_folder (header);
2066
2067         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2068         priv->total = 1;
2069         priv->done = 0;
2070
2071         /* Get account and set it into mail_operation */
2072         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2073         
2074         /* Check for cached messages */
2075         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2076                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2077         else 
2078                 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2079         
2080         /* Create the helper */
2081         helper = g_slice_new0 (GetMsgInfo);
2082         helper->header = g_object_ref (header);
2083         helper->mail_op = g_object_ref (self);
2084         helper->user_callback = user_callback;
2085         helper->user_data = user_data;
2086         helper->destroy_notify = NULL;
2087         helper->last_total_bytes = 0;
2088         helper->sum_total_bytes = 0;
2089         helper->total_bytes = tny_header_get_message_size (header);
2090
2091         modest_mail_operation_notify_start (self);
2092         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2093
2094         g_object_unref (G_OBJECT (folder));
2095 }
2096
2097 static void     
2098 get_msg_status_cb (GObject *obj,
2099                    TnyStatus *status,  
2100                    gpointer user_data)
2101 {
2102         GetMsgInfo *helper = NULL;
2103
2104         g_return_if_fail (status != NULL);
2105         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2106
2107         helper = (GetMsgInfo *) user_data;
2108         g_return_if_fail (helper != NULL);       
2109
2110         /* Notify progress */
2111         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2112                                               &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2113 }
2114
2115 static void
2116 get_msg_async_cb (TnyFolder *folder, 
2117                   gboolean canceled, 
2118                   TnyMsg *msg, 
2119                   GError *err, 
2120                   gpointer user_data)
2121 {
2122         GetMsgInfo *info = NULL;
2123         ModestMailOperationPrivate *priv = NULL;
2124         gboolean finished;
2125
2126         info = (GetMsgInfo *) user_data;
2127
2128         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2129         priv->done++;
2130         finished = (priv->done == priv->total) ? TRUE : FALSE;
2131
2132         /* Check errors */
2133         if (canceled || err) {
2134                 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2135                 if (!priv->error)
2136                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2137                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2138                                      err->message);
2139         } else {
2140                 /* Set the success status before calling the user callback */
2141                 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2142                         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2143         }
2144
2145
2146         /* Call the user callback */
2147         if (info->user_callback)
2148                 info->user_callback (info->mail_op, info->header, canceled, 
2149                                      msg, err, info->user_data);
2150
2151         /* Notify about operation end if this is the last callback */
2152         if (finished) {
2153                 /* Free user data */
2154                 if (info->destroy_notify)
2155                         info->destroy_notify (info->user_data);
2156
2157                 /* Notify about operation end */
2158                 modest_mail_operation_notify_end (info->mail_op);
2159         }
2160
2161         /* Clean */
2162         g_object_unref (info->header);
2163         g_object_unref (info->mail_op);
2164         g_slice_free (GetMsgInfo, info);
2165 }
2166
2167 void 
2168 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2169                                      TnyList *header_list, 
2170                                      GetMsgAsyncUserCallback user_callback,
2171                                      gpointer user_data,
2172                                      GDestroyNotify notify)
2173 {
2174         ModestMailOperationPrivate *priv = NULL;
2175         gboolean size_ok = TRUE;
2176         gint max_size;
2177         TnyIterator *iter = NULL;
2178         
2179         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2180         
2181         /* Init mail operation */
2182         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2183         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2184         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2185         priv->done = 0;
2186         priv->total = tny_list_get_length(header_list);
2187
2188         /* Get account and set it into mail_operation */
2189         if (tny_list_get_length (header_list) >= 1) {
2190                 iter = tny_list_create_iterator (header_list);
2191                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2192                 if (header) {
2193                         TnyFolder *folder = tny_header_get_folder (header);
2194                         if (folder) {           
2195                                 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2196                                 g_object_unref (folder);
2197                         }
2198                         g_object_unref (header);
2199                 }
2200
2201                 if (tny_list_get_length (header_list) == 1) {
2202                         g_object_unref (iter);
2203                         iter = NULL;
2204                 }
2205         }
2206
2207         /* Get msg size limit */
2208         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
2209                                          MODEST_CONF_MSG_SIZE_LIMIT, 
2210                                          &(priv->error));
2211         if (priv->error) {
2212                 g_clear_error (&(priv->error));
2213                 max_size = G_MAXINT;
2214         } else {
2215                 max_size = max_size * KB;
2216         }
2217
2218         /* Check message size limits. If there is only one message
2219            always retrieve it */
2220         if (iter != NULL) {
2221                 while (!tny_iterator_is_done (iter) && size_ok) {
2222                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2223                         if (header) {
2224                                 if (tny_header_get_message_size (header) >= max_size)
2225                                         size_ok = FALSE;
2226                                 g_object_unref (header);
2227                         }
2228
2229                         tny_iterator_next (iter);
2230                 }
2231                 g_object_unref (iter);
2232         }
2233
2234         if (size_ok) {
2235                 const gint msg_list_size = compute_message_list_size (header_list);
2236
2237                 modest_mail_operation_notify_start (self);
2238                 iter = tny_list_create_iterator (header_list);
2239                 while (!tny_iterator_is_done (iter)) { 
2240                         GetMsgInfo *msg_info = NULL;
2241                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2242                         TnyFolder *folder = tny_header_get_folder (header);
2243                         
2244                         /* Create the message info */
2245                         msg_info = g_slice_new0 (GetMsgInfo);
2246                         msg_info->mail_op = g_object_ref (self);
2247                         msg_info->header = g_object_ref (header);
2248                         msg_info->user_callback = user_callback;
2249                         msg_info->user_data = user_data;
2250                         msg_info->destroy_notify = notify;
2251                         msg_info->last_total_bytes = 0;
2252                         msg_info->sum_total_bytes = 0;
2253                         msg_info->total_bytes = msg_list_size;
2254                         
2255                         /* The callback will call it per each header */
2256                         tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2257                         
2258                         /* Free and go on */
2259                         g_object_unref (header);
2260                         g_object_unref (folder);
2261                         tny_iterator_next (iter);
2262                 }
2263                 g_object_unref (iter);
2264         } else {
2265                 /* Set status failed and set an error */
2266                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2267                 /* FIXME: the error msg is different for pop */
2268                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2269                              MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2270                              _("emev_ni_ui_imap_msg_size_exceed_error"));
2271                 /* Remove from queue and free resources */
2272                 modest_mail_operation_notify_end (self);
2273                 if (notify)
2274                         notify (user_data);
2275         }
2276 }
2277
2278
2279 void 
2280 modest_mail_operation_remove_msg (ModestMailOperation *self,  
2281                                   TnyHeader *header,
2282                                   gboolean remove_to_trash /*ignored*/)
2283 {
2284         TnyFolder *folder;
2285         ModestMailOperationPrivate *priv;
2286
2287         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2288         g_return_if_fail (TNY_IS_HEADER (header));
2289
2290         if (remove_to_trash)
2291                 g_warning ("remove to trash is not implemented");
2292
2293         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2294         folder = tny_header_get_folder (header);
2295
2296         /* Get account and set it into mail_operation */
2297         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2298         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2299         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2300
2301         /* remove message from folder */
2302         tny_folder_remove_msg (folder, header, &(priv->error));
2303         if (!priv->error) {
2304                 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2305                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2306
2307                 modest_mail_operation_notify_start (self);
2308
2309                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2310                     TNY_IS_CAMEL_POP_FOLDER (folder))
2311                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> dont expunge */
2312                 else
2313                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2314         }
2315         
2316         
2317         /* Set status */
2318         if (!priv->error)
2319                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2320         else
2321                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2322
2323         /* Free */
2324         g_object_unref (G_OBJECT (folder));
2325
2326         /* Notify about operation end */
2327         modest_mail_operation_notify_end (self);
2328 }
2329
2330 void 
2331 modest_mail_operation_remove_msgs (ModestMailOperation *self,  
2332                                    TnyList *headers,
2333                                   gboolean remove_to_trash /*ignored*/)
2334 {
2335         TnyFolder *folder;
2336         ModestMailOperationPrivate *priv;
2337         TnyIterator *iter = NULL;
2338         TnyHeader *header = NULL;
2339         TnyList *remove_headers = NULL;
2340         TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2341
2342         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2343         g_return_if_fail (TNY_IS_LIST (headers));
2344
2345         if (remove_to_trash)
2346                 g_warning ("remove to trash is not implemented");
2347
2348         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2349
2350         remove_headers = g_object_ref(headers);
2351
2352         /* Get folder from first header and sync it */
2353         iter = tny_list_create_iterator (headers);
2354         header = TNY_HEADER (tny_iterator_get_current (iter));
2355         folder = tny_header_get_folder (header);
2356
2357         /* Don't remove messages that are being sent */
2358         if (modest_tny_folder_is_local_folder (folder)) {
2359                 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2360         }
2361         if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2362                 TnyTransportAccount *traccount = NULL;
2363                 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2364                 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2365                 if (traccount) {
2366                         ModestTnySendQueueStatus status;
2367                         ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2368                         TnyIterator *iter = tny_list_create_iterator(headers);
2369                         g_object_unref(remove_headers);
2370                         remove_headers = TNY_LIST(tny_simple_list_new());
2371                         while (!tny_iterator_is_done(iter)) {
2372                                 char *msg_id;
2373                                 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2374                                 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2375                                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2376                                 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2377                                         tny_list_append(remove_headers, G_OBJECT(hdr));
2378                                 }
2379                                 g_object_unref(hdr);
2380                                 g_free(msg_id);
2381                                 tny_iterator_next(iter);
2382                         }
2383                         g_object_unref(iter);
2384                         g_object_unref(traccount);
2385                 }
2386         }
2387
2388         /* Get account and set it into mail_operation */
2389         priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2390         priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2391         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2392
2393         /* remove message from folder */
2394         modest_mail_operation_notify_start (self);
2395
2396         tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2397         if (!priv->error) {
2398                 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) || 
2399                     TNY_IS_CAMEL_POP_FOLDER (folder))
2400                         tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */ 
2401                 else
2402                         /* local folders */
2403                         tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2404         }
2405         
2406         
2407         /* Set status */
2408         if (!priv->error)
2409                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2410         else
2411                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2412
2413         /* Free */
2414         g_object_unref (remove_headers);
2415         g_object_unref (header);
2416         g_object_unref (iter);
2417         g_object_unref (G_OBJECT (folder));
2418
2419         /* Notify about operation end */
2420         modest_mail_operation_notify_end (self);
2421 }
2422
2423 static void
2424 notify_progress_of_multiple_messages (ModestMailOperation *self,
2425                                       TnyStatus *status,
2426                                       gint *last_total_bytes,
2427                                       gint *sum_total_bytes,
2428                                       gint total_bytes, 
2429                                       gboolean increment_done)
2430 {
2431         ModestMailOperationPrivate *priv;
2432         ModestMailOperationState *state;
2433         gboolean is_num_bytes;
2434
2435         priv =  MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2436
2437         /* We know that tinymail sends us information about
2438            transferred bytes with this particular message */
2439         is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2440
2441         state = modest_mail_operation_clone_state (self);
2442         if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2443                 /* We know that we're in a different message when the
2444                    total number of bytes to transfer is different. Of
2445                    course it could fail if we're transferring messages
2446                    of the same size, but this is a workarround */
2447                 if (status->of_total != *last_total_bytes) {
2448                         /* We need to increment the done when there is
2449                            no information about each individual
2450                            message, we need to do this in message
2451                            transfers, and we don't do it for getting
2452                            messages */
2453                         if (increment_done)
2454                                 priv->done++;
2455                         *sum_total_bytes += *last_total_bytes;
2456                         *last_total_bytes = status->of_total;
2457                 }
2458                 state->bytes_done += status->position + *sum_total_bytes;
2459                 state->bytes_total = total_bytes;
2460
2461                 /* Notify the status change. Only notify about changes
2462                    referred to bytes */
2463                 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 
2464                                0, state, NULL);
2465         }
2466
2467         g_slice_free (ModestMailOperationState, state);
2468 }
2469
2470 static void
2471 transfer_msgs_status_cb (GObject *obj,
2472                          TnyStatus *status,  
2473                          gpointer user_data)
2474 {
2475         XFerMsgAsyncHelper *helper;
2476
2477         g_return_if_fail (status != NULL);
2478         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2479
2480         helper = (XFerMsgAsyncHelper *) user_data;
2481         g_return_if_fail (helper != NULL);       
2482
2483         /* Notify progress */
2484         notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes), 
2485                                               &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2486 }
2487
2488
2489 static void
2490 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2491 {
2492         XFerMsgAsyncHelper *helper;
2493         ModestMailOperation *self;
2494         ModestMailOperationPrivate *priv;
2495         TnyIterator *iter = NULL;
2496         TnyHeader *header = NULL;
2497
2498         helper = (XFerMsgAsyncHelper *) user_data;
2499         self = helper->mail_op;
2500
2501         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2502
2503         if (err) {
2504                 priv->error = g_error_copy (err);
2505                 priv->done = 0;
2506                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
2507         } else if (cancelled) {
2508                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2509                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2510                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2511                              _("Error trying to refresh the contents of %s"),
2512                              tny_folder_get_name (folder));
2513         } else {
2514                 priv->done = 1;
2515                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2516
2517                 /* Update folder counts */
2518                 tny_folder_poke_status (folder);                
2519                 tny_folder_poke_status (helper->dest_folder);           
2520         }
2521
2522         
2523         /* Mark headers as deleted and seen */
2524         if ((helper->delete) && 
2525             (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2526                 iter = tny_list_create_iterator (helper->headers);
2527                 while (!tny_iterator_is_done (iter)) {
2528                         header = TNY_HEADER (tny_iterator_get_current (iter));
2529                         tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2530                         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2531                         g_object_unref (header);
2532
2533                         tny_iterator_next (iter);
2534                 }
2535
2536         }
2537                 
2538
2539         /* Notify about operation end */
2540         modest_mail_operation_notify_end (self);
2541
2542         /* If user defined callback function was defined, call it */
2543         if (helper->user_callback) {
2544                 /* This is not a GDK lock because we are a Tinymail callback and
2545                  * Tinymail already acquires the Gdk lock */
2546
2547                 /* no gdk_threads_enter (), CHECKED */
2548                 helper->user_callback (self, helper->user_data);
2549                 /* no gdk_threads_leave (), CHECKED */
2550         }
2551
2552         /* Free */
2553         if (helper->headers)
2554                 g_object_unref (helper->headers);
2555         if (helper->dest_folder)
2556                 g_object_unref (helper->dest_folder);
2557         if (helper->mail_op)
2558                 g_object_unref (helper->mail_op);
2559         if (folder)
2560                 g_object_unref (folder);
2561         if (iter)
2562                 g_object_unref (iter);
2563         g_slice_free (XFerMsgAsyncHelper, helper);
2564 }
2565
2566 static guint
2567 compute_message_list_size (TnyList *headers)
2568 {
2569         TnyIterator *iter;
2570         guint size = 0;
2571
2572         iter = tny_list_create_iterator (headers);
2573         while (!tny_iterator_is_done (iter)) {
2574                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2575                 size += tny_header_get_message_size (header);
2576                 g_object_unref (header);
2577                 tny_iterator_next (iter);
2578         }
2579         g_object_unref (iter);
2580
2581         return size;
2582 }
2583
2584 static guint
2585 compute_message_array_size (GPtrArray *headers)
2586 {
2587         guint size = 0;
2588         gint i;
2589
2590         for (i = 0; i < headers->len; i++) {
2591                 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2592                 size += tny_header_get_message_size (header);
2593         }
2594
2595         return size;
2596 }
2597
2598
2599 void
2600 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2601                                  TnyList *headers, 
2602                                  TnyFolder *folder, 
2603                                  gboolean delete_original,
2604                                  XferAsyncUserCallback user_callback,
2605                                  gpointer user_data)
2606 {
2607         ModestMailOperationPrivate *priv = NULL;
2608         TnyIterator *iter = NULL;
2609         TnyFolder *src_folder = NULL;
2610         XFerMsgAsyncHelper *helper = NULL;
2611         TnyHeader *header = NULL;
2612         ModestTnyFolderRules rules = 0;
2613
2614         g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2615         g_return_if_fail (headers && TNY_IS_LIST (headers));
2616         g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2617
2618         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2619         priv->total = tny_list_get_length (headers);
2620         priv->done = 0;
2621         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2622         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2623
2624         /* Apply folder rules */
2625         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2626         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2627                 /* Set status failed and set an error */
2628                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2629                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2630                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2631                              _CS("ckct_ib_unable_to_paste_here"));
2632                 /* Notify the queue */
2633                 modest_mail_operation_notify_end (self);
2634                 return;
2635         }
2636                 
2637         /* Get source folder */
2638         iter = tny_list_create_iterator (headers);
2639         header = TNY_HEADER (tny_iterator_get_current (iter));
2640         if (header) {
2641                 src_folder = tny_header_get_folder (header);
2642                 g_object_unref (header);
2643         }
2644         g_object_unref (iter);
2645
2646         if (src_folder == NULL) {
2647                 /* Notify the queue */
2648                 modest_mail_operation_notify_end (self);
2649
2650                 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2651                 return;
2652         }
2653
2654         
2655         /* Check folder source and destination */
2656         if (src_folder == folder) {
2657                 /* Set status failed and set an error */
2658                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2659                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2660                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2661                              _("mail_in_ui_folder_copy_target_error"));
2662                 
2663                 /* Notify the queue */
2664                 modest_mail_operation_notify_end (self);
2665                 
2666                 /* Free */
2667                 g_object_unref (src_folder);            
2668                 return;
2669         }
2670
2671         /* Create the helper */
2672         helper = g_slice_new0 (XFerMsgAsyncHelper);
2673         helper->mail_op = g_object_ref(self);
2674         helper->dest_folder = g_object_ref(folder);
2675         helper->headers = g_object_ref(headers);
2676         helper->user_callback = user_callback;
2677         helper->user_data = user_data;
2678         helper->delete = delete_original;
2679         helper->last_total_bytes = 0;
2680         helper->sum_total_bytes = 0;
2681         helper->total_bytes = compute_message_list_size (headers);
2682
2683         /* Get account and set it into mail_operation */
2684         priv->account = modest_tny_folder_get_account (src_folder);
2685
2686         /* Transfer messages */
2687         modest_mail_operation_notify_start (self);
2688         tny_folder_transfer_msgs_async (src_folder, 
2689                                         headers, 
2690                                         folder, 
2691                                         delete_original, 
2692                                         transfer_msgs_cb, 
2693                                         transfer_msgs_status_cb,
2694                                         helper);
2695 }
2696
2697
2698 static void
2699 on_refresh_folder (TnyFolder   *folder, 
2700                    gboolean     cancelled, 
2701                    GError     *error,
2702                    gpointer     user_data)
2703 {
2704         RefreshAsyncHelper *helper = NULL;
2705         ModestMailOperation *self = NULL;
2706         ModestMailOperationPrivate *priv = NULL;
2707
2708         helper = (RefreshAsyncHelper *) user_data;
2709         self = helper->mail_op;
2710         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2711
2712         g_return_if_fail(priv!=NULL);
2713
2714         if (error) {
2715                 priv->error = g_error_copy (error);
2716                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2717                 goto out;
2718         }
2719
2720         if (cancelled) {
2721                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2722                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2723                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2724                              _("Error trying to refresh the contents of %s"),
2725                              tny_folder_get_name (folder));
2726                 goto out;
2727         }
2728
2729         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2730  out:
2731
2732         /* Call user defined callback, if it exists */
2733         if (helper->user_callback) {
2734
2735                 /* This is not a GDK lock because we are a Tinymail callback and
2736                  * Tinymail already acquires the Gdk lock */
2737                 helper->user_callback (self, folder, helper->user_data);
2738         }
2739
2740         /* Free */
2741         g_slice_free (RefreshAsyncHelper, helper);
2742
2743         /* Notify about operation end */
2744         modest_mail_operation_notify_end (self);
2745         g_object_unref(self);
2746 }
2747
2748 static void
2749 on_refresh_folder_status_update (GObject *obj,
2750                                  TnyStatus *status,
2751                                  gpointer user_data)
2752 {
2753         RefreshAsyncHelper *helper = NULL;
2754         ModestMailOperation *self = NULL;
2755         ModestMailOperationPrivate *priv = NULL;
2756         ModestMailOperationState *state;
2757
2758         g_return_if_fail (user_data != NULL);
2759         g_return_if_fail (status != NULL);
2760         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2761
2762         helper = (RefreshAsyncHelper *) user_data;
2763         self = helper->mail_op;
2764         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2765
2766         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2767
2768         priv->done = status->position;
2769         priv->total = status->of_total;
2770
2771         state = modest_mail_operation_clone_state (self);
2772
2773         /* This is not a GDK lock because we are a Tinymail callback and
2774          * Tinymail already acquires the Gdk lock */
2775         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2776
2777         g_slice_free (ModestMailOperationState, state);
2778 }
2779
2780 void 
2781 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
2782                                        TnyFolder *folder,
2783                                        RefreshAsyncUserCallback user_callback,
2784                                        gpointer user_data)
2785 {
2786         ModestMailOperationPrivate *priv = NULL;
2787         RefreshAsyncHelper *helper = NULL;
2788
2789         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2790
2791         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2792
2793         /* Get account and set it into mail_operation */
2794         priv->account = modest_tny_folder_get_account  (folder);
2795         priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2796
2797         /* Create the helper */
2798         helper = g_slice_new0 (RefreshAsyncHelper);
2799         helper->mail_op = g_object_ref(self);
2800         helper->user_callback = user_callback;
2801         helper->user_data = user_data;
2802
2803         /* Refresh the folder. TODO: tinymail could issue a status
2804            updates before the callback call then this could happen. We
2805            must review the design */
2806         modest_mail_operation_notify_start (self);
2807         tny_folder_refresh_async (folder,
2808                                   on_refresh_folder,
2809                                   on_refresh_folder_status_update,
2810                                   helper);
2811 }
2812
2813
2814 static void
2815 modest_mail_operation_notify_start (ModestMailOperation *self)
2816 {
2817         ModestMailOperationPrivate *priv = NULL;
2818
2819         g_return_if_fail (self);
2820
2821         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2822
2823         /* Ensure that all the fields are filled correctly */
2824         g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2825
2826         /* Notify the observers about the mail operation. We do not
2827            wrapp this emission because we assume that this function is
2828            always called from within the main lock */
2829         g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2830 }
2831
2832 /**
2833  *
2834  * It's used by the mail operation queue to notify the observers
2835  * attached to that signal that the operation finished. We need to use
2836  * that because tinymail does not give us the progress of a given
2837  * operation when it finishes (it directly calls the operation
2838  * callback).
2839  */
2840 static void
2841 modest_mail_operation_notify_end (ModestMailOperation *self)
2842 {
2843         ModestMailOperationPrivate *priv = NULL;
2844
2845         g_return_if_fail (self);
2846
2847         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2848
2849         /* Notify the observers about the mail operation end. We do
2850            not wrapp this emission because we assume that this
2851            function is always called from within the main lock */
2852         g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2853
2854         /* Remove the error user data */
2855         if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2856                 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2857 }
2858
2859 TnyAccount *
2860 modest_mail_operation_get_account (ModestMailOperation *self)
2861 {
2862         ModestMailOperationPrivate *priv = NULL;
2863
2864         g_return_val_if_fail (self, NULL);
2865
2866         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2867
2868         return (priv->account) ? g_object_ref (priv->account) : NULL;
2869 }
2870
2871 void
2872 modest_mail_operation_noop (ModestMailOperation *self)
2873 {
2874         ModestMailOperationPrivate *priv = NULL;
2875
2876         g_return_if_fail (self);
2877
2878         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2879         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2880         priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2881         priv->done = 0;
2882         priv->total = 0;
2883
2884         /* This mail operation does nothing actually */
2885         modest_mail_operation_notify_start (self);
2886         modest_mail_operation_notify_end (self);
2887 }