db8b916bf33c9e466e1e80a45ee7964edf2e95c0
[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 <camel/camel-stream-mem.h>
41 #include <glib/gi18n.h>
42 #include <modest-tny-account.h>
43 #include <modest-tny-send-queue.h>
44 #include <modest-runtime.h>
45 #include "modest-text-utils.h"
46 #include "modest-tny-msg.h"
47 #include "modest-tny-platform-factory.h"
48 #include "modest-marshal.h"
49 #include "modest-formatter.h"
50 #include "modest-error.h"
51
52 /* 'private'/'protected' functions */
53 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
54 static void modest_mail_operation_init       (ModestMailOperation *obj);
55 static void modest_mail_operation_finalize   (GObject *obj);
56
57 static void     status_update_cb     (TnyFolder *folder, 
58                                       const gchar *what, 
59                                       gint status, 
60                                       gint oftotal,
61                                       gpointer user_data);
62 static void     folder_refresh_cb    (TnyFolder *folder, 
63                                       gboolean canceled,
64                                       GError **err,
65                                       gpointer user_data);
66 static void     update_folders_cb    (TnyFolderStore *self, 
67                                       TnyList *list, 
68                                       GError **err, 
69                                       gpointer user_data);
70 static void     add_attachments      (TnyMsg *msg, 
71                                       GList *attachments_list);
72
73 static void        modest_mail_operation_xfer_folder       (ModestMailOperation *self,
74                                                             TnyFolder *folder,
75                                                             TnyFolderStore *parent,
76                                                             gboolean delete_original);
77
78 static gboolean    modest_mail_operation_xfer_msg          (ModestMailOperation *self,
79                                                             TnyHeader *header, 
80                                                             TnyFolder *folder, 
81                                                             gboolean delete_original);
82
83 enum _ModestMailOperationSignals 
84 {
85         PROGRESS_CHANGED_SIGNAL,
86
87         NUM_SIGNALS
88 };
89
90 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
91 struct _ModestMailOperationPrivate {
92         guint                      done;
93         guint                      total;
94         ModestMailOperationStatus  status;
95         GError                    *error;
96 };
97
98 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
99                                                    MODEST_TYPE_MAIL_OPERATION, \
100                                                    ModestMailOperationPrivate))
101
102 #define CHECK_EXCEPTION(priv, new_status, op)  if (priv->error) {\
103                                                    priv->status = new_status;\
104                                                    op;\
105                                                }
106
107 typedef struct _RefreshFolderAsyncHelper
108 {
109         ModestMailOperation *mail_op;
110         TnyIterator *iter;
111         guint failed;
112         guint canceled;
113
114 } RefreshFolderAsyncHelper;
115
116 /* globals */
117 static GObjectClass *parent_class = NULL;
118
119 static guint signals[NUM_SIGNALS] = {0};
120
121 GType
122 modest_mail_operation_get_type (void)
123 {
124         static GType my_type = 0;
125         if (!my_type) {
126                 static const GTypeInfo my_info = {
127                         sizeof(ModestMailOperationClass),
128                         NULL,           /* base init */
129                         NULL,           /* base finalize */
130                         (GClassInitFunc) modest_mail_operation_class_init,
131                         NULL,           /* class finalize */
132                         NULL,           /* class data */
133                         sizeof(ModestMailOperation),
134                         1,              /* n_preallocs */
135                         (GInstanceInitFunc) modest_mail_operation_init,
136                         NULL
137                 };
138                 my_type = g_type_register_static (G_TYPE_OBJECT,
139                                                   "ModestMailOperation",
140                                                   &my_info, 0);
141         }
142         return my_type;
143 }
144
145 static void
146 modest_mail_operation_class_init (ModestMailOperationClass *klass)
147 {
148         GObjectClass *gobject_class;
149         gobject_class = (GObjectClass*) klass;
150
151         parent_class            = g_type_class_peek_parent (klass);
152         gobject_class->finalize = modest_mail_operation_finalize;
153
154         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
155
156         /**
157          * ModestMailOperation::progress-changed
158          * @self: the #MailOperation that emits the signal
159          * @user_data: user data set when the signal handler was connected
160          *
161          * Emitted when the progress of a mail operation changes
162          */
163         signals[PROGRESS_CHANGED_SIGNAL] = 
164                 g_signal_new ("progress_changed",
165                               G_TYPE_FROM_CLASS (gobject_class),
166                               G_SIGNAL_RUN_FIRST,
167                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
168                               NULL, NULL,
169                               g_cclosure_marshal_VOID__VOID,
170                               G_TYPE_NONE, 0);
171 }
172
173 static void
174 modest_mail_operation_init (ModestMailOperation *obj)
175 {
176         ModestMailOperationPrivate *priv;
177
178         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
179
180         priv->status   = MODEST_MAIL_OPERATION_STATUS_INVALID;
181         priv->error    = NULL;
182         priv->done     = 0;
183         priv->total    = 0;
184 }
185
186 static void
187 modest_mail_operation_finalize (GObject *obj)
188 {
189         ModestMailOperationPrivate *priv;
190
191         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
192
193         if (priv->error) {
194                 g_error_free (priv->error);
195                 priv->error = NULL;
196         }
197
198         G_OBJECT_CLASS(parent_class)->finalize (obj);
199 }
200
201 ModestMailOperation*
202 modest_mail_operation_new (void)
203 {
204         return MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
205 }
206
207
208 void
209 modest_mail_operation_send_mail (ModestMailOperation *self,
210                                  TnyTransportAccount *transport_account,
211                                  TnyMsg* msg)
212 {
213         TnySendQueue *send_queue;
214         
215         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
216         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
217
218         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
219         if (!TNY_IS_SEND_QUEUE(send_queue))
220                 g_printerr ("modest: could not find send queue for account\n");
221         else
222                 tny_send_queue_add (send_queue, msg);
223 }
224
225 void
226 modest_mail_operation_send_new_mail (ModestMailOperation *self,
227                                      TnyTransportAccount *transport_account,
228                                      const gchar *from,  const gchar *to,
229                                      const gchar *cc,  const gchar *bcc,
230                                      const gchar *subject, const gchar *body,
231                                      const GList *attachments_list)
232 {
233         TnyMsg *new_msg;
234         ModestMailOperationPrivate *priv = NULL;
235
236         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
237         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
238
239         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
240
241         /* Check parametters */
242         if (to == NULL) {
243                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
244                              MODEST_MAIL_OPERATION_ERROR_MISSING_PARAMETER,
245                              _("Error trying to send a mail. You need to set at least one recipient"));
246                 return;
247         }
248
249         new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, body, NULL); /* FIXME: attachments */
250         if (!new_msg) {
251                 g_printerr ("modest: failed to create a new msg\n");
252                 return;
253         }
254         
255         modest_mail_operation_send_mail (self, transport_account, new_msg);
256
257         g_object_unref (G_OBJECT(new_msg));
258 }
259
260 static void
261 add_if_attachment (gpointer data, gpointer user_data)
262 {
263         TnyMimePart *part;
264         GList *attachments_list;
265
266         part = TNY_MIME_PART (data);
267         attachments_list = (GList *) user_data;
268
269         if (tny_mime_part_is_attachment (part))
270                 attachments_list = g_list_prepend (attachments_list, part);
271 }
272
273
274 static TnyMsg *
275 create_reply_forward_mail (TnyMsg *msg, const gchar *from, gboolean is_reply, guint type)
276 {
277         TnyPlatformFactory *fact;
278         TnyMsg *new_msg;
279         TnyHeader *new_header, *header;
280         gchar *new_subject;
281         TnyMimePart *body;
282         ModestFormatter *formatter;
283
284         /* Get body from original msg. Always look for the text/plain
285            part of the message to create the reply/forwarded mail */
286         header = tny_msg_get_header (msg);
287         body   = modest_tny_msg_find_body_part (msg, FALSE);
288
289         /* TODO: select the formatter from account prefs */
290         formatter = modest_formatter_new ("text/plain");
291
292         /* Format message body */
293         if (is_reply) {
294                 switch (type) {
295                 case MODEST_MAIL_OPERATION_REPLY_TYPE_CITE:
296                 default:
297                         new_msg = modest_formatter_cite  (formatter, body, header);
298                         break;
299                 case MODEST_MAIL_OPERATION_REPLY_TYPE_QUOTE:
300                         new_msg = modest_formatter_quote (formatter, body, header);
301                         break;
302                 }
303         } else {
304                 switch (type) {
305                 case MODEST_MAIL_OPERATION_FORWARD_TYPE_INLINE:
306                 default:
307                         new_msg = modest_formatter_inline  (formatter, body, header);
308                         break;
309                 case MODEST_MAIL_OPERATION_FORWARD_TYPE_ATTACHMENT:
310                         new_msg = modest_formatter_attach (formatter, body, header);
311                         break;
312                 }
313         }
314         g_object_unref (G_OBJECT(formatter));
315         g_object_unref (G_OBJECT(body));
316         
317         /* Fill the header */
318         fact = modest_tny_platform_factory_get_instance ();
319         new_header = TNY_HEADER (tny_platform_factory_new_header (fact));
320         tny_msg_set_header (new_msg, new_header);
321         tny_header_set_from (new_header, from);
322         tny_header_set_replyto (new_header, from);
323
324         /* Change the subject */
325         new_subject =
326                 (gchar *) modest_text_utils_derived_subject (tny_header_get_subject(header),
327                                                              (is_reply) ? _("Re:") : _("Fwd:"));
328         tny_header_set_subject (new_header, (const gchar *) new_subject);
329         g_free (new_subject);
330
331         /* Clean */
332         g_object_unref (G_OBJECT (new_header));
333         g_object_unref (G_OBJECT (header));
334
335         return new_msg;
336 }
337
338 TnyMsg* 
339 modest_mail_operation_create_forward_mail (TnyMsg *msg, 
340                                            const gchar *from,
341                                            ModestMailOperationForwardType forward_type)
342 {
343         TnyMsg *new_msg;
344         TnyList *parts = NULL;
345         GList *attachments_list = NULL;
346
347         new_msg = create_reply_forward_mail (msg, from, FALSE, forward_type);
348
349         /* Add attachments */
350         parts = TNY_LIST (tny_simple_list_new());
351         tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
352         tny_list_foreach (parts, add_if_attachment, attachments_list);
353         add_attachments (new_msg, attachments_list);
354
355         /* Clean */
356         if (attachments_list)
357                 g_list_free (attachments_list);
358         g_object_unref (G_OBJECT (parts));
359
360         return new_msg;
361 }
362
363 TnyMsg* 
364 modest_mail_operation_create_reply_mail (TnyMsg *msg, 
365                                          const gchar *from,
366                                          ModestMailOperationReplyType reply_type,
367                                          ModestMailOperationReplyMode reply_mode)
368 {
369         TnyMsg *new_msg = NULL;
370         TnyHeader *new_header, *header;
371         const gchar* reply_to;
372         gchar *new_cc = NULL;
373         const gchar *cc = NULL, *bcc = NULL;
374         GString *tmp = NULL;
375
376         new_msg = create_reply_forward_mail (msg, from, TRUE, reply_type);
377
378         /* Fill the header */
379         header = tny_msg_get_header (msg);
380         new_header = tny_msg_get_header (new_msg);
381         reply_to = tny_header_get_replyto (header);
382
383         if (reply_to)
384                 tny_header_set_to (new_header, reply_to);
385         else
386                 tny_header_set_to (new_header, tny_header_get_from (header));
387
388         switch (reply_mode) {
389         case MODEST_MAIL_OPERATION_REPLY_MODE_SENDER:
390                 /* Do not fill neither cc nor bcc */
391                 break;
392         case MODEST_MAIL_OPERATION_REPLY_MODE_LIST:
393                 /* TODO */
394                 break;
395         case MODEST_MAIL_OPERATION_REPLY_MODE_ALL:
396                 /* Concatenate to, cc and bcc */
397                 cc = tny_header_get_cc (header);
398                 bcc = tny_header_get_bcc (header);
399
400                 tmp = g_string_new (tny_header_get_to (header));
401                 if (cc)  g_string_append_printf (tmp, ",%s",cc);
402                 if (bcc) g_string_append_printf (tmp, ",%s",bcc);
403
404                /* Remove my own address from the cc list. TODO:
405                   remove also the To: of the new message, needed due
406                   to the new reply_to feature */
407                 new_cc = (gchar *)
408                         modest_text_utils_remove_address ((const gchar *) tmp->str,
409                                                           from);
410                 /* FIXME: remove also the mails from the new To: */
411                 tny_header_set_cc (new_header, new_cc);
412
413                 /* Clean */
414                 g_string_free (tmp, TRUE);
415                 g_free (new_cc);
416                 break;
417         }
418
419         /* Clean */
420         g_object_unref (G_OBJECT (new_header));
421         g_object_unref (G_OBJECT (header));
422
423         return new_msg;
424 }
425
426 static void
427 status_update_cb (TnyFolder *folder, const gchar *what, gint status, gint oftotal, gpointer user_data) 
428 {
429         g_print ("%s status: %d, of total %d\n", what, status, oftotal);
430 }
431
432 static void
433 folder_refresh_cb (TnyFolder *folder, gboolean canceled, GError **err, gpointer user_data)
434 {
435         ModestMailOperation *self = NULL;
436         ModestMailOperationPrivate *priv = NULL;
437         RefreshFolderAsyncHelper *helper;
438
439         helper = (RefreshFolderAsyncHelper *) user_data;
440         self = MODEST_MAIL_OPERATION (helper->mail_op);
441         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
442
443         if ((canceled && *err) || *err) {
444                 priv->error = g_error_copy (*err);
445                 helper->failed++;
446         } else if (canceled) {
447                 helper->canceled++;
448                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
449                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
450                              _("Error trying to refresh folder %s. Operation canceled"),
451                              tny_folder_get_name (folder));
452         } else {
453                 priv->done++;
454         }
455
456         if (priv->done == priv->total)
457                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
458         else if ((priv->done + helper->canceled + helper->failed) == priv->total) {
459                 if (helper->failed == priv->total)
460                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
461                 else if (helper->failed == priv->total)
462                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
463                 else
464                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
465         }
466         tny_iterator_next (helper->iter);
467         if (tny_iterator_is_done (helper->iter)) {
468                 TnyList *list;
469                 list = tny_iterator_get_list (helper->iter);
470                 g_object_unref (G_OBJECT (helper->iter));
471                 g_object_unref (G_OBJECT (list));
472                 g_slice_free (RefreshFolderAsyncHelper, helper);
473         } else {
474                 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (helper->iter));
475                 tny_folder_refresh_async (folder, folder_refresh_cb,
476                                           status_update_cb, 
477                                           helper);
478                 g_object_unref (G_OBJECT(folder));
479         }
480         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
481 }
482
483
484 static void
485 update_folders_cb (TnyFolderStore *folder_store, TnyList *list, GError **err, gpointer user_data)
486 {
487         ModestMailOperation *self;
488         ModestMailOperationPrivate *priv;
489         RefreshFolderAsyncHelper *helper;
490         TnyFolder *folder;
491         
492         self  = MODEST_MAIL_OPERATION (user_data);
493         priv  = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
494
495         if (*err) {
496                 priv->error = g_error_copy (*err);
497                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
498                 return;
499         }
500
501         priv->total = tny_list_get_length (list);
502         priv->done = 0;
503         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
504
505         helper = g_slice_new0 (RefreshFolderAsyncHelper);
506         helper->mail_op = self;
507         helper->iter = tny_list_create_iterator (list);
508         helper->failed = 0;
509         helper->canceled = 0;
510
511         /* Async refresh folders */
512         folder = TNY_FOLDER (tny_iterator_get_current (helper->iter));
513         tny_folder_refresh_async (folder, folder_refresh_cb,
514                                   status_update_cb, helper);
515         g_object_unref (G_OBJECT(folder));
516 }
517
518 gboolean
519 modest_mail_operation_update_account (ModestMailOperation *self,
520                                       TnyStoreAccount *store_account)
521 {
522         ModestMailOperationPrivate *priv;
523         TnyList *folders;
524         TnyFolderStoreQuery *query;
525
526         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
527         g_return_val_if_fail (TNY_IS_STORE_ACCOUNT(store_account), FALSE);
528
529         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
530
531         /* Get subscribed folders & refresh them */
532         folders = TNY_LIST (tny_simple_list_new ());
533         query = tny_folder_store_query_new ();
534         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
535         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
536                                             folders, update_folders_cb, query, self);
537         g_object_unref (query);
538
539         return TRUE;
540 }
541
542 ModestMailOperationStatus
543 modest_mail_operation_get_status (ModestMailOperation *self)
544 {
545         ModestMailOperationPrivate *priv;
546
547         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
548         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
549                               MODEST_MAIL_OPERATION_STATUS_INVALID);
550
551         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
552         return priv->status;
553 }
554
555 const GError *
556 modest_mail_operation_get_error (ModestMailOperation *self)
557 {
558         ModestMailOperationPrivate *priv;
559
560         g_return_val_if_fail (self, NULL);
561         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
562
563         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
564         return priv->error;
565 }
566
567 gboolean 
568 modest_mail_operation_cancel (ModestMailOperation *self)
569 {
570         /* TODO */
571         return TRUE;
572 }
573
574 guint 
575 modest_mail_operation_get_task_done (ModestMailOperation *self)
576 {
577         ModestMailOperationPrivate *priv;
578
579         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
580
581         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
582         return priv->done;
583 }
584
585 guint 
586 modest_mail_operation_get_task_total (ModestMailOperation *self)
587 {
588         ModestMailOperationPrivate *priv;
589
590         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
591
592         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
593         return priv->total;
594 }
595
596 gboolean
597 modest_mail_operation_is_finished (ModestMailOperation *self)
598 {
599         ModestMailOperationPrivate *priv;
600         gboolean retval = FALSE;
601
602         if (!MODEST_IS_MAIL_OPERATION (self)) {
603                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
604                 return retval;
605         }
606
607         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
608
609         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
610             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
611             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
612             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
613                 retval = TRUE;
614         } else {
615                 retval = FALSE;
616         }
617
618         return retval;
619 }
620
621 /* ******************************************************************* */
622 /* ************************** STORE  ACTIONS ************************* */
623 /* ******************************************************************* */
624
625
626 TnyFolder *
627 modest_mail_operation_create_folder (ModestMailOperation *self,
628                                      TnyFolderStore *parent,
629                                      const gchar *name)
630 {
631         ModestMailOperationPrivate *priv;
632         TnyFolder *new_folder = NULL;
633         TnyStoreAccount *store_account;
634
635         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
636         g_return_val_if_fail (name, NULL);
637
638         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
639
640         /* Create the folder */
641         new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
642         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, return NULL);
643
644         /* Subscribe to folder */
645         if (!tny_folder_is_subscribed (new_folder)) {
646                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (TNY_FOLDER (parent)));
647                 tny_store_account_subscribe (store_account, new_folder);
648                 g_object_unref (G_OBJECT (store_account));
649         }
650
651         return new_folder;
652 }
653
654 void
655 modest_mail_operation_remove_folder (ModestMailOperation *self,
656                                      TnyFolder *folder,
657                                      gboolean remove_to_trash)
658 {
659         TnyFolderStore *folder_store;
660
661         g_return_if_fail (TNY_IS_FOLDER (folder));
662
663         /* Get folder store */
664         folder_store = TNY_FOLDER_STORE (tny_folder_get_account (folder));
665
666         /* Delete folder or move to trash */
667         if (remove_to_trash) {
668                 TnyFolder *trash_folder;
669                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(folder_store),
670                                                                       TNY_FOLDER_TYPE_TRASH);
671                 /* TODO: error_handling */
672                 modest_mail_operation_move_folder (self, folder, 
673                                                    TNY_FOLDER_STORE (trash_folder));
674         } else {
675                 tny_folder_store_remove_folder (folder_store, folder, NULL); /* FIXME */
676                 g_object_unref (G_OBJECT (folder));
677         }
678
679         /* Free instances */
680         g_object_unref (G_OBJECT (folder_store));
681 }
682
683 void
684 modest_mail_operation_rename_folder (ModestMailOperation *self,
685                                      TnyFolder *folder,
686                                      const gchar *name)
687 {
688         ModestMailOperationPrivate *priv;
689
690         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
691         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
692         g_return_if_fail (name);
693
694         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
695
696         /* FIXME: better error handling */
697         if (strrchr (name, '/') != NULL)
698                 return;
699
700         /* Rename. Camel handles folder subscription/unsubscription */
701         tny_folder_set_name (folder, name, &(priv->error));
702         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, return);
703  }
704
705 void
706 modest_mail_operation_move_folder (ModestMailOperation *self,
707                                    TnyFolder *folder,
708                                    TnyFolderStore *parent)
709 {
710         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
711         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
712         g_return_if_fail (TNY_IS_FOLDER (folder));
713         
714         modest_mail_operation_xfer_folder (self, folder, parent, TRUE);
715 }
716
717 void
718 modest_mail_operation_copy_folder (ModestMailOperation *self,
719                                    TnyFolder *folder,
720                                    TnyFolderStore *parent)
721 {
722         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
723         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
724         g_return_if_fail (TNY_IS_FOLDER (folder));
725
726         modest_mail_operation_xfer_folder (self, folder, parent, FALSE);
727 }
728
729 static void
730 modest_mail_operation_xfer_folder (ModestMailOperation *self,
731                                    TnyFolder *folder,
732                                    TnyFolderStore *parent,
733                                    gboolean delete_original)
734 {
735         ModestMailOperationPrivate *priv;
736         const gchar *folder_name;
737         TnyFolder *dest_folder = NULL, *child = NULL;
738         TnyIterator *iter = NULL;
739         TnyList *folders = NULL, *headers = NULL;
740
741         g_return_if_fail (TNY_IS_FOLDER (folder));
742         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
743
744         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
745
746         /* Create the destination folder */
747         folder_name = tny_folder_get_name (folder);
748         dest_folder = modest_mail_operation_create_folder (self, parent, folder_name);
749         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, goto clean);
750
751         /* Transfer messages */
752         headers = TNY_LIST (tny_simple_list_new ());
753         tny_folder_get_headers (folder, headers, FALSE, &(priv->error));
754         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, goto clean);
755
756         tny_folder_transfer_msgs (folder, headers, dest_folder, delete_original, &(priv->error));
757         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, goto clean);
758
759         /* Recurse children */
760         folders = TNY_LIST (tny_simple_list_new ());
761         tny_folder_store_get_folders (TNY_FOLDER_STORE (folder), folders, NULL,  &(priv->error));
762         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, goto clean);
763
764         iter = tny_list_create_iterator (folders);
765         while (!tny_iterator_is_done (iter)) {
766                 child = TNY_FOLDER (tny_iterator_get_current (iter));
767                 modest_mail_operation_xfer_folder (self, child, TNY_FOLDER_STORE (dest_folder),
768                                                    delete_original);
769                 tny_iterator_next (iter);
770                 g_object_unref (G_OBJECT(child));
771         }
772
773         /* Delete source folder (if needed) */
774         if (delete_original)
775                 modest_mail_operation_remove_folder (self, folder, FALSE);
776
777         /* Clean up */
778  clean:
779         if (dest_folder)
780                 g_object_unref (G_OBJECT (dest_folder));
781         if (headers)
782                 g_object_unref (G_OBJECT (headers));
783         if (folders)
784                 g_object_unref (G_OBJECT (folders));
785         if (iter)
786                 g_object_unref (G_OBJECT (iter));
787 }
788
789
790 /* ******************************************************************* */
791 /* **************************  MSG  ACTIONS  ************************* */
792 /* ******************************************************************* */
793
794 gboolean 
795 modest_mail_operation_copy_msg (ModestMailOperation *self,
796                                 TnyHeader *header, 
797                                 TnyFolder *folder)
798 {
799         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
800         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
801
802         return modest_mail_operation_xfer_msg (self, header, folder, FALSE);
803 }
804
805 gboolean 
806 modest_mail_operation_move_msg (ModestMailOperation *self,
807                                 TnyHeader *header, 
808                                 TnyFolder *folder)
809 {
810         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
811         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
812
813         return modest_mail_operation_xfer_msg (self, header, folder, TRUE);
814 }
815
816 void 
817 modest_mail_operation_remove_msg (ModestMailOperation *self,
818                                   TnyHeader *header,
819                                   gboolean remove_to_trash)
820 {
821         TnyFolder *folder;
822
823         g_return_if_fail (TNY_IS_HEADER (header));
824
825         folder = tny_header_get_folder (header);
826
827         /* Delete or move to trash */
828         if (remove_to_trash) {
829                 TnyFolder *trash_folder;
830                 TnyStoreAccount *store_account;
831
832                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
833                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
834                                                                       TNY_FOLDER_TYPE_TRASH);
835                 if (trash_folder) {
836                         modest_mail_operation_move_msg (self, header, trash_folder);
837 /*                      g_object_unref (trash_folder); */
838                 } else {
839                         ModestMailOperationPrivate *priv;
840
841                         /* Set status failed and set an error */
842                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
843                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
844                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
845                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
846                                      _("Error trying to delete a message. Trash folder not found"));
847                 }
848
849                 g_object_unref (G_OBJECT (store_account));
850         } else {
851                 tny_folder_remove_msg (folder, header, NULL); /* FIXME */
852                 tny_folder_sync(folder, TRUE, NULL); /* FIXME */
853         }
854
855         /* Free */
856         g_object_unref (folder);
857 }
858
859 static void
860 transfer_msgs_cb (TnyFolder *folder, GError **err, gpointer user_data)
861 {
862         ModestMailOperationPrivate *priv;
863
864         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(user_data);
865         priv->done = 1;
866         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
867
868         g_signal_emit (G_OBJECT (user_data), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
869 }
870
871 static gboolean
872 modest_mail_operation_xfer_msg (ModestMailOperation *self,
873                                 TnyHeader *header, 
874                                 TnyFolder *folder, 
875                                 gboolean delete_original)
876 {
877         ModestMailOperationPrivate *priv;
878         TnyFolder *src_folder;
879         TnyList *headers;
880
881         src_folder = tny_header_get_folder (header);
882         headers = tny_simple_list_new ();
883
884         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
885         priv->total = 1;
886         priv->done = 0;
887         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
888
889         tny_list_prepend (headers, G_OBJECT (header));
890         tny_folder_transfer_msgs_async (src_folder, headers, folder, 
891                                         delete_original, transfer_msgs_cb, self);
892
893         /* Free */
894         g_object_unref (headers);
895         g_object_unref (folder);
896
897         return TRUE;
898 }
899
900
901 /* ******************************************************************* */
902 /* ************************* UTILIY FUNCTIONS ************************ */
903 /* ******************************************************************* */
904
905 static void
906 add_attachments (TnyMsg *msg, GList *attachments_list)
907 {
908         GList *pos;
909         TnyMimePart *attachment_part, *old_attachment;
910         const gchar *attachment_content_type;
911         const gchar *attachment_filename;
912         TnyStream *attachment_stream;
913         TnyPlatformFactory *fact;
914
915         fact = modest_tny_platform_factory_get_instance ();
916         for (pos = (GList *)attachments_list; pos; pos = pos->next) {
917
918                 old_attachment = pos->data;
919                 attachment_filename = tny_mime_part_get_filename (old_attachment);
920                 attachment_stream = tny_mime_part_get_stream (old_attachment);
921                 attachment_part = tny_platform_factory_new_mime_part (fact);
922                 
923                 attachment_content_type = tny_mime_part_get_content_type (old_attachment);
924                                  
925                 tny_mime_part_construct_from_stream (attachment_part,
926                                                      attachment_stream,
927                                                      attachment_content_type);
928                 tny_stream_reset (attachment_stream);
929                 
930                 tny_mime_part_set_filename (attachment_part, attachment_filename);
931                 
932                 tny_mime_part_add_part (TNY_MIME_PART (msg), attachment_part);
933 /*              g_object_unref (attachment_part); */
934         }
935 }
936