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