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");
559 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
562 /* TODO: connect to the msg-sent in order to know when
563 the mail operation is finished */
565 tny_send_queue_add (send_queue, msg, &(priv->error));
567 /* TODO: we're setting always success, do the check in
569 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
572 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)
573 modest_platform_information_banner (NULL, NULL, _("mcen_ib_outbox_waiting_to_be_sent"));
575 /* TODO: do this in the handler of the "msg-sent"
576 signal.Notify about operation end */
577 modest_mail_operation_notify_end (self);
581 idle_create_msg_cb (gpointer idle_data)
583 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
585 /* This is a GDK lock because we are an idle callback and
586 * info->callback can contain Gtk+ code */
588 gdk_threads_enter (); /* CHECKED */
589 info->callback (info->mail_op, info->msg, info->userdata);
590 gdk_threads_leave (); /* CHECKED */
592 g_object_unref (info->mail_op);
594 g_object_unref (info->msg);
595 g_slice_free (CreateMsgIdleInfo, info);
601 create_msg_thread (gpointer thread_data)
603 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
604 TnyMsg *new_msg = NULL;
605 ModestMailOperationPrivate *priv;
607 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
608 if (info->html_body == NULL) {
609 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
610 info->bcc, info->subject, info->plain_body,
611 info->attachments_list); /* FIXME: attachments */
613 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
614 info->bcc, info->subject, info->html_body,
615 info->plain_body, info->attachments_list);
620 TnyHeaderFlags flags = 0;
622 /* Set priority flags in message */
623 header = tny_msg_get_header (new_msg);
624 if (info->priority_flags != 0)
625 flags |= info->priority_flags;
627 /* Set attachment flags in message */
628 if (info->attachments_list != NULL)
629 flags |= TNY_HEADER_FLAG_ATTACHMENTS;
631 tny_header_set_flags (header, flags);
632 g_object_unref (G_OBJECT(header));
634 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
635 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
636 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
637 "modest: failed to create a new msg\n");
645 g_free (info->plain_body);
646 g_free (info->html_body);
647 g_free (info->subject);
648 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
649 g_list_free (info->attachments_list);
651 if (info->callback) {
652 CreateMsgIdleInfo *idle_info;
653 idle_info = g_slice_new0 (CreateMsgIdleInfo);
654 idle_info->mail_op = info->mail_op;
655 g_object_ref (info->mail_op);
656 idle_info->msg = new_msg;
658 g_object_ref (new_msg);
659 idle_info->callback = info->callback;
660 idle_info->userdata = info->userdata;
661 g_idle_add (idle_create_msg_cb, idle_info);
663 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
666 g_object_unref (info->mail_op);
667 g_slice_free (CreateMsgInfo, info);
672 modest_mail_operation_create_msg (ModestMailOperation *self,
673 const gchar *from, const gchar *to,
674 const gchar *cc, const gchar *bcc,
675 const gchar *subject, const gchar *plain_body,
676 const gchar *html_body,
677 const GList *attachments_list,
678 TnyHeaderFlags priority_flags,
679 ModestMailOperationCreateMsgCallback callback,
682 CreateMsgInfo *info = NULL;
684 info = g_slice_new0 (CreateMsgInfo);
685 info->mail_op = self;
688 info->from = g_strdup (from);
689 info->to = g_strdup (to);
690 info->cc = g_strdup (cc);
691 info->bcc = g_strdup (bcc);
692 info->subject = g_strdup (subject);
693 info->plain_body = g_strdup (plain_body);
694 info->html_body = g_strdup (html_body);
695 info->attachments_list = g_list_copy ((GList *) attachments_list);
696 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
697 info->priority_flags = priority_flags;
699 info->callback = callback;
700 info->userdata = userdata;
702 g_thread_create (create_msg_thread, info, FALSE, NULL);
707 TnyTransportAccount *transport_account;
712 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
716 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
717 TnyFolder *draft_folder = NULL;
718 TnyFolder *outbox_folder = NULL;
726 /* Call mail operation */
727 modest_mail_operation_send_mail (self, info->transport_account, msg);
729 /* Remove old mail from its source folder */
730 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
731 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
732 if (info->draft_msg != NULL) {
733 TnyFolder *folder = NULL;
734 TnyFolder *src_folder = NULL;
735 TnyFolderType folder_type;
736 folder = tny_msg_get_folder (info->draft_msg);
737 if (folder == NULL) goto end;
738 folder_type = modest_tny_folder_guess_folder_type (folder);
739 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
740 src_folder = outbox_folder;
742 src_folder = draft_folder;
744 /* Note: This can fail (with a warning) if the message is not really already in a folder,
745 * because this function requires it to have a UID. */
746 header = tny_msg_get_header (info->draft_msg);
747 tny_folder_remove_msg (src_folder, header, NULL);
749 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
750 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
752 g_object_unref (header);
753 g_object_unref (folder);
760 g_object_unref (info->draft_msg);
762 g_object_unref (draft_folder);
764 g_object_unref (outbox_folder);
765 if (info->transport_account)
766 g_object_unref (info->transport_account);
767 g_slice_free (SendNewMailInfo, info);
768 modest_mail_operation_notify_end (self);
772 modest_mail_operation_send_new_mail (ModestMailOperation *self,
773 TnyTransportAccount *transport_account,
775 const gchar *from, const gchar *to,
776 const gchar *cc, const gchar *bcc,
777 const gchar *subject, const gchar *plain_body,
778 const gchar *html_body,
779 const GList *attachments_list,
780 TnyHeaderFlags priority_flags)
782 ModestMailOperationPrivate *priv = NULL;
783 SendNewMailInfo *info;
785 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
786 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
788 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
790 /* Check parametters */
792 /* Set status failed and set an error */
793 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
794 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
795 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
796 _("Error trying to send a mail. You need to set at least one recipient"));
799 info = g_slice_new0 (SendNewMailInfo);
800 info->transport_account = transport_account;
801 if (transport_account)
802 g_object_ref (transport_account);
803 info->draft_msg = draft_msg;
805 g_object_ref (draft_msg);
806 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
807 attachments_list, priority_flags,
808 modest_mail_operation_send_new_mail_cb, info);
814 TnyTransportAccount *transport_account;
816 ModestMsgEditWindow *edit_window;
820 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
824 TnyFolder *src_folder = NULL;
825 TnyFolder *folder = NULL;
826 TnyHeader *header = NULL;
827 ModestMailOperationPrivate *priv = NULL;
828 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
830 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
832 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
833 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
834 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
835 "modest: failed to create a new msg\n");
839 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
841 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
842 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
843 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
844 "modest: failed to create a new msg\n");
849 tny_folder_add_msg (folder, msg, &(priv->error));
851 if ((!priv->error) && (info->draft_msg != NULL)) {
852 header = tny_msg_get_header (info->draft_msg);
853 src_folder = tny_header_get_folder (header);
855 /* Remove the old draft expunging it */
856 tny_folder_remove_msg (src_folder, header, NULL);
857 /* tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
858 /* tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
860 tny_folder_sync (folder, TRUE, &(priv->error)); /* FALSE --> don't expunge */
861 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /* expunge */
863 g_object_unref (header);
867 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
869 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
871 if (info->edit_window)
872 modest_msg_edit_window_set_draft (info->edit_window, msg);
877 g_object_unref (G_OBJECT(folder));
879 g_object_unref (G_OBJECT(src_folder));
880 if (info->edit_window)
881 g_object_unref (G_OBJECT(info->edit_window));
883 g_object_unref (G_OBJECT (info->draft_msg));
884 if (info->transport_account)
885 g_object_unref (G_OBJECT(info->transport_account));
886 g_slice_free (SaveToDraftsInfo, info);
888 modest_mail_operation_notify_end (self);
892 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
893 TnyTransportAccount *transport_account,
895 ModestMsgEditWindow *edit_window,
896 const gchar *from, const gchar *to,
897 const gchar *cc, const gchar *bcc,
898 const gchar *subject, const gchar *plain_body,
899 const gchar *html_body,
900 const GList *attachments_list,
901 TnyHeaderFlags priority_flags)
903 ModestMailOperationPrivate *priv = NULL;
904 SaveToDraftsInfo *info = NULL;
906 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
907 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
909 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
911 /* Get account and set it into mail_operation */
912 priv->account = g_object_ref (transport_account);
914 info = g_slice_new0 (SaveToDraftsInfo);
915 info->transport_account = g_object_ref (transport_account);
916 info->draft_msg = draft_msg;
918 g_object_ref (draft_msg);
919 info->edit_window = edit_window;
921 g_object_ref (edit_window);
923 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
924 attachments_list, priority_flags,
925 modest_mail_operation_save_to_drafts_cb, info);
931 ModestMailOperation *mail_op;
932 TnyStoreAccount *account;
933 TnyTransportAccount *transport_account;
936 gchar *retrieve_type;
938 UpdateAccountCallback callback;
945 ModestMailOperation *mail_op;
946 TnyMimePart *mime_part;
948 GetMimePartSizeCallback callback;
950 } GetMimePartSizeInfo;
952 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
953 /* We use this folder observer to track the headers that have been
954 * added to a folder */
957 TnyList *new_headers;
958 } InternalFolderObserver;
962 } InternalFolderObserverClass;
964 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
966 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
967 internal_folder_observer,
969 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
973 foreach_add_item (gpointer header, gpointer user_data)
975 tny_list_prepend (TNY_LIST (user_data),
976 g_object_ref (G_OBJECT (header)));
979 /* This is the method that looks for new messages in a folder */
981 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
983 InternalFolderObserver *derived = (InternalFolderObserver *)self;
985 TnyFolderChangeChanged changed;
987 changed = tny_folder_change_get_changed (change);
989 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
992 /* Get added headers */
993 list = tny_simple_list_new ();
994 tny_folder_change_get_added_headers (change, list);
996 /* Add them to the folder observer */
997 tny_list_foreach (list, foreach_add_item,
998 derived->new_headers);
1000 g_object_unref (G_OBJECT (list));
1005 internal_folder_observer_init (InternalFolderObserver *self)
1007 self->new_headers = tny_simple_list_new ();
1010 internal_folder_observer_finalize (GObject *object)
1012 InternalFolderObserver *self;
1014 self = (InternalFolderObserver *) object;
1015 g_object_unref (self->new_headers);
1017 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1020 tny_folder_observer_init (TnyFolderObserverIface *iface)
1022 iface->update_func = internal_folder_observer_update;
1025 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1027 GObjectClass *object_class;
1029 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1030 object_class = (GObjectClass*) klass;
1031 object_class->finalize = internal_folder_observer_finalize;
1037 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1040 TnyList *folders = tny_simple_list_new ();
1042 tny_folder_store_get_folders (store, folders, query, NULL);
1043 iter = tny_list_create_iterator (folders);
1045 while (!tny_iterator_is_done (iter)) {
1047 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1049 tny_list_prepend (all_folders, G_OBJECT (folder));
1050 recurse_folders (folder, query, all_folders);
1051 g_object_unref (G_OBJECT (folder));
1054 tny_iterator_next (iter);
1056 g_object_unref (G_OBJECT (iter));
1057 g_object_unref (G_OBJECT (folders));
1061 * Issues the "progress-changed" signal. The timer won't be removed,
1062 * so you must call g_source_remove to stop the signal emission
1065 idle_notify_progress (gpointer data)
1067 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1068 ModestMailOperationState *state;
1070 state = modest_mail_operation_clone_state (mail_op);
1072 /* This is a GDK lock because we are an idle callback and
1073 * the handlers of this signal can contain Gtk+ code */
1075 gdk_threads_enter (); /* CHECKED */
1076 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1077 gdk_threads_leave (); /* CHECKED */
1079 g_slice_free (ModestMailOperationState, state);
1085 * Issues the "progress-changed" signal and removes the timer. It uses
1086 * a lock to ensure that the progress information of the mail
1087 * operation is not modified while there are notifications pending
1090 idle_notify_progress_once (gpointer data)
1094 pair = (ModestPair *) data;
1096 /* This is a GDK lock because we are an idle callback and
1097 * the handlers of this signal can contain Gtk+ code */
1099 gdk_threads_enter (); /* CHECKED */
1100 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1101 gdk_threads_leave (); /* CHECKED */
1103 /* Free the state and the reference to the mail operation */
1104 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1105 g_object_unref (pair->first);
1111 * Used to notify the queue from the main
1112 * loop. We call it inside an idle call to achieve that
1115 idle_notify_queue (gpointer data)
1117 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1119 /* Do not need to block, the notify end will do it for us */
1120 modest_mail_operation_notify_end (mail_op);
1121 g_object_unref (mail_op);
1127 compare_headers_by_date (gconstpointer a,
1130 TnyHeader **header1, **header2;
1131 time_t sent1, sent2;
1133 header1 = (TnyHeader **) a;
1134 header2 = (TnyHeader **) b;
1136 sent1 = tny_header_get_date_sent (*header1);
1137 sent2 = tny_header_get_date_sent (*header2);
1139 /* We want the most recent ones (greater time_t) at the
1148 set_last_updated_idle (gpointer data)
1151 /* This is a GDK lock because we are an idle callback and
1152 * modest_account_mgr_set_int can contain Gtk+ code */
1154 gdk_threads_enter (); /* CHECKED - please recheck */
1156 /* It does not matter if the time is not exactly the same than
1157 the time when this idle was called, it's just an
1158 approximation and it won't be very different */
1160 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
1162 MODEST_ACCOUNT_LAST_UPDATED,
1166 gdk_threads_leave (); /* CHECKED - please recheck */
1172 idle_update_account_cb (gpointer data)
1174 UpdateAccountInfo *idle_info;
1176 idle_info = (UpdateAccountInfo *) data;
1178 /* This is a GDK lock because we are an idle callback and
1179 * idle_info->callback can contain Gtk+ code */
1181 gdk_threads_enter (); /* CHECKED */
1182 idle_info->callback (idle_info->mail_op,
1183 idle_info->new_headers,
1184 idle_info->user_data);
1185 gdk_threads_leave (); /* CHECKED */
1188 g_object_unref (idle_info->mail_op);
1196 update_account_thread (gpointer thr_user_data)
1198 static gboolean first_time = TRUE;
1199 UpdateAccountInfo *info = NULL;
1200 TnyList *all_folders = NULL;
1201 GPtrArray *new_headers = NULL;
1202 TnyIterator *iter = NULL;
1203 TnyFolderStoreQuery *query = NULL;
1204 ModestMailOperationPrivate *priv = NULL;
1205 ModestTnySendQueue *send_queue = NULL;
1206 gint num_new_headers = 0;
1208 info = (UpdateAccountInfo *) thr_user_data;
1209 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1211 /* Get account and set it into mail_operation */
1212 priv->account = g_object_ref (info->account);
1215 * Previousl, we did this for POP3, to do a logout-login upon send/receive,
1216 * because many POP-servers (like Gmail) do not
1217 * show any updates unless we do that.
1218 * But that didn't work with gmail anyway,
1219 * and tinymail now takes care of this itself by disconnecting
1220 * automatically after using the connection.
1223 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account))
1224 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1227 /* Get all the folders. We can do it synchronously because
1228 we're already running in a different thread than the UI */
1229 all_folders = tny_simple_list_new ();
1230 query = tny_folder_store_query_new ();
1231 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1232 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1237 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1241 iter = tny_list_create_iterator (all_folders);
1242 while (!tny_iterator_is_done (iter)) {
1243 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1245 recurse_folders (folder, query, all_folders);
1246 g_object_unref (folder);
1248 tny_iterator_next (iter);
1250 g_object_unref (G_OBJECT (iter));
1252 /* Update status and notify. We need to call the notification
1253 with a source function in order to call it from the main
1254 loop. We need that in order not to get into trouble with
1255 Gtk+. We use a timeout in order to provide more status
1256 information, because the sync tinymail call does not
1257 provide it for the moment */
1258 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1260 /* Refresh folders */
1261 num_new_headers = 0;
1262 new_headers = g_ptr_array_new ();
1263 iter = tny_list_create_iterator (all_folders);
1265 while (!tny_iterator_is_done (iter) && !priv->error &&
1266 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1268 InternalFolderObserver *observer;
1269 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1271 /* Refresh the folder */
1272 /* Our observer receives notification of new emails during folder refreshes,
1273 * so we can use observer->new_headers.
1275 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1276 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1278 /* This gets the status information (headers) from the server.
1279 * We use the blocking version, because we are already in a separate
1283 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
1284 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1287 /* If the retrieve type is full messages, refresh and get the messages */
1288 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1290 iter = tny_list_create_iterator (observer->new_headers);
1291 while (!tny_iterator_is_done (iter)) {
1292 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1294 /* Apply per-message size limits */
1295 if (tny_header_get_message_size (header) < info->max_size)
1296 g_ptr_array_add (new_headers, g_object_ref (header));
1298 g_object_unref (header);
1299 tny_iterator_next (iter);
1301 g_object_unref (iter);
1303 /* We do not need to do it the first time
1304 because it's automatically done by the tree
1306 if (G_UNLIKELY (!first_time))
1307 tny_folder_poke_status (TNY_FOLDER (folder));
1310 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1311 g_object_unref (observer);
1315 g_object_unref (G_OBJECT (folder));
1318 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1320 tny_iterator_next (iter);
1323 g_object_unref (G_OBJECT (iter));
1324 g_source_remove (timeout);
1326 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1327 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1328 new_headers->len > 0) {
1332 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1334 /* Apply message count limit */
1335 /* If the number of messages exceeds the maximum, ask the
1336 * user to download them all,
1337 * as per the UI spec "Retrieval Limits" section in 4.4:
1339 if (new_headers->len > info->retrieve_limit) {
1340 /* TODO: Ask the user, instead of just
1342 * mail_nc_msg_count_limit_exceeded, with 'Get
1343 * all' and 'Newest only' buttons. */
1344 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1345 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1346 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1347 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1348 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1353 priv->total = MIN (new_headers->len, info->retrieve_limit);
1354 while (msg_num < priv->total) {
1356 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1357 TnyFolder *folder = tny_header_get_folder (header);
1358 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1359 ModestMailOperationState *state;
1363 /* We can not just use the mail operation because the
1364 values of done and total could change before the
1366 state = modest_mail_operation_clone_state (info->mail_op);
1367 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1368 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1369 pair, (GDestroyNotify) modest_pair_free);
1371 g_object_unref (msg);
1372 g_object_unref (folder);
1378 /* Get the number of new headers and free them */
1379 num_new_headers = new_headers->len;
1380 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1381 g_ptr_array_free (new_headers, FALSE);
1383 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1386 /* Perform send (if operation was not cancelled) */
1389 if (priv->account != NULL)
1390 g_object_unref (priv->account);
1391 priv->account = g_object_ref (info->transport_account);
1393 send_queue = modest_runtime_get_send_queue (info->transport_account);
1395 modest_tny_send_queue_try_to_send (send_queue);
1397 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1398 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1399 "cannot create a send queue for %s\n",
1400 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1401 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1404 /* Check if the operation was a success */
1406 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1408 /* Update the last updated key */
1409 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1410 set_last_updated_idle,
1411 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1412 (GDestroyNotify) g_free);
1417 if (info->callback) {
1418 UpdateAccountInfo *idle_info;
1420 /* This thread is not in the main lock */
1421 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1422 idle_info->mail_op = g_object_ref (info->mail_op);
1423 idle_info->new_headers = num_new_headers;
1424 idle_info->callback = info->callback;
1425 idle_info->user_data = info->user_data;
1426 g_idle_add (idle_update_account_cb, idle_info);
1429 /* Notify about operation end. Note that the info could be
1430 freed before this idle happens, but the mail operation will
1432 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1435 g_object_unref (query);
1436 g_object_unref (all_folders);
1437 g_object_unref (info->account);
1438 g_object_unref (info->transport_account);
1439 g_free (info->retrieve_type);
1440 g_slice_free (UpdateAccountInfo, info);
1448 modest_mail_operation_update_account (ModestMailOperation *self,
1449 const gchar *account_name,
1450 UpdateAccountCallback callback,
1453 GThread *thread = NULL;
1454 UpdateAccountInfo *info = NULL;
1455 ModestMailOperationPrivate *priv = NULL;
1456 ModestAccountMgr *mgr = NULL;
1457 TnyStoreAccount *store_account = NULL;
1458 TnyTransportAccount *transport_account = NULL;
1460 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1461 g_return_val_if_fail (account_name, FALSE);
1463 /* Init mail operation. Set total and done to 0, and do not
1464 update them, this way the progress objects will know that
1465 we have no clue about the number of the objects */
1466 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1469 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1471 /* Get the Modest account */
1472 store_account = (TnyStoreAccount *)
1473 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1475 TNY_ACCOUNT_TYPE_STORE);
1477 /* Make sure that we have a connection, and request one
1479 * TODO: Is there some way to trigger this for every attempt to
1480 * use the network? */
1481 if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1484 if (!store_account) {
1485 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1486 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1487 "cannot get tny store account for %s\n", account_name);
1492 /* Get the transport account, we can not do it in the thread
1493 due to some problems with dbus */
1494 transport_account = (TnyTransportAccount *)
1495 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1497 if (!transport_account) {
1498 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1499 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1500 "cannot get tny transport account for %s\n", account_name);
1504 /* Create the helper object */
1505 info = g_slice_new (UpdateAccountInfo);
1506 info->mail_op = self;
1507 info->account = store_account;
1508 info->transport_account = transport_account;
1509 info->callback = callback;
1510 info->user_data = user_data;
1512 /* Get the message size limit */
1513 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1514 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1515 if (info->max_size == 0)
1516 info->max_size = G_MAXINT;
1518 info->max_size = info->max_size * KB;
1520 /* Get per-account retrieval type */
1521 mgr = modest_runtime_get_account_mgr ();
1522 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1523 MODEST_ACCOUNT_RETRIEVE, FALSE);
1525 /* Get per-account message amount retrieval limit */
1526 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1527 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1528 if (info->retrieve_limit == 0)
1529 info->retrieve_limit = G_MAXINT;
1531 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1533 /* Set account busy */
1534 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1535 priv->account_name = g_strdup(account_name);
1537 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1542 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1544 callback (self, 0, user_data);
1545 modest_mail_operation_notify_end (self);
1549 /* ******************************************************************* */
1550 /* ************************** STORE ACTIONS ************************* */
1551 /* ******************************************************************* */
1555 modest_mail_operation_create_folder (ModestMailOperation *self,
1556 TnyFolderStore *parent,
1559 ModestMailOperationPrivate *priv;
1560 TnyFolder *new_folder = NULL;
1562 TnyList *list = tny_simple_list_new ();
1563 TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1565 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1566 g_return_val_if_fail (name, NULL);
1568 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1570 /* Check for already existing folder */
1571 tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1572 tny_folder_store_get_folders (parent, list, query, NULL);
1573 g_object_unref (G_OBJECT (query));
1575 if (tny_list_get_length (list) > 0) {
1576 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1577 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1578 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1579 _CS("ckdg_ib_folder_already_exists"));
1582 g_object_unref (G_OBJECT (list));
1585 if (TNY_IS_FOLDER (parent)) {
1586 /* Check folder rules */
1587 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1588 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1589 /* Set status failed and set an error */
1590 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1591 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1592 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1593 _("mail_in_ui_folder_create_error"));
1597 if (!strcmp (name, " ") || strchr (name, '/')) {
1598 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1599 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1600 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1601 _("mail_in_ui_folder_create_error"));
1605 /* Create the folder */
1606 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1607 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1609 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1612 /* Notify about operation end */
1613 modest_mail_operation_notify_end (self);
1619 modest_mail_operation_remove_folder (ModestMailOperation *self,
1621 gboolean remove_to_trash)
1623 TnyAccount *account;
1624 ModestMailOperationPrivate *priv;
1625 ModestTnyFolderRules rules;
1627 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1628 g_return_if_fail (TNY_IS_FOLDER (folder));
1630 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1632 /* Check folder rules */
1633 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1634 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1635 /* Set status failed and set an error */
1636 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1637 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1638 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1639 _("mail_in_ui_folder_delete_error"));
1643 /* Get the account */
1644 account = modest_tny_folder_get_account (folder);
1645 priv->account = g_object_ref(account);
1647 /* Delete folder or move to trash */
1648 if (remove_to_trash) {
1649 TnyFolder *trash_folder = NULL;
1650 trash_folder = modest_tny_account_get_special_folder (account,
1651 TNY_FOLDER_TYPE_TRASH);
1652 /* TODO: error_handling */
1654 modest_mail_operation_xfer_folder (self, folder,
1655 TNY_FOLDER_STORE (trash_folder),
1657 g_object_unref (trash_folder);
1660 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1662 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1663 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1666 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1669 g_object_unref (G_OBJECT (parent));
1671 g_object_unref (G_OBJECT (account));
1674 /* Notify about operation end */
1675 modest_mail_operation_notify_end (self);
1679 transfer_folder_status_cb (GObject *obj,
1683 ModestMailOperation *self;
1684 ModestMailOperationPrivate *priv;
1685 ModestMailOperationState *state;
1686 XFerMsgAsyncHelper *helper;
1688 g_return_if_fail (status != NULL);
1689 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1691 helper = (XFerMsgAsyncHelper *) user_data;
1692 g_return_if_fail (helper != NULL);
1694 self = helper->mail_op;
1695 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1697 priv->done = status->position;
1698 priv->total = status->of_total;
1700 state = modest_mail_operation_clone_state (self);
1702 /* This is not a GDK lock because we are a Tinymail callback
1703 * which is already GDK locked by Tinymail */
1705 /* no gdk_threads_enter (), CHECKED */
1707 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1709 /* no gdk_threads_leave (), CHECKED */
1711 g_slice_free (ModestMailOperationState, state);
1716 transfer_folder_cb (TnyFolder *folder,
1718 TnyFolderStore *into,
1719 TnyFolder *new_folder,
1723 XFerMsgAsyncHelper *helper;
1724 ModestMailOperation *self = NULL;
1725 ModestMailOperationPrivate *priv = NULL;
1727 helper = (XFerMsgAsyncHelper *) user_data;
1728 g_return_if_fail (helper != NULL);
1730 self = helper->mail_op;
1731 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1734 priv->error = g_error_copy (err);
1736 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1737 } else if (cancelled) {
1738 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1739 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1740 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1741 _("Transference of %s was cancelled."),
1742 tny_folder_get_name (folder));
1745 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1748 /* Notify about operation end */
1749 modest_mail_operation_notify_end (self);
1751 /* If user defined callback function was defined, call it */
1752 if (helper->user_callback) {
1754 /* This is not a GDK lock because we are a Tinymail callback
1755 * which is already GDK locked by Tinymail */
1757 /* no gdk_threads_enter (), CHECKED */
1758 helper->user_callback (priv->source, helper->user_data);
1759 /* no gdk_threads_leave () , CHECKED */
1763 g_object_unref (helper->mail_op);
1764 g_slice_free (XFerMsgAsyncHelper, helper);
1769 * This function checks if the new name is a valid name for our local
1770 * folders account. The new name could not be the same than then name
1771 * of any of the mandatory local folders
1773 * We can not rely on tinymail because tinymail does not check the
1774 * name of the virtual folders that the account could have in the case
1775 * that we're doing a rename (because it directly calls Camel which
1776 * knows nothing about our virtual folders).
1778 * In the case of an actual copy/move (i.e. move/copy a folder between
1779 * accounts) tinymail uses the tny_folder_store_create_account which
1780 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1781 * checks the new name of the folder, so this call in that case
1782 * wouldn't be needed. *But* NOTE that if tinymail changes its
1783 * implementation (if folder transfers within the same account is no
1784 * longer implemented as a rename) this call will allow Modest to work
1787 * If the new name is not valid, this function will set the status to
1788 * failed and will set also an error in the mail operation
1791 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1792 TnyFolderStore *into,
1793 const gchar *new_name)
1795 if (TNY_IS_ACCOUNT (into) &&
1796 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1797 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1799 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1800 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1801 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1802 _("ckdg_ib_folder_already_exists"));
1809 * This function checks if @ancestor is an acestor of @folder and
1810 * returns TRUE in that case
1813 folder_is_ancestor (TnyFolder *folder,
1814 TnyFolderStore *ancestor)
1816 TnyFolder *tmp = NULL;
1817 gboolean found = FALSE;
1820 while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1821 TnyFolderStore *folder_store;
1823 folder_store = tny_folder_get_folder_store (tmp);
1824 if (ancestor == folder_store)
1827 tmp = g_object_ref (folder_store);
1828 g_object_unref (folder_store);
1834 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1836 TnyFolderStore *parent,
1837 gboolean delete_original,
1838 XferMsgsAsynUserCallback user_callback,
1841 ModestMailOperationPrivate *priv = NULL;
1842 ModestTnyFolderRules parent_rules = 0, rules;
1843 XFerMsgAsyncHelper *helper = NULL;
1844 const gchar *folder_name = NULL;
1845 const gchar *error_msg;
1847 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1848 g_return_if_fail (TNY_IS_FOLDER (folder));
1849 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1851 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1852 folder_name = tny_folder_get_name (folder);
1854 /* Set the error msg */
1855 error_msg = (delete_original) ?
1856 _("mail_in_ui_folder_move_target_error") :
1857 _("mail_in_ui_folder_copy_target_error");
1859 /* Get account and set it into mail_operation */
1860 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1861 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1863 /* Get folder rules */
1864 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1865 if (TNY_IS_FOLDER (parent))
1866 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1868 /* The moveable restriction is applied also to copy operation */
1869 if ((gpointer) parent == (gpointer) folder ||
1870 (!TNY_IS_FOLDER_STORE (parent)) ||
1871 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1873 /* Set status failed and set an error */
1874 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1875 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1876 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1879 /* Notify the queue */
1880 modest_mail_operation_notify_end (self);
1882 } else if (TNY_IS_FOLDER (parent) &&
1883 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1885 /* Set status failed and set an error */
1886 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1887 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1888 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1891 /* Notify the queue */
1892 modest_mail_operation_notify_end (self);
1894 } else if (TNY_IS_FOLDER (parent) &&
1895 TNY_IS_FOLDER_STORE (folder) &&
1896 folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (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,
1903 /* Notify the queue */
1904 modest_mail_operation_notify_end (self);
1906 } else if (TNY_IS_FOLDER_STORE (parent) &&
1907 modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1908 /* Check that the new folder name is not used by any
1911 /* Set status failed and set an error */
1912 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1913 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1914 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1917 /* Notify the queue */
1918 modest_mail_operation_notify_end (self);
1920 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1921 /* Check that the new folder name is not used by any
1922 special local folder */
1924 /* Set status failed and set an error */
1925 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1926 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1927 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1930 /* Notify the queue */
1931 modest_mail_operation_notify_end (self);
1933 /* Create the helper */
1934 helper = g_slice_new0 (XFerMsgAsyncHelper);
1935 helper->mail_op = g_object_ref(self);
1936 helper->dest_folder = NULL;
1937 helper->headers = NULL;
1938 helper->user_callback = user_callback;
1939 helper->user_data = user_data;
1941 /* Move/Copy folder */
1942 tny_folder_copy_async (folder,
1944 tny_folder_get_name (folder),
1947 transfer_folder_status_cb,
1954 modest_mail_operation_rename_folder (ModestMailOperation *self,
1958 ModestMailOperationPrivate *priv;
1959 ModestTnyFolderRules rules;
1960 XFerMsgAsyncHelper *helper;
1962 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1963 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1964 g_return_if_fail (name);
1966 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1968 /* Get account and set it into mail_operation */
1969 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1971 /* Check folder rules */
1972 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1973 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1974 /* Set status failed and set an error */
1975 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1976 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1977 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1978 _("FIXME: unable to rename"));
1980 /* Notify about operation end */
1981 modest_mail_operation_notify_end (self);
1982 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1983 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1984 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1985 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1986 _("FIXME: unable to rename"));
1987 /* Notify about operation end */
1988 modest_mail_operation_notify_end (self);
1990 TnyFolderStore *into;
1992 into = tny_folder_get_folder_store (folder);
1994 /* Check that the new folder name is not used by any
1995 special local folder */
1996 if (new_name_valid_if_local_account (priv, into, name)) {
1997 /* Create the helper */
1998 helper = g_slice_new0 (XFerMsgAsyncHelper);
1999 helper->mail_op = g_object_ref(self);
2000 helper->dest_folder = NULL;
2001 helper->headers = NULL;
2002 helper->user_callback = NULL;
2003 helper->user_data = NULL;
2005 /* Rename. Camel handles folder subscription/unsubscription */
2006 tny_folder_copy_async (folder, into, name, TRUE,
2008 transfer_folder_status_cb,
2011 modest_mail_operation_notify_end (self);
2013 g_object_unref (into);
2017 /* ******************************************************************* */
2018 /* ************************** MSG ACTIONS ************************* */
2019 /* ******************************************************************* */
2021 void modest_mail_operation_get_msg (ModestMailOperation *self,
2023 GetMsgAsyncUserCallback user_callback,
2026 GetMsgAsyncHelper *helper = NULL;
2028 ModestMailOperationPrivate *priv;
2030 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2031 g_return_if_fail (TNY_IS_HEADER (header));
2033 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2034 folder = tny_header_get_folder (header);
2036 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2038 /* Get message from folder */
2040 /* Get account and set it into mail_operation */
2041 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2043 /* Check for cached messages */
2044 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2045 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2047 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2049 helper = g_slice_new0 (GetMsgAsyncHelper);
2050 helper->mail_op = self;
2051 helper->user_callback = user_callback;
2052 helper->user_data = user_data;
2053 helper->header = g_object_ref (header);
2055 // The callback's reference so that the mail op is not
2056 // finalized until the async operation is completed even if
2057 // the user canceled the request meanwhile.
2058 g_object_ref (G_OBJECT (helper->mail_op));
2060 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2062 g_object_unref (G_OBJECT (folder));
2064 /* Set status failed and set an error */
2065 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2066 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2067 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2068 _("Error trying to get a message. No folder found for header"));
2070 /* Notify the queue */
2071 modest_mail_operation_notify_end (self);
2076 get_msg_cb (TnyFolder *folder,
2082 GetMsgAsyncHelper *helper = NULL;
2083 ModestMailOperation *self = NULL;
2084 ModestMailOperationPrivate *priv = NULL;
2086 helper = (GetMsgAsyncHelper *) user_data;
2087 g_return_if_fail (helper != NULL);
2088 self = helper->mail_op;
2089 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2090 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2092 /* Check errors and cancel */
2094 priv->error = g_error_copy (error);
2095 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2096 } else if (cancelled) {
2097 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2098 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2099 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2100 _("Error trying to refresh the contents of %s"),
2101 tny_folder_get_name (folder));
2103 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2106 /* If user defined callback function was defined, call it even
2107 if the operation failed*/
2108 if (helper->user_callback) {
2109 /* This is not a GDK lock because we are a Tinymail callback
2110 * which is already GDK locked by Tinymail */
2112 /* no gdk_threads_enter (), CHECKED */
2113 helper->user_callback (self, helper->header, msg, helper->user_data);
2114 /* no gdk_threads_leave (), CHECKED */
2117 /* Notify about operation end */
2118 modest_mail_operation_notify_end (self);
2120 g_object_unref (helper->mail_op);
2121 g_object_unref (helper->header);
2122 g_slice_free (GetMsgAsyncHelper, helper);
2127 get_msg_status_cb (GObject *obj,
2131 GetMsgAsyncHelper *helper = NULL;
2132 ModestMailOperation *self;
2133 ModestMailOperationPrivate *priv;
2134 ModestMailOperationState *state;
2136 g_return_if_fail (status != NULL);
2137 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2139 helper = (GetMsgAsyncHelper *) user_data;
2140 g_return_if_fail (helper != NULL);
2142 self = helper->mail_op;
2143 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2148 state = modest_mail_operation_clone_state (self);
2149 state->bytes_done = status->position;
2150 state->bytes_total = status->of_total;
2152 /* This is not a GDK lock because we are a Tinymail callback
2153 * which is already GDK locked by Tinymail */
2155 /* no gdk_threads_enter (), CHECKED */
2156 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2157 /* no gdk_threads_leave (), CHECKED */
2159 g_slice_free (ModestMailOperationState, state);
2162 /****************************************************/
2164 ModestMailOperation *mail_op;
2166 GetMsgAsyncUserCallback user_callback;
2168 GDestroyNotify notify;
2172 GetMsgAsyncUserCallback user_callback;
2176 ModestMailOperation *mail_op;
2177 } NotifyGetMsgsInfo;
2181 * Used by get_msgs_full_thread to call the user_callback for each
2182 * message that has been read
2185 notify_get_msgs_full (gpointer data)
2187 NotifyGetMsgsInfo *info;
2189 info = (NotifyGetMsgsInfo *) data;
2191 /* This is a GDK lock because we are an idle callback and
2192 * because info->user_callback can contain Gtk+ code */
2194 gdk_threads_enter (); /* CHECKED */
2195 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2196 gdk_threads_leave (); /* CHECKED */
2198 g_slice_free (NotifyGetMsgsInfo, info);
2204 * Used by get_msgs_full_thread to free al the thread resources and to
2205 * call the destroy function for the passed user_data
2208 get_msgs_full_destroyer (gpointer data)
2210 GetFullMsgsInfo *info;
2212 info = (GetFullMsgsInfo *) data;
2216 /* This is a GDK lock because we are an idle callback and
2217 * because info->notify can contain Gtk+ code */
2219 gdk_threads_enter (); /* CHECKED */
2220 info->notify (info->user_data);
2221 gdk_threads_leave (); /* CHECKED */
2225 g_object_unref (info->headers);
2226 g_slice_free (GetFullMsgsInfo, info);
2232 get_msgs_full_thread (gpointer thr_user_data)
2234 GetFullMsgsInfo *info;
2235 ModestMailOperationPrivate *priv = NULL;
2236 TnyIterator *iter = NULL;
2238 info = (GetFullMsgsInfo *) thr_user_data;
2239 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2241 iter = tny_list_create_iterator (info->headers);
2242 while (!tny_iterator_is_done (iter)) {
2246 header = TNY_HEADER (tny_iterator_get_current (iter));
2247 folder = tny_header_get_folder (header);
2249 /* Check for cached messages */
2250 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2251 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2253 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2255 /* Get message from folder */
2258 /* The callback will call it per each header */
2259 msg = tny_folder_get_msg (folder, header, &(priv->error));
2262 ModestMailOperationState *state;
2267 /* notify progress */
2268 state = modest_mail_operation_clone_state (info->mail_op);
2269 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2270 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2271 pair, (GDestroyNotify) modest_pair_free);
2273 /* The callback is the responsible for
2274 freeing the message */
2275 if (info->user_callback) {
2276 NotifyGetMsgsInfo *info_notify;
2277 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2278 info_notify->user_callback = info->user_callback;
2279 info_notify->mail_op = info->mail_op;
2280 info_notify->header = g_object_ref (header);
2281 info_notify->msg = g_object_ref (msg);
2282 info_notify->user_data = info->user_data;
2283 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2284 notify_get_msgs_full,
2287 g_object_unref (msg);
2290 /* Set status failed and set an error */
2291 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2292 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2293 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2294 "Error trying to get a message. No folder found for header");
2298 g_object_unref (header);
2300 tny_iterator_next (iter);
2303 /* Set operation status */
2304 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2305 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2307 /* Notify about operation end */
2308 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2310 /* Free thread resources. Will be called after all previous idles */
2311 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2317 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2318 TnyList *header_list,
2319 GetMsgAsyncUserCallback user_callback,
2321 GDestroyNotify notify)
2323 TnyHeader *header = NULL;
2324 TnyFolder *folder = NULL;
2326 ModestMailOperationPrivate *priv = NULL;
2327 GetFullMsgsInfo *info = NULL;
2328 gboolean size_ok = TRUE;
2330 TnyIterator *iter = NULL;
2332 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2334 /* Init mail operation */
2335 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2336 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2338 priv->total = tny_list_get_length(header_list);
2340 /* Get account and set it into mail_operation */
2341 if (tny_list_get_length (header_list) >= 1) {
2342 iter = tny_list_create_iterator (header_list);
2343 header = TNY_HEADER (tny_iterator_get_current (iter));
2345 folder = tny_header_get_folder (header);
2347 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2349 g_object_unref (folder);
2352 g_object_unref (header);
2355 if (tny_list_get_length (header_list) == 1) {
2356 g_object_unref (iter);
2361 /* Get msg size limit */
2362 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2363 MODEST_CONF_MSG_SIZE_LIMIT,
2366 g_clear_error (&(priv->error));
2367 max_size = G_MAXINT;
2369 max_size = max_size * KB;
2372 /* Check message size limits. If there is only one message
2373 always retrieve it */
2375 while (!tny_iterator_is_done (iter) && size_ok) {
2376 header = TNY_HEADER (tny_iterator_get_current (iter));
2378 if (tny_header_get_message_size (header) >= max_size)
2380 g_object_unref (header);
2383 tny_iterator_next (iter);
2385 g_object_unref (iter);
2389 /* Create the info */
2390 info = g_slice_new0 (GetFullMsgsInfo);
2391 info->mail_op = self;
2392 info->user_callback = user_callback;
2393 info->user_data = user_data;
2394 info->headers = g_object_ref (header_list);
2395 info->notify = notify;
2397 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2399 /* Set status failed and set an error */
2400 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2401 /* FIXME: the error msg is different for pop */
2402 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2403 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2404 _("emev_ni_ui_imap_msg_size_exceed_error"));
2405 /* Remove from queue and free resources */
2406 modest_mail_operation_notify_end (self);
2414 modest_mail_operation_remove_msg (ModestMailOperation *self,
2416 gboolean remove_to_trash /*ignored*/)
2419 ModestMailOperationPrivate *priv;
2421 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2422 g_return_if_fail (TNY_IS_HEADER (header));
2424 if (remove_to_trash)
2425 g_warning ("remove to trash is not implemented");
2427 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2428 folder = tny_header_get_folder (header);
2430 /* Get account and set it into mail_operation */
2431 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2433 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2435 /* remove message from folder */
2436 tny_folder_remove_msg (folder, header, &(priv->error));
2438 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2439 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2441 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2442 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2443 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2444 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2445 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2446 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2449 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2450 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2456 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2458 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2461 g_object_unref (G_OBJECT (folder));
2463 /* Notify about operation end */
2464 modest_mail_operation_notify_end (self);
2468 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2470 gboolean remove_to_trash /*ignored*/)
2473 ModestMailOperationPrivate *priv;
2474 TnyIterator *iter = NULL;
2475 TnyHeader *header = NULL;
2477 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2478 g_return_if_fail (TNY_IS_LIST (headers));
2480 if (remove_to_trash)
2481 g_warning ("remove to trash is not implemented");
2483 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2485 /* Get folder from first header and sync it */
2486 iter = tny_list_create_iterator (headers);
2487 header = TNY_HEADER (tny_iterator_get_current (iter));
2488 folder = tny_header_get_folder (header);
2490 /* Get account and set it into mail_operation */
2491 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2493 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2495 /* remove message from folder */
2496 tny_folder_remove_msgs (folder, headers, &(priv->error));
2498 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2499 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2500 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2501 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2502 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2503 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2506 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2507 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2513 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2515 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2518 g_object_unref (header);
2519 g_object_unref (iter);
2520 g_object_unref (G_OBJECT (folder));
2522 /* Notify about operation end */
2523 modest_mail_operation_notify_end (self);
2528 transfer_msgs_status_cb (GObject *obj,
2532 XFerMsgAsyncHelper *helper = NULL;
2533 ModestMailOperation *self;
2534 ModestMailOperationPrivate *priv;
2535 ModestMailOperationState *state;
2538 g_return_if_fail (status != NULL);
2539 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2541 helper = (XFerMsgAsyncHelper *) user_data;
2542 g_return_if_fail (helper != NULL);
2544 self = helper->mail_op;
2545 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2547 priv->done = status->position;
2548 priv->total = status->of_total;
2550 state = modest_mail_operation_clone_state (self);
2552 /* This is not a GDK lock because we are a Tinymail callback and
2553 * Tinymail already acquires the Gdk lock */
2555 /* no gdk_threads_enter (), CHECKED */
2557 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2559 /* no gdk_threads_leave (), CHECKED */
2561 g_slice_free (ModestMailOperationState, state);
2566 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2568 XFerMsgAsyncHelper *helper;
2569 ModestMailOperation *self;
2570 ModestMailOperationPrivate *priv;
2571 TnyIterator *iter = NULL;
2572 TnyHeader *header = NULL;
2574 helper = (XFerMsgAsyncHelper *) user_data;
2575 self = helper->mail_op;
2577 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2580 priv->error = g_error_copy (err);
2582 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2583 } else if (cancelled) {
2584 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2585 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2586 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2587 _("Error trying to refresh the contents of %s"),
2588 tny_folder_get_name (folder));
2591 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2593 /* Update folder counts */
2594 tny_folder_poke_status (folder);
2595 tny_folder_poke_status (helper->dest_folder);
2599 /* Mark headers as deleted and seen */
2600 if ((helper->delete) &&
2601 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2602 iter = tny_list_create_iterator (helper->headers);
2603 while (!tny_iterator_is_done (iter)) {
2604 header = TNY_HEADER (tny_iterator_get_current (iter));
2605 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2606 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2607 g_object_unref (header);
2609 tny_iterator_next (iter);
2615 /* Notify about operation end */
2616 modest_mail_operation_notify_end (self);
2618 /* If user defined callback function was defined, call it */
2619 if (helper->user_callback) {
2620 /* This is not a GDK lock because we are a Tinymail callback and
2621 * Tinymail already acquires the Gdk lock */
2623 /* no gdk_threads_enter (), CHECKED */
2624 helper->user_callback (priv->source, helper->user_data);
2625 /* no gdk_threads_leave (), CHECKED */
2629 if (helper->headers)
2630 g_object_unref (helper->headers);
2631 if (helper->dest_folder)
2632 g_object_unref (helper->dest_folder);
2633 if (helper->mail_op)
2634 g_object_unref (helper->mail_op);
2636 g_object_unref (folder);
2638 g_object_unref (iter);
2639 g_slice_free (XFerMsgAsyncHelper, helper);
2644 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2647 gboolean delete_original,
2648 XferMsgsAsynUserCallback user_callback,
2651 ModestMailOperationPrivate *priv = NULL;
2652 TnyIterator *iter = NULL;
2653 TnyFolder *src_folder = NULL;
2654 XFerMsgAsyncHelper *helper = NULL;
2655 TnyHeader *header = NULL;
2656 ModestTnyFolderRules rules = 0;
2657 const gchar *id1 = NULL;
2658 const gchar *id2 = NULL;
2659 gboolean same_folder = FALSE;
2661 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2662 g_return_if_fail (TNY_IS_LIST (headers));
2663 g_return_if_fail (TNY_IS_FOLDER (folder));
2665 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2668 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2670 /* Apply folder rules */
2671 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2672 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2673 /* Set status failed and set an error */
2674 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2675 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2676 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2677 _CS("ckct_ib_unable_to_paste_here"));
2678 /* Notify the queue */
2679 modest_mail_operation_notify_end (self);
2683 /* Get source folder */
2684 iter = tny_list_create_iterator (headers);
2685 header = TNY_HEADER (tny_iterator_get_current (iter));
2687 src_folder = tny_header_get_folder (header);
2688 g_object_unref (header);
2691 g_object_unref (iter);
2693 /* Check folder source and destination */
2694 id1 = tny_folder_get_id (src_folder);
2695 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2696 same_folder = !g_ascii_strcasecmp (id1, id2);
2698 /* Set status failed and set an error */
2699 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2700 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2701 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2702 _("mcen_ib_unable_to_copy_samefolder"));
2704 /* Notify the queue */
2705 modest_mail_operation_notify_end (self);
2708 g_object_unref (src_folder);
2712 /* Create the helper */
2713 helper = g_slice_new0 (XFerMsgAsyncHelper);
2714 helper->mail_op = g_object_ref(self);
2715 helper->dest_folder = g_object_ref(folder);
2716 helper->headers = g_object_ref(headers);
2717 helper->user_callback = user_callback;
2718 helper->user_data = user_data;
2719 helper->delete = delete_original;
2721 /* Get account and set it into mail_operation */
2722 priv->account = modest_tny_folder_get_account (src_folder);
2724 /* Transfer messages */
2725 tny_folder_transfer_msgs_async (src_folder,
2730 transfer_msgs_status_cb,
2736 on_refresh_folder (TnyFolder *folder,
2741 RefreshAsyncHelper *helper = NULL;
2742 ModestMailOperation *self = NULL;
2743 ModestMailOperationPrivate *priv = NULL;
2745 helper = (RefreshAsyncHelper *) user_data;
2746 self = helper->mail_op;
2747 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2749 g_return_if_fail(priv!=NULL);
2752 priv->error = g_error_copy (error);
2753 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2754 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2760 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2761 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2762 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2763 _("Error trying to refresh the contents of %s"),
2764 tny_folder_get_name (folder));
2765 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2769 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2771 /* Call user defined callback, if it exists */
2772 if (helper->user_callback) {
2774 /* This is not a GDK lock because we are a Tinymail callback and
2775 * Tinymail already acquires the Gdk lock */
2777 /* no gdk_threads_enter (), CHECKED */
2778 helper->user_callback (self, folder, helper->user_data);
2779 /* no gdk_threads_leave (), CHECKED */
2784 g_slice_free (RefreshAsyncHelper, helper);
2786 /* Notify about operation end */
2787 modest_mail_operation_notify_end (self);
2788 g_object_unref(self);
2792 on_refresh_folder_status_update (GObject *obj,
2796 RefreshAsyncHelper *helper = NULL;
2797 ModestMailOperation *self = NULL;
2798 ModestMailOperationPrivate *priv = NULL;
2799 ModestMailOperationState *state;
2801 g_return_if_fail (user_data != NULL);
2802 g_return_if_fail (status != NULL);
2803 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2805 helper = (RefreshAsyncHelper *) user_data;
2806 self = helper->mail_op;
2807 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2809 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2811 priv->done = status->position;
2812 priv->total = status->of_total;
2814 state = modest_mail_operation_clone_state (self);
2816 /* This is not a GDK lock because we are a Tinymail callback and
2817 * Tinymail already acquires the Gdk lock */
2819 /* no gdk_threads_enter (), CHECKED */
2821 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2823 /* no gdk_threads_leave (), CHECKED */
2825 g_slice_free (ModestMailOperationState, state);
2829 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2831 RefreshAsyncUserCallback user_callback,
2834 ModestMailOperationPrivate *priv = NULL;
2835 RefreshAsyncHelper *helper = NULL;
2837 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2839 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2841 /* Get account and set it into mail_operation */
2842 priv->account = modest_tny_folder_get_account (folder);
2844 /* Create the helper */
2845 helper = g_slice_new0 (RefreshAsyncHelper);
2846 helper->mail_op = g_object_ref(self);
2847 helper->user_callback = user_callback;
2848 helper->user_data = user_data;
2850 /* Refresh the folder. TODO: tinymail could issue a status
2851 updates before the callback call then this could happen. We
2852 must review the design */
2853 tny_folder_refresh_async (folder,
2855 on_refresh_folder_status_update,
2861 * It's used by the mail operation queue to notify the observers
2862 * attached to that signal that the operation finished. We need to use
2863 * that because tinymail does not give us the progress of a given
2864 * operation when it finishes (it directly calls the operation
2868 modest_mail_operation_notify_end (ModestMailOperation *self)
2870 ModestMailOperationState *state;
2871 ModestMailOperationPrivate *priv = NULL;
2873 g_return_if_fail (self);
2875 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2877 /* Set the account back to not busy */
2878 if (priv->account_name) {
2879 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2880 priv->account_name, FALSE);
2881 g_free(priv->account_name);
2882 priv->account_name = NULL;
2885 /* Notify the observers about the mail operation end */
2886 /* We do not wrapp this emission because we assume that this
2887 function is always called from within the main lock */
2888 state = modest_mail_operation_clone_state (self);
2889 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2890 g_slice_free (ModestMailOperationState, state);