1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-camel-pop-folder.h>
39 #include <tny-camel-imap-folder.h>
40 #include <tny-camel-mem-stream.h>
41 #include <tny-simple-list.h>
42 #include <tny-send-queue.h>
43 #include <tny-status.h>
44 #include <tny-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include <modest-tny-account.h>
49 #include <modest-tny-send-queue.h>
50 #include <modest-runtime.h>
51 #include "modest-text-utils.h"
52 #include "modest-tny-msg.h"
53 #include "modest-tny-folder.h"
54 #include "modest-tny-account-store.h"
55 #include "modest-tny-platform-factory.h"
56 #include "modest-marshal.h"
57 #include "modest-error.h"
58 #include "modest-mail-operation.h"
63 * Remove all these #ifdef stuff when the tinymail's idle calls become
66 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
68 /* 'private'/'protected' functions */
69 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
70 static void modest_mail_operation_init (ModestMailOperation *obj);
71 static void modest_mail_operation_finalize (GObject *obj);
73 static void get_msg_cb (TnyFolder *folder,
79 static void get_msg_status_cb (GObject *obj,
83 static void modest_mail_operation_notify_end (ModestMailOperation *self);
85 enum _ModestMailOperationSignals
87 PROGRESS_CHANGED_SIGNAL,
92 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
93 struct _ModestMailOperationPrivate {
100 ErrorCheckingUserCallback error_checking;
101 gpointer error_checking_user_data;
102 ModestMailOperationStatus status;
103 ModestMailOperationTypeOperation op_type;
106 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
107 MODEST_TYPE_MAIL_OPERATION, \
108 ModestMailOperationPrivate))
110 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
111 priv->status = new_status;\
114 typedef struct _GetMsgAsyncHelper {
115 ModestMailOperation *mail_op;
117 GetMsgAsyncUserCallback user_callback;
121 typedef struct _RefreshAsyncHelper {
122 ModestMailOperation *mail_op;
123 RefreshAsyncUserCallback user_callback;
125 } RefreshAsyncHelper;
127 typedef struct _XFerMsgAsyncHelper
129 ModestMailOperation *mail_op;
131 TnyFolder *dest_folder;
132 XferMsgsAsynUserCallback user_callback;
135 } XFerMsgAsyncHelper;
137 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
141 static void modest_mail_operation_create_msg (ModestMailOperation *self,
142 const gchar *from, const gchar *to,
143 const gchar *cc, const gchar *bcc,
144 const gchar *subject, const gchar *plain_body,
145 const gchar *html_body, const GList *attachments_list,
146 TnyHeaderFlags priority_flags,
147 ModestMailOperationCreateMsgCallback callback,
150 static gboolean idle_notify_queue (gpointer data);
153 ModestMailOperation *mail_op;
161 GList *attachments_list;
162 TnyHeaderFlags priority_flags;
163 ModestMailOperationCreateMsgCallback callback;
169 ModestMailOperation *mail_op;
171 ModestMailOperationCreateMsgCallback callback;
176 static GObjectClass *parent_class = NULL;
178 static guint signals[NUM_SIGNALS] = {0};
181 modest_mail_operation_get_type (void)
183 static GType my_type = 0;
185 static const GTypeInfo my_info = {
186 sizeof(ModestMailOperationClass),
187 NULL, /* base init */
188 NULL, /* base finalize */
189 (GClassInitFunc) modest_mail_operation_class_init,
190 NULL, /* class finalize */
191 NULL, /* class data */
192 sizeof(ModestMailOperation),
194 (GInstanceInitFunc) modest_mail_operation_init,
197 my_type = g_type_register_static (G_TYPE_OBJECT,
198 "ModestMailOperation",
205 modest_mail_operation_class_init (ModestMailOperationClass *klass)
207 GObjectClass *gobject_class;
208 gobject_class = (GObjectClass*) klass;
210 parent_class = g_type_class_peek_parent (klass);
211 gobject_class->finalize = modest_mail_operation_finalize;
213 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
216 * ModestMailOperation::progress-changed
217 * @self: the #MailOperation that emits the signal
218 * @user_data: user data set when the signal handler was connected
220 * Emitted when the progress of a mail operation changes
222 signals[PROGRESS_CHANGED_SIGNAL] =
223 g_signal_new ("progress-changed",
224 G_TYPE_FROM_CLASS (gobject_class),
226 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
228 g_cclosure_marshal_VOID__POINTER,
229 G_TYPE_NONE, 1, G_TYPE_POINTER);
234 modest_mail_operation_init (ModestMailOperation *obj)
236 ModestMailOperationPrivate *priv;
238 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
240 priv->account = NULL;
241 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
242 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
247 priv->error_checking = NULL;
248 priv->error_checking_user_data = NULL;
252 modest_mail_operation_finalize (GObject *obj)
254 ModestMailOperationPrivate *priv;
256 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
261 g_error_free (priv->error);
265 g_object_unref (priv->source);
269 g_object_unref (priv->account);
270 priv->account = NULL;
274 G_OBJECT_CLASS(parent_class)->finalize (obj);
278 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
281 ModestMailOperation *obj;
282 ModestMailOperationPrivate *priv;
284 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
285 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
287 priv->op_type = op_type;
289 priv->source = g_object_ref(source);
295 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
297 ErrorCheckingUserCallback error_handler,
300 ModestMailOperation *obj;
301 ModestMailOperationPrivate *priv;
303 obj = modest_mail_operation_new (op_type, source);
304 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
306 g_return_val_if_fail (error_handler != NULL, obj);
307 priv->error_checking = error_handler;
313 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
315 ModestMailOperationPrivate *priv;
317 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
318 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
320 if (priv->error_checking != NULL)
321 priv->error_checking (self, priv->error_checking_user_data);
325 ModestMailOperationTypeOperation
326 modest_mail_operation_get_type_operation (ModestMailOperation *self)
328 ModestMailOperationPrivate *priv;
330 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
332 return priv->op_type;
336 modest_mail_operation_is_mine (ModestMailOperation *self,
339 ModestMailOperationPrivate *priv;
341 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
342 if (priv->source == NULL) return FALSE;
344 return priv->source == me;
348 modest_mail_operation_get_source (ModestMailOperation *self)
350 ModestMailOperationPrivate *priv;
352 g_return_val_if_fail (self, NULL);
354 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
356 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
360 return g_object_ref (priv->source);
363 ModestMailOperationStatus
364 modest_mail_operation_get_status (ModestMailOperation *self)
366 ModestMailOperationPrivate *priv;
368 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
369 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
370 MODEST_MAIL_OPERATION_STATUS_INVALID);
372 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
374 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
375 return MODEST_MAIL_OPERATION_STATUS_INVALID;
382 modest_mail_operation_get_error (ModestMailOperation *self)
384 ModestMailOperationPrivate *priv;
386 g_return_val_if_fail (self, NULL);
387 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
389 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
392 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
400 modest_mail_operation_cancel (ModestMailOperation *self)
402 ModestMailOperationPrivate *priv;
403 gboolean canceled = FALSE;
405 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
407 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
409 /* Note that if we call cancel with an already canceled mail
410 operation the progress changed signal won't be emitted */
411 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
415 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
417 /* Cancel the mail operation. We need to wrap it between this
418 start/stop operations to allow following calls to the
420 g_return_val_if_fail (priv->account, FALSE);
421 tny_account_cancel (priv->account);
427 modest_mail_operation_get_task_done (ModestMailOperation *self)
429 ModestMailOperationPrivate *priv;
431 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
433 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
438 modest_mail_operation_get_task_total (ModestMailOperation *self)
440 ModestMailOperationPrivate *priv;
442 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
444 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
449 modest_mail_operation_is_finished (ModestMailOperation *self)
451 ModestMailOperationPrivate *priv;
452 gboolean retval = FALSE;
454 if (!MODEST_IS_MAIL_OPERATION (self)) {
455 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
459 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
461 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
462 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
463 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
464 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
474 modest_mail_operation_get_id (ModestMailOperation *self)
476 ModestMailOperationPrivate *priv;
478 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
480 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
485 modest_mail_operation_set_id (ModestMailOperation *self,
488 ModestMailOperationPrivate *priv;
490 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
492 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
497 * Creates an image of the current state of a mail operation, the
498 * caller must free it
500 static ModestMailOperationState *
501 modest_mail_operation_clone_state (ModestMailOperation *self)
503 ModestMailOperationState *state;
504 ModestMailOperationPrivate *priv;
506 /* FIXME: this should be fixed properly
508 * in some cases, priv was NULL, so checking here to
511 g_return_val_if_fail (self, NULL);
512 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
513 g_return_val_if_fail (priv, NULL);
518 state = g_slice_new (ModestMailOperationState);
520 state->status = priv->status;
521 state->op_type = priv->op_type;
522 state->done = priv->done;
523 state->total = priv->total;
524 state->finished = modest_mail_operation_is_finished (self);
525 state->bytes_done = 0;
526 state->bytes_total = 0;
531 /* ******************************************************************* */
532 /* ************************** SEND ACTIONS ************************* */
533 /* ******************************************************************* */
536 modest_mail_operation_send_mail (ModestMailOperation *self,
537 TnyTransportAccount *transport_account,
540 TnySendQueue *send_queue = NULL;
541 ModestMailOperationPrivate *priv;
543 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
544 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
545 g_return_if_fail (TNY_IS_MSG (msg));
547 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
549 /* Get account and set it into mail_operation */
550 priv->account = g_object_ref (transport_account);
554 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
555 if (!TNY_IS_SEND_QUEUE(send_queue)) {
556 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
557 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
558 "modest: could not find send queue for account\n");
560 /* TODO: connect to the msg-sent in order to know when
561 the mail operation is finished */
562 tny_send_queue_add (send_queue, msg, &(priv->error));
563 /* TODO: we're setting always success, do the check in
565 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
568 /* TODO: do this in the handler of the "msg-sent"
569 signal.Notify about operation end */
570 modest_mail_operation_notify_end (self);
574 idle_create_msg_cb (gpointer idle_data)
576 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
578 /* This is a GDK lock because we are an idle callback and
579 * info->callback can contain Gtk+ code */
581 gdk_threads_enter (); /* CHECKED */
582 info->callback (info->mail_op, info->msg, info->userdata);
583 gdk_threads_leave (); /* CHECKED */
585 g_object_unref (info->mail_op);
587 g_object_unref (info->msg);
588 g_slice_free (CreateMsgIdleInfo, info);
594 create_msg_thread (gpointer thread_data)
596 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
597 TnyMsg *new_msg = NULL;
598 ModestMailOperationPrivate *priv;
600 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
601 if (info->html_body == NULL) {
602 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
603 info->bcc, info->subject, info->plain_body,
604 info->attachments_list); /* FIXME: attachments */
606 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
607 info->bcc, info->subject, info->html_body,
608 info->plain_body, info->attachments_list);
613 TnyHeaderFlags flags = 0;
615 /* Set priority flags in message */
616 header = tny_msg_get_header (new_msg);
617 if (info->priority_flags != 0)
618 flags |= info->priority_flags;
620 /* Set attachment flags in message */
621 if (info->attachments_list != NULL)
622 flags |= TNY_HEADER_FLAG_ATTACHMENTS;
624 tny_header_set_flags (header, flags);
625 g_object_unref (G_OBJECT(header));
627 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
628 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
629 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
630 "modest: failed to create a new msg\n");
638 g_free (info->plain_body);
639 g_free (info->html_body);
640 g_free (info->subject);
641 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
642 g_list_free (info->attachments_list);
644 if (info->callback) {
645 CreateMsgIdleInfo *idle_info;
646 idle_info = g_slice_new0 (CreateMsgIdleInfo);
647 idle_info->mail_op = info->mail_op;
648 g_object_ref (info->mail_op);
649 idle_info->msg = new_msg;
651 g_object_ref (new_msg);
652 idle_info->callback = info->callback;
653 idle_info->userdata = info->userdata;
654 g_idle_add (idle_create_msg_cb, idle_info);
656 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
659 g_object_unref (info->mail_op);
660 g_slice_free (CreateMsgInfo, info);
665 modest_mail_operation_create_msg (ModestMailOperation *self,
666 const gchar *from, const gchar *to,
667 const gchar *cc, const gchar *bcc,
668 const gchar *subject, const gchar *plain_body,
669 const gchar *html_body,
670 const GList *attachments_list,
671 TnyHeaderFlags priority_flags,
672 ModestMailOperationCreateMsgCallback callback,
675 CreateMsgInfo *info = NULL;
677 info = g_slice_new0 (CreateMsgInfo);
678 info->mail_op = self;
681 info->from = g_strdup (from);
682 info->to = g_strdup (to);
683 info->cc = g_strdup (cc);
684 info->bcc = g_strdup (bcc);
685 info->subject = g_strdup (subject);
686 info->plain_body = g_strdup (plain_body);
687 info->html_body = g_strdup (html_body);
688 info->attachments_list = g_list_copy ((GList *) attachments_list);
689 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
690 info->priority_flags = priority_flags;
692 info->callback = callback;
693 info->userdata = userdata;
695 g_thread_create (create_msg_thread, info, FALSE, NULL);
700 TnyTransportAccount *transport_account;
705 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
709 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
710 TnyFolder *draft_folder = NULL;
711 TnyFolder *outbox_folder = NULL;
718 /* Call mail operation */
719 modest_mail_operation_send_mail (self, info->transport_account, msg);
721 /* Remove old mail from its source folder */
722 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
723 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
724 if (info->draft_msg != NULL) {
725 TnyFolder *folder = NULL;
726 TnyFolder *src_folder = NULL;
727 TnyFolderType folder_type;
728 folder = tny_msg_get_folder (info->draft_msg);
729 if (folder == NULL) goto end;
730 folder_type = modest_tny_folder_guess_folder_type (folder);
731 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
732 src_folder = outbox_folder;
734 src_folder = draft_folder;
736 /* Note: This can fail (with a warning) if the message is not really already in a folder,
737 * because this function requires it to have a UID. */
738 header = tny_msg_get_header (info->draft_msg);
739 tny_folder_remove_msg (src_folder, header, NULL);
740 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
741 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
742 g_object_unref (header);
743 g_object_unref (folder);
748 g_object_unref (info->draft_msg);
750 g_object_unref (draft_folder);
752 g_object_unref (outbox_folder);
753 if (info->transport_account)
754 g_object_unref (info->transport_account);
755 g_slice_free (SendNewMailInfo, info);
756 modest_mail_operation_notify_end (self);
760 modest_mail_operation_send_new_mail (ModestMailOperation *self,
761 TnyTransportAccount *transport_account,
763 const gchar *from, const gchar *to,
764 const gchar *cc, const gchar *bcc,
765 const gchar *subject, const gchar *plain_body,
766 const gchar *html_body,
767 const GList *attachments_list,
768 TnyHeaderFlags priority_flags)
770 ModestMailOperationPrivate *priv = NULL;
771 SendNewMailInfo *info;
773 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
774 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
776 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
778 /* Check parametters */
780 /* Set status failed and set an error */
781 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
782 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
783 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
784 _("Error trying to send a mail. You need to set at least one recipient"));
787 info = g_slice_new0 (SendNewMailInfo);
788 info->transport_account = transport_account;
789 if (transport_account)
790 g_object_ref (transport_account);
791 info->draft_msg = draft_msg;
793 g_object_ref (draft_msg);
794 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
795 attachments_list, priority_flags,
796 modest_mail_operation_send_new_mail_cb, info);
802 TnyTransportAccount *transport_account;
804 ModestMsgEditWindow *edit_window;
808 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
812 TnyFolder *src_folder = NULL;
813 TnyFolder *folder = NULL;
814 TnyHeader *header = NULL;
815 ModestMailOperationPrivate *priv = NULL;
816 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
818 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
820 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
821 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
822 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
823 "modest: failed to create a new msg\n");
827 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
829 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
830 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
831 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
832 "modest: failed to create a new msg\n");
837 tny_folder_add_msg (folder, msg, &(priv->error));
839 if ((!priv->error) && (info->draft_msg != NULL)) {
840 header = tny_msg_get_header (info->draft_msg);
841 src_folder = tny_header_get_folder (header);
842 /* Remove the old draft expunging it */
843 tny_folder_remove_msg (src_folder, header, NULL);
844 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
845 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
846 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /* expunge */
847 g_object_unref (header);
851 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
853 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
855 if (info->edit_window)
856 modest_msg_edit_window_set_draft (info->edit_window, msg);
861 g_object_unref (G_OBJECT(folder));
863 g_object_unref (G_OBJECT(src_folder));
864 if (info->edit_window)
865 g_object_unref (G_OBJECT(info->edit_window));
867 g_object_unref (G_OBJECT (info->draft_msg));
868 if (info->transport_account)
869 g_object_unref (G_OBJECT(info->transport_account));
870 g_slice_free (SaveToDraftsInfo, info);
872 modest_mail_operation_notify_end (self);
876 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
877 TnyTransportAccount *transport_account,
879 ModestMsgEditWindow *edit_window,
880 const gchar *from, const gchar *to,
881 const gchar *cc, const gchar *bcc,
882 const gchar *subject, const gchar *plain_body,
883 const gchar *html_body,
884 const GList *attachments_list,
885 TnyHeaderFlags priority_flags)
887 ModestMailOperationPrivate *priv = NULL;
888 SaveToDraftsInfo *info = NULL;
890 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
891 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
893 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
895 /* Get account and set it into mail_operation */
896 priv->account = g_object_ref (transport_account);
898 info = g_slice_new0 (SaveToDraftsInfo);
899 info->transport_account = g_object_ref (transport_account);
900 info->draft_msg = draft_msg;
902 g_object_ref (draft_msg);
903 info->edit_window = edit_window;
905 g_object_ref (edit_window);
907 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
908 attachments_list, priority_flags,
909 modest_mail_operation_save_to_drafts_cb, info);
915 ModestMailOperation *mail_op;
916 TnyStoreAccount *account;
917 TnyTransportAccount *transport_account;
920 gchar *retrieve_type;
922 UpdateAccountCallback callback;
929 ModestMailOperation *mail_op;
930 TnyMimePart *mime_part;
932 GetMimePartSizeCallback callback;
934 } GetMimePartSizeInfo;
936 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
937 /* We use this folder observer to track the headers that have been
938 * added to a folder */
941 TnyList *new_headers;
942 } InternalFolderObserver;
946 } InternalFolderObserverClass;
948 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
950 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
951 internal_folder_observer,
953 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
957 foreach_add_item (gpointer header, gpointer user_data)
959 tny_list_prepend (TNY_LIST (user_data),
960 g_object_ref (G_OBJECT (header)));
963 /* This is the method that looks for new messages in a folder */
965 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
967 InternalFolderObserver *derived = (InternalFolderObserver *)self;
969 TnyFolderChangeChanged changed;
971 changed = tny_folder_change_get_changed (change);
973 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
976 /* Get added headers */
977 list = tny_simple_list_new ();
978 tny_folder_change_get_added_headers (change, list);
980 /* Add them to the folder observer */
981 tny_list_foreach (list, foreach_add_item,
982 derived->new_headers);
984 g_object_unref (G_OBJECT (list));
989 internal_folder_observer_init (InternalFolderObserver *self)
991 self->new_headers = tny_simple_list_new ();
994 internal_folder_observer_finalize (GObject *object)
996 InternalFolderObserver *self;
998 self = (InternalFolderObserver *) object;
999 g_object_unref (self->new_headers);
1001 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1004 tny_folder_observer_init (TnyFolderObserverIface *iface)
1006 iface->update_func = internal_folder_observer_update;
1009 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1011 GObjectClass *object_class;
1013 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1014 object_class = (GObjectClass*) klass;
1015 object_class->finalize = internal_folder_observer_finalize;
1021 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1024 TnyList *folders = tny_simple_list_new ();
1026 tny_folder_store_get_folders (store, folders, query, NULL);
1027 iter = tny_list_create_iterator (folders);
1029 while (!tny_iterator_is_done (iter)) {
1031 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1033 tny_list_prepend (all_folders, G_OBJECT (folder));
1034 recurse_folders (folder, query, all_folders);
1035 g_object_unref (G_OBJECT (folder));
1038 tny_iterator_next (iter);
1040 g_object_unref (G_OBJECT (iter));
1041 g_object_unref (G_OBJECT (folders));
1045 * Issues the "progress-changed" signal. The timer won't be removed,
1046 * so you must call g_source_remove to stop the signal emission
1049 idle_notify_progress (gpointer data)
1051 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1052 ModestMailOperationState *state;
1054 state = modest_mail_operation_clone_state (mail_op);
1056 /* This is a GDK lock because we are an idle callback and
1057 * the handlers of this signal can contain Gtk+ code */
1059 gdk_threads_enter (); /* CHECKED */
1060 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1061 gdk_threads_leave (); /* CHECKED */
1063 g_slice_free (ModestMailOperationState, state);
1069 * Issues the "progress-changed" signal and removes the timer. It uses
1070 * a lock to ensure that the progress information of the mail
1071 * operation is not modified while there are notifications pending
1074 idle_notify_progress_once (gpointer data)
1078 pair = (ModestPair *) data;
1080 /* This is a GDK lock because we are an idle callback and
1081 * the handlers of this signal can contain Gtk+ code */
1083 gdk_threads_enter (); /* CHECKED */
1084 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1085 gdk_threads_leave (); /* CHECKED */
1087 /* Free the state and the reference to the mail operation */
1088 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1089 g_object_unref (pair->first);
1095 * Used to notify the queue from the main
1096 * loop. We call it inside an idle call to achieve that
1099 idle_notify_queue (gpointer data)
1101 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1103 /* Do not need to block, the notify end will do it for us */
1104 modest_mail_operation_notify_end (mail_op);
1105 g_object_unref (mail_op);
1111 compare_headers_by_date (gconstpointer a,
1114 TnyHeader **header1, **header2;
1115 time_t sent1, sent2;
1117 header1 = (TnyHeader **) a;
1118 header2 = (TnyHeader **) b;
1120 sent1 = tny_header_get_date_sent (*header1);
1121 sent2 = tny_header_get_date_sent (*header2);
1123 /* We want the most recent ones (greater time_t) at the
1132 set_last_updated_idle (gpointer data)
1135 /* This is a GDK lock because we are an idle callback and
1136 * modest_account_mgr_set_int can contain Gtk+ code */
1138 gdk_threads_enter (); /* CHECKED - please recheck */
1140 /* It does not matter if the time is not exactly the same than
1141 the time when this idle was called, it's just an
1142 approximation and it won't be very different */
1144 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
1146 MODEST_ACCOUNT_LAST_UPDATED,
1150 gdk_threads_leave (); /* CHECKED - please recheck */
1156 idle_update_account_cb (gpointer data)
1158 UpdateAccountInfo *idle_info;
1160 idle_info = (UpdateAccountInfo *) data;
1162 /* This is a GDK lock because we are an idle callback and
1163 * idle_info->callback can contain Gtk+ code */
1165 gdk_threads_enter (); /* CHECKED */
1166 idle_info->callback (idle_info->mail_op,
1167 idle_info->new_headers,
1168 idle_info->user_data);
1169 gdk_threads_leave (); /* CHECKED */
1172 g_object_unref (idle_info->mail_op);
1180 update_account_thread (gpointer thr_user_data)
1182 static gboolean first_time = TRUE;
1183 UpdateAccountInfo *info = NULL;
1184 TnyList *all_folders = NULL;
1185 GPtrArray *new_headers = NULL;
1186 TnyIterator *iter = NULL;
1187 TnyFolderStoreQuery *query = NULL;
1188 ModestMailOperationPrivate *priv = NULL;
1189 ModestTnySendQueue *send_queue = NULL;
1190 gint num_new_headers = 0;
1192 info = (UpdateAccountInfo *) thr_user_data;
1193 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1195 /* Get account and set it into mail_operation */
1196 priv->account = g_object_ref (info->account);
1199 * Previousl, we did this for POP3, to do a logout-login upon send/receive,
1200 * because many POP-servers (like Gmail) do not
1201 * show any updates unless we do that.
1202 * But that didn't work with gmail anyway,
1203 * and tinymail now takes care of this itself by disconnecting
1204 * automatically after using the connection.
1207 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account))
1208 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1211 /* Get all the folders. We can do it synchronously because
1212 we're already running in a different thread than the UI */
1213 all_folders = tny_simple_list_new ();
1214 query = tny_folder_store_query_new ();
1215 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1216 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1221 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1225 iter = tny_list_create_iterator (all_folders);
1226 while (!tny_iterator_is_done (iter)) {
1227 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1229 recurse_folders (folder, query, all_folders);
1230 g_object_unref (folder);
1232 tny_iterator_next (iter);
1234 g_object_unref (G_OBJECT (iter));
1236 /* Update status and notify. We need to call the notification
1237 with a source function in order to call it from the main
1238 loop. We need that in order not to get into trouble with
1239 Gtk+. We use a timeout in order to provide more status
1240 information, because the sync tinymail call does not
1241 provide it for the moment */
1242 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1244 /* Refresh folders */
1245 num_new_headers = 0;
1246 new_headers = g_ptr_array_new ();
1247 iter = tny_list_create_iterator (all_folders);
1249 while (!tny_iterator_is_done (iter) && !priv->error &&
1250 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1252 InternalFolderObserver *observer;
1253 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1255 /* Refresh the folder */
1256 /* Our observer receives notification of new emails during folder refreshes,
1257 * so we can use observer->new_headers.
1259 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1260 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1262 /* This gets the status information (headers) from the server.
1263 * We use the blocking version, because we are already in a separate
1267 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
1268 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1271 /* If the retrieve type is full messages, refresh and get the messages */
1272 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1274 iter = tny_list_create_iterator (observer->new_headers);
1275 while (!tny_iterator_is_done (iter)) {
1276 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1278 /* Apply per-message size limits */
1279 if (tny_header_get_message_size (header) < info->max_size)
1280 g_ptr_array_add (new_headers, g_object_ref (header));
1282 g_object_unref (header);
1283 tny_iterator_next (iter);
1285 g_object_unref (iter);
1287 /* We do not need to do it the first time
1288 because it's automatically done by the tree
1290 if (G_UNLIKELY (!first_time))
1291 tny_folder_poke_status (TNY_FOLDER (folder));
1293 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1294 g_object_unref (observer);
1298 g_object_unref (G_OBJECT (folder));
1301 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1303 tny_iterator_next (iter);
1306 g_object_unref (G_OBJECT (iter));
1307 g_source_remove (timeout);
1309 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1310 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1311 new_headers->len > 0) {
1315 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1317 /* Apply message count limit */
1318 /* If the number of messages exceeds the maximum, ask the
1319 * user to download them all,
1320 * as per the UI spec "Retrieval Limits" section in 4.4:
1322 if (new_headers->len > info->retrieve_limit) {
1323 /* TODO: Ask the user, instead of just
1325 * mail_nc_msg_count_limit_exceeded, with 'Get
1326 * all' and 'Newest only' buttons. */
1327 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1328 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1329 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1330 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1331 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1336 priv->total = MIN (new_headers->len, info->retrieve_limit);
1337 while (msg_num < priv->total) {
1339 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1340 TnyFolder *folder = tny_header_get_folder (header);
1341 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1342 ModestMailOperationState *state;
1346 /* We can not just use the mail operation because the
1347 values of done and total could change before the
1349 state = modest_mail_operation_clone_state (info->mail_op);
1350 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1351 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1352 pair, (GDestroyNotify) modest_pair_free);
1354 g_object_unref (msg);
1355 g_object_unref (folder);
1361 /* Get the number of new headers and free them */
1362 num_new_headers = new_headers->len;
1363 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1364 g_ptr_array_free (new_headers, FALSE);
1366 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1369 /* Perform send (if operation was not cancelled) */
1372 if (priv->account != NULL)
1373 g_object_unref (priv->account);
1374 priv->account = g_object_ref (info->transport_account);
1376 send_queue = modest_runtime_get_send_queue (info->transport_account);
1378 modest_tny_send_queue_try_to_send (send_queue);
1380 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1381 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1382 "cannot create a send queue for %s\n",
1383 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1384 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1387 /* Check if the operation was a success */
1389 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1391 /* Update the last updated key */
1392 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1393 set_last_updated_idle,
1394 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1395 (GDestroyNotify) g_free);
1400 if (info->callback) {
1401 UpdateAccountInfo *idle_info;
1403 /* This thread is not in the main lock */
1404 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1405 idle_info->mail_op = g_object_ref (info->mail_op);
1406 idle_info->new_headers = num_new_headers;
1407 idle_info->callback = info->callback;
1408 idle_info->user_data = info->user_data;
1409 g_idle_add (idle_update_account_cb, idle_info);
1412 /* Notify about operation end. Note that the info could be
1413 freed before this idle happens, but the mail operation will
1415 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1418 g_object_unref (query);
1419 g_object_unref (all_folders);
1420 g_object_unref (info->account);
1421 g_object_unref (info->transport_account);
1422 g_free (info->retrieve_type);
1423 g_slice_free (UpdateAccountInfo, info);
1431 modest_mail_operation_update_account (ModestMailOperation *self,
1432 const gchar *account_name,
1433 UpdateAccountCallback callback,
1436 GThread *thread = NULL;
1437 UpdateAccountInfo *info = NULL;
1438 ModestMailOperationPrivate *priv = NULL;
1439 ModestAccountMgr *mgr = NULL;
1440 TnyStoreAccount *store_account = NULL;
1441 TnyTransportAccount *transport_account = NULL;
1443 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1444 g_return_val_if_fail (account_name, FALSE);
1446 /* Init mail operation. Set total and done to 0, and do not
1447 update them, this way the progress objects will know that
1448 we have no clue about the number of the objects */
1449 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1452 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1454 /* Get the Modest account */
1455 store_account = (TnyStoreAccount *)
1456 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1458 TNY_ACCOUNT_TYPE_STORE);
1460 /* Make sure that we have a connection, and request one
1462 * TODO: Is there some way to trigger this for every attempt to
1463 * use the network? */
1464 if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1467 if (!store_account) {
1468 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1469 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1470 "cannot get tny store account for %s\n", account_name);
1475 /* Get the transport account, we can not do it in the thread
1476 due to some problems with dbus */
1477 transport_account = (TnyTransportAccount *)
1478 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1480 if (!transport_account) {
1481 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1482 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1483 "cannot get tny transport account for %s\n", account_name);
1487 /* Create the helper object */
1488 info = g_slice_new (UpdateAccountInfo);
1489 info->mail_op = self;
1490 info->account = store_account;
1491 info->transport_account = transport_account;
1492 info->callback = callback;
1493 info->user_data = user_data;
1495 /* Get the message size limit */
1496 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1497 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1498 if (info->max_size == 0)
1499 info->max_size = G_MAXINT;
1501 info->max_size = info->max_size * KB;
1503 /* Get per-account retrieval type */
1504 mgr = modest_runtime_get_account_mgr ();
1505 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1506 MODEST_ACCOUNT_RETRIEVE, FALSE);
1508 /* Get per-account message amount retrieval limit */
1509 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1510 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1511 if (info->retrieve_limit == 0)
1512 info->retrieve_limit = G_MAXINT;
1514 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1516 /* Set account busy */
1517 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1518 priv->account_name = g_strdup(account_name);
1520 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1525 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1527 callback (self, 0, user_data);
1528 modest_mail_operation_notify_end (self);
1532 /* ******************************************************************* */
1533 /* ************************** STORE ACTIONS ************************* */
1534 /* ******************************************************************* */
1538 modest_mail_operation_create_folder (ModestMailOperation *self,
1539 TnyFolderStore *parent,
1542 ModestMailOperationPrivate *priv;
1543 TnyFolder *new_folder = NULL;
1545 TnyList *list = tny_simple_list_new ();
1546 TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1548 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1549 g_return_val_if_fail (name, NULL);
1551 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1553 /* Check for already existing folder */
1554 tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1555 tny_folder_store_get_folders (parent, list, query, NULL);
1556 g_object_unref (G_OBJECT (query));
1558 if (tny_list_get_length (list) > 0) {
1559 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1560 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1561 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1562 _CS("ckdg_ib_folder_already_exists"));
1565 g_object_unref (G_OBJECT (list));
1568 if (TNY_IS_FOLDER (parent)) {
1569 /* Check folder rules */
1570 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1571 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1572 /* Set status failed and set an error */
1573 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1574 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1575 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1576 _("mail_in_ui_folder_create_error"));
1580 if (!strcmp (name, " ") || strchr (name, '/')) {
1581 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1582 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1583 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1584 _("mail_in_ui_folder_create_error"));
1588 /* Create the folder */
1589 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1590 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1592 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1595 /* Notify about operation end */
1596 modest_mail_operation_notify_end (self);
1602 modest_mail_operation_remove_folder (ModestMailOperation *self,
1604 gboolean remove_to_trash)
1606 TnyAccount *account;
1607 ModestMailOperationPrivate *priv;
1608 ModestTnyFolderRules rules;
1610 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1611 g_return_if_fail (TNY_IS_FOLDER (folder));
1613 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1615 /* Check folder rules */
1616 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1617 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1618 /* Set status failed and set an error */
1619 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1620 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1621 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1622 _("mail_in_ui_folder_delete_error"));
1626 /* Get the account */
1627 account = modest_tny_folder_get_account (folder);
1628 priv->account = g_object_ref(account);
1630 /* Delete folder or move to trash */
1631 if (remove_to_trash) {
1632 TnyFolder *trash_folder = NULL;
1633 trash_folder = modest_tny_account_get_special_folder (account,
1634 TNY_FOLDER_TYPE_TRASH);
1635 /* TODO: error_handling */
1637 modest_mail_operation_xfer_folder (self, folder,
1638 TNY_FOLDER_STORE (trash_folder),
1640 g_object_unref (trash_folder);
1643 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1645 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1646 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1649 g_object_unref (G_OBJECT (parent));
1651 g_object_unref (G_OBJECT (account));
1654 /* Notify about operation end */
1655 modest_mail_operation_notify_end (self);
1659 transfer_folder_status_cb (GObject *obj,
1663 ModestMailOperation *self;
1664 ModestMailOperationPrivate *priv;
1665 ModestMailOperationState *state;
1666 XFerMsgAsyncHelper *helper;
1668 g_return_if_fail (status != NULL);
1669 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1671 helper = (XFerMsgAsyncHelper *) user_data;
1672 g_return_if_fail (helper != NULL);
1674 self = helper->mail_op;
1675 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1677 priv->done = status->position;
1678 priv->total = status->of_total;
1680 state = modest_mail_operation_clone_state (self);
1682 /* This is not a GDK lock because we are a Tinymail callback
1683 * which is already GDK locked by Tinymail */
1685 /* no gdk_threads_enter (), CHECKED */
1687 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1689 /* no gdk_threads_leave (), CHECKED */
1691 g_slice_free (ModestMailOperationState, state);
1696 transfer_folder_cb (TnyFolder *folder, gboolean cancelled,
1697 TnyFolderStore *into,
1698 TnyFolder *new_folder,
1702 XFerMsgAsyncHelper *helper;
1703 ModestMailOperation *self = NULL;
1704 ModestMailOperationPrivate *priv = NULL;
1706 helper = (XFerMsgAsyncHelper *) user_data;
1707 g_return_if_fail (helper != NULL);
1709 self = helper->mail_op;
1710 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1713 priv->error = g_error_copy (err);
1715 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1716 } else if (cancelled) {
1717 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1718 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1719 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1720 _("Transference of %s was cancelled."),
1721 tny_folder_get_name (folder));
1724 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1727 /* Notify about operation end */
1728 modest_mail_operation_notify_end (self);
1730 /* If user defined callback function was defined, call it */
1731 if (helper->user_callback) {
1733 /* This is not a GDK lock because we are a Tinymail callback
1734 * which is already GDK locked by Tinymail */
1736 /* no gdk_threads_enter (), CHECKED */
1737 helper->user_callback (priv->source, helper->user_data);
1738 /* no gdk_threads_leave () , CHECKED */
1742 g_object_unref (helper->mail_op);
1743 g_slice_free (XFerMsgAsyncHelper, helper);
1748 * This function checks if the new name is a valid name for our local
1749 * folders account. The new name could not be the same than then name
1750 * of any of the mandatory local folders
1752 * We can not rely on tinymail because tinymail does not check the
1753 * name of the virtual folders that the account could have in the case
1754 * that we're doing a rename (because it directly calls Camel which
1755 * knows nothing about our virtual folders).
1757 * In the case of an actual copy/move (i.e. move/copy a folder between
1758 * accounts) tinymail uses the tny_folder_store_create_account which
1759 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1760 * checks the new name of the folder, so this call in that case
1761 * wouldn't be needed. *But* NOTE that if tinymail changes its
1762 * implementation (if folder transfers within the same account is no
1763 * longer implemented as a rename) this call will allow Modest to work
1766 * If the new name is not valid, this function will set the status to
1767 * failed and will set also an error in the mail operation
1770 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1771 TnyFolderStore *into,
1772 const gchar *new_name)
1774 if (TNY_IS_ACCOUNT (into) &&
1775 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1776 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1778 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1779 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1780 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1781 _("FIXME: folder name already in use"));
1788 * This function checks if @ancestor is an acestor of @folder and
1789 * returns TRUE in that case
1792 folder_is_ancestor (TnyFolder *folder,
1793 TnyFolderStore *ancestor)
1795 TnyFolder *tmp = NULL;
1796 gboolean found = FALSE;
1799 while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1800 TnyFolderStore *folder_store;
1802 folder_store = tny_folder_get_folder_store (tmp);
1803 if (ancestor == folder_store)
1806 tmp = g_object_ref (folder_store);
1807 g_object_unref (folder_store);
1813 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1815 TnyFolderStore *parent,
1816 gboolean delete_original,
1817 XferMsgsAsynUserCallback user_callback,
1820 ModestMailOperationPrivate *priv = NULL;
1821 ModestTnyFolderRules parent_rules = 0, rules;
1822 XFerMsgAsyncHelper *helper = NULL;
1823 const gchar *folder_name = NULL;
1825 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1826 g_return_if_fail (TNY_IS_FOLDER (folder));
1827 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1829 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1830 folder_name = tny_folder_get_name (folder);
1832 /* Get account and set it into mail_operation */
1833 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1834 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1836 /* Get folder rules */
1837 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1838 if (TNY_IS_FOLDER (parent))
1839 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1841 /* The moveable restriction is applied also to copy operation */
1842 if ((gpointer) parent == (gpointer) folder ||
1843 (!TNY_IS_FOLDER_STORE (parent)) ||
1844 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1846 /* Set status failed and set an error */
1847 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1848 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1849 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1850 _("mail_in_ui_folder_move_target_error"));
1852 /* Notify the queue */
1853 modest_mail_operation_notify_end (self);
1855 } else if (TNY_IS_FOLDER (parent) &&
1856 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1858 /* Set status failed and set an error */
1859 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1860 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1861 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1862 _("FIXME: parent folder does not accept new folders"));
1864 /* Notify the queue */
1865 modest_mail_operation_notify_end (self);
1867 } else if (TNY_IS_FOLDER (parent) &&
1868 TNY_IS_FOLDER_STORE (folder) &&
1869 folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1870 /* Set status failed and set an error */
1871 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1872 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1873 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1874 _("mail_in_ui_folder_copy_target_error"));
1876 /* Notify the queue */
1877 modest_mail_operation_notify_end (self);
1879 } else if (TNY_IS_FOLDER_STORE (parent) &&
1880 modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1881 /* Check that the new folder name is not used by any
1884 /* Set status failed and set an error */
1885 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1886 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1887 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1888 _("mail_in_ui_folder_move_target_error"));
1890 /* Notify the queue */
1891 modest_mail_operation_notify_end (self);
1893 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1894 /* Check that the new folder name is not used by any
1895 special local folder */
1897 /* Set status failed and set an error */
1898 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1899 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1900 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1901 _("mail_in_ui_folder_move_target_error"));
1903 /* Notify the queue */
1904 modest_mail_operation_notify_end (self);
1906 /* Create the helper */
1907 helper = g_slice_new0 (XFerMsgAsyncHelper);
1908 helper->mail_op = g_object_ref(self);
1909 helper->dest_folder = NULL;
1910 helper->headers = NULL;
1911 helper->user_callback = user_callback;
1912 helper->user_data = user_data;
1914 /* Move/Copy folder */
1915 tny_folder_copy_async (folder,
1917 tny_folder_get_name (folder),
1920 transfer_folder_status_cb,
1927 modest_mail_operation_rename_folder (ModestMailOperation *self,
1931 ModestMailOperationPrivate *priv;
1932 ModestTnyFolderRules rules;
1933 XFerMsgAsyncHelper *helper;
1935 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1936 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1937 g_return_if_fail (name);
1939 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1941 /* Get account and set it into mail_operation */
1942 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1944 /* Check folder rules */
1945 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1946 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1947 /* Set status failed and set an error */
1948 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1949 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1950 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1951 _("FIXME: unable to rename"));
1953 /* Notify about operation end */
1954 modest_mail_operation_notify_end (self);
1955 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1956 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1957 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1958 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1959 _("FIXME: unable to rename"));
1960 /* Notify about operation end */
1961 modest_mail_operation_notify_end (self);
1963 TnyFolderStore *into;
1965 into = tny_folder_get_folder_store (folder);
1967 /* Check that the new folder name is not used by any
1968 special local folder */
1969 if (new_name_valid_if_local_account (priv, into, name)) {
1970 /* Create the helper */
1971 helper = g_slice_new0 (XFerMsgAsyncHelper);
1972 helper->mail_op = g_object_ref(self);
1973 helper->dest_folder = NULL;
1974 helper->headers = NULL;
1975 helper->user_callback = NULL;
1976 helper->user_data = NULL;
1978 /* Rename. Camel handles folder subscription/unsubscription */
1979 tny_folder_copy_async (folder, into, name, TRUE,
1981 transfer_folder_status_cb,
1984 modest_mail_operation_notify_end (self);
1986 g_object_unref (into);
1990 /* ******************************************************************* */
1991 /* ************************** MSG ACTIONS ************************* */
1992 /* ******************************************************************* */
1994 void modest_mail_operation_get_msg (ModestMailOperation *self,
1996 GetMsgAsyncUserCallback user_callback,
1999 GetMsgAsyncHelper *helper = NULL;
2001 ModestMailOperationPrivate *priv;
2003 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2004 g_return_if_fail (TNY_IS_HEADER (header));
2006 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2007 folder = tny_header_get_folder (header);
2009 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2011 /* Get message from folder */
2013 /* Get account and set it into mail_operation */
2014 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2016 /* Check for cached messages */
2017 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2018 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2020 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2022 helper = g_slice_new0 (GetMsgAsyncHelper);
2023 helper->mail_op = self;
2024 helper->user_callback = user_callback;
2025 helper->user_data = user_data;
2026 helper->header = g_object_ref (header);
2028 // The callback's reference so that the mail op is not
2029 // finalized until the async operation is completed even if
2030 // the user canceled the request meanwhile.
2031 g_object_ref (G_OBJECT (helper->mail_op));
2033 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2035 g_object_unref (G_OBJECT (folder));
2037 /* Set status failed and set an error */
2038 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2039 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2040 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2041 _("Error trying to get a message. No folder found for header"));
2043 /* Notify the queue */
2044 modest_mail_operation_notify_end (self);
2049 get_msg_cb (TnyFolder *folder,
2055 GetMsgAsyncHelper *helper = NULL;
2056 ModestMailOperation *self = NULL;
2057 ModestMailOperationPrivate *priv = NULL;
2059 helper = (GetMsgAsyncHelper *) user_data;
2060 g_return_if_fail (helper != NULL);
2061 self = helper->mail_op;
2062 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2063 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2065 /* Check errors and cancel */
2067 priv->error = g_error_copy (error);
2068 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2069 } else if (cancelled) {
2070 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2071 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2072 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2073 _("Error trying to refresh the contents of %s"),
2074 tny_folder_get_name (folder));
2076 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2079 /* If user defined callback function was defined, call it even
2080 if the operation failed*/
2081 if (helper->user_callback) {
2082 /* This is not a GDK lock because we are a Tinymail callback
2083 * which is already GDK locked by Tinymail */
2085 /* no gdk_threads_enter (), CHECKED */
2086 helper->user_callback (self, helper->header, msg, helper->user_data);
2087 /* no gdk_threads_leave (), CHECKED */
2090 /* Notify about operation end */
2091 modest_mail_operation_notify_end (self);
2093 g_object_unref (helper->mail_op);
2094 g_object_unref (helper->header);
2095 g_slice_free (GetMsgAsyncHelper, helper);
2100 get_msg_status_cb (GObject *obj,
2104 GetMsgAsyncHelper *helper = NULL;
2105 ModestMailOperation *self;
2106 ModestMailOperationPrivate *priv;
2107 ModestMailOperationState *state;
2109 g_return_if_fail (status != NULL);
2110 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2112 helper = (GetMsgAsyncHelper *) user_data;
2113 g_return_if_fail (helper != NULL);
2115 self = helper->mail_op;
2116 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2121 state = modest_mail_operation_clone_state (self);
2122 state->bytes_done = status->position;
2123 state->bytes_total = status->of_total;
2125 /* This is not a GDK lock because we are a Tinymail callback
2126 * which is already GDK locked by Tinymail */
2128 /* no gdk_threads_enter (), CHECKED */
2129 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2130 /* no gdk_threads_leave (), CHECKED */
2132 g_slice_free (ModestMailOperationState, state);
2135 /****************************************************/
2137 ModestMailOperation *mail_op;
2139 GetMsgAsyncUserCallback user_callback;
2141 GDestroyNotify notify;
2145 GetMsgAsyncUserCallback user_callback;
2149 ModestMailOperation *mail_op;
2150 } NotifyGetMsgsInfo;
2154 * Used by get_msgs_full_thread to call the user_callback for each
2155 * message that has been read
2158 notify_get_msgs_full (gpointer data)
2160 NotifyGetMsgsInfo *info;
2162 info = (NotifyGetMsgsInfo *) data;
2164 /* This is a GDK lock because we are an idle callback and
2165 * because info->user_callback can contain Gtk+ code */
2167 gdk_threads_enter (); /* CHECKED */
2168 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2169 gdk_threads_leave (); /* CHECKED */
2171 g_slice_free (NotifyGetMsgsInfo, info);
2177 * Used by get_msgs_full_thread to free al the thread resources and to
2178 * call the destroy function for the passed user_data
2181 get_msgs_full_destroyer (gpointer data)
2183 GetFullMsgsInfo *info;
2185 info = (GetFullMsgsInfo *) data;
2189 /* This is a GDK lock because we are an idle callback and
2190 * because info->notify can contain Gtk+ code */
2192 gdk_threads_enter (); /* CHECKED */
2193 info->notify (info->user_data);
2194 gdk_threads_leave (); /* CHECKED */
2198 g_object_unref (info->headers);
2199 g_slice_free (GetFullMsgsInfo, info);
2205 get_msgs_full_thread (gpointer thr_user_data)
2207 GetFullMsgsInfo *info;
2208 ModestMailOperationPrivate *priv = NULL;
2209 TnyIterator *iter = NULL;
2211 info = (GetFullMsgsInfo *) thr_user_data;
2212 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2214 iter = tny_list_create_iterator (info->headers);
2215 while (!tny_iterator_is_done (iter)) {
2219 header = TNY_HEADER (tny_iterator_get_current (iter));
2220 folder = tny_header_get_folder (header);
2222 /* Check for cached messages */
2223 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2224 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2226 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2228 /* Get message from folder */
2231 /* The callback will call it per each header */
2232 msg = tny_folder_get_msg (folder, header, &(priv->error));
2235 ModestMailOperationState *state;
2240 /* notify progress */
2241 state = modest_mail_operation_clone_state (info->mail_op);
2242 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2243 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2244 pair, (GDestroyNotify) modest_pair_free);
2246 /* The callback is the responsible for
2247 freeing the message */
2248 if (info->user_callback) {
2249 NotifyGetMsgsInfo *info_notify;
2250 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2251 info_notify->user_callback = info->user_callback;
2252 info_notify->mail_op = info->mail_op;
2253 info_notify->header = g_object_ref (header);
2254 info_notify->msg = g_object_ref (msg);
2255 info_notify->user_data = info->user_data;
2256 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2257 notify_get_msgs_full,
2260 g_object_unref (msg);
2263 /* Set status failed and set an error */
2264 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2265 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2266 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2267 "Error trying to get a message. No folder found for header");
2271 g_object_unref (header);
2273 tny_iterator_next (iter);
2276 /* Set operation status */
2277 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2278 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2280 /* Notify about operation end */
2281 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2283 /* Free thread resources. Will be called after all previous idles */
2284 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2290 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2291 TnyList *header_list,
2292 GetMsgAsyncUserCallback user_callback,
2294 GDestroyNotify notify)
2296 TnyHeader *header = NULL;
2297 TnyFolder *folder = NULL;
2299 ModestMailOperationPrivate *priv = NULL;
2300 GetFullMsgsInfo *info = NULL;
2301 gboolean size_ok = TRUE;
2303 TnyIterator *iter = NULL;
2305 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2307 /* Init mail operation */
2308 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2309 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2311 priv->total = tny_list_get_length(header_list);
2313 /* Get account and set it into mail_operation */
2314 if (tny_list_get_length (header_list) >= 1) {
2315 iter = tny_list_create_iterator (header_list);
2316 header = TNY_HEADER (tny_iterator_get_current (iter));
2318 folder = tny_header_get_folder (header);
2320 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2322 g_object_unref (folder);
2325 g_object_unref (header);
2328 if (tny_list_get_length (header_list) == 1) {
2329 g_object_unref (iter);
2334 /* Get msg size limit */
2335 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2336 MODEST_CONF_MSG_SIZE_LIMIT,
2339 g_clear_error (&(priv->error));
2340 max_size = G_MAXINT;
2342 max_size = max_size * KB;
2345 /* Check message size limits. If there is only one message
2346 always retrieve it */
2348 while (!tny_iterator_is_done (iter) && size_ok) {
2349 header = TNY_HEADER (tny_iterator_get_current (iter));
2351 if (tny_header_get_message_size (header) >= max_size)
2353 g_object_unref (header);
2356 tny_iterator_next (iter);
2358 g_object_unref (iter);
2362 /* Create the info */
2363 info = g_slice_new0 (GetFullMsgsInfo);
2364 info->mail_op = self;
2365 info->user_callback = user_callback;
2366 info->user_data = user_data;
2367 info->headers = g_object_ref (header_list);
2368 info->notify = notify;
2370 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2372 /* Set status failed and set an error */
2373 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2374 /* FIXME: the error msg is different for pop */
2375 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2376 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2377 _("emev_ni_ui_imap_msg_size_exceed_error"));
2378 /* Remove from queue and free resources */
2379 modest_mail_operation_notify_end (self);
2387 modest_mail_operation_remove_msg (ModestMailOperation *self, TnyHeader *header,
2388 gboolean remove_to_trash /*ignored*/)
2391 ModestMailOperationPrivate *priv;
2393 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2394 g_return_if_fail (TNY_IS_HEADER (header));
2396 if (remove_to_trash)
2397 g_warning ("remove to trash is not implemented");
2399 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2400 folder = tny_header_get_folder (header);
2402 /* Get account and set it into mail_operation */
2403 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2405 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2407 /* remove message from folder */
2408 tny_folder_remove_msg (folder, header, &(priv->error));
2409 /* if (!priv->error) { */
2410 /* tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
2411 /* tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
2413 /* if (TNY_IS_CAMEL_IMAP_FOLDER (folder)) */
2414 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2415 /* /\* tny_folder_sync (folder, FALSE, &(priv->error)); /\\* FALSE --> don't expunge *\\/ *\/ */
2416 /* else if (TNY_IS_CAMEL_POP_FOLDER (folder)) */
2417 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2418 /* /\* tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2420 /* /\* local folders *\/ */
2421 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2422 /* /\* tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2428 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2430 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2433 g_object_unref (G_OBJECT (folder));
2435 /* Notify about operation end */
2436 modest_mail_operation_notify_end (self);
2440 transfer_msgs_status_cb (GObject *obj,
2444 XFerMsgAsyncHelper *helper = NULL;
2445 ModestMailOperation *self;
2446 ModestMailOperationPrivate *priv;
2447 ModestMailOperationState *state;
2450 g_return_if_fail (status != NULL);
2451 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2453 helper = (XFerMsgAsyncHelper *) user_data;
2454 g_return_if_fail (helper != NULL);
2456 self = helper->mail_op;
2457 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2459 priv->done = status->position;
2460 priv->total = status->of_total;
2462 state = modest_mail_operation_clone_state (self);
2464 /* This is not a GDK lock because we are a Tinymail callback and
2465 * Tinymail already acquires the Gdk lock */
2467 /* no gdk_threads_enter (), CHECKED */
2469 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2471 /* no gdk_threads_leave (), CHECKED */
2473 g_slice_free (ModestMailOperationState, state);
2478 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2480 XFerMsgAsyncHelper *helper;
2481 ModestMailOperation *self;
2482 ModestMailOperationPrivate *priv;
2483 TnyIterator *iter = NULL;
2484 TnyHeader *header = NULL;
2486 helper = (XFerMsgAsyncHelper *) user_data;
2487 self = helper->mail_op;
2489 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2492 priv->error = g_error_copy (err);
2494 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2495 } else if (cancelled) {
2496 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2497 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2498 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2499 _("Error trying to refresh the contents of %s"),
2500 tny_folder_get_name (folder));
2503 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2507 /* Mark headers as deleted and seen */
2508 if ((helper->delete) &&
2509 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2510 iter = tny_list_create_iterator (helper->headers);
2511 while (!tny_iterator_is_done (iter)) {
2512 header = TNY_HEADER (tny_iterator_get_current (iter));
2513 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2514 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2515 g_object_unref (header);
2517 tny_iterator_next (iter);
2523 /* Notify about operation end */
2524 modest_mail_operation_notify_end (self);
2526 /* If user defined callback function was defined, call it */
2527 if (helper->user_callback) {
2528 /* This is not a GDK lock because we are a Tinymail callback and
2529 * Tinymail already acquires the Gdk lock */
2531 /* no gdk_threads_enter (), CHECKED */
2532 helper->user_callback (priv->source, helper->user_data);
2533 /* no gdk_threads_leave (), CHECKED */
2537 if (helper->headers)
2538 g_object_unref (helper->headers);
2539 if (helper->dest_folder)
2540 g_object_unref (helper->dest_folder);
2541 if (helper->mail_op)
2542 g_object_unref (helper->mail_op);
2544 g_object_unref (folder);
2546 g_object_unref (iter);
2547 g_slice_free (XFerMsgAsyncHelper, helper);
2552 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2555 gboolean delete_original,
2556 XferMsgsAsynUserCallback user_callback,
2559 ModestMailOperationPrivate *priv = NULL;
2560 TnyIterator *iter = NULL;
2561 TnyFolder *src_folder = NULL;
2562 XFerMsgAsyncHelper *helper = NULL;
2563 TnyHeader *header = NULL;
2564 ModestTnyFolderRules rules = 0;
2565 const gchar *id1 = NULL;
2566 const gchar *id2 = NULL;
2567 gboolean same_folder = FALSE;
2569 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2570 g_return_if_fail (TNY_IS_LIST (headers));
2571 g_return_if_fail (TNY_IS_FOLDER (folder));
2573 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2576 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2578 /* Apply folder rules */
2579 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2580 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2581 /* Set status failed and set an error */
2582 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2583 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2584 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2585 _CS("ckct_ib_unable_to_paste_here"));
2586 /* Notify the queue */
2587 modest_mail_operation_notify_end (self);
2591 /* Get source folder */
2592 iter = tny_list_create_iterator (headers);
2593 header = TNY_HEADER (tny_iterator_get_current (iter));
2595 src_folder = tny_header_get_folder (header);
2596 g_object_unref (header);
2599 g_object_unref (iter);
2601 /* Check folder source and destination */
2602 id1 = tny_folder_get_id (src_folder);
2603 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2604 same_folder = !g_ascii_strcasecmp (id1, id2);
2606 /* Set status failed and set an error */
2607 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2608 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2609 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2610 _("mcen_ib_unable_to_copy_samefolder"));
2612 /* Notify the queue */
2613 modest_mail_operation_notify_end (self);
2616 g_object_unref (src_folder);
2620 /* Create the helper */
2621 helper = g_slice_new0 (XFerMsgAsyncHelper);
2622 helper->mail_op = g_object_ref(self);
2623 helper->dest_folder = g_object_ref(folder);
2624 helper->headers = g_object_ref(headers);
2625 helper->user_callback = user_callback;
2626 helper->user_data = user_data;
2627 helper->delete = delete_original;
2629 /* Get account and set it into mail_operation */
2630 priv->account = modest_tny_folder_get_account (src_folder);
2632 /* Transfer messages */
2633 tny_folder_transfer_msgs_async (src_folder,
2638 transfer_msgs_status_cb,
2644 on_refresh_folder (TnyFolder *folder,
2649 RefreshAsyncHelper *helper = NULL;
2650 ModestMailOperation *self = NULL;
2651 ModestMailOperationPrivate *priv = NULL;
2653 helper = (RefreshAsyncHelper *) user_data;
2654 self = helper->mail_op;
2655 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2657 g_return_if_fail(priv!=NULL);
2660 priv->error = g_error_copy (error);
2661 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2662 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2668 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2669 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2670 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2671 _("Error trying to refresh the contents of %s"),
2672 tny_folder_get_name (folder));
2673 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2677 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2679 /* Call user defined callback, if it exists */
2680 if (helper->user_callback) {
2682 /* This is not a GDK lock because we are a Tinymail callback and
2683 * Tinymail already acquires the Gdk lock */
2685 /* no gdk_threads_enter (), CHECKED */
2686 helper->user_callback (self, folder, helper->user_data);
2687 /* no gdk_threads_leave (), CHECKED */
2692 g_slice_free (RefreshAsyncHelper, helper);
2694 /* Notify about operation end */
2695 modest_mail_operation_notify_end (self);
2696 g_object_unref(self);
2700 on_refresh_folder_status_update (GObject *obj,
2704 RefreshAsyncHelper *helper = NULL;
2705 ModestMailOperation *self = NULL;
2706 ModestMailOperationPrivate *priv = NULL;
2707 ModestMailOperationState *state;
2709 g_return_if_fail (user_data != NULL);
2710 g_return_if_fail (status != NULL);
2711 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2713 helper = (RefreshAsyncHelper *) user_data;
2714 self = helper->mail_op;
2715 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2717 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2719 priv->done = status->position;
2720 priv->total = status->of_total;
2722 state = modest_mail_operation_clone_state (self);
2724 /* This is not a GDK lock because we are a Tinymail callback and
2725 * Tinymail already acquires the Gdk lock */
2727 /* no gdk_threads_enter (), CHECKED */
2729 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2731 /* no gdk_threads_leave (), CHECKED */
2733 g_slice_free (ModestMailOperationState, state);
2737 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2739 RefreshAsyncUserCallback user_callback,
2742 ModestMailOperationPrivate *priv = NULL;
2743 RefreshAsyncHelper *helper = NULL;
2745 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2747 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2749 /* Get account and set it into mail_operation */
2750 priv->account = modest_tny_folder_get_account (folder);
2752 /* Create the helper */
2753 helper = g_slice_new0 (RefreshAsyncHelper);
2754 helper->mail_op = g_object_ref(self);
2755 helper->user_callback = user_callback;
2756 helper->user_data = user_data;
2758 /* Refresh the folder. TODO: tinymail could issue a status
2759 updates before the callback call then this could happen. We
2760 must review the design */
2761 tny_folder_refresh_async (folder,
2763 on_refresh_folder_status_update,
2769 * It's used by the mail operation queue to notify the observers
2770 * attached to that signal that the operation finished. We need to use
2771 * that because tinymail does not give us the progress of a given
2772 * operation when it finishes (it directly calls the operation
2776 modest_mail_operation_notify_end (ModestMailOperation *self)
2778 ModestMailOperationState *state;
2779 ModestMailOperationPrivate *priv = NULL;
2781 g_return_if_fail (self);
2783 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2785 /* Set the account back to not busy */
2786 if (priv->account_name) {
2787 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2788 priv->account_name, FALSE);
2789 g_free(priv->account_name);
2790 priv->account_name = NULL;
2793 /* Notify the observers about the mail operation end */
2794 /* We do not wrapp this emission because we assume that this
2795 function is always called from within the main lock */
2796 state = modest_mail_operation_clone_state (self);
2797 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2798 g_slice_free (ModestMailOperationState, state);