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