* src/modest-ui-actions.[ch]:
[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_folders_cb    (TnyFolderStore *self, 
60                                       TnyList *list, 
61                                       GError **err, 
62                                       gpointer user_data);
63
64 enum _ModestMailOperationSignals 
65 {
66         PROGRESS_CHANGED_SIGNAL,
67
68         NUM_SIGNALS
69 };
70
71 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
72 struct _ModestMailOperationPrivate {
73         guint                      done;
74         guint                      total;
75         ModestMailOperationStatus  status;      
76         ModestMailOperationId      id;          
77         GError                    *error;
78 };
79
80 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
81                                                    MODEST_TYPE_MAIL_OPERATION, \
82                                                    ModestMailOperationPrivate))
83
84 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
85                                                    priv->status = new_status;\
86                                                }
87
88 typedef struct _RefreshFolderAsyncHelper
89 {
90         ModestMailOperation *mail_op;
91         TnyIterator *iter;
92         guint failed;
93         guint canceled;
94
95 } RefreshFolderAsyncHelper;
96
97 typedef struct _XFerMsgAsyncHelper
98 {
99         ModestMailOperation *mail_op;
100         TnyList *headers;
101         TnyFolder *dest_folder;
102
103 } XFerMsgAsyncHelper;
104
105
106 /* globals */
107 static GObjectClass *parent_class = NULL;
108
109 static guint signals[NUM_SIGNALS] = {0};
110
111 GType
112 modest_mail_operation_get_type (void)
113 {
114         static GType my_type = 0;
115         if (!my_type) {
116                 static const GTypeInfo my_info = {
117                         sizeof(ModestMailOperationClass),
118                         NULL,           /* base init */
119                         NULL,           /* base finalize */
120                         (GClassInitFunc) modest_mail_operation_class_init,
121                         NULL,           /* class finalize */
122                         NULL,           /* class data */
123                         sizeof(ModestMailOperation),
124                         1,              /* n_preallocs */
125                         (GInstanceInitFunc) modest_mail_operation_init,
126                         NULL
127                 };
128                 my_type = g_type_register_static (G_TYPE_OBJECT,
129                                                   "ModestMailOperation",
130                                                   &my_info, 0);
131         }
132         return my_type;
133 }
134
135 static void
136 modest_mail_operation_class_init (ModestMailOperationClass *klass)
137 {
138         GObjectClass *gobject_class;
139         gobject_class = (GObjectClass*) klass;
140
141         parent_class            = g_type_class_peek_parent (klass);
142         gobject_class->finalize = modest_mail_operation_finalize;
143
144         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
145
146         /**
147          * ModestMailOperation::progress-changed
148          * @self: the #MailOperation that emits the signal
149          * @user_data: user data set when the signal handler was connected
150          *
151          * Emitted when the progress of a mail operation changes
152          */
153         signals[PROGRESS_CHANGED_SIGNAL] = 
154                 g_signal_new ("progress-changed",
155                               G_TYPE_FROM_CLASS (gobject_class),
156                               G_SIGNAL_RUN_FIRST,
157                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
158                               NULL, NULL,
159                               g_cclosure_marshal_VOID__VOID,
160                               G_TYPE_NONE, 0);
161 }
162
163 static void
164 modest_mail_operation_init (ModestMailOperation *obj)
165 {
166         ModestMailOperationPrivate *priv;
167
168         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
169
170         priv->status   = MODEST_MAIL_OPERATION_STATUS_INVALID;
171         priv->id       = MODEST_MAIL_OPERATION_ID_UNKNOWN;
172         priv->error    = NULL;
173         priv->done     = 0;
174         priv->total    = 0;
175 }
176
177 static void
178 modest_mail_operation_finalize (GObject *obj)
179 {
180         ModestMailOperationPrivate *priv;
181
182         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
183
184         if (priv->error) {
185                 g_error_free (priv->error);
186                 priv->error = NULL;
187         }
188
189         G_OBJECT_CLASS(parent_class)->finalize (obj);
190 }
191
192 ModestMailOperation*
193 modest_mail_operation_new (ModestMailOperationId id)
194 {
195         ModestMailOperation *obj;
196         ModestMailOperationPrivate *priv;
197
198
199         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
200         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
201
202         priv->id = id;
203
204         return obj;
205 }
206
207
208
209 ModestMailOperationId
210 modest_mail_operation_get_id (ModestMailOperation *self)
211 {
212         ModestMailOperationPrivate *priv;
213
214         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
215         
216         return priv->id;
217 }
218
219 void
220 modest_mail_operation_send_mail (ModestMailOperation *self,
221                                  TnyTransportAccount *transport_account,
222                                  TnyMsg* msg)
223 {
224         TnySendQueue *send_queue;
225         
226         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
227         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
228         g_return_if_fail (TNY_IS_MSG (msg));
229         
230         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
231         if (!TNY_IS_SEND_QUEUE(send_queue))
232                 g_printerr ("modest: could not find send queue for account\n");
233         else {
234                 GError *err = NULL;
235                 tny_send_queue_add (send_queue, msg, &err);
236                 if (err) {
237                         g_printerr ("modest: error adding msg to send queue: %s\n",
238                                     err->message);
239                         g_error_free (err);
240                 } else
241                         g_message ("modest: message added to send queue");
242         }
243
244         /* Notify the queue */
245         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
246 }
247
248 void
249 modest_mail_operation_send_new_mail (ModestMailOperation *self,
250                                      TnyTransportAccount *transport_account,
251                                      const gchar *from,  const gchar *to,
252                                      const gchar *cc,  const gchar *bcc,
253                                      const gchar *subject, const gchar *plain_body,
254                                      const gchar *html_body,
255                                      const GList *attachments_list,
256                                      TnyHeaderFlags priority_flags)
257 {
258         TnyMsg *new_msg;
259         ModestMailOperationPrivate *priv = NULL;
260         /* GList *node = NULL; */
261
262         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
263         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
264
265         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
266
267         /* Check parametters */
268         if (to == NULL) {
269                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
270                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
271                              _("Error trying to send a mail. You need to set at least one recipient"));
272                 return;
273         }
274
275         if (html_body == NULL) {
276                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
277         } else {
278                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
279         }
280         if (!new_msg) {
281                 g_printerr ("modest: failed to create a new msg\n");
282                 return;
283         }
284
285         /* TODO: add priority handling. It's received in the priority_flags operator, and
286            it should have effect in the sending operation */
287
288         /* Call mail operation */
289         modest_mail_operation_send_mail (self, transport_account, new_msg);
290
291         /* Free */
292         g_object_unref (G_OBJECT (new_msg));
293 }
294
295 void
296 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
297                                       TnyTransportAccount *transport_account,
298                                       const gchar *from,  const gchar *to,
299                                       const gchar *cc,  const gchar *bcc,
300                                       const gchar *subject, const gchar *plain_body,
301                                       const gchar *html_body,
302                                       const GList *attachments_list,
303                                       TnyHeaderFlags priority_flags)
304 {
305         TnyMsg *msg = NULL;
306         TnyFolder *folder = NULL;
307         ModestMailOperationPrivate *priv = NULL;
308         GError *err = NULL;
309
310         /* GList *node = NULL; */
311
312         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
313         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
314
315         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
316
317         if (html_body == NULL) {
318                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
319         } else {
320                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
321         }
322         if (!msg) {
323                 g_printerr ("modest: failed to create a new msg\n");
324                 goto cleanup;
325         }
326
327         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
328         if (!folder) {
329                 g_printerr ("modest: failed to find Drafts folder\n");
330                 goto cleanup;
331         }
332         
333         tny_folder_add_msg (folder, msg, &err);
334         if (err) {
335                 g_printerr ("modest: error adding msg to Drafts folder: %s",
336                             err->message);
337                 g_error_free (err);
338                 goto cleanup;
339         }
340
341         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
342
343         /* Free */
344 cleanup:
345         if (msg)
346                 g_object_unref (G_OBJECT(msg));
347         if (folder)
348                 g_object_unref (G_OBJECT(folder));
349 }
350
351 static void
352 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
353 {
354         TnyIterator *iter;
355         TnyList *folders = tny_simple_list_new ();
356
357         tny_folder_store_get_folders (store, folders, query, NULL);
358         iter = tny_list_create_iterator (folders);
359
360         while (!tny_iterator_is_done (iter)) {
361
362                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
363
364                 tny_list_prepend (all_folders, G_OBJECT (folder));
365
366                 recurse_folders (folder, query, all_folders);
367             
368                 g_object_unref (G_OBJECT (folder));
369
370                 tny_iterator_next (iter);
371         }
372          g_object_unref (G_OBJECT (iter));
373          g_object_unref (G_OBJECT (folders));
374 }
375
376 static void
377 update_folders_cb (TnyFolderStore *folder_store, TnyList *list, GError **err, gpointer user_data)
378 {
379         ModestMailOperation *self;
380         ModestMailOperationPrivate *priv;
381         TnyIterator *iter;
382         TnyList *all_folders;
383         
384         self  = MODEST_MAIL_OPERATION (user_data);
385         priv  = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
386
387         g_message (__FUNCTION__);
388         
389         if (*err) {
390                 priv->error = g_error_copy (*err);
391                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
392                 goto out;
393         }
394
395         /* Get all the folders We can do it synchronously because
396            we're already running in a different thread than the UI */
397         all_folders = tny_list_copy (list);
398         iter = tny_list_create_iterator (all_folders);
399         while (!tny_iterator_is_done (iter)) {
400                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
401
402                 recurse_folders (folder, NULL, all_folders);
403                 tny_iterator_next (iter);
404         }
405         g_object_unref (G_OBJECT (iter));
406
407         /* Refresh folders */
408         iter = tny_list_create_iterator (all_folders);
409         priv->total = tny_list_get_length (all_folders);
410
411         while (!tny_iterator_is_done (iter) && !priv->error) {
412
413                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
414
415                 /* Refresh the folder */
416                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
417
418                 if (priv->error) {
419                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
420                 } else {        
421                         /* Update status and notify */
422                         priv->done++;
423                         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
424                 }
425     
426                 g_object_unref (G_OBJECT (folder));
427             
428                 tny_iterator_next (iter);
429         }
430
431         g_object_unref (G_OBJECT (iter));
432  out:
433         g_object_unref (G_OBJECT (list));
434
435         /* Check if the operation was a success */
436         if (priv->done == priv->total && !priv->error)
437                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
438
439         /* Free */
440         g_object_unref (G_OBJECT (folder_store));
441
442         /* Notify the queue */
443         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
444 }
445
446 gboolean
447 modest_mail_operation_update_account (ModestMailOperation *self,
448                                       TnyStoreAccount *store_account)
449 {
450         ModestMailOperationPrivate *priv;
451         TnyList *folders;
452
453         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
454         g_return_val_if_fail (TNY_IS_STORE_ACCOUNT(store_account), FALSE);
455
456         /* Pick async call reference */
457         g_object_ref (store_account);
458
459         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
460
461         priv->total = 0;
462         priv->done  = 0;
463         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
464         
465         /* Get subscribed folders & refresh them */
466         folders = TNY_LIST (tny_simple_list_new ());
467
468         g_message ("tny_folder_store_get_folders_async");
469         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
470                                             folders, update_folders_cb, NULL, self);
471         
472         return TRUE;
473 }
474
475 ModestMailOperationStatus
476 modest_mail_operation_get_status (ModestMailOperation *self)
477 {
478         ModestMailOperationPrivate *priv;
479
480         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
481         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
482                               MODEST_MAIL_OPERATION_STATUS_INVALID);
483
484         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
485         return priv->status;
486 }
487
488 const GError *
489 modest_mail_operation_get_error (ModestMailOperation *self)
490 {
491         ModestMailOperationPrivate *priv;
492
493         g_return_val_if_fail (self, NULL);
494         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
495
496         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
497         return priv->error;
498 }
499
500 gboolean 
501 modest_mail_operation_cancel (ModestMailOperation *self)
502 {
503         /* TODO */
504         return TRUE;
505 }
506
507 guint 
508 modest_mail_operation_get_task_done (ModestMailOperation *self)
509 {
510         ModestMailOperationPrivate *priv;
511
512         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
513
514         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
515         return priv->done;
516 }
517
518 guint 
519 modest_mail_operation_get_task_total (ModestMailOperation *self)
520 {
521         ModestMailOperationPrivate *priv;
522
523         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
524
525         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
526         return priv->total;
527 }
528
529 gboolean
530 modest_mail_operation_is_finished (ModestMailOperation *self)
531 {
532         ModestMailOperationPrivate *priv;
533         gboolean retval = FALSE;
534
535         if (!MODEST_IS_MAIL_OPERATION (self)) {
536                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
537                 return retval;
538         }
539
540         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
541
542         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
543             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
544             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
545             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
546                 retval = TRUE;
547         } else {
548                 retval = FALSE;
549         }
550
551         return retval;
552 }
553
554 /* ******************************************************************* */
555 /* ************************** STORE  ACTIONS ************************* */
556 /* ******************************************************************* */
557
558
559 TnyFolder *
560 modest_mail_operation_create_folder (ModestMailOperation *self,
561                                      TnyFolderStore *parent,
562                                      const gchar *name)
563 {
564         ModestTnyFolderRules rules;
565         ModestMailOperationPrivate *priv;
566         TnyFolder *new_folder = NULL;
567         gboolean can_create = FALSE;
568
569         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
570         g_return_val_if_fail (name, NULL);
571
572         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
573
574         /* Check parent */
575         if (!TNY_IS_FOLDER (parent)) {
576                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
577                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
578                              _("mail_in_ui_folder_create_error"));
579         } else {
580                 /* Check folder rules */
581                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
582                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)
583                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
584                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
585                                      _("mail_in_ui_folder_create_error"));
586                 else
587                         can_create = TRUE;              
588         }
589
590         if (can_create) {
591                 /* Create the folder */
592                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
593                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
594         }
595
596         /* Notify the queue */
597         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
598
599         return new_folder;
600 }
601
602 void
603 modest_mail_operation_remove_folder (ModestMailOperation *self,
604                                      TnyFolder           *folder,
605                                      gboolean             remove_to_trash)
606 {
607         TnyAccount *account;
608         ModestMailOperationPrivate *priv;
609         ModestTnyFolderRules rules;
610
611         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
612         g_return_if_fail (TNY_IS_FOLDER (folder));
613
614         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
615
616         /* Check folder rules */
617         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
618         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
619                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
620                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
621                              _("mail_in_ui_folder_delete_error"));
622                 goto end;
623         }
624
625         /* Get the account */
626         account = tny_folder_get_account (folder);
627
628         /* Delete folder or move to trash */
629         if (remove_to_trash) {
630                 TnyFolder *trash_folder, *new_folder;
631                 trash_folder = modest_tny_account_get_special_folder (account,
632                                                                       TNY_FOLDER_TYPE_TRASH);
633                 /* TODO: error_handling */
634                 new_folder = modest_mail_operation_xfer_folder (self, folder, 
635                                                                 TNY_FOLDER_STORE (trash_folder), TRUE);
636                 g_object_unref (G_OBJECT (new_folder));
637         } else {
638                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
639
640                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
641                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
642
643                 if (parent)
644                         g_object_unref (G_OBJECT (parent));
645         }
646         g_object_unref (G_OBJECT (account));
647
648  end:
649         /* Notify the queue */
650         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
651 }
652
653 void
654 modest_mail_operation_rename_folder (ModestMailOperation *self,
655                                      TnyFolder *folder,
656                                      const gchar *name)
657 {
658         ModestMailOperationPrivate *priv;
659         ModestTnyFolderRules rules;
660
661         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
662         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
663         g_return_if_fail (name);
664
665         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
666
667         /* Check folder rules */
668         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
669         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
670                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
671                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
672                              _("FIXME: unable to rename"));
673         } else {
674                 /* Rename. Camel handles folder subscription/unsubscription */
675                 tny_folder_set_name (folder, name, &(priv->error));
676                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
677         }
678
679         /* Notify the queue */
680         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
681  }
682
683 TnyFolder *
684 modest_mail_operation_xfer_folder (ModestMailOperation *self,
685                                    TnyFolder *folder,
686                                    TnyFolderStore *parent,
687                                    gboolean delete_original)
688 {
689         ModestMailOperationPrivate *priv;
690         TnyFolder *new_folder = NULL;
691         ModestTnyFolderRules rules;
692
693         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
694         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
695         g_return_val_if_fail (TNY_IS_FOLDER (folder), NULL);
696
697         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
698
699         /* The moveable restriction is applied also to copy operation */
700         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
701         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
702                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
703                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
704                              _("FIXME: unable to rename"));
705         } else {
706                 /* Move/Copy folder */
707                 new_folder = tny_folder_copy (folder,
708                                               parent,
709                                               tny_folder_get_name (folder),
710                                               delete_original, 
711                                               &(priv->error));
712         }
713
714         /* Notify the queue */
715         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
716
717         return new_folder;
718 }
719
720
721 /* ******************************************************************* */
722 /* **************************  MSG  ACTIONS  ************************* */
723 /* ******************************************************************* */
724
725 void 
726 modest_mail_operation_remove_msg (ModestMailOperation *self,
727                                   TnyHeader *header,
728                                   gboolean remove_to_trash)
729 {
730         TnyFolder *folder;
731         ModestMailOperationPrivate *priv;
732
733         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
734         g_return_if_fail (TNY_IS_HEADER (header));
735
736         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
737         folder = tny_header_get_folder (header);
738
739         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
740
741         /* Delete or move to trash */
742         if (remove_to_trash) {
743                 TnyFolder *trash_folder;
744                 TnyStoreAccount *store_account;
745
746                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
747                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
748                                                                       TNY_FOLDER_TYPE_TRASH);
749                 if (trash_folder) {
750                         TnyList *headers;
751
752                         /* Create list */
753                         headers = tny_simple_list_new ();
754                         tny_list_append (headers, G_OBJECT (header));
755                         g_object_unref (header);
756
757                         /* Move to trash */
758                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE);
759                         g_object_unref (headers);
760 /*                      g_object_unref (trash_folder); */
761                 } else {
762                         ModestMailOperationPrivate *priv;
763
764                         /* Set status failed and set an error */
765                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
766                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
767                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
768                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
769                                      _("Error trying to delete a message. Trash folder not found"));
770                 }
771
772                 g_object_unref (G_OBJECT (store_account));
773         } else {
774                 tny_folder_remove_msg (folder, header, &(priv->error));
775                 if (!priv->error)
776                         tny_folder_sync(folder, TRUE, &(priv->error));
777         }
778
779         /* Set status */
780         if (!priv->error)
781                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
782         else
783                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
784
785         /* Free */
786         g_object_unref (G_OBJECT (folder));
787
788         /* Notify the queue */
789         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
790 }
791
792 static void
793 transfer_msgs_cb (TnyFolder *folder, GError **err, gpointer user_data)
794 {
795         XFerMsgAsyncHelper *helper;
796         ModestMailOperation *self;
797         ModestMailOperationPrivate *priv;
798
799         helper = (XFerMsgAsyncHelper *) user_data;
800         self = helper->mail_op;
801         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
802
803         if (*err) {
804                 priv->error = g_error_copy (*err);
805                 priv->done = 0;
806                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
807         } else {
808                 priv->done = 1;
809                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
810         }
811
812         /* Free */
813         g_object_unref (helper->headers);
814         g_object_unref (helper->dest_folder);
815         g_object_unref (folder);
816         g_free (helper);
817
818         /* Notify the queue */
819         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
820 }
821
822 void
823 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
824                                  TnyList *headers, 
825                                  TnyFolder *folder, 
826                                  gboolean delete_original)
827 {
828         ModestMailOperationPrivate *priv;
829         TnyIterator *iter;
830         TnyFolder *src_folder;
831         XFerMsgAsyncHelper *helper;
832         TnyHeader *header;
833
834         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
835         g_return_if_fail (TNY_IS_LIST (headers));
836         g_return_if_fail (TNY_IS_FOLDER (folder));
837
838         /* Pick references for async calls */
839         g_object_ref (folder);
840
841         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
842         priv->total = 1;
843         priv->done = 0;
844         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
845
846         /* Create the helper */
847         helper = g_malloc0 (sizeof (XFerMsgAsyncHelper));
848         helper->mail_op = self;
849         helper->dest_folder = folder;
850         helper->headers = headers;
851
852         /* Get source folder */
853         iter = tny_list_create_iterator (headers);
854         header = TNY_HEADER (tny_iterator_get_current (iter));
855         src_folder = tny_header_get_folder (header);
856         g_object_unref (header);
857         g_object_unref (iter);
858
859         /* Transfer messages */
860         tny_folder_transfer_msgs_async (src_folder, 
861                                         headers, 
862                                         folder, 
863                                         delete_original, 
864                                         transfer_msgs_cb, 
865                                         helper);
866 }
867
868 static void
869 on_refresh_folder (TnyFolder   *folder, 
870                    gboolean     cancelled, 
871                    GError     **error,
872                    gpointer     user_data)
873 {
874         ModestMailOperation *self;
875         ModestMailOperationPrivate *priv;
876
877         self = MODEST_MAIL_OPERATION (user_data);
878         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
879
880         if (*error) {
881                 priv->error = g_error_copy (*error);
882                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
883                 goto out;
884         }
885
886         if (cancelled) {
887                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
888                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
889                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
890                              _("Error trying to refresh the contents of %s"),
891                              tny_folder_get_name (folder));
892                 goto out;
893         }
894
895         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
896
897  out:
898         /* Free */
899         g_object_unref (folder);
900
901         /* Notify the queue */
902         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
903 }
904
905 static void
906 on_refresh_folder_status_update (GObject *obj,
907                                  TnyStatus *status,  
908                                  gpointer user_data)
909 {
910         ModestMailOperation *self;
911         ModestMailOperationPrivate *priv;
912
913         g_return_if_fail (status != NULL);
914         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
915
916         self = MODEST_MAIL_OPERATION (user_data);
917         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
918
919         priv->done = status->position;
920         priv->total = status->of_total;
921
922         if (priv->done == 1 && priv->total == 100)
923                 return;
924
925         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
926 }
927
928 void 
929 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
930                                        TnyFolder *folder)
931 {
932         ModestMailOperationPrivate *priv;
933
934         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
935
936         /* Pick a reference */
937         g_object_ref (folder);
938
939         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
940
941         /* Refresh the folder. TODO: tinymail could issue a status
942            updates before the callback call then this could happen. We
943            must review the design */
944         tny_folder_refresh_async (folder,
945                                   on_refresh_folder,
946                                   on_refresh_folder_status_update,
947 /*                                NULL, */
948                                   self);
949 }
950
951 void
952 _modest_mail_operation_notify_end (ModestMailOperation *self)
953 {
954         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
955 }