* ui-actions, 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 <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 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         GError                    *error;
86 };
87
88 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
89                                                    MODEST_TYPE_MAIL_OPERATION, \
90                                                    ModestMailOperationPrivate))
91
92 #define CHECK_EXCEPTION(priv, new_status, op)  if (priv->error) {\
93                                                    priv->status = new_status;\
94                                                    op;\
95                                                }
96
97 typedef struct _RefreshFolderAsyncHelper
98 {
99         ModestMailOperation *mail_op;
100         TnyIterator *iter;
101         guint failed;
102         guint canceled;
103
104 } RefreshFolderAsyncHelper;
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->error    = NULL;
172         priv->done     = 0;
173         priv->total    = 0;
174 }
175
176 static void
177 modest_mail_operation_finalize (GObject *obj)
178 {
179         ModestMailOperationPrivate *priv;
180
181         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
182
183         if (priv->error) {
184                 g_error_free (priv->error);
185                 priv->error = NULL;
186         }
187
188         G_OBJECT_CLASS(parent_class)->finalize (obj);
189 }
190
191 ModestMailOperation*
192 modest_mail_operation_new (void)
193 {
194         return MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
195 }
196
197
198 void
199 modest_mail_operation_send_mail (ModestMailOperation *self,
200                                  TnyTransportAccount *transport_account,
201                                  TnyMsg* msg)
202 {
203         TnySendQueue *send_queue;
204         
205         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
206         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
207
208         g_message ("modest: send mail");
209         
210         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
211         if (!TNY_IS_SEND_QUEUE(send_queue))
212                 g_printerr ("modest: could not find send queue for account\n");
213         else {
214                 GError *err = NULL;
215                 tny_send_queue_add (send_queue, msg, &err);
216                 if (err) {
217                         g_printerr ("modest: error adding msg to send queue: %s\n",
218                                     err->message);
219                         g_error_free (err);
220                 } else
221                         g_message ("modest: message added to send queue");
222         }
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         TnyMsg *new_msg;
278         TnyHeader *new_header, *header;
279         gchar *new_subject;
280         TnyMimePart *body;
281         ModestFormatter *formatter;
282
283         /* Get body from original msg. Always look for the text/plain
284            part of the message to create the reply/forwarded mail */
285         header = tny_msg_get_header (msg);
286         body   = modest_tny_msg_find_body_part (msg, FALSE);
287
288         /* TODO: select the formatter from account prefs */
289         formatter = modest_formatter_new ("text/plain");
290
291         /* Format message body */
292         if (is_reply) {
293                 switch (type) {
294                 case MODEST_MAIL_OPERATION_REPLY_TYPE_CITE:
295                 default:
296                         new_msg = modest_formatter_cite  (formatter, body, header);
297                         break;
298                 case MODEST_MAIL_OPERATION_REPLY_TYPE_QUOTE:
299                         new_msg = modest_formatter_quote (formatter, body, header);
300                         break;
301                 }
302         } else {
303                 switch (type) {
304                 case MODEST_MAIL_OPERATION_FORWARD_TYPE_INLINE:
305                 default:
306                         new_msg = modest_formatter_inline  (formatter, body, header);
307                         break;
308                 case MODEST_MAIL_OPERATION_FORWARD_TYPE_ATTACHMENT:
309                         new_msg = modest_formatter_attach (formatter, body, header);
310                         break;
311                 }
312         }
313         g_object_unref (G_OBJECT(formatter));
314         g_object_unref (G_OBJECT(body));
315         
316         /* Fill the header */
317         new_header = TNY_HEADER (tny_platform_factory_new_header
318                                  (modest_runtime_get_platform_factory()));
319         tny_msg_set_header (new_msg, new_header);
320         tny_header_set_from (new_header, from);
321         tny_header_set_replyto (new_header, from);
322
323         /* Change the subject */
324         new_subject =
325                 (gchar *) modest_text_utils_derived_subject (tny_header_get_subject(header),
326                                                              (is_reply) ? _("Re:") : _("Fwd:"));
327         tny_header_set_subject (new_header, (const gchar *) new_subject);
328         g_free (new_subject);
329
330         /* Clean */
331         g_object_unref (G_OBJECT (new_header));
332         g_object_unref (G_OBJECT (header));
333
334         return new_msg;
335 }
336
337 TnyMsg* 
338 modest_mail_operation_create_forward_mail (TnyMsg *msg, 
339                                            const gchar *from,
340                                            ModestMailOperationForwardType forward_type)
341 {
342         TnyMsg *new_msg;
343         TnyList *parts = NULL;
344         GList *attachments_list = NULL;
345
346         new_msg = create_reply_forward_mail (msg, from, FALSE, forward_type);
347
348         /* Add attachments */
349         parts = TNY_LIST (tny_simple_list_new());
350         tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
351         tny_list_foreach (parts, add_if_attachment, attachments_list);
352         add_attachments (new_msg, attachments_list);
353
354         /* Clean */
355         if (attachments_list)
356                 g_list_free (attachments_list);
357         g_object_unref (G_OBJECT (parts));
358
359         return new_msg;
360 }
361
362 TnyMsg* 
363 modest_mail_operation_create_reply_mail (TnyMsg *msg, 
364                                          const gchar *from,
365                                          ModestMailOperationReplyType reply_type,
366                                          ModestMailOperationReplyMode reply_mode)
367 {
368         TnyMsg *new_msg = NULL;
369         TnyHeader *new_header, *header;
370         const gchar* reply_to;
371         gchar *new_cc = NULL;
372         const gchar *cc = NULL, *bcc = NULL;
373         GString *tmp = NULL;
374
375         new_msg = create_reply_forward_mail (msg, from, TRUE, reply_type);
376
377         /* Fill the header */
378         header = tny_msg_get_header (msg);
379         new_header = tny_msg_get_header (new_msg);
380         reply_to = tny_header_get_replyto (header);
381
382         if (reply_to)
383                 tny_header_set_to (new_header, reply_to);
384         else
385                 tny_header_set_to (new_header, tny_header_get_from (header));
386
387         switch (reply_mode) {
388         case MODEST_MAIL_OPERATION_REPLY_MODE_SENDER:
389                 /* Do not fill neither cc nor bcc */
390                 break;
391         case MODEST_MAIL_OPERATION_REPLY_MODE_LIST:
392                 /* TODO */
393                 break;
394         case MODEST_MAIL_OPERATION_REPLY_MODE_ALL:
395                 /* Concatenate to, cc and bcc */
396                 cc = tny_header_get_cc (header);
397                 bcc = tny_header_get_bcc (header);
398
399                 tmp = g_string_new (tny_header_get_to (header));
400                 if (cc)  g_string_append_printf (tmp, ",%s",cc);
401                 if (bcc) g_string_append_printf (tmp, ",%s",bcc);
402
403                /* Remove my own address from the cc list. TODO:
404                   remove also the To: of the new message, needed due
405                   to the new reply_to feature */
406                 new_cc = (gchar *)
407                         modest_text_utils_remove_address ((const gchar *) tmp->str,
408                                                           from);
409                 /* FIXME: remove also the mails from the new To: */
410                 tny_header_set_cc (new_header, new_cc);
411
412                 /* Clean */
413                 g_string_free (tmp, TRUE);
414                 g_free (new_cc);
415                 break;
416         }
417
418         /* Clean */
419         g_object_unref (G_OBJECT (new_header));
420         g_object_unref (G_OBJECT (header));
421
422         return new_msg;
423 }
424
425 static void
426 status_update_cb (TnyFolder *folder, const gchar *what, gint status, gint oftotal, gpointer user_data) 
427 {
428         g_print ("%s status: %d, of total %d\n", what, status, oftotal);
429 }
430
431 static void
432 folder_refresh_cb (TnyFolder *folder, gboolean canceled, GError **err, gpointer user_data)
433 {
434         ModestMailOperation *self = NULL;
435         ModestMailOperationPrivate *priv = NULL;
436         RefreshFolderAsyncHelper *helper;
437
438         helper = (RefreshFolderAsyncHelper *) user_data;
439         self = MODEST_MAIL_OPERATION (helper->mail_op);
440         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
441
442         if ((canceled && *err) || *err) {
443                 priv->error = g_error_copy (*err);
444                 helper->failed++;
445         } else if (canceled) {
446                 helper->canceled++;
447                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
448                              MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
449                              _("Error trying to refresh folder %s. Operation canceled"),
450                              tny_folder_get_name (folder));
451         } else {
452                 priv->done++;
453         }
454
455         if (priv->done == priv->total)
456                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
457         else if ((priv->done + helper->canceled + helper->failed) == priv->total) {
458                 if (helper->failed == priv->total)
459                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
460                 else if (helper->failed == priv->total)
461                         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
462                 else
463                         priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
464         }
465         tny_iterator_next (helper->iter);
466         if (tny_iterator_is_done (helper->iter)) {
467                 TnyList *list;
468                 list = tny_iterator_get_list (helper->iter);
469                 g_object_unref (G_OBJECT (helper->iter));
470                 g_object_unref (G_OBJECT (list));
471                 g_slice_free (RefreshFolderAsyncHelper, helper);
472         } else {
473                 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (helper->iter));
474                 if (folder) {
475                         g_message ("modest: refreshing folder %s",
476                                    tny_folder_get_name (folder));
477                         tny_folder_refresh_async (folder, folder_refresh_cb, status_update_cb, helper);
478                         g_object_unref (G_OBJECT(folder)); // FIXME: don't unref yet
479                 }
480         }
481         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
482 }
483
484
485 static void
486 update_folders_cb (TnyFolderStore *folder_store, TnyList *list, GError **err, gpointer user_data)
487 {
488         ModestMailOperation *self;
489         ModestMailOperationPrivate *priv;
490         RefreshFolderAsyncHelper *helper;
491         TnyFolder *folder;
492         
493         self  = MODEST_MAIL_OPERATION (user_data);
494         priv  = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
495
496         if (*err) {
497                 priv->error = g_error_copy (*err);
498                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
499                 return;
500         }
501
502         priv->total = tny_list_get_length (list);
503         priv->done = 0;
504         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
505
506         helper = g_slice_new0 (RefreshFolderAsyncHelper);
507         helper->mail_op = self;
508         helper->iter = tny_list_create_iterator (list);
509         helper->failed = 0;
510         helper->canceled = 0;
511
512         /* Async refresh folders */
513         folder = TNY_FOLDER (tny_iterator_get_current (helper->iter));
514         if (folder) {
515                 g_message ("modest: refreshing folder %s", tny_folder_get_name (folder));
516                 tny_folder_refresh_async (folder, folder_refresh_cb,
517                                           status_update_cb, helper);
518         }
519         //g_object_unref (G_OBJECT(folder)); /* FIXME -==> don't unref yet... */
520 }
521
522 gboolean
523 modest_mail_operation_update_account (ModestMailOperation *self,
524                                       TnyStoreAccount *store_account)
525 {
526         ModestMailOperationPrivate *priv;
527         TnyList *folders;
528         TnyFolderStoreQuery *query;
529
530         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
531         g_return_val_if_fail (TNY_IS_STORE_ACCOUNT(store_account), FALSE);
532
533         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
534
535         /* Get subscribed folders & refresh them */
536         folders = TNY_LIST (tny_simple_list_new ());
537         query = tny_folder_store_query_new ();
538         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
539         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
540                                             folders, update_folders_cb, query, self);
541         g_object_unref (query); /* FIXME */
542         
543         return TRUE;
544 }
545
546 ModestMailOperationStatus
547 modest_mail_operation_get_status (ModestMailOperation *self)
548 {
549         ModestMailOperationPrivate *priv;
550
551         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
552         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
553                               MODEST_MAIL_OPERATION_STATUS_INVALID);
554
555         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
556         return priv->status;
557 }
558
559 const GError *
560 modest_mail_operation_get_error (ModestMailOperation *self)
561 {
562         ModestMailOperationPrivate *priv;
563
564         g_return_val_if_fail (self, NULL);
565         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
566
567         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
568         return priv->error;
569 }
570
571 gboolean 
572 modest_mail_operation_cancel (ModestMailOperation *self)
573 {
574         /* TODO */
575         return TRUE;
576 }
577
578 guint 
579 modest_mail_operation_get_task_done (ModestMailOperation *self)
580 {
581         ModestMailOperationPrivate *priv;
582
583         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
584
585         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
586         return priv->done;
587 }
588
589 guint 
590 modest_mail_operation_get_task_total (ModestMailOperation *self)
591 {
592         ModestMailOperationPrivate *priv;
593
594         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
595
596         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
597         return priv->total;
598 }
599
600 gboolean
601 modest_mail_operation_is_finished (ModestMailOperation *self)
602 {
603         ModestMailOperationPrivate *priv;
604         gboolean retval = FALSE;
605
606         if (!MODEST_IS_MAIL_OPERATION (self)) {
607                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
608                 return retval;
609         }
610
611         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
612
613         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
614             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
615             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
616             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
617                 retval = TRUE;
618         } else {
619                 retval = FALSE;
620         }
621
622         return retval;
623 }
624
625 /* ******************************************************************* */
626 /* ************************** STORE  ACTIONS ************************* */
627 /* ******************************************************************* */
628
629
630 TnyFolder *
631 modest_mail_operation_create_folder (ModestMailOperation *self,
632                                      TnyFolderStore *parent,
633                                      const gchar *name)
634 {
635         ModestMailOperationPrivate *priv;
636         TnyFolder *new_folder = NULL;
637         TnyStoreAccount *store_account;
638
639         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
640         g_return_val_if_fail (name, NULL);
641
642         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
643
644         /* Create the folder */
645         new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
646         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, return NULL);
647
648         /* Subscribe to folder */
649         if (!tny_folder_is_subscribed (new_folder)) {
650                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (TNY_FOLDER (parent)));
651                 tny_store_account_subscribe (store_account, new_folder);
652                 g_object_unref (G_OBJECT (store_account));
653         }
654
655         return new_folder;
656 }
657
658 void
659 modest_mail_operation_remove_folder (ModestMailOperation *self,
660                                      TnyFolder           *folder,
661                                      gboolean             remove_to_trash)
662 {
663         TnyFolderStore *parent;
664         TnyAccount *account;
665         ModestMailOperationPrivate *priv;
666
667         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
668         g_return_if_fail (TNY_IS_FOLDER (folder));
669
670         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
671
672         /* Get the account */
673         account = tny_folder_get_account (folder);
674
675         /* Delete folder or move to trash */
676         if (remove_to_trash) {
677                 TnyFolder *trash_folder, *new_folder;
678                 trash_folder = modest_tny_account_get_special_folder (account,
679                                                                       TNY_FOLDER_TYPE_TRASH);
680                 /* TODO: error_handling */
681                 new_folder = modest_mail_operation_xfer_folder (self, folder, 
682                                                                 TNY_FOLDER_STORE (trash_folder), TRUE);
683                 g_object_unref (G_OBJECT (new_folder));
684         } else {
685                 parent = tny_folder_get_folder_store (folder);
686
687                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
688                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, );
689
690                 if (parent)
691                         g_object_unref (G_OBJECT (parent));
692         }
693         g_object_unref (G_OBJECT (account));
694 }
695
696 void
697 modest_mail_operation_rename_folder (ModestMailOperation *self,
698                                      TnyFolder *folder,
699                                      const gchar *name)
700 {
701         ModestMailOperationPrivate *priv;
702
703         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
704         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
705         g_return_if_fail (name);
706
707         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
708
709         /* FIXME: better error handling */
710         if (strrchr (name, '/') != NULL)
711                 return;
712
713         /* Rename. Camel handles folder subscription/unsubscription */
714         tny_folder_set_name (folder, name, &(priv->error));
715         CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED, return);
716  }
717
718 TnyFolder *
719 modest_mail_operation_xfer_folder (ModestMailOperation *self,
720                                    TnyFolder *folder,
721                                    TnyFolderStore *parent,
722                                    gboolean delete_original)
723 {
724         ModestMailOperationPrivate *priv;
725
726         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
727         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
728         g_return_val_if_fail (TNY_IS_FOLDER (folder), NULL);
729
730         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
731
732         return tny_folder_copy (folder, 
733                                 parent, 
734                                 tny_folder_get_name (folder), 
735                                 delete_original, 
736                                 &(priv->error));
737 }
738
739
740 /* ******************************************************************* */
741 /* **************************  MSG  ACTIONS  ************************* */
742 /* ******************************************************************* */
743
744 void 
745 modest_mail_operation_remove_msg (ModestMailOperation *self,
746                                   TnyHeader *header,
747                                   gboolean remove_to_trash)
748 {
749         TnyFolder *folder;
750
751         g_return_if_fail (TNY_IS_HEADER (header));
752
753         folder = tny_header_get_folder (header);
754
755         /* Delete or move to trash */
756         if (remove_to_trash) {
757                 TnyFolder *trash_folder;
758                 TnyStoreAccount *store_account;
759
760                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
761                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
762                                                                       TNY_FOLDER_TYPE_TRASH);
763                 if (trash_folder) {
764                         modest_mail_operation_xfer_msg (self, header, trash_folder, TRUE);
765 /*                      g_object_unref (trash_folder); */
766                 } else {
767                         ModestMailOperationPrivate *priv;
768
769                         /* Set status failed and set an error */
770                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
771                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
772                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
773                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
774                                      _("Error trying to delete a message. Trash folder not found"));
775                 }
776
777                 g_object_unref (G_OBJECT (store_account));
778         } else {
779                 tny_folder_remove_msg (folder, header, NULL); /* FIXME */
780                 tny_folder_sync(folder, TRUE, NULL); /* FIXME */
781         }
782
783         /* Free */
784         g_object_unref (folder);
785 }
786
787 static void
788 transfer_msgs_cb (TnyFolder *folder, GError **err, gpointer user_data)
789 {
790         ModestMailOperationPrivate *priv;
791
792         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(user_data);
793
794         if (*err) {
795                 priv->error = g_error_copy (*err);
796                 priv->done = 0;
797                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
798         } else {
799                 priv->done = 1;
800                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
801         }
802
803         g_signal_emit (G_OBJECT (user_data), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
804 }
805
806 gboolean
807 modest_mail_operation_xfer_msg (ModestMailOperation *self,
808                                 TnyHeader *header, 
809                                 TnyFolder *folder, 
810                                 gboolean delete_original)
811 {
812         ModestMailOperationPrivate *priv;
813         TnyFolder *src_folder;
814         TnyList *headers;
815
816         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
817         g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
818         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
819
820         src_folder = tny_header_get_folder (header);
821         headers = tny_simple_list_new ();
822
823         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
824         priv->total = 1;
825         priv->done = 0;
826         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
827
828         tny_list_prepend (headers, G_OBJECT (header));
829         tny_folder_transfer_msgs_async (src_folder, headers, folder, 
830                                         delete_original, transfer_msgs_cb, 
831                                         g_object_ref(self));
832
833         /* Free */
834         /* FIXME: don't free 'm yet */
835         ///g_object_unref (headers);
836         ///g_object_unref (src_folder);
837
838         return TRUE;
839 }
840
841
842 /* ******************************************************************* */
843 /* ************************* UTILIY FUNCTIONS ************************ */
844 /* ******************************************************************* */
845
846 static void
847 add_attachments (TnyMsg *msg, GList *attachments_list)
848 {
849         GList *pos;
850         TnyMimePart *attachment_part, *old_attachment;
851         const gchar *attachment_content_type;
852         const gchar *attachment_filename;
853         TnyStream *attachment_stream;
854
855         for (pos = (GList *)attachments_list; pos; pos = pos->next) {
856
857                 old_attachment = pos->data;
858                 attachment_filename = tny_mime_part_get_filename (old_attachment);
859                 attachment_stream = tny_mime_part_get_stream (old_attachment);
860                 attachment_part = tny_platform_factory_new_mime_part
861                         (modest_runtime_get_platform_factory());
862                 
863                 attachment_content_type = tny_mime_part_get_content_type (old_attachment);
864                                  
865                 tny_mime_part_construct_from_stream (attachment_part,
866                                                      attachment_stream,
867                                                      attachment_content_type);
868                 tny_stream_reset (attachment_stream);
869                 
870                 tny_mime_part_set_filename (attachment_part, attachment_filename);
871                 
872                 tny_mime_part_add_part (TNY_MIME_PART (msg), attachment_part);
873 /*              g_object_unref (attachment_part); */
874         }
875 }
876