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