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;
1824 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1825 g_return_if_fail (TNY_IS_FOLDER (folder));
1826 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1828 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1830 /* Get account and set it into mail_operation */
1831 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1832 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1834 /* Get folder rules */
1835 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1836 if (TNY_IS_FOLDER (parent))
1837 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1839 /* The moveable restriction is applied also to copy operation */
1840 if ((gpointer) parent == (gpointer) folder ||
1841 (!TNY_IS_FOLDER_STORE (parent)) ||
1842 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1843 /* Set status failed and set an error */
1844 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1845 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1846 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1847 _("mail_in_ui_folder_move_target_error"));
1849 /* Notify the queue */
1850 modest_mail_operation_notify_end (self);
1851 } else if (TNY_IS_FOLDER (parent) &&
1852 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1853 /* Set status failed and set an error */
1854 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1855 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1856 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1857 _("FIXME: parent folder does not accept new folders"));
1859 /* Notify the queue */
1860 modest_mail_operation_notify_end (self);
1861 } else if (TNY_IS_FOLDER (parent) &&
1862 TNY_IS_FOLDER_STORE (folder) &&
1863 folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1864 /* Set status failed and set an error */
1865 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1866 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1867 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1868 _("mail_in_ui_folder_copy_target_error"));
1870 /* Notify the queue */
1871 modest_mail_operation_notify_end (self);
1873 /* Check that the new folder name is not used by any
1874 special local folder */
1875 if (new_name_valid_if_local_account (priv, parent,
1876 tny_folder_get_name (folder))) {
1877 /* Create the helper */
1878 helper = g_slice_new0 (XFerMsgAsyncHelper);
1879 helper->mail_op = g_object_ref(self);
1880 helper->dest_folder = NULL;
1881 helper->headers = NULL;
1882 helper->user_callback = user_callback;
1883 helper->user_data = user_data;
1885 /* Move/Copy folder */
1886 tny_folder_copy_async (folder,
1888 tny_folder_get_name (folder),
1891 transfer_folder_status_cb,
1894 modest_mail_operation_notify_end (self);
1900 modest_mail_operation_rename_folder (ModestMailOperation *self,
1904 ModestMailOperationPrivate *priv;
1905 ModestTnyFolderRules rules;
1906 XFerMsgAsyncHelper *helper;
1908 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1909 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1910 g_return_if_fail (name);
1912 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1914 /* Get account and set it into mail_operation */
1915 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1917 /* Check folder rules */
1918 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1919 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1920 /* Set status failed and set an error */
1921 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1922 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1923 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1924 _("FIXME: unable to rename"));
1926 /* Notify about operation end */
1927 modest_mail_operation_notify_end (self);
1928 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1929 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1930 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1931 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1932 _("FIXME: unable to rename"));
1933 /* Notify about operation end */
1934 modest_mail_operation_notify_end (self);
1936 TnyFolderStore *into;
1938 into = tny_folder_get_folder_store (folder);
1940 /* Check that the new folder name is not used by any
1941 special local folder */
1942 if (new_name_valid_if_local_account (priv, into, name)) {
1943 /* Create the helper */
1944 helper = g_slice_new0 (XFerMsgAsyncHelper);
1945 helper->mail_op = g_object_ref(self);
1946 helper->dest_folder = NULL;
1947 helper->headers = NULL;
1948 helper->user_callback = NULL;
1949 helper->user_data = NULL;
1951 /* Rename. Camel handles folder subscription/unsubscription */
1952 tny_folder_copy_async (folder, into, name, TRUE,
1954 transfer_folder_status_cb,
1957 modest_mail_operation_notify_end (self);
1959 g_object_unref (into);
1963 /* ******************************************************************* */
1964 /* ************************** MSG ACTIONS ************************* */
1965 /* ******************************************************************* */
1967 void modest_mail_operation_get_msg (ModestMailOperation *self,
1969 GetMsgAsyncUserCallback user_callback,
1972 GetMsgAsyncHelper *helper = NULL;
1974 ModestMailOperationPrivate *priv;
1976 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1977 g_return_if_fail (TNY_IS_HEADER (header));
1979 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1980 folder = tny_header_get_folder (header);
1982 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1984 /* Get message from folder */
1986 /* Get account and set it into mail_operation */
1987 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1989 /* Check for cached messages */
1990 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
1991 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1993 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1995 helper = g_slice_new0 (GetMsgAsyncHelper);
1996 helper->mail_op = self;
1997 helper->user_callback = user_callback;
1998 helper->user_data = user_data;
1999 helper->header = g_object_ref (header);
2001 // The callback's reference so that the mail op is not
2002 // finalized until the async operation is completed even if
2003 // the user canceled the request meanwhile.
2004 g_object_ref (G_OBJECT (helper->mail_op));
2006 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2008 g_object_unref (G_OBJECT (folder));
2010 /* Set status failed and set an error */
2011 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2012 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2013 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2014 _("Error trying to get a message. No folder found for header"));
2016 /* Notify the queue */
2017 modest_mail_operation_notify_end (self);
2022 get_msg_cb (TnyFolder *folder,
2028 GetMsgAsyncHelper *helper = NULL;
2029 ModestMailOperation *self = NULL;
2030 ModestMailOperationPrivate *priv = NULL;
2032 helper = (GetMsgAsyncHelper *) user_data;
2033 g_return_if_fail (helper != NULL);
2034 self = helper->mail_op;
2035 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2036 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2038 /* Check errors and cancel */
2040 priv->error = g_error_copy (error);
2041 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2042 } else if (cancelled) {
2043 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2044 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2045 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2046 _("Error trying to refresh the contents of %s"),
2047 tny_folder_get_name (folder));
2049 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2052 /* If user defined callback function was defined, call it even
2053 if the operation failed*/
2054 if (helper->user_callback) {
2055 /* This is not a GDK lock because we are a Tinymail callback
2056 * which is already GDK locked by Tinymail */
2058 /* no gdk_threads_enter (), CHECKED */
2059 helper->user_callback (self, helper->header, msg, helper->user_data);
2060 /* no gdk_threads_leave (), CHECKED */
2063 /* Notify about operation end */
2064 modest_mail_operation_notify_end (self);
2066 g_object_unref (helper->mail_op);
2067 g_object_unref (helper->header);
2068 g_slice_free (GetMsgAsyncHelper, helper);
2073 get_msg_status_cb (GObject *obj,
2077 GetMsgAsyncHelper *helper = NULL;
2078 ModestMailOperation *self;
2079 ModestMailOperationPrivate *priv;
2080 ModestMailOperationState *state;
2082 g_return_if_fail (status != NULL);
2083 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2085 helper = (GetMsgAsyncHelper *) user_data;
2086 g_return_if_fail (helper != NULL);
2088 self = helper->mail_op;
2089 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2094 state = modest_mail_operation_clone_state (self);
2095 state->bytes_done = status->position;
2096 state->bytes_total = status->of_total;
2098 /* This is not a GDK lock because we are a Tinymail callback
2099 * which is already GDK locked by Tinymail */
2101 /* no gdk_threads_enter (), CHECKED */
2102 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2103 /* no gdk_threads_leave (), CHECKED */
2105 g_slice_free (ModestMailOperationState, state);
2108 /****************************************************/
2110 ModestMailOperation *mail_op;
2112 GetMsgAsyncUserCallback user_callback;
2114 GDestroyNotify notify;
2118 GetMsgAsyncUserCallback user_callback;
2122 ModestMailOperation *mail_op;
2123 } NotifyGetMsgsInfo;
2127 * Used by get_msgs_full_thread to call the user_callback for each
2128 * message that has been read
2131 notify_get_msgs_full (gpointer data)
2133 NotifyGetMsgsInfo *info;
2135 info = (NotifyGetMsgsInfo *) data;
2137 /* This is a GDK lock because we are an idle callback and
2138 * because info->user_callback can contain Gtk+ code */
2140 gdk_threads_enter (); /* CHECKED */
2141 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2142 gdk_threads_leave (); /* CHECKED */
2144 g_slice_free (NotifyGetMsgsInfo, info);
2150 * Used by get_msgs_full_thread to free al the thread resources and to
2151 * call the destroy function for the passed user_data
2154 get_msgs_full_destroyer (gpointer data)
2156 GetFullMsgsInfo *info;
2158 info = (GetFullMsgsInfo *) data;
2162 /* This is a GDK lock because we are an idle callback and
2163 * because info->notify can contain Gtk+ code */
2165 gdk_threads_enter (); /* CHECKED */
2166 info->notify (info->user_data);
2167 gdk_threads_leave (); /* CHECKED */
2171 g_object_unref (info->headers);
2172 g_slice_free (GetFullMsgsInfo, info);
2178 get_msgs_full_thread (gpointer thr_user_data)
2180 GetFullMsgsInfo *info;
2181 ModestMailOperationPrivate *priv = NULL;
2182 TnyIterator *iter = NULL;
2184 info = (GetFullMsgsInfo *) thr_user_data;
2185 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2187 iter = tny_list_create_iterator (info->headers);
2188 while (!tny_iterator_is_done (iter)) {
2192 header = TNY_HEADER (tny_iterator_get_current (iter));
2193 folder = tny_header_get_folder (header);
2195 /* Check for cached messages */
2196 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2197 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2199 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2201 /* Get message from folder */
2204 /* The callback will call it per each header */
2205 msg = tny_folder_get_msg (folder, header, &(priv->error));
2208 ModestMailOperationState *state;
2213 /* notify progress */
2214 state = modest_mail_operation_clone_state (info->mail_op);
2215 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2216 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2217 pair, (GDestroyNotify) modest_pair_free);
2219 /* The callback is the responsible for
2220 freeing the message */
2221 if (info->user_callback) {
2222 NotifyGetMsgsInfo *info_notify;
2223 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2224 info_notify->user_callback = info->user_callback;
2225 info_notify->mail_op = info->mail_op;
2226 info_notify->header = g_object_ref (header);
2227 info_notify->msg = g_object_ref (msg);
2228 info_notify->user_data = info->user_data;
2229 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2230 notify_get_msgs_full,
2233 g_object_unref (msg);
2236 /* Set status failed and set an error */
2237 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2238 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2239 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2240 "Error trying to get a message. No folder found for header");
2244 g_object_unref (header);
2246 tny_iterator_next (iter);
2249 /* Set operation status */
2250 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2251 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2253 /* Notify about operation end */
2254 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2256 /* Free thread resources. Will be called after all previous idles */
2257 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2263 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2264 TnyList *header_list,
2265 GetMsgAsyncUserCallback user_callback,
2267 GDestroyNotify notify)
2269 TnyHeader *header = NULL;
2270 TnyFolder *folder = NULL;
2272 ModestMailOperationPrivate *priv = NULL;
2273 GetFullMsgsInfo *info = NULL;
2274 gboolean size_ok = TRUE;
2276 TnyIterator *iter = NULL;
2278 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2280 /* Init mail operation */
2281 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2282 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2284 priv->total = tny_list_get_length(header_list);
2286 /* Get account and set it into mail_operation */
2287 if (tny_list_get_length (header_list) >= 1) {
2288 iter = tny_list_create_iterator (header_list);
2289 header = TNY_HEADER (tny_iterator_get_current (iter));
2291 folder = tny_header_get_folder (header);
2293 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2295 g_object_unref (folder);
2298 g_object_unref (header);
2301 if (tny_list_get_length (header_list) == 1) {
2302 g_object_unref (iter);
2307 /* Get msg size limit */
2308 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2309 MODEST_CONF_MSG_SIZE_LIMIT,
2312 g_clear_error (&(priv->error));
2313 max_size = G_MAXINT;
2315 max_size = max_size * KB;
2318 /* Check message size limits. If there is only one message
2319 always retrieve it */
2321 while (!tny_iterator_is_done (iter) && size_ok) {
2322 header = TNY_HEADER (tny_iterator_get_current (iter));
2324 if (tny_header_get_message_size (header) >= max_size)
2326 g_object_unref (header);
2329 tny_iterator_next (iter);
2331 g_object_unref (iter);
2335 /* Create the info */
2336 info = g_slice_new0 (GetFullMsgsInfo);
2337 info->mail_op = self;
2338 info->user_callback = user_callback;
2339 info->user_data = user_data;
2340 info->headers = g_object_ref (header_list);
2341 info->notify = notify;
2343 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2345 /* Set status failed and set an error */
2346 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2347 /* FIXME: the error msg is different for pop */
2348 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2349 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2350 _("emev_ni_ui_imap_msg_size_exceed_error"));
2351 /* Remove from queue and free resources */
2352 modest_mail_operation_notify_end (self);
2360 modest_mail_operation_remove_msg (ModestMailOperation *self, TnyHeader *header,
2361 gboolean remove_to_trash /*ignored*/)
2364 ModestMailOperationPrivate *priv;
2366 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2367 g_return_if_fail (TNY_IS_HEADER (header));
2369 if (remove_to_trash)
2370 g_warning ("remove to trash is not implemented");
2372 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2373 folder = tny_header_get_folder (header);
2375 /* Get account and set it into mail_operation */
2376 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2378 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2380 /* remove message from folder */
2381 tny_folder_remove_msg (folder, header, &(priv->error));
2382 /* if (!priv->error) { */
2383 /* tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
2384 /* tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
2386 /* if (TNY_IS_CAMEL_IMAP_FOLDER (folder)) */
2387 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2388 /* /\* tny_folder_sync (folder, FALSE, &(priv->error)); /\\* FALSE --> don't expunge *\\/ *\/ */
2389 /* else if (TNY_IS_CAMEL_POP_FOLDER (folder)) */
2390 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2391 /* /\* tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2393 /* /\* local folders *\/ */
2394 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2395 /* /\* tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2401 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2403 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2406 g_object_unref (G_OBJECT (folder));
2408 /* Notify about operation end */
2409 modest_mail_operation_notify_end (self);
2413 transfer_msgs_status_cb (GObject *obj,
2417 XFerMsgAsyncHelper *helper = NULL;
2418 ModestMailOperation *self;
2419 ModestMailOperationPrivate *priv;
2420 ModestMailOperationState *state;
2423 g_return_if_fail (status != NULL);
2424 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2426 helper = (XFerMsgAsyncHelper *) user_data;
2427 g_return_if_fail (helper != NULL);
2429 self = helper->mail_op;
2430 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2432 priv->done = status->position;
2433 priv->total = status->of_total;
2435 state = modest_mail_operation_clone_state (self);
2437 /* This is not a GDK lock because we are a Tinymail callback and
2438 * Tinymail already acquires the Gdk lock */
2440 /* no gdk_threads_enter (), CHECKED */
2442 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2444 /* no gdk_threads_leave (), CHECKED */
2446 g_slice_free (ModestMailOperationState, state);
2451 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2453 XFerMsgAsyncHelper *helper;
2454 ModestMailOperation *self;
2455 ModestMailOperationPrivate *priv;
2456 TnyIterator *iter = NULL;
2457 TnyHeader *header = NULL;
2459 helper = (XFerMsgAsyncHelper *) user_data;
2460 self = helper->mail_op;
2462 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2465 priv->error = g_error_copy (err);
2467 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2468 } else if (cancelled) {
2469 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2470 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2471 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2472 _("Error trying to refresh the contents of %s"),
2473 tny_folder_get_name (folder));
2476 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2480 /* Mark headers as deleted and seen */
2481 if ((helper->delete) &&
2482 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2483 iter = tny_list_create_iterator (helper->headers);
2484 while (!tny_iterator_is_done (iter)) {
2485 header = TNY_HEADER (tny_iterator_get_current (iter));
2486 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2487 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2488 g_object_unref (header);
2490 tny_iterator_next (iter);
2496 /* Notify about operation end */
2497 modest_mail_operation_notify_end (self);
2499 /* If user defined callback function was defined, call it */
2500 if (helper->user_callback) {
2501 /* This is not a GDK lock because we are a Tinymail callback and
2502 * Tinymail already acquires the Gdk lock */
2504 /* no gdk_threads_enter (), CHECKED */
2505 helper->user_callback (priv->source, helper->user_data);
2506 /* no gdk_threads_leave (), CHECKED */
2510 if (helper->headers)
2511 g_object_unref (helper->headers);
2512 if (helper->dest_folder)
2513 g_object_unref (helper->dest_folder);
2514 if (helper->mail_op)
2515 g_object_unref (helper->mail_op);
2517 g_object_unref (folder);
2519 g_object_unref (iter);
2520 g_slice_free (XFerMsgAsyncHelper, helper);
2525 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2528 gboolean delete_original,
2529 XferMsgsAsynUserCallback user_callback,
2532 ModestMailOperationPrivate *priv = NULL;
2533 TnyIterator *iter = NULL;
2534 TnyFolder *src_folder = NULL;
2535 XFerMsgAsyncHelper *helper = NULL;
2536 TnyHeader *header = NULL;
2537 ModestTnyFolderRules rules = 0;
2538 const gchar *id1 = NULL;
2539 const gchar *id2 = NULL;
2540 gboolean same_folder = FALSE;
2542 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2543 g_return_if_fail (TNY_IS_LIST (headers));
2544 g_return_if_fail (TNY_IS_FOLDER (folder));
2546 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2549 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2551 /* Apply folder rules */
2552 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2553 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2554 /* Set status failed and set an error */
2555 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2556 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2557 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2558 _CS("ckct_ib_unable_to_paste_here"));
2559 /* Notify the queue */
2560 modest_mail_operation_notify_end (self);
2564 /* Get source folder */
2565 iter = tny_list_create_iterator (headers);
2566 header = TNY_HEADER (tny_iterator_get_current (iter));
2568 src_folder = tny_header_get_folder (header);
2569 g_object_unref (header);
2572 g_object_unref (iter);
2574 /* Check folder source and destination */
2575 id1 = tny_folder_get_id (src_folder);
2576 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2577 same_folder = !g_ascii_strcasecmp (id1, id2);
2579 /* Set status failed and set an error */
2580 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2581 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2582 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2583 _("mcen_ib_unable_to_copy_samefolder"));
2585 /* Notify the queue */
2586 modest_mail_operation_notify_end (self);
2589 g_object_unref (src_folder);
2593 /* Create the helper */
2594 helper = g_slice_new0 (XFerMsgAsyncHelper);
2595 helper->mail_op = g_object_ref(self);
2596 helper->dest_folder = g_object_ref(folder);
2597 helper->headers = g_object_ref(headers);
2598 helper->user_callback = user_callback;
2599 helper->user_data = user_data;
2600 helper->delete = delete_original;
2602 /* Get account and set it into mail_operation */
2603 priv->account = modest_tny_folder_get_account (src_folder);
2605 /* Transfer messages */
2606 tny_folder_transfer_msgs_async (src_folder,
2611 transfer_msgs_status_cb,
2617 on_refresh_folder (TnyFolder *folder,
2622 RefreshAsyncHelper *helper = NULL;
2623 ModestMailOperation *self = NULL;
2624 ModestMailOperationPrivate *priv = NULL;
2626 helper = (RefreshAsyncHelper *) user_data;
2627 self = helper->mail_op;
2628 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2630 g_return_if_fail(priv!=NULL);
2633 priv->error = g_error_copy (error);
2634 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2635 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2641 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2642 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2643 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2644 _("Error trying to refresh the contents of %s"),
2645 tny_folder_get_name (folder));
2646 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2650 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2652 /* Call user defined callback, if it exists */
2653 if (helper->user_callback) {
2655 /* This is not a GDK lock because we are a Tinymail callback and
2656 * Tinymail already acquires the Gdk lock */
2658 /* no gdk_threads_enter (), CHECKED */
2659 helper->user_callback (self, folder, helper->user_data);
2660 /* no gdk_threads_leave (), CHECKED */
2665 g_slice_free (RefreshAsyncHelper, helper);
2667 /* Notify about operation end */
2668 modest_mail_operation_notify_end (self);
2669 g_object_unref(self);
2673 on_refresh_folder_status_update (GObject *obj,
2677 RefreshAsyncHelper *helper = NULL;
2678 ModestMailOperation *self = NULL;
2679 ModestMailOperationPrivate *priv = NULL;
2680 ModestMailOperationState *state;
2682 g_return_if_fail (user_data != NULL);
2683 g_return_if_fail (status != NULL);
2684 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2686 helper = (RefreshAsyncHelper *) user_data;
2687 self = helper->mail_op;
2688 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2690 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2692 priv->done = status->position;
2693 priv->total = status->of_total;
2695 state = modest_mail_operation_clone_state (self);
2697 /* This is not a GDK lock because we are a Tinymail callback and
2698 * Tinymail already acquires the Gdk lock */
2700 /* no gdk_threads_enter (), CHECKED */
2702 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2704 /* no gdk_threads_leave (), CHECKED */
2706 g_slice_free (ModestMailOperationState, state);
2710 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2712 RefreshAsyncUserCallback user_callback,
2715 ModestMailOperationPrivate *priv = NULL;
2716 RefreshAsyncHelper *helper = NULL;
2718 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2720 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2722 /* Get account and set it into mail_operation */
2723 priv->account = modest_tny_folder_get_account (folder);
2725 /* Create the helper */
2726 helper = g_slice_new0 (RefreshAsyncHelper);
2727 helper->mail_op = g_object_ref(self);
2728 helper->user_callback = user_callback;
2729 helper->user_data = user_data;
2731 /* Refresh the folder. TODO: tinymail could issue a status
2732 updates before the callback call then this could happen. We
2733 must review the design */
2734 tny_folder_refresh_async (folder,
2736 on_refresh_folder_status_update,
2742 * It's used by the mail operation queue to notify the observers
2743 * attached to that signal that the operation finished. We need to use
2744 * that because tinymail does not give us the progress of a given
2745 * operation when it finishes (it directly calls the operation
2749 modest_mail_operation_notify_end (ModestMailOperation *self)
2751 ModestMailOperationState *state;
2752 ModestMailOperationPrivate *priv = NULL;
2754 g_return_if_fail (self);
2756 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2758 /* Set the account back to not busy */
2759 if (priv->account_name) {
2760 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2761 priv->account_name, FALSE);
2762 g_free(priv->account_name);
2763 priv->account_name = NULL;
2766 /* Notify the observers about the mail operation end */
2767 /* We do not wrapp this emission because we assume that this
2768 function is always called from within the main lock */
2769 state = modest_mail_operation_clone_state (self);
2770 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2771 g_slice_free (ModestMailOperationState, state);