* Added per-message size limit to get_msgs mail operation
[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 "modest-mail-operation.h"
31 /* include other impl specific header files */
32 #include <string.h>
33 #include <stdarg.h>
34 #include <tny-mime-part.h>
35 #include <tny-store-account.h>
36 #include <tny-folder-store.h>
37 #include <tny-folder-store-query.h>
38 #include <tny-camel-stream.h>
39 #include <tny-simple-list.h>
40 #include <tny-send-queue.h>
41 #include <tny-status.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include <modest-tny-account.h>
45 #include <modest-tny-send-queue.h>
46 #include <modest-runtime.h>
47 #include "modest-text-utils.h"
48 #include "modest-tny-msg.h"
49 #include "modest-tny-folder.h"
50 #include "modest-tny-platform-factory.h"
51 #include "modest-marshal.h"
52 #include "modest-error.h"
53
54 /* 'private'/'protected' functions */
55 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
56 static void modest_mail_operation_init       (ModestMailOperation *obj);
57 static void modest_mail_operation_finalize   (GObject *obj);
58
59 /* static void     update_process_msg_status_cb (GObject *obj, */
60 /*                                            TnyStatus *status,   */
61 /*                                            gpointer user_data); */
62 static void     get_msg_cb (TnyFolder *folder, 
63                             gboolean cancelled, 
64                             TnyMsg *msg, 
65                             GError **err, 
66                             gpointer user_data);
67
68 static void     get_msg_status_cb (GObject *obj,
69                                    TnyStatus *status,  
70                                    gpointer user_data);
71
72
73 enum _ModestMailOperationSignals 
74 {
75         PROGRESS_CHANGED_SIGNAL,
76
77         NUM_SIGNALS
78 };
79
80 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
81 struct _ModestMailOperationPrivate {
82         guint                      done;
83         guint                      total;
84         ModestMailOperationStatus  status;      
85         ModestMailOperationId      id;          
86         GObject                   *source;
87         GError                    *error;
88 };
89
90 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
91                                                    MODEST_TYPE_MAIL_OPERATION, \
92                                                    ModestMailOperationPrivate))
93
94 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
95                                                    priv->status = new_status;\
96                                                }
97
98 typedef struct _GetMsgAsyncHelper {     
99         ModestMailOperation *mail_op;
100         GetMsgAsynUserCallback user_callback;   
101         guint pending_ops;
102         gpointer user_data;
103 } GetMsgAsyncHelper;
104
105 typedef struct _XFerMsgAsyncHelper
106 {
107         ModestMailOperation *mail_op;
108         TnyList *headers;
109         TnyFolder *dest_folder;
110         XferMsgsAsynUserCallback user_callback; 
111         gpointer user_data;
112 } XFerMsgAsyncHelper;
113
114 typedef struct _XFerFolderAsyncHelper
115 {
116         ModestMailOperation *mail_op;
117
118 } XFerFolderAsyncHelper;
119
120 /* globals */
121 static GObjectClass *parent_class = NULL;
122
123 static guint signals[NUM_SIGNALS] = {0};
124
125 GType
126 modest_mail_operation_get_type (void)
127 {
128         static GType my_type = 0;
129         if (!my_type) {
130                 static const GTypeInfo my_info = {
131                         sizeof(ModestMailOperationClass),
132                         NULL,           /* base init */
133                         NULL,           /* base finalize */
134                         (GClassInitFunc) modest_mail_operation_class_init,
135                         NULL,           /* class finalize */
136                         NULL,           /* class data */
137                         sizeof(ModestMailOperation),
138                         1,              /* n_preallocs */
139                         (GInstanceInitFunc) modest_mail_operation_init,
140                         NULL
141                 };
142                 my_type = g_type_register_static (G_TYPE_OBJECT,
143                                                   "ModestMailOperation",
144                                                   &my_info, 0);
145         }
146         return my_type;
147 }
148
149 static void
150 modest_mail_operation_class_init (ModestMailOperationClass *klass)
151 {
152         GObjectClass *gobject_class;
153         gobject_class = (GObjectClass*) klass;
154
155         parent_class            = g_type_class_peek_parent (klass);
156         gobject_class->finalize = modest_mail_operation_finalize;
157
158         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
159
160         /**
161          * ModestMailOperation::progress-changed
162          * @self: the #MailOperation that emits the signal
163          * @user_data: user data set when the signal handler was connected
164          *
165          * Emitted when the progress of a mail operation changes
166          */
167         signals[PROGRESS_CHANGED_SIGNAL] = 
168                 g_signal_new ("progress-changed",
169                               G_TYPE_FROM_CLASS (gobject_class),
170                               G_SIGNAL_RUN_FIRST,
171                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
172                               NULL, NULL,
173                               g_cclosure_marshal_VOID__VOID,
174                               G_TYPE_NONE, 0);
175 }
176
177 static void
178 modest_mail_operation_init (ModestMailOperation *obj)
179 {
180         ModestMailOperationPrivate *priv;
181
182         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
183
184         priv->status   = MODEST_MAIL_OPERATION_STATUS_INVALID;
185         priv->id       = MODEST_MAIL_OPERATION_ID_UNKNOWN;
186         priv->error    = NULL;
187         priv->done     = 0;
188         priv->total    = 0;
189         priv->source = NULL;
190 }
191
192 static void
193 modest_mail_operation_finalize (GObject *obj)
194 {
195         ModestMailOperationPrivate *priv;
196
197         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
198
199         if (priv->error) {
200                 g_error_free (priv->error);
201                 priv->error = NULL;
202         }
203         if (priv->source) {
204                 g_object_unref (priv->source);
205                 priv->source = NULL;
206         }
207
208         G_OBJECT_CLASS(parent_class)->finalize (obj);
209 }
210
211 ModestMailOperation*
212 modest_mail_operation_new (ModestMailOperationId id, 
213                            GObject *source)
214 {
215         ModestMailOperation *obj;
216         ModestMailOperationPrivate *priv;
217                 
218         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
219         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
220
221         priv->id = id;
222         if (source != NULL)
223                 priv->source = g_object_ref(source);
224
225         return obj;
226 }
227
228
229 ModestMailOperationId
230 modest_mail_operation_get_id (ModestMailOperation *self)
231 {
232         ModestMailOperationPrivate *priv;
233
234         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
235         
236         return priv->id;
237 }
238
239 gboolean 
240 modest_mail_operation_is_mine (ModestMailOperation *self, 
241                                GObject *me)
242 {
243         ModestMailOperationPrivate *priv;
244
245         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
246         if (priv->source == NULL) return FALSE;
247
248         return priv->source == me;
249 }
250
251
252 void
253 modest_mail_operation_send_mail (ModestMailOperation *self,
254                                  TnyTransportAccount *transport_account,
255                                  TnyMsg* msg)
256 {
257         TnySendQueue *send_queue;
258         
259         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
260         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
261         g_return_if_fail (TNY_IS_MSG (msg));
262         
263         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
264         if (!TNY_IS_SEND_QUEUE(send_queue))
265                 g_printerr ("modest: could not find send queue for account\n");
266         else {
267                 GError *err = NULL;
268                 tny_send_queue_add (send_queue, msg, &err);
269                 if (err) {
270                         g_printerr ("modest: error adding msg to send queue: %s\n",
271                                     err->message);
272                         g_error_free (err);
273                 } else {
274                         /* g_message ("modest: message added to send queue"); */
275                 }
276         }
277
278         /* Notify the queue */
279         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
280 }
281
282 void
283 modest_mail_operation_send_new_mail (ModestMailOperation *self,
284                                      TnyTransportAccount *transport_account,
285                                      const gchar *from,  const gchar *to,
286                                      const gchar *cc,  const gchar *bcc,
287                                      const gchar *subject, const gchar *plain_body,
288                                      const gchar *html_body,
289                                      const GList *attachments_list,
290                                      TnyHeaderFlags priority_flags)
291 {
292         TnyMsg *new_msg;
293         ModestMailOperationPrivate *priv = NULL;
294         /* GList *node = NULL; */
295
296         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
297         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
298
299         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
300
301         /* Check parametters */
302         if (to == NULL) {
303                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
304                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
305                              _("Error trying to send a mail. You need to set at least one recipient"));
306                 return;
307         }
308
309         if (html_body == NULL) {
310                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
311         } else {
312                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
313         }
314         if (!new_msg) {
315                 g_printerr ("modest: failed to create a new msg\n");
316                 return;
317         }
318
319         /* TODO: add priority handling. It's received in the priority_flags operator, and
320            it should have effect in the sending operation */
321
322         /* Call mail operation */
323         modest_mail_operation_send_mail (self, transport_account, new_msg);
324
325         /* Free */
326         g_object_unref (G_OBJECT (new_msg));
327 }
328
329 void
330 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
331                                       TnyTransportAccount *transport_account,
332                                       const gchar *from,  const gchar *to,
333                                       const gchar *cc,  const gchar *bcc,
334                                       const gchar *subject, const gchar *plain_body,
335                                       const gchar *html_body,
336                                       const GList *attachments_list,
337                                       TnyHeaderFlags priority_flags)
338 {
339         TnyMsg *msg = NULL;
340         TnyFolder *folder = NULL;
341         ModestMailOperationPrivate *priv = NULL;
342         GError *err = NULL;
343
344         /* GList *node = NULL; */
345
346         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
347         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
348
349         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
350
351         if (html_body == NULL) {
352                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
353         } else {
354                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
355         }
356         if (!msg) {
357                 g_printerr ("modest: failed to create a new msg\n");
358                 goto cleanup;
359         }
360
361         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
362         if (!folder) {
363                 g_printerr ("modest: failed to find Drafts folder\n");
364                 goto cleanup;
365         }
366         
367         tny_folder_add_msg (folder, msg, &err);
368         if (err) {
369                 g_printerr ("modest: error adding msg to Drafts folder: %s",
370                             err->message);
371                 g_error_free (err);
372                 goto cleanup;
373         }
374
375         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
376
377         /* Free */
378 cleanup:
379         if (msg)
380                 g_object_unref (G_OBJECT(msg));
381         if (folder)
382                 g_object_unref (G_OBJECT(folder));
383 }
384
385 typedef struct 
386 {
387         ModestMailOperation *mail_op;
388         TnyStoreAccount *account;
389         TnyTransportAccount *transport_account;
390 } UpdateAccountInfo;
391
392 static void
393 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
394 {
395         TnyIterator *iter;
396         TnyList *folders = tny_simple_list_new ();
397
398         tny_folder_store_get_folders (store, folders, query, NULL);
399         iter = tny_list_create_iterator (folders);
400
401         while (!tny_iterator_is_done (iter)) {
402
403                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
404
405                 tny_list_prepend (all_folders, G_OBJECT (folder));
406                 recurse_folders (folder, query, all_folders);    
407                 g_object_unref (G_OBJECT (folder));
408
409                 tny_iterator_next (iter);
410         }
411          g_object_unref (G_OBJECT (iter));
412          g_object_unref (G_OBJECT (folders));
413 }
414
415 /* 
416  * Used by update_account_thread to emit the "progress-changed" signal
417  * from the main loop. We call it inside an idle call to achieve that
418  */
419 static gboolean
420 notify_update_account_observers (gpointer data)
421 {
422         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
423
424         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
425
426         return TRUE;
427 }
428
429 /* 
430  * Used by update_account_thread to notify the queue from the main
431  * loop. We call it inside an idle call to achieve that
432  */
433 static gboolean
434 notify_update_account_queue (gpointer data)
435 {
436         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
437
438         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), 
439                                             mail_op);
440         g_object_unref (mail_op);
441
442         return FALSE;
443 }
444
445 static gpointer
446 update_account_thread (gpointer thr_user_data)
447 {
448         UpdateAccountInfo *info;
449         TnyList *all_folders = NULL;
450         TnyIterator *iter = NULL;
451         TnyFolderStoreQuery *query = NULL;
452         ModestMailOperationPrivate *priv;
453         ModestTnySendQueue *send_queue;
454         gint timeout;
455
456         info = (UpdateAccountInfo *) thr_user_data;
457         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
458
459         /* Get all the folders We can do it synchronously because
460            we're already running in a different thread than the UI */
461         all_folders = tny_simple_list_new ();
462         query = tny_folder_store_query_new ();
463         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
464         tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
465                                       all_folders,
466                                       query,
467                                       &(priv->error));
468         if (priv->error) {
469                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
470                 goto out;
471         }
472
473         iter = tny_list_create_iterator (all_folders);
474         while (!tny_iterator_is_done (iter)) {
475                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
476
477                 recurse_folders (folder, query, all_folders);
478                 tny_iterator_next (iter);
479         }
480         g_object_unref (G_OBJECT (iter));
481
482         /* Update status and notify. We need to call the notification
483            with a source functopm in order to call it from the main
484            loop. We need that in order not to get into trouble with
485            Gtk+. We use a timeout in order to provide more status
486            information, because the sync tinymail call does not
487            provide it for the moment */
488         timeout = g_timeout_add (250, notify_update_account_observers, info->mail_op);
489
490         /* Refresh folders */
491         iter = tny_list_create_iterator (all_folders);
492         while (!tny_iterator_is_done (iter) && !priv->error) {
493
494                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
495
496                 /* Refresh the folder */
497                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
498
499                 /* TODO: Apply retrieval types */
500
501                 /* TODO: apply per-message size limits */
502
503                 /* TODO: apply message count limit */
504
505                 if (priv->error) {
506                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
507                 }
508
509                 g_object_unref (G_OBJECT (folder));
510                 tny_iterator_next (iter);
511         }
512         g_object_unref (G_OBJECT (iter));
513         g_source_remove (timeout);
514
515         /* Perform send */
516         priv->id = MODEST_MAIL_OPERATION_ID_SEND;
517
518         send_queue = modest_tny_send_queue_new (TNY_CAMEL_TRANSPORT_ACCOUNT(info->transport_account));
519
520         timeout = g_timeout_add (250, notify_update_account_observers, info->mail_op);
521         modest_tny_send_queue_flush (send_queue);
522         g_source_remove (timeout);
523
524         g_object_unref (G_OBJECT(send_queue));
525         
526         /* Check if the operation was a success */
527         if (!priv->error) {
528                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
529
530                 /* Update the last updated key */
531                 modest_account_mgr_set_int (modest_runtime_get_account_mgr (), 
532                                             tny_account_get_id (TNY_ACCOUNT (info->account)), 
533                                             MODEST_ACCOUNT_LAST_UPDATED, 
534                                             time(NULL), 
535                                             TRUE);
536         }
537
538  out:
539         /* Notify the queue. Note that the info could be freed before
540            this idle happens, but the mail operation will be still
541            alive */
542         g_idle_add (notify_update_account_queue, info->mail_op);
543
544         /* Frees */
545         g_object_unref (query);
546         g_object_unref (all_folders);
547         g_object_unref (info->account);
548         g_object_unref (info->transport_account);
549         g_slice_free (UpdateAccountInfo, info);
550
551         return NULL;
552 }
553
554 gboolean
555 modest_mail_operation_update_account (ModestMailOperation *self,
556                                       const gchar *account_name)
557 {
558         GThread *thread;
559         UpdateAccountInfo *info;
560         ModestMailOperationPrivate *priv;
561         TnyStoreAccount *modest_account;
562         TnyTransportAccount *transport_account;
563
564         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
565         g_return_val_if_fail (account_name, FALSE);
566
567         /* Init mail operation. Set total and done to 0, and do not
568            update them, this way the progress objects will know that
569            we have no clue about the number of the objects */
570         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
571         priv->total = 0;
572         priv->done  = 0;
573         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
574         
575         /* Get the Modest account */
576         modest_account = (TnyStoreAccount *)
577                 modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store (),
578                                                                      account_name,
579                                                                      TNY_ACCOUNT_TYPE_STORE);
580
581         if (!modest_account) {
582                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
583                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
584                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
585                              "cannot get tny store account for %s\n", account_name);
586                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), 
587                                                     self);
588                 return FALSE;
589         }
590
591         /* Get the transport account, we can not do it in the thread
592            due to some problems with dbus */
593         transport_account = (TnyTransportAccount *)
594                 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
595                                                                                     account_name);
596         if (!transport_account) {
597                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
598                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
599                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
600                              "cannot get tny transport account for %s\n", account_name);
601                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), 
602                                                     self);
603                 return FALSE;
604         }
605
606         /* Create the helper object */
607         info = g_slice_new (UpdateAccountInfo);
608         info->mail_op = self;
609         info->account = modest_account;
610         info->transport_account = transport_account;
611
612         thread = g_thread_create (update_account_thread, info, FALSE, NULL);
613
614         return TRUE;
615 }
616
617 ModestMailOperationStatus
618 modest_mail_operation_get_status (ModestMailOperation *self)
619 {
620         ModestMailOperationPrivate *priv;
621
622         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
623         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
624                               MODEST_MAIL_OPERATION_STATUS_INVALID);
625
626         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
627         return priv->status;
628 }
629
630 const GError *
631 modest_mail_operation_get_error (ModestMailOperation *self)
632 {
633         ModestMailOperationPrivate *priv;
634
635         g_return_val_if_fail (self, NULL);
636         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
637
638         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
639         return priv->error;
640 }
641
642 gboolean 
643 modest_mail_operation_cancel (ModestMailOperation *self)
644 {
645         ModestMailOperationPrivate *priv;
646
647         if (!MODEST_IS_MAIL_OPERATION (self)) {
648                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
649                 return FALSE;
650         }
651
652         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
653
654         /* TODO: Tinymail does not support cancel operation  */
655         
656         /* Set new status */
657         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
658
659         /* Notify the queue */
660         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
661
662         return TRUE;
663 }
664
665 guint 
666 modest_mail_operation_get_task_done (ModestMailOperation *self)
667 {
668         ModestMailOperationPrivate *priv;
669
670         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
671
672         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
673         return priv->done;
674 }
675
676 guint 
677 modest_mail_operation_get_task_total (ModestMailOperation *self)
678 {
679         ModestMailOperationPrivate *priv;
680
681         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
682
683         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
684         return priv->total;
685 }
686
687 gboolean
688 modest_mail_operation_is_finished (ModestMailOperation *self)
689 {
690         ModestMailOperationPrivate *priv;
691         gboolean retval = FALSE;
692
693         if (!MODEST_IS_MAIL_OPERATION (self)) {
694                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
695                 return retval;
696         }
697
698         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
699
700         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
701             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
702             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
703             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
704                 retval = TRUE;
705         } else {
706                 retval = FALSE;
707         }
708
709         return retval;
710 }
711
712 /* ******************************************************************* */
713 /* ************************** STORE  ACTIONS ************************* */
714 /* ******************************************************************* */
715
716
717 TnyFolder *
718 modest_mail_operation_create_folder (ModestMailOperation *self,
719                                      TnyFolderStore *parent,
720                                      const gchar *name)
721 {
722         ModestTnyFolderRules rules;
723         ModestMailOperationPrivate *priv;
724         TnyFolder *new_folder = NULL;
725         gboolean can_create = FALSE;
726
727         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
728         g_return_val_if_fail (name, NULL);
729         
730         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
731
732         /* Check parent */
733         if (!TNY_IS_FOLDER (parent)) {
734                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
735                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
736                              _("mail_in_ui_folder_create_error"));
737         } else {
738                 /* Check folder rules */
739                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
740                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)
741                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
742                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
743                                      _("mail_in_ui_folder_create_error"));
744                 else
745                         can_create = TRUE;              
746         }
747
748         if (can_create) {
749                 /* Create the folder */
750                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
751                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
752         }
753
754         /* Notify the queue */
755         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
756
757         return new_folder;
758 }
759
760 void
761 modest_mail_operation_remove_folder (ModestMailOperation *self,
762                                      TnyFolder           *folder,
763                                      gboolean             remove_to_trash)
764 {
765         TnyAccount *account;
766         ModestMailOperationPrivate *priv;
767         ModestTnyFolderRules rules;
768
769         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
770         g_return_if_fail (TNY_IS_FOLDER (folder));
771         
772         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
773         
774         /* Check folder rules */
775         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
776         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
777                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
778                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
779                              _("mail_in_ui_folder_delete_error"));
780                 goto end;
781         }
782
783         /* Get the account */
784         account = tny_folder_get_account (folder);
785
786         /* Delete folder or move to trash */
787         if (remove_to_trash) {
788                 TnyFolder *trash_folder = NULL;
789 /*              TnyFolder *trash_folder, *new_folder; */
790                 trash_folder = modest_tny_account_get_special_folder (account,
791                                                                       TNY_FOLDER_TYPE_TRASH);
792                 /* TODO: error_handling */
793                  modest_mail_operation_xfer_folder (self, folder,
794                                                     TNY_FOLDER_STORE (trash_folder), TRUE);
795 /*              new_folder = modest_mail_operation_xfer_folder (self, folder,  */
796 /*                                                              TNY_FOLDER_STORE (trash_folder), TRUE); */
797 /*              g_object_unref (G_OBJECT (new_folder)); */
798         } else {
799                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
800
801                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
802                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
803
804                 if (parent)
805                         g_object_unref (G_OBJECT (parent));
806         }
807         g_object_unref (G_OBJECT (account));
808
809  end:
810         /* Notify the queue */
811         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
812 }
813
814 void
815 modest_mail_operation_rename_folder (ModestMailOperation *self,
816                                      TnyFolder *folder,
817                                      const gchar *name)
818 {
819         ModestMailOperationPrivate *priv;
820         ModestTnyFolderRules rules;
821
822         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
823         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
824         g_return_if_fail (name);
825         
826         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
827
828         /* Check folder rules */
829         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
830         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
831                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
832                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
833                              _("FIXME: unable to rename"));
834         } else {
835                 /* Rename. Camel handles folder subscription/unsubscription */
836
837                 TnyFolderStore *into;
838                 TnyFolder *nfol;
839
840                 into = tny_folder_get_folder_store (folder);
841                 nfol = tny_folder_copy (folder, into, name, TRUE, &(priv->error));
842                 if (into)
843                         g_object_unref (into);
844                 if (nfol)
845                         g_object_unref (nfol);
846
847                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
848         }
849
850         /* Notify the queue */
851         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
852  }
853
854 static void
855 transfer_folder_status_cb (GObject *obj,
856                            TnyStatus *status,  
857                            gpointer user_data)
858 {
859         XFerMsgAsyncHelper *helper = NULL;
860         ModestMailOperation *self;
861         ModestMailOperationPrivate *priv;
862
863         g_return_if_fail (status != NULL);
864         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
865
866         helper = (XFerMsgAsyncHelper *) user_data;
867         g_return_if_fail (helper != NULL);       
868
869         /* Temporary FIX: useful when tinymail send us status
870            information *after* calling the function callback */
871         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
872                 return;
873
874         self = helper->mail_op;
875         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
876
877         if ((status->position == 1) && (status->of_total == 100))
878                 return;
879
880         priv->done = status->position;
881         priv->total = status->of_total;
882
883
884         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
885 }
886
887
888 static void
889 transfer_folder_cb (TnyFolder *folder, TnyFolderStore *into, gboolean cancelled, TnyFolder *new_folder, GError **err, gpointer user_data)
890 {
891         XFerFolderAsyncHelper *helper = NULL;
892         ModestMailOperation *self = NULL;
893         ModestMailOperationPrivate *priv = NULL;
894
895         helper = (XFerFolderAsyncHelper *) user_data;
896         self = helper->mail_op;
897
898         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
899
900         if (*err) {
901                 priv->error = g_error_copy (*err);
902                 priv->done = 0;
903                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
904         } else if (cancelled) {
905                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
906                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
907                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
908                              _("Error trying to refresh the contents of %s"),
909                              tny_folder_get_name (folder));
910         } else {
911                 priv->done = 1;
912                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
913         }
914
915         /* Free */
916         g_slice_free   (XFerFolderAsyncHelper, helper);
917         g_object_unref (folder);
918         g_object_unref (into);
919         g_object_unref (new_folder);
920
921         /* Notify the queue */
922         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
923 }
924
925 TnyFolder *
926 modest_mail_operation_xfer_folder (ModestMailOperation *self,
927                                    TnyFolder *folder,
928                                    TnyFolderStore *parent,
929                                    gboolean delete_original)
930 {
931         ModestMailOperationPrivate *priv;
932         TnyFolder *new_folder = NULL;
933         ModestTnyFolderRules rules;
934
935         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
936         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
937         g_return_val_if_fail (TNY_IS_FOLDER (folder), NULL);
938
939         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
940
941         /* The moveable restriction is applied also to copy operation */
942         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
943         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
944                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
945                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
946                              _("FIXME: unable to rename"));
947         } else {
948                 /* Move/Copy folder */
949                 new_folder = tny_folder_copy (folder,
950                                               parent,
951                                               tny_folder_get_name (folder),
952                                               delete_original,
953                                               &(priv->error));
954         }
955
956         /* Notify the queue */
957         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
958
959         return new_folder;
960 }
961
962 void
963 modest_mail_operation_xfer_folder_async (ModestMailOperation *self,
964                                          TnyFolder *folder,
965                                          TnyFolderStore *parent,
966                                          gboolean delete_original)
967 {
968         XFerFolderAsyncHelper *helper = NULL;
969         ModestMailOperationPrivate *priv = NULL;
970         ModestTnyFolderRules rules;
971
972         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
973         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
974         g_return_if_fail (TNY_IS_FOLDER (folder));
975
976         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
977
978         /* Pick references for async calls */
979         g_object_ref (folder);
980         g_object_ref (parent);
981
982         /* The moveable restriction is applied also to copy operation */
983         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
984         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
985                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
986                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
987                              _("FIXME: unable to rename"));
988         } else {
989                 helper = g_slice_new0 (XFerFolderAsyncHelper);
990                 helper->mail_op = self;
991
992                 /* Move/Copy folder */          
993                 tny_folder_copy_async (folder,
994                                        parent,
995                                        tny_folder_get_name (folder),
996                                        delete_original,
997                                        transfer_folder_cb,
998                                        transfer_folder_status_cb,
999                                        helper);
1000         }
1001 }
1002
1003
1004 /* ******************************************************************* */
1005 /* **************************  MSG  ACTIONS  ************************* */
1006 /* ******************************************************************* */
1007
1008 void modest_mail_operation_get_msg (ModestMailOperation *self,
1009                                     TnyHeader *header,
1010                                     GetMsgAsynUserCallback user_callback,
1011                                     gpointer user_data)
1012 {
1013         GetMsgAsyncHelper *helper = NULL;
1014         TnyFolder *folder;
1015         ModestMailOperationPrivate *priv;
1016         
1017         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1018         g_return_if_fail (TNY_IS_HEADER (header));
1019         
1020         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1021         folder = tny_header_get_folder (header);
1022
1023         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1024
1025         /* Get message from folder */
1026         if (folder) {
1027                 helper = g_slice_new0 (GetMsgAsyncHelper);
1028                 helper->mail_op = self;
1029                 helper->user_callback = user_callback;
1030                 helper->pending_ops = 1;
1031                 helper->user_data = user_data;
1032
1033                 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1034
1035                 g_object_unref (G_OBJECT (folder));
1036         } else {
1037                 /* Set status failed and set an error */
1038                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1039                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1040                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1041                              _("Error trying to get a message. No folder found for header"));
1042         }
1043 }
1044
1045 static void
1046 get_msg_cb (TnyFolder *folder, 
1047             gboolean cancelled, 
1048             TnyMsg *msg, 
1049             GError **error, 
1050             gpointer user_data)
1051 {
1052         GetMsgAsyncHelper *helper = NULL;
1053         ModestMailOperation *self = NULL;
1054         ModestMailOperationPrivate *priv = NULL;
1055
1056         helper = (GetMsgAsyncHelper *) user_data;
1057         g_return_if_fail (helper != NULL);       
1058         self = helper->mail_op;
1059         g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1060         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1061         
1062         helper->pending_ops--;
1063
1064         /* Check errors and cancel */
1065         if (*error) {
1066                 priv->error = g_error_copy (*error);
1067                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1068                 goto out;
1069         }
1070         if (cancelled) {
1071                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1072                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1073                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1074                              _("Error trying to refresh the contents of %s"),
1075                              tny_folder_get_name (folder));
1076                 goto out;
1077         }
1078
1079         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1080
1081         /* If user defined callback function was defined, call it */
1082         if (helper->user_callback) {
1083                 helper->user_callback (priv->source, msg, helper->user_data);
1084         }
1085
1086         /* Free */
1087  out:
1088         if (helper->pending_ops == 0) {
1089                 g_slice_free (GetMsgAsyncHelper, helper);
1090                 
1091                 /* Notify the queue */
1092                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);  
1093         }
1094 }
1095
1096 static void     
1097 get_msg_status_cb (GObject *obj,
1098                    TnyStatus *status,  
1099                    gpointer user_data)
1100 {
1101         GetMsgAsyncHelper *helper = NULL;
1102         ModestMailOperation *self;
1103         ModestMailOperationPrivate *priv;
1104
1105         g_return_if_fail (status != NULL);
1106         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1107
1108         helper = (GetMsgAsyncHelper *) user_data;
1109         g_return_if_fail (helper != NULL);       
1110
1111         /* Temporary FIX: useful when tinymail send us status
1112            information *after* calling the function callback */
1113         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1114                 return;
1115
1116         self = helper->mail_op;
1117         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1118
1119         if ((status->position == 1) && (status->of_total == 100))
1120                 return;
1121
1122         priv->done = 1;
1123         priv->total = 1;
1124
1125         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1126 }
1127
1128 /****************************************************/
1129 typedef struct {
1130         ModestMailOperation *mail_op;
1131         TnyList *headers;
1132         GetMsgAsynUserCallback user_callback;
1133         gpointer user_data;
1134         GDestroyNotify notify;
1135 } GetFullMsgsInfo;
1136
1137 /* 
1138  * Used by get_msgs_full_thread to emit the "progress-changed" signal
1139  * from the main loop. We call it inside an idle call to achieve that
1140  */
1141 static gboolean
1142 notify_get_msgs_full_observers (gpointer data)
1143 {
1144         ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1145
1146         g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1147
1148         g_object_unref (mail_op);
1149
1150         return FALSE;
1151 }
1152
1153 typedef struct {
1154         GetMsgAsynUserCallback user_callback;
1155         TnyMsg *msg;
1156         gpointer user_data;
1157         GObject *source;
1158 } NotifyGetMsgsInfo;
1159
1160
1161 /* 
1162  * Used by get_msgs_full_thread to call the user_callback for each
1163  * message that has been read
1164  */
1165 static gboolean
1166 notify_get_msgs_full (gpointer data)
1167 {
1168         NotifyGetMsgsInfo *info;
1169
1170         info = (NotifyGetMsgsInfo *) data;      
1171
1172         /* Call the user callback */
1173         info->user_callback (info->source, info->msg, info->user_data);
1174
1175         g_slice_free (NotifyGetMsgsInfo, info);
1176
1177         return FALSE;
1178 }
1179
1180 /* 
1181  * Used by get_msgs_full_thread to free al the thread resources and to
1182  * call the destroy function for the passed user_data
1183  */
1184 static gboolean
1185 get_msgs_full_destroyer (gpointer data)
1186 {
1187         GetFullMsgsInfo *info;
1188
1189         info = (GetFullMsgsInfo *) data;
1190
1191         if (info->notify)
1192                 info->notify (info->user_data);
1193
1194         /* free */
1195         g_object_unref (info->headers);
1196         g_slice_free (GetFullMsgsInfo, info);
1197
1198         return FALSE;
1199 }
1200
1201 static gpointer
1202 get_msgs_full_thread (gpointer thr_user_data)
1203 {
1204         GetFullMsgsInfo *info;
1205         ModestMailOperationPrivate *priv = NULL;
1206         TnyIterator *iter = NULL;
1207         
1208         info = (GetFullMsgsInfo *) thr_user_data;       
1209         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1210
1211         iter = tny_list_create_iterator (info->headers);
1212         while (!tny_iterator_is_done (iter)) { 
1213                 TnyHeader *header;
1214                 TnyFolder *folder;
1215                 
1216                 header = TNY_HEADER (tny_iterator_get_current (iter));
1217                 folder = tny_header_get_folder (header);
1218                                 
1219                 /* Get message from folder */
1220                 if (folder) {
1221                         TnyMsg *msg;
1222                         /* The callback will call it per each header */
1223                         msg = tny_folder_get_msg (folder, header, &(priv->error));
1224
1225                         if (msg) {
1226                                 priv->done++;
1227
1228                                 /* notify progress */
1229                                 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1230                                                  notify_get_msgs_full_observers, 
1231                                                  g_object_ref (info->mail_op), NULL);
1232
1233                                 /* The callback is the responsible for
1234                                    freeing the message */
1235                                 if (info->user_callback) {
1236                                         NotifyGetMsgsInfo *info_notify;
1237                                         info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1238                                         info_notify->user_callback = info->user_callback;
1239                                         info_notify->source = priv->source;
1240                                         info_notify->msg = msg;
1241                                         info_notify->user_data = info->user_data;
1242                                         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1243                                                          notify_get_msgs_full, 
1244                                                          info_notify, NULL);
1245                                 } else {
1246                                         g_object_unref (msg);
1247                                 }
1248                         }
1249                 } else {
1250                         /* Set status failed and set an error */
1251                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1252                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1253                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1254                                      "Error trying to get a message. No folder found for header");
1255                 }
1256                 g_object_unref (header);                
1257                 tny_iterator_next (iter);
1258         }
1259
1260         /* Notify the queue */
1261         g_idle_add (notify_update_account_queue, info->mail_op);
1262
1263         /* Free thread resources. Will be called after all previous idles */
1264         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1265
1266         return NULL;
1267 }
1268
1269 void 
1270 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1271                                      TnyList *header_list, 
1272                                      GetMsgAsynUserCallback user_callback,
1273                                      gpointer user_data,
1274                                      GDestroyNotify notify)
1275 {
1276         GThread *thread;
1277         ModestMailOperationPrivate *priv = NULL;
1278         GetFullMsgsInfo *info = NULL;
1279         gboolean size_ok = TRUE;
1280         gint max_size;
1281         GError *error = NULL;
1282         const gint KB = 1024;
1283         
1284         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1285         
1286         /* Init mail operation */
1287         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1288         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1289         priv->done = 0;
1290         priv->total = tny_list_get_length(header_list);
1291
1292         /* Get msg size limit */
1293         max_size  = modest_conf_get_int (modest_runtime_get_conf (), 
1294                                          MODEST_CONF_MSG_SIZE_LIMIT, 
1295                                          &error);
1296         if (error) {
1297                 g_clear_error (&error);
1298                 max_size = G_MAXINT;
1299         } else {
1300                 max_size = max_size * KB;
1301         }
1302
1303         /* Check message size limits. If there is only one message
1304            always retrieve it */
1305         if (tny_list_get_length (header_list) > 1) {
1306                 TnyIterator *iter;
1307
1308                 iter = tny_list_create_iterator (header_list);
1309                 while (!tny_iterator_is_done (iter) && size_ok) {
1310                         TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1311                         if (tny_header_get_message_size (header) >= max_size)
1312                                 size_ok = FALSE;
1313                         g_object_unref (header);
1314                         tny_iterator_next (iter);
1315                 }
1316                 g_object_unref (iter);
1317         }
1318
1319         if (size_ok) {
1320                 /* Create the info */
1321                 info = g_slice_new0 (GetFullMsgsInfo);
1322                 info->mail_op = self;
1323                 info->user_callback = user_callback;
1324                 info->user_data = user_data;
1325                 info->headers = g_object_ref (header_list);
1326                 info->notify = notify;
1327
1328                 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1329         } else {
1330                 /* FIXME: the error msg is different for pop */
1331                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1332                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
1333                              _("emev_ni_ui_imap_msg_sizelimit_error"));
1334                 /* Remove from queue and free resources */
1335                 modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1336                 if (notify)
1337                         notify (user_data);
1338         }
1339 }
1340
1341
1342 void 
1343 modest_mail_operation_remove_msg (ModestMailOperation *self,
1344                                   TnyHeader *header,
1345                                   gboolean remove_to_trash)
1346 {
1347         TnyFolder *folder;
1348         ModestMailOperationPrivate *priv;
1349
1350         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1351         g_return_if_fail (TNY_IS_HEADER (header));
1352
1353         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1354         folder = tny_header_get_folder (header);
1355
1356         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1357
1358         /* Delete or move to trash */
1359         if (remove_to_trash) {
1360                 TnyFolder *trash_folder;
1361                 TnyStoreAccount *store_account;
1362
1363                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
1364                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1365                                                                       TNY_FOLDER_TYPE_TRASH);
1366                 if (trash_folder) {
1367                         TnyList *headers;
1368
1369                         /* Create list */
1370                         headers = tny_simple_list_new ();
1371                         tny_list_append (headers, G_OBJECT (header));
1372                         g_object_unref (header);
1373
1374                         /* Move to trash */
1375                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1376                         g_object_unref (headers);
1377 /*                      g_object_unref (trash_folder); */
1378                 } else {
1379                         ModestMailOperationPrivate *priv;
1380
1381                         /* Set status failed and set an error */
1382                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1383                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1384                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1385                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1386                                      _("Error trying to delete a message. Trash folder not found"));
1387                 }
1388
1389                 g_object_unref (G_OBJECT (store_account));
1390         } else {
1391                 tny_folder_remove_msg (folder, header, &(priv->error));
1392                 if (!priv->error)
1393                         tny_folder_sync(folder, TRUE, &(priv->error));
1394         }
1395
1396         /* Set status */
1397         if (!priv->error)
1398                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1399         else
1400                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1401
1402         /* Free */
1403         g_object_unref (G_OBJECT (folder));
1404
1405         /* Notify the queue */
1406         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1407 }
1408
1409 static void
1410 transfer_msgs_status_cb (GObject *obj,
1411                          TnyStatus *status,  
1412                          gpointer user_data)
1413 {
1414         XFerMsgAsyncHelper *helper = NULL;
1415         ModestMailOperation *self;
1416         ModestMailOperationPrivate *priv;
1417
1418         g_return_if_fail (status != NULL);
1419         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1420
1421         helper = (XFerMsgAsyncHelper *) user_data;
1422         g_return_if_fail (helper != NULL);       
1423
1424         /* Temporary FIX: useful when tinymail send us status
1425            information *after* calling the function callback */
1426         if (!MODEST_IS_MAIL_OPERATION (helper->mail_op))
1427                 return;
1428
1429         self = helper->mail_op;
1430         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1431
1432         if ((status->position == 1) && (status->of_total == 100))
1433                 return;
1434
1435         priv->done = status->position;
1436         priv->total = status->of_total;
1437
1438         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1439 }
1440
1441
1442 static void
1443 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1444 {
1445         XFerMsgAsyncHelper *helper;
1446         ModestMailOperation *self;
1447         ModestMailOperationPrivate *priv;
1448
1449         helper = (XFerMsgAsyncHelper *) user_data;
1450         self = helper->mail_op;
1451
1452         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1453
1454         if (*err) {
1455                 priv->error = g_error_copy (*err);
1456                 priv->done = 0;
1457                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;     
1458         } else if (cancelled) {
1459                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1460                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1461                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1462                              _("Error trying to refresh the contents of %s"),
1463                              tny_folder_get_name (folder));
1464         } else {
1465                 priv->done = 1;
1466                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1467         }
1468
1469         /* If user defined callback function was defined, call it */
1470         if (helper->user_callback) {
1471                 helper->user_callback (priv->source, helper->user_data);
1472         }
1473
1474         /* Free */
1475         g_object_unref (helper->headers);
1476         g_object_unref (helper->dest_folder);
1477         g_object_unref (helper->mail_op);
1478         g_slice_free   (XFerMsgAsyncHelper, helper);
1479         g_object_unref (folder);
1480
1481         /* Notify the queue */
1482         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1483 }
1484
1485 void
1486 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1487                                  TnyList *headers, 
1488                                  TnyFolder *folder, 
1489                                  gboolean delete_original,
1490                                  XferMsgsAsynUserCallback user_callback,
1491                                  gpointer user_data)
1492 {
1493         ModestMailOperationPrivate *priv;
1494         TnyIterator *iter;
1495         TnyFolder *src_folder;
1496         XFerMsgAsyncHelper *helper;
1497         TnyHeader *header;
1498
1499         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1500         g_return_if_fail (TNY_IS_LIST (headers));
1501         g_return_if_fail (TNY_IS_FOLDER (folder));
1502
1503         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1504         priv->total = 1;
1505         priv->done = 0;
1506         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1507
1508         /* Create the helper */
1509         helper = g_slice_new0 (XFerMsgAsyncHelper);
1510         helper->mail_op = g_object_ref(self);
1511         helper->dest_folder = g_object_ref(folder);
1512         helper->headers = g_object_ref(headers);
1513         helper->user_callback = user_callback;
1514         helper->user_data = user_data;
1515
1516         /* Get source folder */
1517         iter = tny_list_create_iterator (headers);
1518         header = TNY_HEADER (tny_iterator_get_current (iter));
1519         src_folder = tny_header_get_folder (header);
1520         g_object_unref (header);
1521         g_object_unref (iter);
1522
1523         /* Transfer messages */
1524         tny_folder_transfer_msgs_async (src_folder, 
1525                                         headers, 
1526                                         folder, 
1527                                         delete_original, 
1528                                         transfer_msgs_cb, 
1529                                         transfer_msgs_status_cb,
1530                                         helper);
1531 }
1532
1533
1534 static void
1535 on_refresh_folder (TnyFolder   *folder, 
1536                    gboolean     cancelled, 
1537                    GError     **error,
1538                    gpointer     user_data)
1539 {
1540         ModestMailOperation *self;
1541         ModestMailOperationPrivate *priv;
1542
1543         self = MODEST_MAIL_OPERATION (user_data);
1544         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1545
1546         if (*error) {
1547                 priv->error = g_error_copy (*error);
1548                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1549                 goto out;
1550         }
1551
1552         if (cancelled) {
1553                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1554                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1555                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1556                              _("Error trying to refresh the contents of %s"),
1557                              tny_folder_get_name (folder));
1558                 goto out;
1559         }
1560
1561         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1562
1563  out:
1564         /* Free */
1565         g_object_unref (folder);
1566
1567         /* Notify the queue */
1568         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1569 }
1570
1571 static void
1572 on_refresh_folder_status_update (GObject *obj,
1573                                  TnyStatus *status,
1574                                  gpointer user_data)
1575 {
1576         ModestMailOperation *self;
1577         ModestMailOperationPrivate *priv;
1578
1579         g_return_if_fail (status != NULL);
1580         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1581
1582         /* Temporary FIX: useful when tinymail send us status
1583            information *after* calling the function callback */
1584         if (!MODEST_IS_MAIL_OPERATION (user_data))
1585                 return;
1586
1587         self = MODEST_MAIL_OPERATION (user_data);
1588         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1589
1590         priv->done = status->position;
1591         priv->total = status->of_total;
1592
1593         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1594 }
1595
1596 void 
1597 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1598                                        TnyFolder *folder)
1599 {
1600         ModestMailOperationPrivate *priv;
1601
1602         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1603
1604         /* Pick a reference */
1605         g_object_ref (folder);
1606
1607         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1608
1609         /* Refresh the folder. TODO: tinymail could issue a status
1610            updates before the callback call then this could happen. We
1611            must review the design */
1612         tny_folder_refresh_async (folder,
1613                                   on_refresh_folder,
1614                                   on_refresh_folder_status_update,
1615                                   self);
1616 }
1617
1618 void
1619 _modest_mail_operation_notify_end (ModestMailOperation *self)
1620 {
1621         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1622 }