* Modified the API of the message window
[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         if (*err) {
531                 priv->error = g_error_copy (*err);
532                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
533                 return;
534         }
535
536         priv->total = tny_list_get_length (list);
537         priv->done = 0;
538         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
539
540         helper = g_slice_new0 (RefreshFolderAsyncHelper);
541         helper->mail_op = mail_op;
542         helper->iter = tny_list_create_iterator (list);
543         helper->failed = 0;
544         helper->canceled = 0;
545
546         /* Async refresh folders */
547         tny_folder_refresh_async (TNY_FOLDER (tny_iterator_get_current (helper->iter)),
548                                   folder_refresh_cb,
549                                   status_update_cb, 
550                                   helper);
551 }
552
553 gboolean
554 modest_mail_operation_update_account (ModestMailOperation *mail_op,
555                                       TnyStoreAccount *store_account)
556 {
557         ModestMailOperationPrivate *priv;
558         TnyList *folders;
559         TnyFolderStoreQuery *query;
560
561         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (mail_op), FALSE);
562         g_return_val_if_fail (TNY_IS_STORE_ACCOUNT(store_account), FALSE);
563
564         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
565
566         /* Get subscribed folders & refresh them */
567         folders = TNY_LIST (tny_simple_list_new ());
568         query = tny_folder_store_query_new ();
569         tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
570         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
571                                             folders, update_folders_cb, query, mail_op);
572         g_object_unref (query);
573
574         return TRUE;
575 }
576
577 ModestMailOperationStatus
578 modest_mail_operation_get_status (ModestMailOperation *mail_op)
579 {
580         ModestMailOperationPrivate *priv;
581
582 /*      g_return_val_if_fail (mail_op, MODEST_MAIL_OPERATION_STATUS_INVALID); */
583 /*      g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (mail_op),  */
584 /*                            MODEST_MAIL_OPERATION_STATUS_INVALID); */
585
586         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
587         return priv->status;
588 }
589
590 const GError *
591 modest_mail_operation_get_error (ModestMailOperation *mail_op)
592 {
593         ModestMailOperationPrivate *priv;
594
595 /*      g_return_val_if_fail (mail_op, NULL); */
596 /*      g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (mail_op), NULL); */
597
598         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
599         return priv->error;
600 }
601
602 void 
603 modest_mail_operation_cancel (ModestMailOperation *mail_op)
604 {
605         /* TODO */
606 }
607
608 guint 
609 modest_mail_operation_get_task_done (ModestMailOperation *mail_op)
610 {
611         ModestMailOperationPrivate *priv;
612
613         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (mail_op), 0);
614
615         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
616         return priv->done;
617 }
618
619 guint 
620 modest_mail_operation_get_task_total (ModestMailOperation *mail_op)
621 {
622         ModestMailOperationPrivate *priv;
623
624         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (mail_op), 0);
625
626         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
627         return priv->total;
628 }
629
630 gboolean
631 modest_mail_operation_is_finished (ModestMailOperation *mail_op)
632 {
633         ModestMailOperationPrivate *priv;
634         gboolean retval = FALSE;
635
636         if (!MODEST_IS_MAIL_OPERATION (mail_op)) {
637                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
638                 return retval;
639         }
640
641         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_op);
642
643         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
644             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
645             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
646             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
647                 retval = TRUE;
648         } else {
649                 retval = FALSE;
650         }
651
652         return retval;
653 }
654
655 /* ******************************************************************* */
656 /* ************************** STORE  ACTIONS ************************* */
657 /* ******************************************************************* */
658
659
660 TnyFolder *
661 modest_mail_operation_create_folder (ModestMailOperation *mail_op,
662                                      TnyFolderStore *parent,
663                                      const gchar *name)
664 {
665         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
666         g_return_val_if_fail (name, NULL);
667
668         TnyFolder *new_folder = NULL;
669         TnyStoreAccount *store_account;
670
671         /* Create the folder */
672         new_folder = tny_folder_store_create_folder (parent, name, NULL); /* FIXME */
673         if (!new_folder) 
674                 return NULL;
675
676         /* Subscribe to folder */
677         if (!tny_folder_is_subscribed (new_folder)) {
678                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (TNY_FOLDER (parent)));
679                 tny_store_account_subscribe (store_account, new_folder);
680                 g_object_unref (G_OBJECT (store_account));
681         }
682
683         return new_folder;
684 }
685
686 void
687 modest_mail_operation_remove_folder (ModestMailOperation *mail_op,
688                                      TnyFolder *folder,
689                                      gboolean remove_to_trash)
690 {
691         TnyFolderStore *folder_store;
692
693         g_return_if_fail (TNY_IS_FOLDER (folder));
694
695         /* Get folder store */
696         folder_store = TNY_FOLDER_STORE (tny_folder_get_account (folder));
697
698         /* Delete folder or move to trash */
699         if (remove_to_trash) {
700                 TnyFolder *trash_folder;
701
702                 trash_folder = modest_mail_operation_find_trash_folder (mail_op,
703                                                                         TNY_STORE_ACCOUNT (folder_store));
704
705                 /* TODO: error_handling */
706                 modest_mail_operation_move_folder (mail_op, 
707                                                    folder, 
708                                                    TNY_FOLDER_STORE (trash_folder));
709         } else {
710                 tny_folder_store_remove_folder (folder_store, folder, NULL); /* FIXME */
711                 g_object_unref (G_OBJECT (folder));
712         }
713
714         /* Free instances */
715         g_object_unref (G_OBJECT (folder_store));
716 }
717
718 void
719 modest_mail_operation_rename_folder (ModestMailOperation *mail_op,
720                                      TnyFolder *folder,
721                                      const gchar *name)
722 {
723         g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_op));
724         g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
725
726         /* FIXME: better error handling */
727         if (strrchr (name, '/') != NULL)
728                 return;
729
730         /* Rename. Camel handles folder subscription/unsubscription */
731         tny_folder_set_name (folder, name, NULL); /* FIXME */
732  }
733
734 void
735 modest_mail_operation_move_folder (ModestMailOperation *mail_op,
736                                    TnyFolder *folder,
737                                    TnyFolderStore *parent)
738 {
739         g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_op));
740         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
741         g_return_if_fail (TNY_IS_FOLDER (folder));
742         
743         modest_mail_operation_xfer_folder (mail_op, folder, parent, TRUE);
744 }
745
746 void
747 modest_mail_operation_copy_folder (ModestMailOperation *mail_op,
748                                    TnyFolder *folder,
749                                    TnyFolderStore *parent)
750 {
751         g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_op));
752         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
753         g_return_if_fail (TNY_IS_FOLDER (folder));
754
755         modest_mail_operation_xfer_folder (mail_op, folder, parent, FALSE);
756 }
757
758 static void
759 modest_mail_operation_xfer_folder (ModestMailOperation *mail_op,
760                                    TnyFolder *folder,
761                                    TnyFolderStore *parent,
762                                    gboolean delete_original)
763 {
764         const gchar *folder_name;
765         TnyFolder *dest_folder, *child;
766         TnyIterator *iter;
767         TnyList *folders, *headers;
768
769         g_return_if_fail (TNY_IS_FOLDER (folder));
770         g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
771
772         /* Create the destination folder */
773         folder_name = tny_folder_get_name (folder);
774         dest_folder = modest_mail_operation_create_folder (mail_op, 
775                                                            parent, folder_name);
776
777         /* Transfer messages */
778         headers = TNY_LIST (tny_simple_list_new ());
779         tny_folder_get_headers (folder, headers, FALSE, NULL); /* FIXME */
780         tny_folder_transfer_msgs (folder, headers, dest_folder, delete_original, NULL); /* FIXME */
781
782         /* Recurse children */
783         folders = TNY_LIST (tny_simple_list_new ());
784         tny_folder_store_get_folders (TNY_FOLDER_STORE (folder), folders, NULL, NULL ); /* FIXME */
785         iter = tny_list_create_iterator (folders);
786
787         while (!tny_iterator_is_done (iter)) {
788
789                 child = TNY_FOLDER (tny_iterator_get_current (iter));
790                 modest_mail_operation_xfer_folder (mail_op, child,
791                                                    TNY_FOLDER_STORE (dest_folder),
792                                                    delete_original);
793                 tny_iterator_next (iter);
794         }
795
796         /* Delete source folder (if needed) */
797         if (delete_original)
798                 modest_mail_operation_remove_folder (mail_op, folder, FALSE);
799
800         /* Clean up */
801         g_object_unref (G_OBJECT (dest_folder));
802         g_object_unref (G_OBJECT (headers));
803         g_object_unref (G_OBJECT (folders));
804         g_object_unref (G_OBJECT (iter));
805 }
806
807
808 /* FIXME: this method should be rewritten when the policy for the
809    Trash folder becomes clearer */
810 static TnyFolder *
811 modest_mail_operation_find_trash_folder (ModestMailOperation *mail_op,
812                                          TnyStoreAccount *store_account)
813 {
814         TnyList *folders;
815         TnyIterator *iter;
816         gboolean found;
817         /*TnyFolderStoreQuery *query;*/
818         TnyFolder *trash_folder;
819
820         /* Look for Trash folder */
821         folders = TNY_LIST (tny_simple_list_new ());
822         tny_folder_store_get_folders (TNY_FOLDER_STORE (store_account),
823                                       folders, NULL, NULL); /* FIXME */
824         iter = tny_list_create_iterator (folders);
825
826         found = FALSE;
827         while (!tny_iterator_is_done (iter) && !found) {
828
829                 trash_folder = TNY_FOLDER (tny_iterator_get_current (iter));
830                 if (tny_folder_get_folder_type (trash_folder) == TNY_FOLDER_TYPE_TRASH)
831                         found = TRUE;
832                 else
833                         tny_iterator_next (iter);
834         }
835
836         /* Clean up */
837         g_object_unref (G_OBJECT (folders));
838         g_object_unref (G_OBJECT (iter));
839
840         /* TODO: better error handling management */
841         if (!found) 
842                 return NULL;
843         else
844                 return trash_folder;
845 }
846
847 /* ******************************************************************* */
848 /* **************************  MSG  ACTIONS  ************************* */
849 /* ******************************************************************* */
850
851 void 
852 modest_mail_operation_copy_msg (ModestMailOperation *mail_op,
853                                 TnyHeader *header, 
854                                 TnyFolder *folder)
855 {
856         g_return_if_fail (TNY_IS_HEADER (header));
857         g_return_if_fail (TNY_IS_FOLDER (folder));
858
859         modest_mail_operation_xfer_msg (mail_op, header, folder, FALSE);
860 }
861
862 void 
863 modest_mail_operation_move_msg (ModestMailOperation *mail_op,
864                                 TnyHeader *header, 
865                                 TnyFolder *folder)
866 {
867         g_return_if_fail (TNY_IS_HEADER (header));
868         g_return_if_fail (TNY_IS_FOLDER (folder));
869
870         modest_mail_operation_xfer_msg (mail_op, header, folder, TRUE);
871 }
872
873 void 
874 modest_mail_operation_remove_msg (ModestMailOperation *mail_op,
875                                   TnyHeader *header,
876                                   gboolean remove_to_trash)
877 {
878         TnyFolder *folder;
879
880         g_return_if_fail (TNY_IS_HEADER (header));
881
882         folder = tny_header_get_folder (header);
883
884         /* Delete or move to trash */
885         if (remove_to_trash) {
886                 TnyFolder *trash_folder;
887                 TnyStoreAccount *store_account;
888
889                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
890                 trash_folder = modest_mail_operation_find_trash_folder (mail_op, store_account);
891
892                 modest_mail_operation_move_msg (mail_op, header, trash_folder);
893
894                 g_object_unref (G_OBJECT (store_account));
895         } else {
896                 tny_folder_remove_msg (folder, header, NULL); /* FIXME */
897                 tny_folder_expunge (folder, NULL); /* FIXME */
898         }
899
900         /* Free */
901         g_object_unref (folder);
902 }
903
904 static void
905 modest_mail_operation_xfer_msg (ModestMailOperation *mail_op,
906                                 TnyHeader *header, 
907                                 TnyFolder *folder, 
908                                 gboolean delete_original)
909 {
910         TnyFolder *src_folder;
911         TnyList *headers;
912
913         src_folder = tny_header_get_folder (header);
914         headers = tny_simple_list_new ();
915
916         /* Move */
917         tny_list_prepend (headers, G_OBJECT (header));
918         tny_folder_transfer_msgs (src_folder, headers, folder, delete_original, NULL); /* FIXME */
919
920         /* Free */
921         g_object_unref (headers);
922         g_object_unref (folder);
923 }
924
925
926 /* ******************************************************************* */
927 /* ************************* UTILIY FUNCTIONS ************************ */
928 /* ******************************************************************* */
929 static gboolean
930 is_ascii(const gchar *s)
931 {
932         while (s[0]) {
933                 if (s[0] & 128 || s[0] < 32)
934                         return FALSE;
935                 s++;
936         }
937         return TRUE;
938 }
939
940 static char *
941 get_content_type(const gchar *s)
942 {
943         GString *type;
944         
945         type = g_string_new("text/plain");
946         if (!is_ascii(s)) {
947                 if (g_utf8_validate(s, -1, NULL)) {
948                         g_string_append(type, "; charset=\"utf-8\"");
949                 } else {
950                         /* it should be impossible to reach this, but better safe than sorry */
951                         g_warning("invalid utf8 in message");
952                         g_string_append(type, "; charset=\"latin1\"");
953                 }
954         }
955         return g_string_free(type, FALSE);
956 }
957
958 static GQuark 
959 modest_error_quark (void)
960 {
961         static GQuark err_q = 0;
962         
963         if (err_q == 0)
964                 err_q = g_quark_from_static_string ("modest-error-quark");
965         
966         return err_q;
967 }
968
969 static void
970 add_attachments (TnyMsg *msg, GList *attachments_list)
971 {
972         GList *pos;
973         TnyMimePart *attachment_part, *old_attachment;
974         const gchar *attachment_content_type;
975         const gchar *attachment_filename;
976         TnyStream *attachment_stream;
977
978         for (pos = (GList *)attachments_list; pos; pos = pos->next) {
979
980                 old_attachment = pos->data;
981                 attachment_filename = tny_mime_part_get_filename (old_attachment);
982                 attachment_stream = tny_mime_part_get_stream (old_attachment);
983                 attachment_part = TNY_MIME_PART (tny_camel_mime_part_new (camel_mime_part_new()));
984                 
985                 attachment_content_type = tny_mime_part_get_content_type (old_attachment);
986                                  
987                 tny_mime_part_construct_from_stream (attachment_part,
988                                                      attachment_stream,
989                                                      attachment_content_type);
990                 tny_stream_reset (attachment_stream);
991                 
992                 tny_mime_part_set_filename (attachment_part, attachment_filename);
993                 
994                 tny_mime_part_add_part (TNY_MIME_PART (msg), attachment_part);
995 /*              g_object_unref (attachment_part); */
996         }
997 }
998
999
1000 static TnyMimePart *
1001 add_body_part (TnyMsg *msg, 
1002                const gchar *body,
1003                const gchar *content_type,
1004                gboolean has_attachments)
1005 {
1006         TnyMimePart *text_body_part = NULL;
1007         TnyStream *text_body_stream;
1008
1009         /* Create the stream */
1010         text_body_stream = TNY_STREAM (tny_camel_stream_new
1011                                        (camel_stream_mem_new_with_buffer
1012                                         (body, strlen(body))));
1013
1014         /* Create body part if needed */
1015         if (has_attachments)
1016                 text_body_part = 
1017                         TNY_MIME_PART (tny_camel_mime_part_new (camel_mime_part_new()));
1018         else
1019                 text_body_part = TNY_MIME_PART(msg);
1020
1021         /* Construct MIME part */
1022         tny_stream_reset (text_body_stream);
1023         tny_mime_part_construct_from_stream (text_body_part,
1024                                              text_body_stream,
1025                                              content_type);
1026         tny_stream_reset (text_body_stream);
1027
1028         /* Add part if needed */
1029         if (has_attachments) {
1030                 tny_mime_part_add_part (TNY_MIME_PART (msg), text_body_part);
1031                 g_object_unref (G_OBJECT(text_body_part));
1032         }
1033
1034         /* Clean */
1035         g_object_unref (text_body_stream);
1036
1037         return text_body_part;
1038 }