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