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