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