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