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 /* GDK LOCK: this one is here because we call this from a thread */
579 /* callback could be 'send mail' or 'save to draft'; i dont knonw */
580 /* why these tow operations must serialized, i think it could be */
582 gdk_threads_enter ();
583 info->callback (info->mail_op, info->msg, info->userdata);
584 gdk_threads_leave ();
586 g_object_unref (info->mail_op);
588 g_object_unref (info->msg);
589 g_slice_free (CreateMsgIdleInfo, info);
595 create_msg_thread (gpointer thread_data)
597 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
598 TnyMsg *new_msg = NULL;
599 ModestMailOperationPrivate *priv;
601 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
602 if (info->html_body == NULL) {
603 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
604 info->bcc, info->subject, info->plain_body,
605 info->attachments_list); /* FIXME: attachments */
607 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
608 info->bcc, info->subject, info->html_body,
609 info->plain_body, info->attachments_list);
614 /* Set priority flags in message */
615 header = tny_msg_get_header (new_msg);
616 if (info->priority_flags != 0)
617 tny_header_set_flags (header, info->priority_flags);
618 if (info->attachments_list != NULL) {
619 tny_header_set_flags (header, TNY_HEADER_FLAG_ATTACHMENTS);
621 g_object_unref (G_OBJECT(header));
623 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
624 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
625 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
626 "modest: failed to create a new msg\n");
634 g_free (info->plain_body);
635 g_free (info->html_body);
636 g_free (info->subject);
637 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
638 g_list_free (info->attachments_list);
640 if (info->callback) {
641 CreateMsgIdleInfo *idle_info;
642 idle_info = g_slice_new0 (CreateMsgIdleInfo);
643 idle_info->mail_op = info->mail_op;
644 g_object_ref (info->mail_op);
645 idle_info->msg = new_msg;
647 g_object_ref (new_msg);
648 idle_info->callback = info->callback;
649 idle_info->userdata = info->userdata;
650 g_idle_add (idle_create_msg_cb, idle_info);
652 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
655 g_object_unref (info->mail_op);
656 g_slice_free (CreateMsgInfo, info);
661 modest_mail_operation_create_msg (ModestMailOperation *self,
662 const gchar *from, const gchar *to,
663 const gchar *cc, const gchar *bcc,
664 const gchar *subject, const gchar *plain_body,
665 const gchar *html_body,
666 const GList *attachments_list,
667 TnyHeaderFlags priority_flags,
668 ModestMailOperationCreateMsgCallback callback,
671 CreateMsgInfo *info = NULL;
673 info = g_slice_new0 (CreateMsgInfo);
674 info->mail_op = self;
677 info->from = g_strdup (from);
678 info->to = g_strdup (to);
679 info->cc = g_strdup (cc);
680 info->bcc = g_strdup (bcc);
681 info->subject = g_strdup (subject);
682 info->plain_body = g_strdup (plain_body);
683 info->html_body = g_strdup (html_body);
684 info->attachments_list = g_list_copy ((GList *) attachments_list);
685 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
686 info->priority_flags = priority_flags;
688 info->callback = callback;
689 info->userdata = userdata;
691 g_thread_create (create_msg_thread, info, FALSE, NULL);
696 TnyTransportAccount *transport_account;
701 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
705 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
706 TnyFolder *draft_folder = NULL;
707 TnyFolder *outbox_folder = NULL;
714 /* Call mail operation */
715 modest_mail_operation_send_mail (self, info->transport_account, msg);
717 /* Remove old mail from its source folder */
718 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
719 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
720 if (info->draft_msg != NULL) {
721 TnyFolder *folder = NULL;
722 TnyFolder *src_folder = NULL;
723 TnyFolderType folder_type;
724 folder = tny_msg_get_folder (info->draft_msg);
725 if (folder == NULL) goto end;
726 folder_type = modest_tny_folder_guess_folder_type (folder);
727 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
728 src_folder = outbox_folder;
730 src_folder = draft_folder;
732 /* Note: This can fail (with a warning) if the message is not really already in a folder,
733 * because this function requires it to have a UID. */
734 header = tny_msg_get_header (info->draft_msg);
735 tny_folder_remove_msg (src_folder, header, NULL);
736 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
737 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
738 g_object_unref (header);
739 g_object_unref (folder);
744 g_object_unref (info->draft_msg);
746 g_object_unref (draft_folder);
748 g_object_unref (outbox_folder);
749 if (info->transport_account)
750 g_object_unref (info->transport_account);
751 g_slice_free (SendNewMailInfo, info);
752 modest_mail_operation_notify_end (self);
756 modest_mail_operation_send_new_mail (ModestMailOperation *self,
757 TnyTransportAccount *transport_account,
759 const gchar *from, const gchar *to,
760 const gchar *cc, const gchar *bcc,
761 const gchar *subject, const gchar *plain_body,
762 const gchar *html_body,
763 const GList *attachments_list,
764 TnyHeaderFlags priority_flags)
766 ModestMailOperationPrivate *priv = NULL;
767 SendNewMailInfo *info;
769 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
770 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
772 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
774 /* Check parametters */
776 /* Set status failed and set an error */
777 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
778 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
779 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
780 _("Error trying to send a mail. You need to set at least one recipient"));
783 info = g_slice_new0 (SendNewMailInfo);
784 info->transport_account = transport_account;
785 if (transport_account)
786 g_object_ref (transport_account);
787 info->draft_msg = draft_msg;
789 g_object_ref (draft_msg);
790 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
791 attachments_list, priority_flags,
792 modest_mail_operation_send_new_mail_cb, info);
798 TnyTransportAccount *transport_account;
800 ModestMsgEditWindow *edit_window;
804 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
808 TnyFolder *src_folder = NULL;
809 TnyFolder *folder = NULL;
810 TnyHeader *header = NULL;
811 ModestMailOperationPrivate *priv = NULL;
812 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
814 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
816 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
817 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
818 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
819 "modest: failed to create a new msg\n");
823 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
825 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
826 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
827 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
828 "modest: failed to create a new msg\n");
833 tny_folder_add_msg (folder, msg, &(priv->error));
835 if ((!priv->error) && (info->draft_msg != NULL)) {
836 header = tny_msg_get_header (info->draft_msg);
837 src_folder = tny_header_get_folder (header);
838 /* Remove the old draft expunging it */
839 tny_folder_remove_msg (src_folder, header, NULL);
840 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
841 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
842 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /* expunge */
843 g_object_unref (header);
847 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
849 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
851 if (info->edit_window)
852 modest_msg_edit_window_set_draft (info->edit_window, msg);
857 g_object_unref (G_OBJECT(folder));
859 g_object_unref (G_OBJECT(src_folder));
860 if (info->edit_window)
861 g_object_unref (G_OBJECT(info->edit_window));
863 g_object_unref (G_OBJECT (info->draft_msg));
864 if (info->transport_account)
865 g_object_unref (G_OBJECT(info->transport_account));
866 g_slice_free (SaveToDraftsInfo, info);
868 modest_mail_operation_notify_end (self);
872 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
873 TnyTransportAccount *transport_account,
875 ModestMsgEditWindow *edit_window,
876 const gchar *from, const gchar *to,
877 const gchar *cc, const gchar *bcc,
878 const gchar *subject, const gchar *plain_body,
879 const gchar *html_body,
880 const GList *attachments_list,
881 TnyHeaderFlags priority_flags)
883 ModestMailOperationPrivate *priv = NULL;
884 SaveToDraftsInfo *info = NULL;
886 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
887 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
889 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
891 /* Get account and set it into mail_operation */
892 priv->account = g_object_ref (transport_account);
894 info = g_slice_new0 (SaveToDraftsInfo);
895 info->transport_account = g_object_ref (transport_account);
896 info->draft_msg = draft_msg;
898 g_object_ref (draft_msg);
899 info->edit_window = edit_window;
901 g_object_ref (edit_window);
903 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
904 attachments_list, priority_flags,
905 modest_mail_operation_save_to_drafts_cb, info);
911 ModestMailOperation *mail_op;
912 TnyStoreAccount *account;
913 TnyTransportAccount *transport_account;
916 gchar *retrieve_type;
918 UpdateAccountCallback callback;
925 ModestMailOperation *mail_op;
926 TnyMimePart *mime_part;
928 GetMimePartSizeCallback callback;
930 } GetMimePartSizeInfo;
932 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
933 /* We use this folder observer to track the headers that have been
934 * added to a folder */
937 TnyList *new_headers;
938 } InternalFolderObserver;
942 } InternalFolderObserverClass;
944 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
946 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
947 internal_folder_observer,
949 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
953 foreach_add_item (gpointer header, gpointer user_data)
955 tny_list_prepend (TNY_LIST (user_data),
956 g_object_ref (G_OBJECT (header)));
959 /* This is the method that looks for new messages in a folder */
961 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
963 InternalFolderObserver *derived = (InternalFolderObserver *)self;
965 TnyFolderChangeChanged changed;
967 changed = tny_folder_change_get_changed (change);
969 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
972 /* Get added headers */
973 list = tny_simple_list_new ();
974 tny_folder_change_get_added_headers (change, list);
976 /* Add them to the folder observer */
977 tny_list_foreach (list, foreach_add_item,
978 derived->new_headers);
980 g_object_unref (G_OBJECT (list));
985 internal_folder_observer_init (InternalFolderObserver *self)
987 self->new_headers = tny_simple_list_new ();
990 internal_folder_observer_finalize (GObject *object)
992 InternalFolderObserver *self;
994 self = (InternalFolderObserver *) object;
995 g_object_unref (self->new_headers);
997 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1000 tny_folder_observer_init (TnyFolderObserverIface *iface)
1002 iface->update_func = internal_folder_observer_update;
1005 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1007 GObjectClass *object_class;
1009 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1010 object_class = (GObjectClass*) klass;
1011 object_class->finalize = internal_folder_observer_finalize;
1017 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1020 TnyList *folders = tny_simple_list_new ();
1022 tny_folder_store_get_folders (store, folders, query, NULL);
1023 iter = tny_list_create_iterator (folders);
1025 while (!tny_iterator_is_done (iter)) {
1027 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1029 tny_list_prepend (all_folders, G_OBJECT (folder));
1030 recurse_folders (folder, query, all_folders);
1031 g_object_unref (G_OBJECT (folder));
1034 tny_iterator_next (iter);
1036 g_object_unref (G_OBJECT (iter));
1037 g_object_unref (G_OBJECT (folders));
1041 * Issues the "progress-changed" signal. The timer won't be removed,
1042 * so you must call g_source_remove to stop the signal emission
1045 idle_notify_progress (gpointer data)
1047 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1048 ModestMailOperationState *state;
1050 state = modest_mail_operation_clone_state (mail_op);
1051 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1052 /* GDK LOCK: this one is here because we call this from a thread */
1053 /* However, refresh_account operations call async operations on */
1054 /* on tinymail, which also emmit 'progress-changed' signal. This */
1055 /* may be is required to serialize executions of progress-changed'*/
1056 /* signal handlers. */
1057 gdk_threads_enter ();
1059 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1060 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1061 gdk_threads_leave ();
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 /* GDK LOCK: this one is here because we call this from a thread */
1081 /* However, refresh_account operations call async operations on */
1082 /* on tinymail, which also emmit 'progress-changed' signal. This */
1083 /* may be is required to serialize executions of progress-changed'*/
1084 /* signal handlers */
1085 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1086 gdk_threads_enter ();
1088 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1089 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1090 gdk_threads_leave ();
1093 /* Free the state and the reference to the mail operation */
1094 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1095 g_object_unref (pair->first);
1101 * Used to notify the queue from the main
1102 * loop. We call it inside an idle call to achieve that
1105 idle_notify_queue (gpointer data)
1107 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1109 /* Do not need to block, the notify end will do it for us */
1110 modest_mail_operation_notify_end (mail_op);
1111 g_object_unref (mail_op);
1117 compare_headers_by_date (gconstpointer a,
1120 TnyHeader **header1, **header2;
1121 time_t sent1, sent2;
1123 header1 = (TnyHeader **) a;
1124 header2 = (TnyHeader **) b;
1126 sent1 = tny_header_get_date_sent (*header1);
1127 sent2 = tny_header_get_date_sent (*header2);
1129 /* We want the most recent ones (greater time_t) at the
1138 set_last_updated_idle (gpointer data)
1140 /* GDK LOCK: this one is here because we call this from a thread */
1141 /* and executed with g_idle_add_full during process of method */
1142 /* update_account_thread, to serialize access to LAST_UPDATED */
1143 /* property of each account */
1144 gdk_threads_enter ();
1146 /* It does not matter if the time is not exactly the same than
1147 the time when this idle was called, it's just an
1148 approximation and it won't be very different */
1149 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
1151 MODEST_ACCOUNT_LAST_UPDATED,
1155 gdk_threads_leave ();
1161 idle_update_account_cb (gpointer data)
1163 UpdateAccountInfo *idle_info;
1165 idle_info = (UpdateAccountInfo *) data;
1167 /* GDK LOCK: this one is here because we call this from a thread */
1168 /* and executed with g_idle_add during update_account_thread, to */
1169 /* serialize the execution of refresh user callback (mainloop) */
1170 /* received as parameter in modest_mail_operation_update_account */
1171 gdk_threads_enter ();
1172 idle_info->callback (idle_info->mail_op,
1173 idle_info->new_headers,
1174 idle_info->user_data);
1175 gdk_threads_leave ();
1178 g_object_unref (idle_info->mail_op);
1186 update_account_thread (gpointer thr_user_data)
1188 static gboolean first_time = TRUE;
1189 UpdateAccountInfo *info = NULL;
1190 TnyList *all_folders = NULL;
1191 GPtrArray *new_headers = NULL;
1192 TnyIterator *iter = NULL;
1193 TnyFolderStoreQuery *query = NULL;
1194 ModestMailOperationPrivate *priv = NULL;
1195 ModestTnySendQueue *send_queue = NULL;
1196 gint num_new_headers = 0;
1198 info = (UpdateAccountInfo *) thr_user_data;
1199 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1201 /* Get account and set it into mail_operation */
1202 priv->account = g_object_ref (info->account);
1205 * Previousl, we did this for POP3, to do a logout-login upon send/receive,
1206 * because many POP-servers (like Gmail) do not
1207 * show any updates unless we do that.
1208 * But that didn't work with gmail anyway,
1209 * and tinymail now takes care of this itself by disconnecting
1210 * automatically after using the connection.
1213 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account))
1214 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1217 /* Get all the folders. We can do it synchronously because
1218 we're already running in a different thread than the UI */
1219 all_folders = tny_simple_list_new ();
1220 query = tny_folder_store_query_new ();
1221 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1222 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1227 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1231 iter = tny_list_create_iterator (all_folders);
1232 while (!tny_iterator_is_done (iter)) {
1233 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1235 recurse_folders (folder, query, all_folders);
1236 g_object_unref (folder);
1238 tny_iterator_next (iter);
1240 g_object_unref (G_OBJECT (iter));
1242 /* Update status and notify. We need to call the notification
1243 with a source function in order to call it from the main
1244 loop. We need that in order not to get into trouble with
1245 Gtk+. We use a timeout in order to provide more status
1246 information, because the sync tinymail call does not
1247 provide it for the moment */
1248 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1250 /* Refresh folders */
1251 num_new_headers = 0;
1252 new_headers = g_ptr_array_new ();
1253 iter = tny_list_create_iterator (all_folders);
1255 while (!tny_iterator_is_done (iter) && !priv->error &&
1256 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1258 InternalFolderObserver *observer;
1259 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1261 /* Refresh the folder */
1262 /* Our observer receives notification of new emails during folder refreshes,
1263 * so we can use observer->new_headers.
1265 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1266 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1268 /* This gets the status information (headers) from the server.
1269 * We use the blocking version, because we are already in a separate
1273 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
1274 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1277 /* If the retrieve type is full messages, refresh and get the messages */
1278 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1280 iter = tny_list_create_iterator (observer->new_headers);
1281 while (!tny_iterator_is_done (iter)) {
1282 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1284 /* Apply per-message size limits */
1285 if (tny_header_get_message_size (header) < info->max_size)
1286 g_ptr_array_add (new_headers, g_object_ref (header));
1288 g_object_unref (header);
1289 tny_iterator_next (iter);
1291 g_object_unref (iter);
1293 /* We do not need to do it the first time
1294 because it's automatically done by the tree
1296 if (G_UNLIKELY (!first_time))
1297 tny_folder_poke_status (TNY_FOLDER (folder));
1299 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1300 g_object_unref (observer);
1304 g_object_unref (G_OBJECT (folder));
1307 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1309 tny_iterator_next (iter);
1312 g_object_unref (G_OBJECT (iter));
1313 g_source_remove (timeout);
1315 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1316 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1317 new_headers->len > 0) {
1321 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1323 /* Apply message count limit */
1324 /* If the number of messages exceeds the maximum, ask the
1325 * user to download them all,
1326 * as per the UI spec "Retrieval Limits" section in 4.4:
1328 if (new_headers->len > info->retrieve_limit) {
1329 /* TODO: Ask the user, instead of just
1331 * mail_nc_msg_count_limit_exceeded, with 'Get
1332 * all' and 'Newest only' buttons. */
1333 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1334 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1335 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1336 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1337 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1342 priv->total = MIN (new_headers->len, info->retrieve_limit);
1343 while (msg_num < priv->total) {
1345 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1346 TnyFolder *folder = tny_header_get_folder (header);
1347 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1348 ModestMailOperationState *state;
1352 /* We can not just use the mail operation because the
1353 values of done and total could change before the
1355 state = modest_mail_operation_clone_state (info->mail_op);
1356 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1357 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1358 pair, (GDestroyNotify) modest_pair_free);
1360 g_object_unref (msg);
1361 g_object_unref (folder);
1367 /* Get the number of new headers and free them */
1368 num_new_headers = new_headers->len;
1369 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1370 g_ptr_array_free (new_headers, FALSE);
1372 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1375 /* Perform send (if operation was not cancelled) */
1376 /* priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND; */
1379 if (priv->account != NULL)
1380 g_object_unref (priv->account);
1381 priv->account = g_object_ref (info->transport_account);
1383 send_queue = modest_runtime_get_send_queue (info->transport_account);
1385 /* timeout = g_timeout_add (250, idle_notify_progress, info->mail_op); */
1386 modest_tny_send_queue_try_to_send (send_queue);
1387 /* g_source_remove (timeout); */
1389 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1390 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1391 "cannot create a send queue for %s\n",
1392 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1393 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1396 /* Check if the operation was a success */
1398 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1400 /* Update the last updated key */
1401 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1402 set_last_updated_idle,
1403 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1404 (GDestroyNotify) g_free);
1409 if (info->callback) {
1410 UpdateAccountInfo *idle_info;
1412 /* This thread is not in the main lock */
1413 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1414 idle_info->mail_op = g_object_ref (info->mail_op);
1415 idle_info->new_headers = num_new_headers;
1416 idle_info->callback = info->callback;
1417 g_idle_add (idle_update_account_cb, idle_info);
1420 /* Notify about operation end. Note that the info could be
1421 freed before this idle happens, but the mail operation will
1423 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1426 g_object_unref (query);
1427 g_object_unref (all_folders);
1428 g_object_unref (info->account);
1429 g_object_unref (info->transport_account);
1430 g_free (info->retrieve_type);
1431 g_slice_free (UpdateAccountInfo, info);
1439 modest_mail_operation_update_account (ModestMailOperation *self,
1440 const gchar *account_name,
1441 UpdateAccountCallback callback,
1444 GThread *thread = NULL;
1445 UpdateAccountInfo *info = NULL;
1446 ModestMailOperationPrivate *priv = NULL;
1447 ModestAccountMgr *mgr = NULL;
1448 TnyStoreAccount *store_account = NULL;
1449 TnyTransportAccount *transport_account = NULL;
1451 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1452 g_return_val_if_fail (account_name, FALSE);
1454 /* Init mail operation. Set total and done to 0, and do not
1455 update them, this way the progress objects will know that
1456 we have no clue about the number of the objects */
1457 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1460 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1462 /* Get the Modest account */
1463 store_account = (TnyStoreAccount *)
1464 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1466 TNY_ACCOUNT_TYPE_STORE);
1468 /* Make sure that we have a connection, and request one
1470 * TODO: Is there some way to trigger this for every attempt to
1471 * use the network? */
1472 if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1475 if (!store_account) {
1476 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1477 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1478 "cannot get tny store account for %s\n", account_name);
1483 /* Get the transport account, we can not do it in the thread
1484 due to some problems with dbus */
1485 transport_account = (TnyTransportAccount *)
1486 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1488 if (!transport_account) {
1489 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1490 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1491 "cannot get tny transport account for %s\n", account_name);
1495 /* Create the helper object */
1496 info = g_slice_new (UpdateAccountInfo);
1497 info->mail_op = self;
1498 info->account = store_account;
1499 info->transport_account = transport_account;
1500 info->callback = callback;
1501 info->user_data = user_data;
1503 /* Get the message size limit */
1504 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1505 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1506 if (info->max_size == 0)
1507 info->max_size = G_MAXINT;
1509 info->max_size = info->max_size * KB;
1511 /* Get per-account retrieval type */
1512 mgr = modest_runtime_get_account_mgr ();
1513 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1514 MODEST_ACCOUNT_RETRIEVE, FALSE);
1516 /* Get per-account message amount retrieval limit */
1517 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1518 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1519 if (info->retrieve_limit == 0)
1520 info->retrieve_limit = G_MAXINT;
1522 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1524 /* Set account busy */
1525 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1526 priv->account_name = g_strdup(account_name);
1528 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1533 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1535 callback (self, 0, user_data);
1536 modest_mail_operation_notify_end (self);
1540 /* ******************************************************************* */
1541 /* ************************** STORE ACTIONS ************************* */
1542 /* ******************************************************************* */
1546 modest_mail_operation_create_folder (ModestMailOperation *self,
1547 TnyFolderStore *parent,
1550 ModestMailOperationPrivate *priv;
1551 TnyFolder *new_folder = NULL;
1553 TnyList *list = tny_simple_list_new ();
1554 TnyFolderStoreQuery *query = tny_folder_store_query_new ();
1556 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1557 g_return_val_if_fail (name, NULL);
1559 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1561 /* Check for already existing folder */
1562 tny_folder_store_query_add_item (query, name, TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME);
1563 tny_folder_store_get_folders (parent, list, query, NULL);
1564 g_object_unref (G_OBJECT (query));
1566 if (tny_list_get_length (list) > 0) {
1567 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1568 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1569 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1570 _CS("ckdg_ib_folder_already_exists"));
1573 g_object_unref (G_OBJECT (list));
1576 if (TNY_IS_FOLDER (parent)) {
1577 /* Check folder rules */
1578 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1579 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1580 /* Set status failed and set an error */
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 if (!strcmp (name, " ") || strchr (name, '/')) {
1589 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1590 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1591 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1592 _("mail_in_ui_folder_create_error"));
1596 /* Create the folder */
1597 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1598 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1600 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1603 /* Notify about operation end */
1604 modest_mail_operation_notify_end (self);
1610 modest_mail_operation_remove_folder (ModestMailOperation *self,
1612 gboolean remove_to_trash)
1614 TnyAccount *account;
1615 ModestMailOperationPrivate *priv;
1616 ModestTnyFolderRules rules;
1618 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1619 g_return_if_fail (TNY_IS_FOLDER (folder));
1621 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1623 /* Check folder rules */
1624 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1625 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1626 /* Set status failed and set an error */
1627 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1628 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1629 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1630 _("mail_in_ui_folder_delete_error"));
1634 /* Get the account */
1635 account = modest_tny_folder_get_account (folder);
1636 priv->account = g_object_ref(account);
1638 /* Delete folder or move to trash */
1639 if (remove_to_trash) {
1640 TnyFolder *trash_folder = NULL;
1641 trash_folder = modest_tny_account_get_special_folder (account,
1642 TNY_FOLDER_TYPE_TRASH);
1643 /* TODO: error_handling */
1645 modest_mail_operation_xfer_folder (self, folder,
1646 TNY_FOLDER_STORE (trash_folder),
1648 g_object_unref (trash_folder);
1651 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1653 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1654 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1657 g_object_unref (G_OBJECT (parent));
1659 g_object_unref (G_OBJECT (account));
1662 /* Notify about operation end */
1663 modest_mail_operation_notify_end (self);
1667 transfer_folder_status_cb (GObject *obj,
1671 ModestMailOperation *self;
1672 ModestMailOperationPrivate *priv;
1673 ModestMailOperationState *state;
1674 XFerMsgAsyncHelper *helper;
1676 g_return_if_fail (status != NULL);
1677 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1679 helper = (XFerMsgAsyncHelper *) user_data;
1680 g_return_if_fail (helper != NULL);
1682 self = helper->mail_op;
1683 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1685 priv->done = status->position;
1686 priv->total = status->of_total;
1688 state = modest_mail_operation_clone_state (self);
1689 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1690 /* GDK LOCK: this one is here because we call this from a thread */
1691 /* However, refresh_account operations call async operations on */
1692 /* on tinymail, which also emmit 'progress-changed' signal. This */
1693 /* may be is required to serialize executions of progress-changed'*/
1694 /* signal handlers. */
1695 gdk_threads_enter ();
1697 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1698 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
1699 gdk_threads_leave ();
1701 g_slice_free (ModestMailOperationState, state);
1706 transfer_folder_cb (TnyFolder *folder,
1707 TnyFolderStore *into,
1709 TnyFolder *new_folder,
1713 XFerMsgAsyncHelper *helper;
1714 ModestMailOperation *self = NULL;
1715 ModestMailOperationPrivate *priv = NULL;
1717 helper = (XFerMsgAsyncHelper *) user_data;
1718 g_return_if_fail (helper != NULL);
1720 self = helper->mail_op;
1721 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1724 priv->error = g_error_copy (*err);
1726 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1727 } else if (cancelled) {
1728 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1729 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1730 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1731 _("Transference of %s was cancelled."),
1732 tny_folder_get_name (folder));
1735 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1738 /* Notify about operation end */
1739 modest_mail_operation_notify_end (self);
1741 /* If user defined callback function was defined, call it */
1742 if (helper->user_callback) {
1743 /* GDK LOCK: I think this one can be removed if Tinymail */
1744 /* wraps the lock itself. */
1745 /* This function is called as idle in tinymail. */
1746 gdk_threads_enter ();
1747 helper->user_callback (priv->source, helper->user_data);
1748 gdk_threads_leave ();
1752 g_object_unref (helper->mail_op);
1753 g_slice_free (XFerMsgAsyncHelper, helper);
1758 * This function checks if the new name is a valid name for our local
1759 * folders account. The new name could not be the same than then name
1760 * of any of the mandatory local folders
1762 * We can not rely on tinymail because tinymail does not check the
1763 * name of the virtual folders that the account could have in the case
1764 * that we're doing a rename (because it directly calls Camel which
1765 * knows nothing about our virtual folders).
1767 * In the case of an actual copy/move (i.e. move/copy a folder between
1768 * accounts) tinymail uses the tny_folder_store_create_account which
1769 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1770 * checks the new name of the folder, so this call in that case
1771 * wouldn't be needed. *But* NOTE that if tinymail changes its
1772 * implementation (if folder transfers within the same account is no
1773 * longer implemented as a rename) this call will allow Modest to work
1776 * If the new name is not valid, this function will set the status to
1777 * failed and will set also an error in the mail operation
1780 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1781 TnyFolderStore *into,
1782 const gchar *new_name)
1784 if (TNY_IS_ACCOUNT (into) &&
1785 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1786 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1788 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1789 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1790 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1791 _("FIXME: folder name already in use"));
1798 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1800 TnyFolderStore *parent,
1801 gboolean delete_original,
1802 XferMsgsAsynUserCallback user_callback,
1805 ModestMailOperationPrivate *priv = NULL;
1806 ModestTnyFolderRules parent_rules = 0, rules;
1807 XFerMsgAsyncHelper *helper = NULL;
1809 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1810 g_return_if_fail (TNY_IS_FOLDER (folder));
1812 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1814 /* Get account and set it into mail_operation */
1815 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1816 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1818 /* Get folder rules */
1819 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1820 if (TNY_IS_FOLDER (parent))
1821 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1823 /* The moveable restriction is applied also to copy operation */
1824 if ((gpointer) parent == (gpointer) folder ||
1825 (!TNY_IS_FOLDER_STORE (parent)) ||
1826 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1827 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1828 /* Set status failed and set an error */
1829 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1830 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1831 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1832 _("mail_in_ui_folder_move_target_error"));
1834 /* Notify the queue */
1835 modest_mail_operation_notify_end (self);
1836 } else if (TNY_IS_FOLDER (parent) &&
1837 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1838 /* Set status failed and set an error */
1839 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1840 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1841 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1842 _("FIXME: parent folder does not accept new folders"));
1844 /* Notify the queue */
1845 modest_mail_operation_notify_end (self);
1849 /* Check that the new folder name is not used by any
1850 special local folder */
1851 if (new_name_valid_if_local_account (priv, parent,
1852 tny_folder_get_name (folder))) {
1853 /* Create the helper */
1854 helper = g_slice_new0 (XFerMsgAsyncHelper);
1855 helper->mail_op = g_object_ref(self);
1856 helper->dest_folder = NULL;
1857 helper->headers = NULL;
1858 helper->user_callback = user_callback;
1859 helper->user_data = user_data;
1861 /* Move/Copy folder */
1862 tny_folder_copy_async (folder,
1864 tny_folder_get_name (folder),
1867 transfer_folder_status_cb,
1870 modest_mail_operation_notify_end (self);
1876 modest_mail_operation_rename_folder (ModestMailOperation *self,
1880 ModestMailOperationPrivate *priv;
1881 ModestTnyFolderRules rules;
1882 XFerMsgAsyncHelper *helper;
1884 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1885 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1886 g_return_if_fail (name);
1888 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1890 /* Get account and set it into mail_operation */
1891 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1893 /* Check folder rules */
1894 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1895 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1896 /* Set status failed and set an error */
1897 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1898 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1899 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1900 _("FIXME: unable to rename"));
1902 /* Notify about operation end */
1903 modest_mail_operation_notify_end (self);
1904 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1905 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1906 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1907 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1908 _("FIXME: unable to rename"));
1909 /* Notify about operation end */
1910 modest_mail_operation_notify_end (self);
1912 TnyFolderStore *into;
1914 into = tny_folder_get_folder_store (folder);
1916 /* Check that the new folder name is not used by any
1917 special local folder */
1918 if (new_name_valid_if_local_account (priv, into, name)) {
1919 /* Create the helper */
1920 helper = g_slice_new0 (XFerMsgAsyncHelper);
1921 helper->mail_op = g_object_ref(self);
1922 helper->dest_folder = NULL;
1923 helper->headers = NULL;
1924 helper->user_callback = NULL;
1925 helper->user_data = NULL;
1927 /* Rename. Camel handles folder subscription/unsubscription */
1928 tny_folder_copy_async (folder, into, name, TRUE,
1930 transfer_folder_status_cb,
1933 modest_mail_operation_notify_end (self);
1935 g_object_unref (into);
1939 /* ******************************************************************* */
1940 /* ************************** MSG ACTIONS ************************* */
1941 /* ******************************************************************* */
1943 void modest_mail_operation_get_msg (ModestMailOperation *self,
1945 GetMsgAsyncUserCallback user_callback,
1948 GetMsgAsyncHelper *helper = NULL;
1950 ModestMailOperationPrivate *priv;
1952 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1953 g_return_if_fail (TNY_IS_HEADER (header));
1955 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1956 folder = tny_header_get_folder (header);
1958 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1960 /* Get message from folder */
1962 /* Get account and set it into mail_operation */
1963 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1965 helper = g_slice_new0 (GetMsgAsyncHelper);
1966 helper->mail_op = self;
1967 helper->user_callback = user_callback;
1968 helper->user_data = user_data;
1969 helper->header = g_object_ref (header);
1971 // The callback's reference so that the mail op is not
1972 // finalized until the async operation is completed even if
1973 // the user canceled the request meanwhile.
1974 g_object_ref (G_OBJECT (helper->mail_op));
1976 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1978 g_object_unref (G_OBJECT (folder));
1980 /* Set status failed and set an error */
1981 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1982 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1983 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1984 _("Error trying to get a message. No folder found for header"));
1986 /* Notify the queue */
1987 modest_mail_operation_notify_end (self);
1992 get_msg_cb (TnyFolder *folder,
1998 GetMsgAsyncHelper *helper = NULL;
1999 ModestMailOperation *self = NULL;
2000 ModestMailOperationPrivate *priv = NULL;
2002 helper = (GetMsgAsyncHelper *) user_data;
2003 g_return_if_fail (helper != NULL);
2004 self = helper->mail_op;
2005 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2006 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2008 /* Check errors and cancel */
2010 priv->error = g_error_copy (*error);
2011 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2012 } else if (cancelled) {
2013 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2014 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2015 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2016 _("Error trying to refresh the contents of %s"),
2017 tny_folder_get_name (folder));
2019 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2022 /* If user defined callback function was defined, call it even
2023 if the operation failed*/
2024 if (helper->user_callback) {
2025 /* This callback is called into an iddle by tinymail,
2026 and idles are not in the main lock */
2027 gdk_threads_enter ();
2028 helper->user_callback (self, helper->header, msg, helper->user_data);
2029 gdk_threads_leave ();
2032 /* Notify about operation end */
2033 modest_mail_operation_notify_end (self);
2035 g_object_unref (helper->mail_op);
2036 g_object_unref (helper->header);
2037 g_slice_free (GetMsgAsyncHelper, helper);
2042 get_msg_status_cb (GObject *obj,
2046 GetMsgAsyncHelper *helper = NULL;
2047 ModestMailOperation *self;
2048 ModestMailOperationPrivate *priv;
2049 ModestMailOperationState *state;
2051 g_return_if_fail (status != NULL);
2052 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2054 helper = (GetMsgAsyncHelper *) user_data;
2055 g_return_if_fail (helper != NULL);
2057 self = helper->mail_op;
2058 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2063 state = modest_mail_operation_clone_state (self);
2064 state->bytes_done = status->position;
2065 state->bytes_total = status->of_total;
2066 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2067 /* GDK LOCK: this one is here because we call this from a thread */
2068 /* However, refresh_account operations call async operations on */
2069 /* on tinymail, which also emmit 'progress-changed' signal. This */
2070 /* may be is required to serialize executions of progress-changed'*/
2071 /* signal handlers. */
2072 gdk_threads_enter ();
2074 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2075 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2076 gdk_threads_leave ();
2078 g_slice_free (ModestMailOperationState, state);
2081 /****************************************************/
2083 ModestMailOperation *mail_op;
2085 GetMsgAsyncUserCallback user_callback;
2087 GDestroyNotify notify;
2091 GetMsgAsyncUserCallback user_callback;
2095 ModestMailOperation *mail_op;
2096 } NotifyGetMsgsInfo;
2100 * Used by get_msgs_full_thread to call the user_callback for each
2101 * message that has been read
2104 notify_get_msgs_full (gpointer data)
2106 NotifyGetMsgsInfo *info;
2108 info = (NotifyGetMsgsInfo *) data;
2110 /* Call the user callback. Idles are not in the main lock, so
2112 gdk_threads_enter ();
2113 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2114 gdk_threads_leave ();
2116 g_slice_free (NotifyGetMsgsInfo, info);
2122 * Used by get_msgs_full_thread to free al the thread resources and to
2123 * call the destroy function for the passed user_data
2126 get_msgs_full_destroyer (gpointer data)
2128 GetFullMsgsInfo *info;
2130 info = (GetFullMsgsInfo *) data;
2133 /* GDK LOCK: this one is here because we call this from */
2135 /* It's used to free user_data, like pasted attachments */
2136 gdk_threads_enter ();
2137 info->notify (info->user_data);
2138 gdk_threads_leave ();
2142 g_object_unref (info->headers);
2143 g_slice_free (GetFullMsgsInfo, info);
2149 get_msgs_full_thread (gpointer thr_user_data)
2151 GetFullMsgsInfo *info;
2152 ModestMailOperationPrivate *priv = NULL;
2153 TnyIterator *iter = NULL;
2155 info = (GetFullMsgsInfo *) thr_user_data;
2156 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2158 iter = tny_list_create_iterator (info->headers);
2159 while (!tny_iterator_is_done (iter)) {
2163 header = TNY_HEADER (tny_iterator_get_current (iter));
2164 folder = tny_header_get_folder (header);
2166 /* Get message from folder */
2169 /* The callback will call it per each header */
2170 msg = tny_folder_get_msg (folder, header, &(priv->error));
2173 ModestMailOperationState *state;
2178 /* notify progress */
2179 state = modest_mail_operation_clone_state (info->mail_op);
2180 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2181 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2182 pair, (GDestroyNotify) modest_pair_free);
2184 /* The callback is the responsible for
2185 freeing the message */
2186 if (info->user_callback) {
2187 NotifyGetMsgsInfo *info_notify;
2188 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2189 info_notify->user_callback = info->user_callback;
2190 info_notify->mail_op = info->mail_op;
2191 info_notify->header = g_object_ref (header);
2192 info_notify->msg = g_object_ref (msg);
2193 info_notify->user_data = info->user_data;
2194 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2195 notify_get_msgs_full,
2198 g_object_unref (msg);
2201 /* Set status failed and set an error */
2202 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2203 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2204 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2205 "Error trying to get a message. No folder found for header");
2209 g_object_unref (header);
2211 tny_iterator_next (iter);
2214 /* Set operation status */
2215 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2216 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2218 /* Notify about operation end */
2219 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2221 /* Free thread resources. Will be called after all previous idles */
2222 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2228 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2229 TnyList *header_list,
2230 GetMsgAsyncUserCallback user_callback,
2232 GDestroyNotify notify)
2234 TnyHeader *header = NULL;
2235 TnyFolder *folder = NULL;
2237 ModestMailOperationPrivate *priv = NULL;
2238 GetFullMsgsInfo *info = NULL;
2239 gboolean size_ok = TRUE;
2241 TnyIterator *iter = NULL;
2243 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2245 /* Init mail operation */
2246 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2247 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2249 priv->total = tny_list_get_length(header_list);
2251 /* Get account and set it into mail_operation */
2252 if (tny_list_get_length (header_list) >= 1) {
2253 iter = tny_list_create_iterator (header_list);
2254 header = TNY_HEADER (tny_iterator_get_current (iter));
2256 folder = tny_header_get_folder (header);
2258 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2260 g_object_unref (folder);
2263 g_object_unref (header);
2266 if (tny_list_get_length (header_list) == 1) {
2267 g_object_unref (iter);
2272 /* Get msg size limit */
2273 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2274 MODEST_CONF_MSG_SIZE_LIMIT,
2277 g_clear_error (&(priv->error));
2278 max_size = G_MAXINT;
2280 max_size = max_size * KB;
2283 /* Check message size limits. If there is only one message
2284 always retrieve it */
2286 while (!tny_iterator_is_done (iter) && size_ok) {
2287 header = TNY_HEADER (tny_iterator_get_current (iter));
2289 if (tny_header_get_message_size (header) >= max_size)
2291 g_object_unref (header);
2294 tny_iterator_next (iter);
2296 g_object_unref (iter);
2300 /* Create the info */
2301 info = g_slice_new0 (GetFullMsgsInfo);
2302 info->mail_op = self;
2303 info->user_callback = user_callback;
2304 info->user_data = user_data;
2305 info->headers = g_object_ref (header_list);
2306 info->notify = notify;
2308 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2310 /* Set status failed and set an error */
2311 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2312 /* FIXME: the error msg is different for pop */
2313 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2314 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2315 _("emev_ni_ui_imap_msg_size_exceed_error"));
2316 /* Remove from queue and free resources */
2317 modest_mail_operation_notify_end (self);
2325 modest_mail_operation_remove_msg (ModestMailOperation *self, TnyHeader *header,
2326 gboolean remove_to_trash /*ignored*/)
2329 ModestMailOperationPrivate *priv;
2331 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2332 g_return_if_fail (TNY_IS_HEADER (header));
2334 if (remove_to_trash)
2335 g_warning ("remove to trash is not implemented");
2337 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2338 folder = tny_header_get_folder (header);
2340 /* Get account and set it into mail_operation */
2341 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2343 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2345 /* remove message from folder */
2346 tny_folder_remove_msg (folder, header, &(priv->error));
2347 /* if (!priv->error) { */
2348 /* tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
2349 /* tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
2351 /* if (TNY_IS_CAMEL_IMAP_FOLDER (folder)) */
2352 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2353 /* /\* tny_folder_sync (folder, FALSE, &(priv->error)); /\\* FALSE --> don't expunge *\\/ *\/ */
2354 /* else if (TNY_IS_CAMEL_POP_FOLDER (folder)) */
2355 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2356 /* /\* tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2358 /* /\* local folders *\/ */
2359 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2360 /* /\* tny_folder_sync (folder, TRUE, &(priv->error)); /\\* TRUE --> expunge *\\/ *\/ */
2366 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2368 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2371 g_object_unref (G_OBJECT (folder));
2373 /* Notify about operation end */
2374 modest_mail_operation_notify_end (self);
2378 transfer_msgs_status_cb (GObject *obj,
2382 XFerMsgAsyncHelper *helper = NULL;
2383 ModestMailOperation *self;
2384 ModestMailOperationPrivate *priv;
2385 ModestMailOperationState *state;
2388 g_return_if_fail (status != NULL);
2389 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2391 helper = (XFerMsgAsyncHelper *) user_data;
2392 g_return_if_fail (helper != NULL);
2394 self = helper->mail_op;
2395 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2397 priv->done = status->position;
2398 priv->total = status->of_total;
2400 state = modest_mail_operation_clone_state (self);
2401 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2402 /* GDK LOCK: this one is here because we call this from a thread */
2403 /* However, refresh_account operations call async operations on */
2404 /* on tinymail, which also emmit 'progress-changed' signal. This */
2405 /* may be is required to serialize executions of progress-changed'*/
2406 /* signal handlers. */
2407 gdk_threads_enter ();
2409 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2410 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2411 gdk_threads_leave ();
2413 g_slice_free (ModestMailOperationState, state);
2418 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
2420 XFerMsgAsyncHelper *helper;
2421 ModestMailOperation *self;
2422 ModestMailOperationPrivate *priv;
2423 TnyIterator *iter = NULL;
2424 TnyHeader *header = NULL;
2426 helper = (XFerMsgAsyncHelper *) user_data;
2427 self = helper->mail_op;
2429 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2432 priv->error = g_error_copy (*err);
2434 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2435 } else if (cancelled) {
2436 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2437 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2438 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2439 _("Error trying to refresh the contents of %s"),
2440 tny_folder_get_name (folder));
2443 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2447 /* Mark headers as deleted and seen */
2448 if ((helper->delete) &&
2449 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2450 iter = tny_list_create_iterator (helper->headers);
2451 while (!tny_iterator_is_done (iter)) {
2452 header = TNY_HEADER (tny_iterator_get_current (iter));
2453 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2454 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2455 g_object_unref (header);
2457 tny_iterator_next (iter);
2463 /* Notify about operation end */
2464 modest_mail_operation_notify_end (self);
2466 /* If user defined callback function was defined, call it */
2467 if (helper->user_callback) {
2468 /* GDK LOCK: I think this one can be removed if Tinymail */
2469 /* wraps the lock itself. */
2470 /* This function is called as idle in tinymail. */
2471 gdk_threads_enter ();
2472 helper->user_callback (priv->source, helper->user_data);
2473 gdk_threads_leave ();
2477 if (helper->headers)
2478 g_object_unref (helper->headers);
2479 if (helper->dest_folder)
2480 g_object_unref (helper->dest_folder);
2481 if (helper->mail_op)
2482 g_object_unref (helper->mail_op);
2484 g_object_unref (folder);
2486 g_object_unref (iter);
2487 g_slice_free (XFerMsgAsyncHelper, helper);
2492 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2495 gboolean delete_original,
2496 XferMsgsAsynUserCallback user_callback,
2499 ModestMailOperationPrivate *priv = NULL;
2500 TnyIterator *iter = NULL;
2501 TnyFolder *src_folder = NULL;
2502 XFerMsgAsyncHelper *helper = NULL;
2503 TnyHeader *header = NULL;
2504 ModestTnyFolderRules rules = 0;
2505 const gchar *id1 = NULL;
2506 const gchar *id2 = NULL;
2507 gboolean same_folder = FALSE;
2509 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2510 g_return_if_fail (TNY_IS_LIST (headers));
2511 g_return_if_fail (TNY_IS_FOLDER (folder));
2513 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2516 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2518 /* Apply folder rules */
2519 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2520 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2521 /* Set status failed and set an error */
2522 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2523 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2524 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2525 _CS("ckct_ib_unable_to_paste_here"));
2526 /* Notify the queue */
2527 modest_mail_operation_notify_end (self);
2531 /* Get source folder */
2532 iter = tny_list_create_iterator (headers);
2533 header = TNY_HEADER (tny_iterator_get_current (iter));
2535 src_folder = tny_header_get_folder (header);
2536 g_object_unref (header);
2539 g_object_unref (iter);
2541 /* Check folder source and destination */
2542 id1 = tny_folder_get_id (src_folder);
2543 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2544 same_folder = !g_ascii_strcasecmp (id1, id2);
2546 /* Set status failed and set an error */
2547 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2548 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2549 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2550 _("mcen_ib_unable_to_copy_samefolder"));
2552 /* Notify the queue */
2553 modest_mail_operation_notify_end (self);
2556 g_object_unref (src_folder);
2560 /* Create the helper */
2561 helper = g_slice_new0 (XFerMsgAsyncHelper);
2562 helper->mail_op = g_object_ref(self);
2563 helper->dest_folder = g_object_ref(folder);
2564 helper->headers = g_object_ref(headers);
2565 helper->user_callback = user_callback;
2566 helper->user_data = user_data;
2567 helper->delete = delete_original;
2569 /* Get account and set it into mail_operation */
2570 priv->account = modest_tny_folder_get_account (src_folder);
2572 /* Transfer messages */
2573 tny_folder_transfer_msgs_async (src_folder,
2578 transfer_msgs_status_cb,
2584 on_refresh_folder (TnyFolder *folder,
2589 RefreshAsyncHelper *helper = NULL;
2590 ModestMailOperation *self = NULL;
2591 ModestMailOperationPrivate *priv = NULL;
2593 helper = (RefreshAsyncHelper *) user_data;
2594 self = helper->mail_op;
2595 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2597 g_return_if_fail(priv!=NULL);
2600 priv->error = g_error_copy (*error);
2601 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2606 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2607 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2608 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2609 _("Error trying to refresh the contents of %s"),
2610 tny_folder_get_name (folder));
2614 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2616 /* Call user defined callback, if it exists */
2617 if (helper->user_callback) {
2618 /* GDK LOCK: I think this one can be removed if Tinymail */
2619 /* wraps the lock itself. */
2620 /* This function is called as idle in tinymail. */
2621 gdk_threads_enter ();
2622 helper->user_callback (self, folder, helper->user_data);
2623 gdk_threads_leave ();
2627 g_slice_free (RefreshAsyncHelper, helper);
2629 /* Notify about operation end */
2630 modest_mail_operation_notify_end (self);
2631 g_object_unref(self);
2635 on_refresh_folder_status_update (GObject *obj,
2639 RefreshAsyncHelper *helper = NULL;
2640 ModestMailOperation *self = NULL;
2641 ModestMailOperationPrivate *priv = NULL;
2642 ModestMailOperationState *state;
2644 g_return_if_fail (user_data != NULL);
2645 g_return_if_fail (status != NULL);
2646 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2648 helper = (RefreshAsyncHelper *) user_data;
2649 self = helper->mail_op;
2650 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2652 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2654 priv->done = status->position;
2655 priv->total = status->of_total;
2657 state = modest_mail_operation_clone_state (self);
2658 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2659 /* GDK LOCK: this one is here because we call this from a thread */
2660 /* However, refresh_account operations call async operations on */
2661 /* on tinymail, which also emmit 'progress-changed' signal. This */
2662 /* may be is required to serialize executions of progress-changed'*/
2663 /* signal handlers. */
2664 gdk_threads_enter ();
2666 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2667 #ifdef TINYMAIL_IDLES_NOT_LOCKED_YET
2668 gdk_threads_leave ();
2670 g_slice_free (ModestMailOperationState, state);
2674 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2676 RefreshAsyncUserCallback user_callback,
2679 ModestMailOperationPrivate *priv = NULL;
2680 RefreshAsyncHelper *helper = NULL;
2682 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2684 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2686 /* Get account and set it into mail_operation */
2687 priv->account = modest_tny_folder_get_account (folder);
2689 /* Create the helper */
2690 helper = g_slice_new0 (RefreshAsyncHelper);
2691 helper->mail_op = g_object_ref(self);
2692 helper->user_callback = user_callback;
2693 helper->user_data = user_data;
2695 /* Refresh the folder. TODO: tinymail could issue a status
2696 updates before the callback call then this could happen. We
2697 must review the design */
2698 tny_folder_refresh_async (folder,
2700 on_refresh_folder_status_update,
2706 * It's used by the mail operation queue to notify the observers
2707 * attached to that signal that the operation finished. We need to use
2708 * that because tinymail does not give us the progress of a given
2709 * operation when it finishes (it directly calls the operation
2713 modest_mail_operation_notify_end (ModestMailOperation *self)
2715 ModestMailOperationState *state;
2716 ModestMailOperationPrivate *priv = NULL;
2718 g_return_if_fail (self);
2720 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2722 /* Set the account back to not busy */
2723 if (priv->account_name) {
2724 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2725 priv->account_name, FALSE);
2726 g_free(priv->account_name);
2727 priv->account_name = NULL;
2730 /* Notify the observers about the mail operation end */
2731 /* We do not wrapp this emission because we assume that this
2732 function is always called from within the main lock */
2733 state = modest_mail_operation_clone_state (self);
2734 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2735 g_slice_free (ModestMailOperationState, state);