* Fixes: NB#55446
[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 <tny-send-queue.h>
41 #include <tny-status.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include <modest-tny-account.h>
45 #include <modest-tny-send-queue.h>
46 #include <modest-runtime.h>
47 #include "modest-text-utils.h"
48 #include "modest-tny-msg.h"
49 #include "modest-tny-folder.h"
50 #include "modest-tny-platform-factory.h"
51 #include "modest-marshal.h"
52 #include "modest-error.h"
53
54 /* 'private'/'protected' functions */
55 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
56 static void modest_mail_operation_init       (ModestMailOperation *obj);
57 static void modest_mail_operation_finalize   (GObject *obj);
58
59 static void     update_folders_cb    (TnyFolderStore *self, 
60                                       TnyList *list, 
61                                       GError **err, 
62                                       gpointer user_data);
63 static void     update_folders_status_cb (GObject *obj,
64                                           TnyStatus *status,  
65                                           gpointer user_data);
66
67 static void     update_process_msg_status_cb (GObject *obj,
68                                               TnyStatus *status,  
69                                           gpointer user_data);
70
71 enum _ModestMailOperationSignals 
72 {
73         PROGRESS_CHANGED_SIGNAL,
74
75         NUM_SIGNALS
76 };
77
78 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
79 struct _ModestMailOperationPrivate {
80         guint                      done;
81         guint                      total;
82         ModestMailOperationStatus  status;      
83         ModestMailOperationId      id;          
84         GObject                   *source;
85         GError                    *error;
86 };
87
88 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
89                                                    MODEST_TYPE_MAIL_OPERATION, \
90                                                    ModestMailOperationPrivate))
91
92 #define CHECK_EXCEPTION(priv, new_status)  if (priv->error) {\
93                                                    priv->status = new_status;\
94                                                }
95
96 typedef struct _RefreshFolderAsyncHelper
97 {
98         ModestMailOperation *mail_op;
99         TnyIterator *iter;
100         guint failed;
101         guint canceled;
102
103 } RefreshFolderAsyncHelper;
104
105 typedef struct _XFerMsgAsyncHelper
106 {
107         ModestMailOperation *mail_op;
108         TnyList *headers;
109         TnyFolder *dest_folder;
110
111 } XFerMsgAsyncHelper;
112
113
114 /* globals */
115 static GObjectClass *parent_class = NULL;
116
117 static guint signals[NUM_SIGNALS] = {0};
118
119 GType
120 modest_mail_operation_get_type (void)
121 {
122         static GType my_type = 0;
123         if (!my_type) {
124                 static const GTypeInfo my_info = {
125                         sizeof(ModestMailOperationClass),
126                         NULL,           /* base init */
127                         NULL,           /* base finalize */
128                         (GClassInitFunc) modest_mail_operation_class_init,
129                         NULL,           /* class finalize */
130                         NULL,           /* class data */
131                         sizeof(ModestMailOperation),
132                         1,              /* n_preallocs */
133                         (GInstanceInitFunc) modest_mail_operation_init,
134                         NULL
135                 };
136                 my_type = g_type_register_static (G_TYPE_OBJECT,
137                                                   "ModestMailOperation",
138                                                   &my_info, 0);
139         }
140         return my_type;
141 }
142
143 static void
144 modest_mail_operation_class_init (ModestMailOperationClass *klass)
145 {
146         GObjectClass *gobject_class;
147         gobject_class = (GObjectClass*) klass;
148
149         parent_class            = g_type_class_peek_parent (klass);
150         gobject_class->finalize = modest_mail_operation_finalize;
151
152         g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
153
154         /**
155          * ModestMailOperation::progress-changed
156          * @self: the #MailOperation that emits the signal
157          * @user_data: user data set when the signal handler was connected
158          *
159          * Emitted when the progress of a mail operation changes
160          */
161         signals[PROGRESS_CHANGED_SIGNAL] = 
162                 g_signal_new ("progress-changed",
163                               G_TYPE_FROM_CLASS (gobject_class),
164                               G_SIGNAL_RUN_FIRST,
165                               G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
166                               NULL, NULL,
167                               g_cclosure_marshal_VOID__VOID,
168                               G_TYPE_NONE, 0);
169 }
170
171 static void
172 modest_mail_operation_init (ModestMailOperation *obj)
173 {
174         ModestMailOperationPrivate *priv;
175
176         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
177
178         priv->status   = MODEST_MAIL_OPERATION_STATUS_INVALID;
179         priv->id       = MODEST_MAIL_OPERATION_ID_UNKNOWN;
180         priv->error    = NULL;
181         priv->done     = 0;
182         priv->total    = 0;
183         priv->source = NULL;
184 }
185
186 static void
187 modest_mail_operation_finalize (GObject *obj)
188 {
189         ModestMailOperationPrivate *priv;
190
191         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
192
193         if (priv->error) {
194                 g_error_free (priv->error);
195                 priv->error = NULL;
196         }
197         if (priv->source) {
198                 g_object_unref (priv->source);
199                 priv->source = NULL;
200         }
201
202         G_OBJECT_CLASS(parent_class)->finalize (obj);
203 }
204
205 ModestMailOperation*
206 modest_mail_operation_new (ModestMailOperationId id, 
207                            GObject *source)
208 {
209         ModestMailOperation *obj;
210         ModestMailOperationPrivate *priv;
211                 
212         obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
213         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
214
215         priv->id = id;
216         if (source != NULL)
217                 priv->source = g_object_ref(source);
218
219         return obj;
220 }
221
222
223 ModestMailOperationId
224 modest_mail_operation_get_id (ModestMailOperation *self)
225 {
226         ModestMailOperationPrivate *priv;
227
228         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
229         
230         return priv->id;
231 }
232
233 gboolean 
234 modest_mail_operation_is_mine (ModestMailOperation *self, 
235                                GObject *me)
236 {
237         ModestMailOperationPrivate *priv;
238
239         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
240         if (priv->source == NULL) return FALSE;
241
242         return priv->source == me;
243 }
244
245
246 void
247 modest_mail_operation_send_mail (ModestMailOperation *self,
248                                  TnyTransportAccount *transport_account,
249                                  TnyMsg* msg)
250 {
251         TnySendQueue *send_queue;
252         
253         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
254         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
255         g_return_if_fail (TNY_IS_MSG (msg));
256         
257         send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
258         if (!TNY_IS_SEND_QUEUE(send_queue))
259                 g_printerr ("modest: could not find send queue for account\n");
260         else {
261                 GError *err = NULL;
262                 tny_send_queue_add (send_queue, msg, &err);
263                 if (err) {
264                         g_printerr ("modest: error adding msg to send queue: %s\n",
265                                     err->message);
266                         g_error_free (err);
267                 } else
268                         g_message ("modest: message added to send queue");
269         }
270
271         /* Notify the queue */
272         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
273 }
274
275 void
276 modest_mail_operation_send_new_mail (ModestMailOperation *self,
277                                      TnyTransportAccount *transport_account,
278                                      const gchar *from,  const gchar *to,
279                                      const gchar *cc,  const gchar *bcc,
280                                      const gchar *subject, const gchar *plain_body,
281                                      const gchar *html_body,
282                                      const GList *attachments_list,
283                                      TnyHeaderFlags priority_flags)
284 {
285         TnyMsg *new_msg;
286         ModestMailOperationPrivate *priv = NULL;
287         /* GList *node = NULL; */
288
289         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
290         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
291
292         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
293
294         /* Check parametters */
295         if (to == NULL) {
296                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
297                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
298                              _("Error trying to send a mail. You need to set at least one recipient"));
299                 return;
300         }
301
302         if (html_body == NULL) {
303                 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
304         } else {
305                 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
306         }
307         if (!new_msg) {
308                 g_printerr ("modest: failed to create a new msg\n");
309                 return;
310         }
311
312         /* TODO: add priority handling. It's received in the priority_flags operator, and
313            it should have effect in the sending operation */
314
315         /* Call mail operation */
316         modest_mail_operation_send_mail (self, transport_account, new_msg);
317
318         /* Free */
319         g_object_unref (G_OBJECT (new_msg));
320 }
321
322 void
323 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
324                                       TnyTransportAccount *transport_account,
325                                       const gchar *from,  const gchar *to,
326                                       const gchar *cc,  const gchar *bcc,
327                                       const gchar *subject, const gchar *plain_body,
328                                       const gchar *html_body,
329                                       const GList *attachments_list,
330                                       TnyHeaderFlags priority_flags)
331 {
332         TnyMsg *msg = NULL;
333         TnyFolder *folder = NULL;
334         ModestMailOperationPrivate *priv = NULL;
335         GError *err = NULL;
336
337         /* GList *node = NULL; */
338
339         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
340         g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
341
342         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
343
344         if (html_body == NULL) {
345                 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
346         } else {
347                 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
348         }
349         if (!msg) {
350                 g_printerr ("modest: failed to create a new msg\n");
351                 goto cleanup;
352         }
353
354         folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
355         if (!folder) {
356                 g_printerr ("modest: failed to find Drafts folder\n");
357                 goto cleanup;
358         }
359         
360         tny_folder_add_msg (folder, msg, &err);
361         if (err) {
362                 g_printerr ("modest: error adding msg to Drafts folder: %s",
363                             err->message);
364                 g_error_free (err);
365                 goto cleanup;
366         }
367
368         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
369
370         /* Free */
371 cleanup:
372         if (msg)
373                 g_object_unref (G_OBJECT(msg));
374         if (folder)
375                 g_object_unref (G_OBJECT(folder));
376 }
377
378 static void
379 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
380 {
381         TnyIterator *iter;
382         TnyList *folders = tny_simple_list_new ();
383
384         tny_folder_store_get_folders (store, folders, query, NULL);
385         iter = tny_list_create_iterator (folders);
386
387         while (!tny_iterator_is_done (iter)) {
388
389                 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
390
391                 tny_list_prepend (all_folders, G_OBJECT (folder));
392
393                 recurse_folders (folder, query, all_folders);
394             
395                 g_object_unref (G_OBJECT (folder));
396
397                 tny_iterator_next (iter);
398         }
399          g_object_unref (G_OBJECT (iter));
400          g_object_unref (G_OBJECT (folders));
401 }
402
403 static void
404 update_folders_status_cb (GObject *obj,
405                           TnyStatus *status,  
406                           gpointer user_data)
407 {
408         ModestMailOperation *self;
409         ModestMailOperationPrivate *priv;
410
411         g_return_if_fail (status != NULL);
412         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
413
414         self = MODEST_MAIL_OPERATION (user_data);
415         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
416
417         priv->done = status->position;
418         priv->total = status->of_total;
419
420         if (priv->done == 1 && priv->total == 100)
421                 return;
422
423         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
424 }
425
426 static void
427 update_folders_cb (TnyFolderStore *folder_store, TnyList *list, GError **err, gpointer user_data)
428 {
429         ModestMailOperation *self;
430         ModestMailOperationPrivate *priv;
431         TnyIterator *iter;
432         TnyList *all_folders;
433         
434         self  = MODEST_MAIL_OPERATION (user_data);
435         priv  = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
436
437         g_message (__FUNCTION__);
438         
439         if (*err) {
440                 priv->error = g_error_copy (*err);
441                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
442                 goto out;
443         }
444
445         /* Get all the folders We can do it synchronously because
446            we're already running in a different thread than the UI */
447         all_folders = tny_list_copy (list);
448         iter = tny_list_create_iterator (all_folders);
449         while (!tny_iterator_is_done (iter)) {
450                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
451
452                 recurse_folders (folder, NULL, all_folders);
453                 tny_iterator_next (iter);
454         }
455         g_object_unref (G_OBJECT (iter));
456
457         /* Refresh folders */
458         iter = tny_list_create_iterator (all_folders);
459         priv->total = tny_list_get_length (all_folders);
460
461         while (!tny_iterator_is_done (iter) && !priv->error) {
462
463                 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
464
465                 /* Refresh the folder */
466                 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
467
468                 if (priv->error) {
469                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
470                 } else {        
471                         /* Update status and notify */
472                         priv->done++;
473                         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
474                 }
475     
476                 g_object_unref (G_OBJECT (folder));
477             
478                 tny_iterator_next (iter);
479         }
480
481         g_object_unref (G_OBJECT (iter));
482  out:
483         g_object_unref (G_OBJECT (list));
484
485         /* Check if the operation was a success */
486         if (priv->done == priv->total && !priv->error)
487                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
488
489         /* Free */
490         g_object_unref (G_OBJECT (folder_store));
491
492         /* Notify the queue */
493         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
494 }
495
496 gboolean
497 modest_mail_operation_update_account (ModestMailOperation *self,
498                                       TnyStoreAccount *store_account)
499 {
500         ModestMailOperationPrivate *priv;
501         TnyList *folders;
502
503         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
504         g_return_val_if_fail (TNY_IS_STORE_ACCOUNT(store_account), FALSE);
505
506         /* Pick async call reference */
507         g_object_ref (store_account);
508
509         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
510
511         priv->total = 0;
512         priv->done  = 0;
513         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
514         
515         /* Get subscribed folders & refresh them */
516         folders = TNY_LIST (tny_simple_list_new ());
517
518         g_message ("tny_folder_store_get_folders_async");
519         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
520                                             folders, update_folders_cb, NULL, update_folders_status_cb, self);
521         
522         return TRUE;
523 }
524
525 ModestMailOperationStatus
526 modest_mail_operation_get_status (ModestMailOperation *self)
527 {
528         ModestMailOperationPrivate *priv;
529
530         g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
531         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
532                               MODEST_MAIL_OPERATION_STATUS_INVALID);
533
534         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
535         return priv->status;
536 }
537
538 const GError *
539 modest_mail_operation_get_error (ModestMailOperation *self)
540 {
541         ModestMailOperationPrivate *priv;
542
543         g_return_val_if_fail (self, NULL);
544         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
545
546         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
547         return priv->error;
548 }
549
550 gboolean 
551 modest_mail_operation_cancel (ModestMailOperation *self)
552 {
553         ModestMailOperationPrivate *priv;
554
555         if (!MODEST_IS_MAIL_OPERATION (self)) {
556                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
557                 return FALSE;
558         }
559
560         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
561
562         /* TODO: Tinymail does not support cancel operation  */
563         
564         /* Set new status */
565         priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
566
567         /* Notify the queue */
568         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
569
570         return TRUE;
571 }
572
573 guint 
574 modest_mail_operation_get_task_done (ModestMailOperation *self)
575 {
576         ModestMailOperationPrivate *priv;
577
578         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
579
580         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
581         return priv->done;
582 }
583
584 guint 
585 modest_mail_operation_get_task_total (ModestMailOperation *self)
586 {
587         ModestMailOperationPrivate *priv;
588
589         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
590
591         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
592         return priv->total;
593 }
594
595 gboolean
596 modest_mail_operation_is_finished (ModestMailOperation *self)
597 {
598         ModestMailOperationPrivate *priv;
599         gboolean retval = FALSE;
600
601         if (!MODEST_IS_MAIL_OPERATION (self)) {
602                 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
603                 return retval;
604         }
605
606         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
607
608         if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS   ||
609             priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED    ||
610             priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED  ||
611             priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
612                 retval = TRUE;
613         } else {
614                 retval = FALSE;
615         }
616
617         return retval;
618 }
619
620 /* ******************************************************************* */
621 /* ************************** STORE  ACTIONS ************************* */
622 /* ******************************************************************* */
623
624
625 TnyFolder *
626 modest_mail_operation_create_folder (ModestMailOperation *self,
627                                      TnyFolderStore *parent,
628                                      const gchar *name)
629 {
630         ModestTnyFolderRules rules;
631         ModestMailOperationPrivate *priv;
632         TnyFolder *new_folder = NULL;
633         gboolean can_create = FALSE;
634
635         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
636         g_return_val_if_fail (name, NULL);
637         
638         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
639
640         /* Check parent */
641         if (!TNY_IS_FOLDER (parent)) {
642                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
643                              MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
644                              _("mail_in_ui_folder_create_error"));
645         } else {
646                 /* Check folder rules */
647                 rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
648                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)
649                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
650                                      MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
651                                      _("mail_in_ui_folder_create_error"));
652                 else
653                         can_create = TRUE;              
654         }
655
656         if (can_create) {
657                 /* Create the folder */
658                 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
659                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
660         }
661
662         /* Notify the queue */
663         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
664
665         return new_folder;
666 }
667
668 void
669 modest_mail_operation_remove_folder (ModestMailOperation *self,
670                                      TnyFolder           *folder,
671                                      gboolean             remove_to_trash)
672 {
673         TnyAccount *account;
674         ModestMailOperationPrivate *priv;
675         ModestTnyFolderRules rules;
676
677         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
678         g_return_if_fail (TNY_IS_FOLDER (folder));
679
680         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
681
682         /* Check folder rules */
683         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
684         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
685                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
686                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
687                              _("mail_in_ui_folder_delete_error"));
688                 goto end;
689         }
690
691         /* Get the account */
692         account = tny_folder_get_account (folder);
693
694         /* Delete folder or move to trash */
695         if (remove_to_trash) {
696                 TnyFolder *trash_folder, *new_folder;
697                 trash_folder = modest_tny_account_get_special_folder (account,
698                                                                       TNY_FOLDER_TYPE_TRASH);
699                 /* TODO: error_handling */
700                 new_folder = modest_mail_operation_xfer_folder (self, folder, 
701                                                                 TNY_FOLDER_STORE (trash_folder), TRUE);
702                 g_object_unref (G_OBJECT (new_folder));
703         } else {
704                 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
705
706                 tny_folder_store_remove_folder (parent, folder, &(priv->error));
707                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
708
709                 if (parent)
710                         g_object_unref (G_OBJECT (parent));
711         }
712         g_object_unref (G_OBJECT (account));
713
714  end:
715         /* Notify the queue */
716         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
717 }
718
719 void
720 modest_mail_operation_rename_folder (ModestMailOperation *self,
721                                      TnyFolder *folder,
722                                      const gchar *name)
723 {
724         ModestMailOperationPrivate *priv;
725         ModestTnyFolderRules rules;
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         /* Check folder rules */
734         rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
735         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
736                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
737                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
738                              _("FIXME: unable to rename"));
739         } else {
740                 /* Rename. Camel handles folder subscription/unsubscription */
741                 tny_folder_set_name (folder, name, &(priv->error));
742                 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
743         }
744
745         /* Notify the queue */
746         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
747  }
748
749 TnyFolder *
750 modest_mail_operation_xfer_folder (ModestMailOperation *self,
751                                    TnyFolder *folder,
752                                    TnyFolderStore *parent,
753                                    gboolean delete_original)
754 {
755         ModestMailOperationPrivate *priv;
756         TnyFolder *new_folder = NULL;
757         ModestTnyFolderRules rules;
758
759         g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
760         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
761         g_return_val_if_fail (TNY_IS_FOLDER (folder), NULL);
762
763         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
764
765         /* The moveable restriction is applied also to copy operation */
766         rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
767         if (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE) {
768                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
769                              MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
770                              _("FIXME: unable to rename"));
771         } else {
772                 /* Move/Copy folder */
773                 new_folder = tny_folder_copy (folder,
774                                               parent,
775                                               tny_folder_get_name (folder),
776                                               delete_original, 
777                                               &(priv->error));
778         }
779
780         /* Notify the queue */
781         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
782
783         return new_folder;
784 }
785
786
787 /* ******************************************************************* */
788 /* **************************  MSG  ACTIONS  ************************* */
789 /* ******************************************************************* */
790
791 void          modest_mail_operation_process_msg     (ModestMailOperation *self,
792                                                      TnyHeader *header, 
793                                                      guint num_ops,
794                                                      TnyGetMsgCallback user_callback,
795                                                      gpointer user_data)
796 {
797         TnyFolder *folder;
798         ModestMailOperationPrivate *priv;
799         
800         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
801         g_return_if_fail (TNY_IS_HEADER (header));
802         
803         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
804         folder = tny_header_get_folder (header);
805
806         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
807         priv->total = num_ops;
808
809         /* Get message from folder */
810         if (folder) {
811                 /* The callback will call it per each header */
812                 tny_folder_get_msg_async (folder, header, user_callback, update_process_msg_status_cb, user_data);
813                 g_object_unref (G_OBJECT (folder));
814         } else {
815                 /* Set status failed and set an error */
816                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
817                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
818                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
819                              _("Error trying to get a message. No folder found for header"));
820         }
821 }
822
823 static void     
824 update_process_msg_status_cb (GObject *obj,
825                               TnyStatus *status,  
826                               gpointer user_data)
827 {
828         ModestMailOperation *self;
829         ModestMailOperationPrivate *priv;
830
831         g_return_if_fail (status != NULL);
832         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
833
834         self = MODEST_MAIL_OPERATION (user_data);
835         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
836
837         priv->done += status->position;
838
839         if (priv->done == 1 && priv->total == 100)
840                 return;
841
842         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
843 }
844
845
846
847 void 
848 modest_mail_operation_remove_msg (ModestMailOperation *self,
849                                   TnyHeader *header,
850                                   gboolean remove_to_trash)
851 {
852         TnyFolder *folder;
853         ModestMailOperationPrivate *priv;
854
855         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
856         g_return_if_fail (TNY_IS_HEADER (header));
857
858         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
859         folder = tny_header_get_folder (header);
860
861         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
862
863         /* Delete or move to trash */
864         if (remove_to_trash) {
865                 TnyFolder *trash_folder;
866                 TnyStoreAccount *store_account;
867
868                 store_account = TNY_STORE_ACCOUNT (tny_folder_get_account (folder));
869                 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
870                                                                       TNY_FOLDER_TYPE_TRASH);
871                 if (trash_folder) {
872                         TnyList *headers;
873
874                         /* Create list */
875                         headers = tny_simple_list_new ();
876                         tny_list_append (headers, G_OBJECT (header));
877                         g_object_unref (header);
878
879                         /* Move to trash */
880                         modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE);
881                         g_object_unref (headers);
882 /*                      g_object_unref (trash_folder); */
883                 } else {
884                         ModestMailOperationPrivate *priv;
885
886                         /* Set status failed and set an error */
887                         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
888                         priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
889                         g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
890                                      MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
891                                      _("Error trying to delete a message. Trash folder not found"));
892                 }
893
894                 g_object_unref (G_OBJECT (store_account));
895         } else {
896                 tny_folder_remove_msg (folder, header, &(priv->error));
897                 if (!priv->error)
898                         tny_folder_sync(folder, TRUE, &(priv->error));
899         }
900
901         /* Set status */
902         if (!priv->error)
903                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
904         else
905                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
906
907         /* Free */
908         g_object_unref (G_OBJECT (folder));
909
910         /* Notify the queue */
911         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
912 }
913
914 static void
915 transfer_msgs_status_cb (GObject *obj,
916                          TnyStatus *status,  
917                          gpointer user_data)
918 {
919 }
920
921
922 static void
923 transfer_msgs_cb (TnyFolder *folder, GError **err, gpointer user_data)
924 {
925         XFerMsgAsyncHelper *helper;
926         ModestMailOperation *self;
927         ModestMailOperationPrivate *priv;
928
929         helper = (XFerMsgAsyncHelper *) user_data;
930         self = helper->mail_op;
931
932         priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
933
934         if (*err) {
935                 priv->error = g_error_copy (*err);
936                 priv->done = 0;
937                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
938         } else {
939                 priv->done = 1;
940                 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
941         }
942
943         /* Free */
944         g_object_unref (helper->headers);
945         g_object_unref (helper->dest_folder);
946         g_object_unref (folder);
947         g_free (helper);
948
949         /* Notify the queue */
950         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
951 }
952
953 void
954 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
955                                  TnyList *headers, 
956                                  TnyFolder *folder, 
957                                  gboolean delete_original)
958 {
959         ModestMailOperationPrivate *priv;
960         TnyIterator *iter;
961         TnyFolder *src_folder;
962         XFerMsgAsyncHelper *helper;
963         TnyHeader *header;
964
965         g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
966         g_return_if_fail (TNY_IS_LIST (headers));
967         g_return_if_fail (TNY_IS_FOLDER (folder));
968
969         /* Pick references for async calls */
970         g_object_ref (folder);
971
972         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
973         priv->total = 1;
974         priv->done = 0;
975         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
976
977         /* Create the helper */
978         helper = g_malloc0 (sizeof (XFerMsgAsyncHelper));
979         helper->mail_op = self;
980         helper->dest_folder = folder;
981         helper->headers = headers;
982
983         /* Get source folder */
984         iter = tny_list_create_iterator (headers);
985         header = TNY_HEADER (tny_iterator_get_current (iter));
986         src_folder = tny_header_get_folder (header);
987         g_object_unref (header);
988         g_object_unref (iter);
989
990         /* Transfer messages */
991         tny_folder_transfer_msgs_async (src_folder, 
992                                         headers, 
993                                         folder, 
994                                         delete_original, 
995                                         transfer_msgs_cb, 
996                                         transfer_msgs_status_cb, 
997                                         helper);
998 }
999
1000
1001 static void
1002 on_refresh_folder (TnyFolder   *folder, 
1003                    gboolean     cancelled, 
1004                    GError     **error,
1005                    gpointer     user_data)
1006 {
1007         ModestMailOperation *self;
1008         ModestMailOperationPrivate *priv;
1009
1010         self = MODEST_MAIL_OPERATION (user_data);
1011         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1012
1013         if (*error) {
1014                 priv->error = g_error_copy (*error);
1015                 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1016                 goto out;
1017         }
1018
1019         if (cancelled) {
1020                 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1021                 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1022                              MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1023                              _("Error trying to refresh the contents of %s"),
1024                              tny_folder_get_name (folder));
1025                 goto out;
1026         }
1027
1028         priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1029
1030  out:
1031         /* Free */
1032         g_object_unref (folder);
1033
1034         /* Notify the queue */
1035         modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self);
1036 }
1037
1038 static void
1039 on_refresh_folder_status_update (GObject *obj,
1040                                  TnyStatus *status,
1041                                  gpointer user_data)
1042 {
1043         ModestMailOperation *self;
1044         ModestMailOperationPrivate *priv;
1045
1046         g_return_if_fail (status != NULL);
1047         g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
1048
1049         /* Temporary FIX: useful when tinymail send us status
1050            information *after* calling the function callback */
1051         if (!MODEST_IS_MAIL_OPERATION (user_data))
1052                 return;
1053
1054         self = MODEST_MAIL_OPERATION (user_data);
1055         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1056
1057         priv->done = status->position;
1058         priv->total = status->of_total;
1059
1060         if (priv->done == 1 && priv->total == 100)
1061                 return;
1062
1063         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1064 }
1065
1066 void 
1067 modest_mail_operation_refresh_folder  (ModestMailOperation *self,
1068                                        TnyFolder *folder)
1069 {
1070         ModestMailOperationPrivate *priv;
1071
1072         priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1073
1074         /* Pick a reference */
1075         g_object_ref (folder);
1076
1077         priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1078
1079         /* Refresh the folder. TODO: tinymail could issue a status
1080            updates before the callback call then this could happen. We
1081            must review the design */
1082         tny_folder_refresh_async (folder,
1083                                   on_refresh_folder,
1084                                   on_refresh_folder_status_update,
1085                                   self);
1086 }
1087
1088 void
1089 _modest_mail_operation_notify_end (ModestMailOperation *self)
1090 {
1091         g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, NULL);
1092 }