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;
308 priv->error_checking_user_data = user_data;
314 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
316 ModestMailOperationPrivate *priv;
318 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
319 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
321 if (priv->error_checking != NULL)
322 priv->error_checking (self, priv->error_checking_user_data);
326 ModestMailOperationTypeOperation
327 modest_mail_operation_get_type_operation (ModestMailOperation *self)
329 ModestMailOperationPrivate *priv;
331 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
333 return priv->op_type;
337 modest_mail_operation_is_mine (ModestMailOperation *self,
340 ModestMailOperationPrivate *priv;
342 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
343 if (priv->source == NULL) return FALSE;
345 return priv->source == me;
349 modest_mail_operation_get_source (ModestMailOperation *self)
351 ModestMailOperationPrivate *priv;
353 g_return_val_if_fail (self, NULL);
355 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
357 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
361 return g_object_ref (priv->source);
364 ModestMailOperationStatus
365 modest_mail_operation_get_status (ModestMailOperation *self)
367 ModestMailOperationPrivate *priv;
369 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
370 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
371 MODEST_MAIL_OPERATION_STATUS_INVALID);
373 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
375 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
376 return MODEST_MAIL_OPERATION_STATUS_INVALID;
383 modest_mail_operation_get_error (ModestMailOperation *self)
385 ModestMailOperationPrivate *priv;
387 g_return_val_if_fail (self, NULL);
388 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
390 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
393 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
401 modest_mail_operation_cancel (ModestMailOperation *self)
403 ModestMailOperationPrivate *priv;
404 gboolean canceled = FALSE;
406 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
408 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
410 /* Note that if we call cancel with an already canceled mail
411 operation the progress changed signal won't be emitted */
412 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
416 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
418 /* Cancel the mail operation. We need to wrap it between this
419 start/stop operations to allow following calls to the
421 g_return_val_if_fail (priv->account, FALSE);
422 tny_account_cancel (priv->account);
428 modest_mail_operation_get_task_done (ModestMailOperation *self)
430 ModestMailOperationPrivate *priv;
432 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
434 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
439 modest_mail_operation_get_task_total (ModestMailOperation *self)
441 ModestMailOperationPrivate *priv;
443 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
445 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
450 modest_mail_operation_is_finished (ModestMailOperation *self)
452 ModestMailOperationPrivate *priv;
453 gboolean retval = FALSE;
455 if (!MODEST_IS_MAIL_OPERATION (self)) {
456 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
460 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
462 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
463 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
464 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
465 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
475 modest_mail_operation_get_id (ModestMailOperation *self)
477 ModestMailOperationPrivate *priv;
479 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
481 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
486 modest_mail_operation_set_id (ModestMailOperation *self,
489 ModestMailOperationPrivate *priv;
491 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
493 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
498 * Creates an image of the current state of a mail operation, the
499 * caller must free it
501 static ModestMailOperationState *
502 modest_mail_operation_clone_state (ModestMailOperation *self)
504 ModestMailOperationState *state;
505 ModestMailOperationPrivate *priv;
507 /* FIXME: this should be fixed properly
509 * in some cases, priv was NULL, so checking here to
512 g_return_val_if_fail (self, NULL);
513 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
514 g_return_val_if_fail (priv, NULL);
519 state = g_slice_new (ModestMailOperationState);
521 state->status = priv->status;
522 state->op_type = priv->op_type;
523 state->done = priv->done;
524 state->total = priv->total;
525 state->finished = modest_mail_operation_is_finished (self);
526 state->bytes_done = 0;
527 state->bytes_total = 0;
532 /* ******************************************************************* */
533 /* ************************** SEND ACTIONS ************************* */
534 /* ******************************************************************* */
537 modest_mail_operation_send_mail (ModestMailOperation *self,
538 TnyTransportAccount *transport_account,
541 TnySendQueue *send_queue = NULL;
542 ModestMailOperationPrivate *priv;
544 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
545 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
546 g_return_if_fail (TNY_IS_MSG (msg));
548 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
550 /* Get account and set it into mail_operation */
551 priv->account = g_object_ref (transport_account);
555 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
556 if (!TNY_IS_SEND_QUEUE(send_queue)) {
557 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
558 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
559 "modest: could not find send queue for account\n");
560 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
563 /* TODO: connect to the msg-sent in order to know when
564 the mail operation is finished */
566 tny_send_queue_add (send_queue, msg, &(priv->error));
568 /* TODO: we're setting always success, do the check in
570 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
573 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)
574 modest_platform_information_banner (NULL, NULL, _("mcen_ib_outbox_waiting_to_be_sent"));
576 /* TODO: do this in the handler of the "msg-sent"
577 signal.Notify about operation end */
578 modest_mail_operation_notify_end (self);
582 idle_create_msg_cb (gpointer idle_data)
584 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
586 /* This is a GDK lock because we are an idle callback and
587 * info->callback can contain Gtk+ code */
589 gdk_threads_enter (); /* CHECKED */
590 info->callback (info->mail_op, info->msg, info->userdata);
591 gdk_threads_leave (); /* CHECKED */
593 g_object_unref (info->mail_op);
595 g_object_unref (info->msg);
596 g_slice_free (CreateMsgIdleInfo, info);
602 create_msg_thread (gpointer thread_data)
604 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
605 TnyMsg *new_msg = NULL;
606 ModestMailOperationPrivate *priv;
608 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
609 if (info->html_body == NULL) {
610 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
611 info->bcc, info->subject, info->plain_body,
612 info->attachments_list); /* FIXME: attachments */
614 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
615 info->bcc, info->subject, info->html_body,
616 info->plain_body, info->attachments_list);
621 TnyHeaderFlags flags = 0;
623 /* Set priority flags in message */
624 header = tny_msg_get_header (new_msg);
625 if (info->priority_flags != 0)
626 flags |= info->priority_flags;
628 /* Set attachment flags in message */
629 if (info->attachments_list != NULL)
630 flags |= TNY_HEADER_FLAG_ATTACHMENTS;
632 tny_header_set_flags (header, flags);
633 g_object_unref (G_OBJECT(header));
635 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
636 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
637 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
638 "modest: failed to create a new msg\n");
646 g_free (info->plain_body);
647 g_free (info->html_body);
648 g_free (info->subject);
649 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
650 g_list_free (info->attachments_list);
652 if (info->callback) {
653 CreateMsgIdleInfo *idle_info;
654 idle_info = g_slice_new0 (CreateMsgIdleInfo);
655 idle_info->mail_op = info->mail_op;
656 g_object_ref (info->mail_op);
657 idle_info->msg = new_msg;
659 g_object_ref (new_msg);
660 idle_info->callback = info->callback;
661 idle_info->userdata = info->userdata;
662 g_idle_add (idle_create_msg_cb, idle_info);
664 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
667 g_object_unref (info->mail_op);
668 g_slice_free (CreateMsgInfo, info);
673 modest_mail_operation_create_msg (ModestMailOperation *self,
674 const gchar *from, const gchar *to,
675 const gchar *cc, const gchar *bcc,
676 const gchar *subject, const gchar *plain_body,
677 const gchar *html_body,
678 const GList *attachments_list,
679 TnyHeaderFlags priority_flags,
680 ModestMailOperationCreateMsgCallback callback,
683 CreateMsgInfo *info = NULL;
685 info = g_slice_new0 (CreateMsgInfo);
686 info->mail_op = self;
689 info->from = g_strdup (from);
690 info->to = g_strdup (to);
691 info->cc = g_strdup (cc);
692 info->bcc = g_strdup (bcc);
693 info->subject = g_strdup (subject);
694 info->plain_body = g_strdup (plain_body);
695 info->html_body = g_strdup (html_body);
696 info->attachments_list = g_list_copy ((GList *) attachments_list);
697 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
698 info->priority_flags = priority_flags;
700 info->callback = callback;
701 info->userdata = userdata;
703 g_thread_create (create_msg_thread, info, FALSE, NULL);
708 TnyTransportAccount *transport_account;
713 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
717 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
718 TnyFolder *draft_folder = NULL;
719 TnyFolder *outbox_folder = NULL;
727 /* Call mail operation */
728 modest_mail_operation_send_mail (self, info->transport_account, msg);
730 /* Remove old mail from its source folder */
731 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
732 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
733 if (info->draft_msg != NULL) {
734 TnyFolder *folder = NULL;
735 TnyFolder *src_folder = NULL;
736 TnyFolderType folder_type;
737 folder = tny_msg_get_folder (info->draft_msg);
738 if (folder == NULL) goto end;
739 folder_type = modest_tny_folder_guess_folder_type (folder);
740 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
741 src_folder = outbox_folder;
743 src_folder = draft_folder;
745 /* Note: This can fail (with a warning) if the message is not really already in a folder,
746 * because this function requires it to have a UID. */
747 header = tny_msg_get_header (info->draft_msg);
748 tny_folder_remove_msg (src_folder, header, NULL);
750 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
751 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
753 g_object_unref (header);
754 g_object_unref (folder);
761 g_object_unref (info->draft_msg);
763 g_object_unref (draft_folder);
765 g_object_unref (outbox_folder);
766 if (info->transport_account)
767 g_object_unref (info->transport_account);
768 g_slice_free (SendNewMailInfo, info);
769 modest_mail_operation_notify_end (self);
773 modest_mail_operation_send_new_mail (ModestMailOperation *self,
774 TnyTransportAccount *transport_account,
776 const gchar *from, const gchar *to,
777 const gchar *cc, const gchar *bcc,
778 const gchar *subject, const gchar *plain_body,
779 const gchar *html_body,
780 const GList *attachments_list,
781 TnyHeaderFlags priority_flags)
783 ModestMailOperationPrivate *priv = NULL;
784 SendNewMailInfo *info;
786 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
787 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
789 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
791 /* Check parametters */
793 /* Set status failed and set an error */
794 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
795 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
796 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
797 _("Error trying to send a mail. You need to set at least one recipient"));
800 info = g_slice_new0 (SendNewMailInfo);
801 info->transport_account = transport_account;
802 if (transport_account)
803 g_object_ref (transport_account);
804 info->draft_msg = draft_msg;
806 g_object_ref (draft_msg);
807 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
808 attachments_list, priority_flags,
809 modest_mail_operation_send_new_mail_cb, info);
815 TnyTransportAccount *transport_account;
817 ModestMsgEditWindow *edit_window;
821 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
825 TnyFolder *src_folder = NULL;
826 TnyFolder *folder = NULL;
827 TnyHeader *header = NULL;
828 ModestMailOperationPrivate *priv = NULL;
829 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
831 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
833 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
834 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
835 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
836 "modest: failed to create a new msg\n");
840 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
842 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
843 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
844 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
845 "modest: failed to create a new msg\n");
850 tny_folder_add_msg (folder, msg, &(priv->error));
852 if ((!priv->error) && (info->draft_msg != NULL)) {
853 header = tny_msg_get_header (info->draft_msg);
854 src_folder = tny_header_get_folder (header);
856 /* Remove the old draft expunging it */
857 tny_folder_remove_msg (src_folder, header, NULL);
858 /* tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
859 /* tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
861 tny_folder_sync (folder, TRUE, &(priv->error)); /* FALSE --> don't expunge */
862 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /* expunge */
864 g_object_unref (header);
868 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
870 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
872 if (info->edit_window)
873 modest_msg_edit_window_set_draft (info->edit_window, msg);
878 g_object_unref (G_OBJECT(folder));
880 g_object_unref (G_OBJECT(src_folder));
881 if (info->edit_window)
882 g_object_unref (G_OBJECT(info->edit_window));
884 g_object_unref (G_OBJECT (info->draft_msg));
885 if (info->transport_account)
886 g_object_unref (G_OBJECT(info->transport_account));
887 g_slice_free (SaveToDraftsInfo, info);
889 modest_mail_operation_notify_end (self);
893 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
894 TnyTransportAccount *transport_account,
896 ModestMsgEditWindow *edit_window,
897 const gchar *from, const gchar *to,
898 const gchar *cc, const gchar *bcc,
899 const gchar *subject, const gchar *plain_body,
900 const gchar *html_body,
901 const GList *attachments_list,
902 TnyHeaderFlags priority_flags)
904 ModestMailOperationPrivate *priv = NULL;
905 SaveToDraftsInfo *info = NULL;
907 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
908 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
910 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
912 /* Get account and set it into mail_operation */
913 priv->account = g_object_ref (transport_account);
915 info = g_slice_new0 (SaveToDraftsInfo);
916 info->transport_account = g_object_ref (transport_account);
917 info->draft_msg = draft_msg;
919 g_object_ref (draft_msg);
920 info->edit_window = edit_window;
922 g_object_ref (edit_window);
924 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
925 attachments_list, priority_flags,
926 modest_mail_operation_save_to_drafts_cb, info);
932 ModestMailOperation *mail_op;
933 TnyStoreAccount *account;
934 TnyTransportAccount *transport_account;
937 gchar *retrieve_type;
939 UpdateAccountCallback callback;
946 ModestMailOperation *mail_op;
947 TnyMimePart *mime_part;
949 GetMimePartSizeCallback callback;
951 } GetMimePartSizeInfo;
953 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
954 /* We use this folder observer to track the headers that have been
955 * added to a folder */
958 TnyList *new_headers;
959 } InternalFolderObserver;
963 } InternalFolderObserverClass;
965 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
967 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
968 internal_folder_observer,
970 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
974 foreach_add_item (gpointer header, gpointer user_data)
976 tny_list_prepend (TNY_LIST (user_data),
977 g_object_ref (G_OBJECT (header)));
980 /* This is the method that looks for new messages in a folder */
982 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
984 InternalFolderObserver *derived = (InternalFolderObserver *)self;
986 TnyFolderChangeChanged changed;
988 changed = tny_folder_change_get_changed (change);
990 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
993 /* Get added headers */
994 list = tny_simple_list_new ();
995 tny_folder_change_get_added_headers (change, list);
997 /* Add them to the folder observer */
998 tny_list_foreach (list, foreach_add_item,
999 derived->new_headers);
1001 g_object_unref (G_OBJECT (list));
1006 internal_folder_observer_init (InternalFolderObserver *self)
1008 self->new_headers = tny_simple_list_new ();
1011 internal_folder_observer_finalize (GObject *object)
1013 InternalFolderObserver *self;
1015 self = (InternalFolderObserver *) object;
1016 g_object_unref (self->new_headers);
1018 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1021 tny_folder_observer_init (TnyFolderObserverIface *iface)
1023 iface->update_func = internal_folder_observer_update;
1026 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1028 GObjectClass *object_class;
1030 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1031 object_class = (GObjectClass*) klass;
1032 object_class->finalize = internal_folder_observer_finalize;
1038 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1041 TnyList *folders = tny_simple_list_new ();
1043 tny_folder_store_get_folders (store, folders, query, NULL);
1044 iter = tny_list_create_iterator (folders);
1046 while (!tny_iterator_is_done (iter)) {
1048 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1050 tny_list_prepend (all_folders, G_OBJECT (folder));
1051 recurse_folders (folder, query, all_folders);
1052 g_object_unref (G_OBJECT (folder));
1055 tny_iterator_next (iter);
1057 g_object_unref (G_OBJECT (iter));
1058 g_object_unref (G_OBJECT (folders));
1062 * Issues the "progress-changed" signal. The timer won't be removed,
1063 * so you must call g_source_remove to stop the signal emission
1066 idle_notify_progress (gpointer data)
1068 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1069 ModestMailOperationState *state;
1071 state = modest_mail_operation_clone_state (mail_op);
1073 /* This is a GDK lock because we are an idle callback and
1074 * the handlers of this signal can contain Gtk+ code */
1076 gdk_threads_enter (); /* CHECKED */
1077 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1078 gdk_threads_leave (); /* CHECKED */
1080 g_slice_free (ModestMailOperationState, state);
1086 * Issues the "progress-changed" signal and removes the timer. It uses
1087 * a lock to ensure that the progress information of the mail
1088 * operation is not modified while there are notifications pending
1091 idle_notify_progress_once (gpointer data)
1095 pair = (ModestPair *) data;
1097 /* This is a GDK lock because we are an idle callback and
1098 * the handlers of this signal can contain Gtk+ code */
1100 gdk_threads_enter (); /* CHECKED */
1101 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1102 gdk_threads_leave (); /* CHECKED */
1104 /* Free the state and the reference to the mail operation */
1105 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1106 g_object_unref (pair->first);
1112 * Used to notify the queue from the main
1113 * loop. We call it inside an idle call to achieve that
1116 idle_notify_queue (gpointer data)
1118 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1120 /* Do not need to block, the notify end will do it for us */
1121 modest_mail_operation_notify_end (mail_op);
1122 g_object_unref (mail_op);
1128 compare_headers_by_date (gconstpointer a,
1131 TnyHeader **header1, **header2;
1132 time_t sent1, sent2;
1134 header1 = (TnyHeader **) a;
1135 header2 = (TnyHeader **) b;
1137 sent1 = tny_header_get_date_sent (*header1);
1138 sent2 = tny_header_get_date_sent (*header2);
1140 /* We want the most recent ones (greater time_t) at the
1149 set_last_updated_idle (gpointer data)
1152 /* This is a GDK lock because we are an idle callback and
1153 * modest_account_mgr_set_int can contain Gtk+ code */
1155 gdk_threads_enter (); /* CHECKED - please recheck */
1157 /* It does not matter if the time is not exactly the same than
1158 the time when this idle was called, it's just an
1159 approximation and it won't be very different */
1161 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
1163 MODEST_ACCOUNT_LAST_UPDATED,
1167 gdk_threads_leave (); /* CHECKED - please recheck */
1173 idle_update_account_cb (gpointer data)
1175 UpdateAccountInfo *idle_info;
1177 idle_info = (UpdateAccountInfo *) data;
1179 /* This is a GDK lock because we are an idle callback and
1180 * idle_info->callback can contain Gtk+ code */
1182 gdk_threads_enter (); /* CHECKED */
1183 idle_info->callback (idle_info->mail_op,
1184 idle_info->new_headers,
1185 idle_info->user_data);
1186 gdk_threads_leave (); /* CHECKED */
1189 g_object_unref (idle_info->mail_op);
1197 update_account_thread (gpointer thr_user_data)
1199 static gboolean first_time = TRUE;
1200 UpdateAccountInfo *info = NULL;
1201 TnyList *all_folders = NULL;
1202 GPtrArray *new_headers = NULL;
1203 TnyIterator *iter = NULL;
1204 TnyFolderStoreQuery *query = NULL;
1205 ModestMailOperationPrivate *priv = NULL;
1206 ModestTnySendQueue *send_queue = NULL;
1207 gint num_new_headers = 0;
1209 info = (UpdateAccountInfo *) thr_user_data;
1210 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1212 /* Get account and set it into mail_operation */
1213 priv->account = g_object_ref (info->account);
1216 * Previousl, we did this for POP3, to do a logout-login upon send/receive,
1217 * because many POP-servers (like Gmail) do not
1218 * show any updates unless we do that.
1219 * But that didn't work with gmail anyway,
1220 * and tinymail now takes care of this itself by disconnecting
1221 * automatically after using the connection.
1224 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account))
1225 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1228 /* Get all the folders. We can do it synchronously because
1229 we're already running in a different thread than the UI */
1230 all_folders = tny_simple_list_new ();
1231 query = tny_folder_store_query_new ();
1232 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1233 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1238 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1242 iter = tny_list_create_iterator (all_folders);
1243 while (!tny_iterator_is_done (iter)) {
1244 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1246 recurse_folders (folder, query, all_folders);
1247 g_object_unref (folder);
1249 tny_iterator_next (iter);
1251 g_object_unref (G_OBJECT (iter));
1253 /* Update status and notify. We need to call the notification
1254 with a source function in order to call it from the main
1255 loop. We need that in order not to get into trouble with
1256 Gtk+. We use a timeout in order to provide more status
1257 information, because the sync tinymail call does not
1258 provide it for the moment */
1259 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1261 /* Refresh folders */
1262 num_new_headers = 0;
1263 new_headers = g_ptr_array_new ();
1264 iter = tny_list_create_iterator (all_folders);
1266 while (!tny_iterator_is_done (iter) && !priv->error &&
1267 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1269 InternalFolderObserver *observer;
1270 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1272 /* Refresh the folder */
1273 /* Our observer receives notification of new emails during folder refreshes,
1274 * so we can use observer->new_headers.
1276 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1277 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1279 /* This gets the status information (headers) from the server.
1280 * We use the blocking version, because we are already in a separate
1284 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
1285 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1288 /* If the retrieve type is full messages, refresh and get the messages */
1289 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1291 iter = tny_list_create_iterator (observer->new_headers);
1292 while (!tny_iterator_is_done (iter)) {
1293 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1295 /* Apply per-message size limits */
1296 if (tny_header_get_message_size (header) < info->max_size)
1297 g_ptr_array_add (new_headers, g_object_ref (header));
1299 g_object_unref (header);
1300 tny_iterator_next (iter);
1302 g_object_unref (iter);
1304 /* We do not need to do it the first time
1305 because it's automatically done by the tree
1307 if (G_UNLIKELY (!first_time))
1308 tny_folder_poke_status (TNY_FOLDER (folder));
1311 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1312 g_object_unref (observer);
1316 g_object_unref (G_OBJECT (folder));
1319 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1321 tny_iterator_next (iter);
1324 g_object_unref (G_OBJECT (iter));
1325 g_source_remove (timeout);
1327 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1328 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1329 new_headers->len > 0) {
1333 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1335 /* Apply message count limit */
1336 /* If the number of messages exceeds the maximum, ask the
1337 * user to download them all,
1338 * as per the UI spec "Retrieval Limits" section in 4.4:
1340 if (new_headers->len > info->retrieve_limit) {
1341 /* TODO: Ask the user, instead of just
1343 * mail_nc_msg_count_limit_exceeded, with 'Get
1344 * all' and 'Newest only' buttons. */
1345 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1346 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1347 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1348 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1349 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1354 priv->total = MIN (new_headers->len, info->retrieve_limit);
1355 while (msg_num < priv->total) {
1357 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1358 TnyFolder *folder = tny_header_get_folder (header);
1359 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1360 ModestMailOperationState *state;
1364 /* We can not just use the mail operation because the
1365 values of done and total could change before the
1367 state = modest_mail_operation_clone_state (info->mail_op);
1368 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1369 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1370 pair, (GDestroyNotify) modest_pair_free);
1372 g_object_unref (msg);
1373 g_object_unref (folder);
1379 /* Get the number of new headers and free them */
1380 num_new_headers = new_headers->len;
1381 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1382 g_ptr_array_free (new_headers, FALSE);
1384 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1387 /* Perform send (if operation was not cancelled) */
1390 if (priv->account != NULL)
1391 g_object_unref (priv->account);
1392 priv->account = g_object_ref (info->transport_account);
1394 send_queue = modest_runtime_get_send_queue (info->transport_account);
1396 modest_tny_send_queue_try_to_send (send_queue);
1398 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1399 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1400 "cannot create a send queue for %s\n",
1401 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1402 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1405 /* Check if the operation was a success */
1407 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1409 /* Update the last updated key */
1410 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1411 set_last_updated_idle,
1412 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1413 (GDestroyNotify) g_free);
1418 if (info->callback) {
1419 UpdateAccountInfo *idle_info;
1421 /* This thread is not in the main lock */
1422 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1423 idle_info->mail_op = g_object_ref (info->mail_op);
1424 idle_info->new_headers = num_new_headers;
1425 idle_info->callback = info->callback;
1426 idle_info->user_data = info->user_data;
1427 g_idle_add (idle_update_account_cb, idle_info);
1430 /* Notify about operation end. Note that the info could be
1431 freed before this idle happens, but the mail operation will
1433 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1436 g_object_unref (query);
1437 g_object_unref (all_folders);
1438 g_object_unref (info->account);
1439 g_object_unref (info->transport_account);
1440 g_free (info->retrieve_type);
1441 g_slice_free (UpdateAccountInfo, info);
1449 modest_mail_operation_update_account (ModestMailOperation *self,
1450 const gchar *account_name,
1451 UpdateAccountCallback callback,
1454 GThread *thread = NULL;
1455 UpdateAccountInfo *info = NULL;
1456 ModestMailOperationPrivate *priv = NULL;
1457 ModestAccountMgr *mgr = NULL;
1458 TnyStoreAccount *store_account = NULL;
1459 TnyTransportAccount *transport_account = NULL;
1461 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1462 g_return_val_if_fail (account_name, FALSE);
1464 /* Init mail operation. Set total and done to 0, and do not
1465 update them, this way the progress objects will know that
1466 we have no clue about the number of the objects */
1467 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1470 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1472 /* Get the Modest account */
1473 store_account = (TnyStoreAccount *)
1474 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1476 TNY_ACCOUNT_TYPE_STORE);
1478 /* Make sure that we have a connection, and request one
1480 * TODO: Is there some way to trigger this for every attempt to
1481 * use the network? */
1482 if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1485 if (!store_account) {
1486 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1487 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1488 "cannot get tny store account for %s\n", account_name);
1493 /* Get the transport account, we can not do it in the thread
1494 due to some problems with dbus */
1495 transport_account = (TnyTransportAccount *)
1496 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1498 if (!transport_account) {
1499 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1500 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1501 "cannot get tny transport account for %s\n", account_name);
1505 /* Create the helper object */
1506 info = g_slice_new (UpdateAccountInfo);
1507 info->mail_op = self;
1508 info->account = store_account;
1509 info->transport_account = transport_account;
1510 info->callback = callback;
1511 info->user_data = user_data;
1513 /* Get the message size limit */
1514 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1515 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1516 if (info->max_size == 0)
1517 info->max_size = G_MAXINT;
1519 info->max_size = info->max_size * KB;
1521 /* Get per-account retrieval type */
1522 mgr = modest_runtime_get_account_mgr ();
1523 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1524 MODEST_ACCOUNT_RETRIEVE, FALSE);
1526 /* Get per-account message amount retrieval limit */
1527 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1528 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1529 if (info->retrieve_limit == 0)
1530 info->retrieve_limit = G_MAXINT;
1532 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1534 /* Set account busy */
1535 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1536 priv->account_name = g_strdup(account_name);
1538 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1543 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1545 callback (self, 0, user_data);
1546 modest_mail_operation_notify_end (self);
1550 /* ******************************************************************* */
1551 /* ************************** STORE ACTIONS ************************* */
1552 /* ******************************************************************* */
1556 modest_mail_operation_create_folder (ModestMailOperation *self,
1557 TnyFolderStore *parent,
1560 ModestMailOperationPrivate *priv;
1561 TnyFolder *new_folder = NULL;
1563 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1564 g_return_val_if_fail (name, NULL);
1566 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1568 /* Check for already existing folder */
1569 if (modest_tny_folder_has_subfolder_with_name (parent, name)) {
1570 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1571 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1572 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1573 _CS("ckdg_ib_folder_already_exists"));
1577 if (TNY_IS_FOLDER (parent)) {
1578 /* Check folder rules */
1579 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1580 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1581 /* Set status failed and set an error */
1582 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1583 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1584 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1585 _("mail_in_ui_folder_create_error"));
1589 if (!strcmp (name, " ") || strchr (name, '/')) {
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 /* Create the folder */
1598 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1599 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1601 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1604 /* Notify about operation end */
1605 modest_mail_operation_notify_end (self);
1611 modest_mail_operation_remove_folder (ModestMailOperation *self,
1613 gboolean remove_to_trash)
1615 TnyAccount *account;
1616 ModestMailOperationPrivate *priv;
1617 ModestTnyFolderRules rules;
1619 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1620 g_return_if_fail (TNY_IS_FOLDER (folder));
1622 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1624 /* Check folder rules */
1625 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1626 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1627 /* Set status failed and set an error */
1628 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1629 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1630 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1631 _("mail_in_ui_folder_delete_error"));
1635 /* Get the account */
1636 account = modest_tny_folder_get_account (folder);
1637 priv->account = g_object_ref(account);
1639 /* Delete folder or move to trash */
1640 if (remove_to_trash) {
1641 TnyFolder *trash_folder = NULL;
1642 trash_folder = modest_tny_account_get_special_folder (account,
1643 TNY_FOLDER_TYPE_TRASH);
1644 /* TODO: error_handling */
1646 modest_mail_operation_xfer_folder (self, folder,
1647 TNY_FOLDER_STORE (trash_folder),
1649 g_object_unref (trash_folder);
1652 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1654 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1655 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1658 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1661 g_object_unref (G_OBJECT (parent));
1663 g_object_unref (G_OBJECT (account));
1666 /* Notify about operation end */
1667 modest_mail_operation_notify_end (self);
1671 transfer_folder_status_cb (GObject *obj,
1675 ModestMailOperation *self;
1676 ModestMailOperationPrivate *priv;
1677 ModestMailOperationState *state;
1678 XFerMsgAsyncHelper *helper;
1680 g_return_if_fail (status != NULL);
1681 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1683 helper = (XFerMsgAsyncHelper *) user_data;
1684 g_return_if_fail (helper != NULL);
1686 self = helper->mail_op;
1687 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1689 priv->done = status->position;
1690 priv->total = status->of_total;
1692 state = modest_mail_operation_clone_state (self);
1694 /* This is not a GDK lock because we are a Tinymail callback
1695 * which is already GDK locked by Tinymail */
1697 /* no gdk_threads_enter (), CHECKED */
1699 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1701 /* no gdk_threads_leave (), CHECKED */
1703 g_slice_free (ModestMailOperationState, state);
1708 transfer_folder_cb (TnyFolder *folder,
1710 TnyFolderStore *into,
1711 TnyFolder *new_folder,
1715 XFerMsgAsyncHelper *helper;
1716 ModestMailOperation *self = NULL;
1717 ModestMailOperationPrivate *priv = NULL;
1719 helper = (XFerMsgAsyncHelper *) user_data;
1720 g_return_if_fail (helper != NULL);
1722 self = helper->mail_op;
1723 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1726 priv->error = g_error_copy (err);
1728 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1729 } else if (cancelled) {
1730 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1731 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1732 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1733 _("Transference of %s was cancelled."),
1734 tny_folder_get_name (folder));
1737 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1740 /* Notify about operation end */
1741 modest_mail_operation_notify_end (self);
1743 /* If user defined callback function was defined, call it */
1744 if (helper->user_callback) {
1746 /* This is not a GDK lock because we are a Tinymail callback
1747 * which is already GDK locked by Tinymail */
1749 /* no gdk_threads_enter (), CHECKED */
1750 helper->user_callback (priv->source, helper->user_data);
1751 /* no gdk_threads_leave () , CHECKED */
1755 g_object_unref (helper->mail_op);
1756 g_slice_free (XFerMsgAsyncHelper, helper);
1761 * This function checks if the new name is a valid name for our local
1762 * folders account. The new name could not be the same than then name
1763 * of any of the mandatory local folders
1765 * We can not rely on tinymail because tinymail does not check the
1766 * name of the virtual folders that the account could have in the case
1767 * that we're doing a rename (because it directly calls Camel which
1768 * knows nothing about our virtual folders).
1770 * In the case of an actual copy/move (i.e. move/copy a folder between
1771 * accounts) tinymail uses the tny_folder_store_create_account which
1772 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1773 * checks the new name of the folder, so this call in that case
1774 * wouldn't be needed. *But* NOTE that if tinymail changes its
1775 * implementation (if folder transfers within the same account is no
1776 * longer implemented as a rename) this call will allow Modest to work
1779 * If the new name is not valid, this function will set the status to
1780 * failed and will set also an error in the mail operation
1783 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1784 TnyFolderStore *into,
1785 const gchar *new_name)
1787 if (TNY_IS_ACCOUNT (into) &&
1788 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1789 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1791 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1792 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1793 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1794 _("ckdg_ib_folder_already_exists"));
1801 * This function checks if @ancestor is an acestor of @folder and
1802 * returns TRUE in that case
1805 folder_is_ancestor (TnyFolder *folder,
1806 TnyFolderStore *ancestor)
1808 TnyFolder *tmp = NULL;
1809 gboolean found = FALSE;
1812 while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1813 TnyFolderStore *folder_store;
1815 folder_store = tny_folder_get_folder_store (tmp);
1816 if (ancestor == folder_store)
1819 tmp = g_object_ref (folder_store);
1820 g_object_unref (folder_store);
1826 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1828 TnyFolderStore *parent,
1829 gboolean delete_original,
1830 XferMsgsAsynUserCallback user_callback,
1833 ModestMailOperationPrivate *priv = NULL;
1834 ModestTnyFolderRules parent_rules = 0, rules;
1835 XFerMsgAsyncHelper *helper = NULL;
1836 const gchar *folder_name = NULL;
1837 const gchar *error_msg;
1839 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1840 g_return_if_fail (TNY_IS_FOLDER (folder));
1841 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1843 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1844 folder_name = tny_folder_get_name (folder);
1846 /* Set the error msg */
1847 error_msg = (delete_original) ?
1848 _("mail_in_ui_folder_move_target_error") :
1849 _("mail_in_ui_folder_copy_target_error");
1851 /* Get account and set it into mail_operation */
1852 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1853 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1855 /* Get folder rules */
1856 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1857 if (TNY_IS_FOLDER (parent))
1858 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1860 /* The moveable restriction is applied also to copy operation */
1861 if ((gpointer) parent == (gpointer) folder ||
1862 (!TNY_IS_FOLDER_STORE (parent)) ||
1863 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1865 /* Set status failed and set an error */
1866 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1867 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1868 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1871 /* Notify the queue */
1872 modest_mail_operation_notify_end (self);
1874 } else if (TNY_IS_FOLDER (parent) &&
1875 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1877 /* Set status failed and set an error */
1878 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1879 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1880 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1883 /* Notify the queue */
1884 modest_mail_operation_notify_end (self);
1886 } else if (TNY_IS_FOLDER (parent) &&
1887 TNY_IS_FOLDER_STORE (folder) &&
1888 folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1889 /* Set status failed and set an error */
1890 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1891 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1892 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1895 /* Notify the queue */
1896 modest_mail_operation_notify_end (self);
1898 } else if (TNY_IS_FOLDER_STORE (parent) &&
1899 modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1900 /* Check that the new folder name is not used by any
1903 /* Set status failed and set an error */
1904 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1905 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1906 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1909 /* Notify the queue */
1910 modest_mail_operation_notify_end (self);
1912 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1913 /* Check that the new folder name is not used by any
1914 special local folder */
1916 /* Set status failed and set an error */
1917 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1918 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1919 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1922 /* Notify the queue */
1923 modest_mail_operation_notify_end (self);
1925 /* Create the helper */
1926 helper = g_slice_new0 (XFerMsgAsyncHelper);
1927 helper->mail_op = g_object_ref(self);
1928 helper->dest_folder = NULL;
1929 helper->headers = NULL;
1930 helper->user_callback = user_callback;
1931 helper->user_data = user_data;
1933 /* Move/Copy folder */
1934 tny_folder_copy_async (folder,
1936 tny_folder_get_name (folder),
1939 transfer_folder_status_cb,
1946 modest_mail_operation_rename_folder (ModestMailOperation *self,
1950 ModestMailOperationPrivate *priv;
1951 ModestTnyFolderRules rules;
1952 XFerMsgAsyncHelper *helper;
1954 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1955 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1956 g_return_if_fail (name);
1958 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1960 /* Get account and set it into mail_operation */
1961 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1963 /* Check folder rules */
1964 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1965 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1966 /* Set status failed and set an error */
1967 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1968 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1969 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1970 _("FIXME: unable to rename"));
1972 /* Notify about operation end */
1973 modest_mail_operation_notify_end (self);
1974 } else if (!strcmp (name, " ") || strchr (name, '/')) {
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"));
1979 /* Notify about operation end */
1980 modest_mail_operation_notify_end (self);
1982 TnyFolderStore *into;
1984 into = tny_folder_get_folder_store (folder);
1986 /* Check that the new folder name is not used by any
1987 special local folder */
1988 if (new_name_valid_if_local_account (priv, into, name)) {
1989 /* Create the helper */
1990 helper = g_slice_new0 (XFerMsgAsyncHelper);
1991 helper->mail_op = g_object_ref(self);
1992 helper->dest_folder = NULL;
1993 helper->headers = NULL;
1994 helper->user_callback = NULL;
1995 helper->user_data = NULL;
1997 /* Rename. Camel handles folder subscription/unsubscription */
1998 tny_folder_copy_async (folder, into, name, TRUE,
2000 transfer_folder_status_cb,
2003 modest_mail_operation_notify_end (self);
2005 g_object_unref (into);
2009 /* ******************************************************************* */
2010 /* ************************** MSG ACTIONS ************************* */
2011 /* ******************************************************************* */
2013 void modest_mail_operation_get_msg (ModestMailOperation *self,
2015 GetMsgAsyncUserCallback user_callback,
2018 GetMsgAsyncHelper *helper = NULL;
2020 ModestMailOperationPrivate *priv;
2022 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2023 g_return_if_fail (TNY_IS_HEADER (header));
2025 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2026 folder = tny_header_get_folder (header);
2028 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2030 /* Get message from folder */
2032 /* Get account and set it into mail_operation */
2033 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2035 /* Check for cached messages */
2036 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2037 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2039 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2041 helper = g_slice_new0 (GetMsgAsyncHelper);
2042 helper->mail_op = self;
2043 helper->user_callback = user_callback;
2044 helper->user_data = user_data;
2045 helper->header = g_object_ref (header);
2047 // The callback's reference so that the mail op is not
2048 // finalized until the async operation is completed even if
2049 // the user canceled the request meanwhile.
2050 g_object_ref (G_OBJECT (helper->mail_op));
2052 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2054 g_object_unref (G_OBJECT (folder));
2056 /* Set status failed and set an error */
2057 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2058 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2059 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2060 _("Error trying to get a message. No folder found for header"));
2062 /* Notify the queue */
2063 modest_mail_operation_notify_end (self);
2068 get_msg_cb (TnyFolder *folder,
2074 GetMsgAsyncHelper *helper = NULL;
2075 ModestMailOperation *self = NULL;
2076 ModestMailOperationPrivate *priv = NULL;
2078 helper = (GetMsgAsyncHelper *) user_data;
2079 g_return_if_fail (helper != NULL);
2080 self = helper->mail_op;
2081 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2082 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2084 /* Check errors and cancel */
2086 priv->error = g_error_copy (error);
2087 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2088 } else if (cancelled) {
2089 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2090 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2091 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2092 _("Error trying to refresh the contents of %s"),
2093 tny_folder_get_name (folder));
2095 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2098 /* If user defined callback function was defined, call it even
2099 if the operation failed*/
2100 if (helper->user_callback) {
2101 /* This is not a GDK lock because we are a Tinymail callback
2102 * which is already GDK locked by Tinymail */
2104 /* no gdk_threads_enter (), CHECKED */
2105 helper->user_callback (self, helper->header, msg, helper->user_data);
2106 /* no gdk_threads_leave (), CHECKED */
2109 /* Notify about operation end */
2110 modest_mail_operation_notify_end (self);
2112 g_object_unref (helper->mail_op);
2113 g_object_unref (helper->header);
2114 g_slice_free (GetMsgAsyncHelper, helper);
2119 get_msg_status_cb (GObject *obj,
2123 GetMsgAsyncHelper *helper = NULL;
2124 ModestMailOperation *self;
2125 ModestMailOperationPrivate *priv;
2126 ModestMailOperationState *state;
2128 g_return_if_fail (status != NULL);
2129 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2131 helper = (GetMsgAsyncHelper *) user_data;
2132 g_return_if_fail (helper != NULL);
2134 self = helper->mail_op;
2135 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2140 state = modest_mail_operation_clone_state (self);
2141 state->bytes_done = status->position;
2142 state->bytes_total = status->of_total;
2144 /* This is not a GDK lock because we are a Tinymail callback
2145 * which is already GDK locked by Tinymail */
2147 /* no gdk_threads_enter (), CHECKED */
2148 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2149 /* no gdk_threads_leave (), CHECKED */
2151 g_slice_free (ModestMailOperationState, state);
2154 /****************************************************/
2156 ModestMailOperation *mail_op;
2158 GetMsgAsyncUserCallback user_callback;
2160 GDestroyNotify notify;
2164 GetMsgAsyncUserCallback user_callback;
2168 ModestMailOperation *mail_op;
2169 } NotifyGetMsgsInfo;
2173 * Used by get_msgs_full_thread to call the user_callback for each
2174 * message that has been read
2177 notify_get_msgs_full (gpointer data)
2179 NotifyGetMsgsInfo *info;
2181 info = (NotifyGetMsgsInfo *) data;
2183 /* This is a GDK lock because we are an idle callback and
2184 * because info->user_callback can contain Gtk+ code */
2186 gdk_threads_enter (); /* CHECKED */
2187 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2188 gdk_threads_leave (); /* CHECKED */
2190 g_slice_free (NotifyGetMsgsInfo, info);
2196 * Used by get_msgs_full_thread to free al the thread resources and to
2197 * call the destroy function for the passed user_data
2200 get_msgs_full_destroyer (gpointer data)
2202 GetFullMsgsInfo *info;
2204 info = (GetFullMsgsInfo *) data;
2208 /* This is a GDK lock because we are an idle callback and
2209 * because info->notify can contain Gtk+ code */
2211 gdk_threads_enter (); /* CHECKED */
2212 info->notify (info->user_data);
2213 gdk_threads_leave (); /* CHECKED */
2217 g_object_unref (info->headers);
2218 g_slice_free (GetFullMsgsInfo, info);
2224 get_msgs_full_thread (gpointer thr_user_data)
2226 GetFullMsgsInfo *info;
2227 ModestMailOperationPrivate *priv = NULL;
2228 TnyIterator *iter = NULL;
2230 info = (GetFullMsgsInfo *) thr_user_data;
2231 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2233 iter = tny_list_create_iterator (info->headers);
2234 while (!tny_iterator_is_done (iter)) {
2238 header = TNY_HEADER (tny_iterator_get_current (iter));
2239 folder = tny_header_get_folder (header);
2241 /* Check for cached messages */
2242 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2243 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2245 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2247 /* Get message from folder */
2250 /* The callback will call it per each header */
2251 msg = tny_folder_get_msg (folder, header, &(priv->error));
2254 ModestMailOperationState *state;
2259 /* notify progress */
2260 state = modest_mail_operation_clone_state (info->mail_op);
2261 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2262 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2263 pair, (GDestroyNotify) modest_pair_free);
2265 /* The callback is the responsible for
2266 freeing the message */
2267 if (info->user_callback) {
2268 NotifyGetMsgsInfo *info_notify;
2269 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2270 info_notify->user_callback = info->user_callback;
2271 info_notify->mail_op = info->mail_op;
2272 info_notify->header = g_object_ref (header);
2273 info_notify->msg = g_object_ref (msg);
2274 info_notify->user_data = info->user_data;
2275 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2276 notify_get_msgs_full,
2279 g_object_unref (msg);
2282 /* Set status failed and set an error */
2283 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2284 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2285 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2286 "Error trying to get a message. No folder found for header");
2290 g_object_unref (header);
2292 tny_iterator_next (iter);
2295 /* Set operation status */
2296 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2297 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2299 /* Notify about operation end */
2300 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2302 /* Free thread resources. Will be called after all previous idles */
2303 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2309 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2310 TnyList *header_list,
2311 GetMsgAsyncUserCallback user_callback,
2313 GDestroyNotify notify)
2315 TnyHeader *header = NULL;
2316 TnyFolder *folder = NULL;
2318 ModestMailOperationPrivate *priv = NULL;
2319 GetFullMsgsInfo *info = NULL;
2320 gboolean size_ok = TRUE;
2322 TnyIterator *iter = NULL;
2324 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2326 /* Init mail operation */
2327 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2328 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2330 priv->total = tny_list_get_length(header_list);
2332 /* Get account and set it into mail_operation */
2333 if (tny_list_get_length (header_list) >= 1) {
2334 iter = tny_list_create_iterator (header_list);
2335 header = TNY_HEADER (tny_iterator_get_current (iter));
2337 folder = tny_header_get_folder (header);
2339 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2341 g_object_unref (folder);
2344 g_object_unref (header);
2347 if (tny_list_get_length (header_list) == 1) {
2348 g_object_unref (iter);
2353 /* Get msg size limit */
2354 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2355 MODEST_CONF_MSG_SIZE_LIMIT,
2358 g_clear_error (&(priv->error));
2359 max_size = G_MAXINT;
2361 max_size = max_size * KB;
2364 /* Check message size limits. If there is only one message
2365 always retrieve it */
2367 while (!tny_iterator_is_done (iter) && size_ok) {
2368 header = TNY_HEADER (tny_iterator_get_current (iter));
2370 if (tny_header_get_message_size (header) >= max_size)
2372 g_object_unref (header);
2375 tny_iterator_next (iter);
2377 g_object_unref (iter);
2381 /* Create the info */
2382 info = g_slice_new0 (GetFullMsgsInfo);
2383 info->mail_op = self;
2384 info->user_callback = user_callback;
2385 info->user_data = user_data;
2386 info->headers = g_object_ref (header_list);
2387 info->notify = notify;
2389 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2391 /* Set status failed and set an error */
2392 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2393 /* FIXME: the error msg is different for pop */
2394 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2395 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2396 _("emev_ni_ui_imap_msg_size_exceed_error"));
2397 /* Remove from queue and free resources */
2398 modest_mail_operation_notify_end (self);
2406 modest_mail_operation_remove_msg (ModestMailOperation *self,
2408 gboolean remove_to_trash /*ignored*/)
2411 ModestMailOperationPrivate *priv;
2413 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2414 g_return_if_fail (TNY_IS_HEADER (header));
2416 if (remove_to_trash)
2417 g_warning ("remove to trash is not implemented");
2419 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2420 folder = tny_header_get_folder (header);
2422 /* Get account and set it into mail_operation */
2423 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2425 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2427 /* remove message from folder */
2428 tny_folder_remove_msg (folder, header, &(priv->error));
2430 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2431 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2433 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2434 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2435 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2436 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2437 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2438 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2441 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2442 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2448 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2450 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2453 g_object_unref (G_OBJECT (folder));
2455 /* Notify about operation end */
2456 modest_mail_operation_notify_end (self);
2460 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2462 gboolean remove_to_trash /*ignored*/)
2465 ModestMailOperationPrivate *priv;
2466 TnyIterator *iter = NULL;
2467 TnyHeader *header = NULL;
2469 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2470 g_return_if_fail (TNY_IS_LIST (headers));
2472 if (remove_to_trash)
2473 g_warning ("remove to trash is not implemented");
2475 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2477 /* Get folder from first header and sync it */
2478 iter = tny_list_create_iterator (headers);
2479 header = TNY_HEADER (tny_iterator_get_current (iter));
2480 folder = tny_header_get_folder (header);
2482 /* Get account and set it into mail_operation */
2483 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2485 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2487 /* remove message from folder */
2488 tny_folder_remove_msgs (folder, headers, &(priv->error));
2490 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2491 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2492 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2493 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2494 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2495 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2498 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2499 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2505 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2507 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2510 g_object_unref (header);
2511 g_object_unref (iter);
2512 g_object_unref (G_OBJECT (folder));
2514 /* Notify about operation end */
2515 modest_mail_operation_notify_end (self);
2520 transfer_msgs_status_cb (GObject *obj,
2524 XFerMsgAsyncHelper *helper = NULL;
2525 ModestMailOperation *self;
2526 ModestMailOperationPrivate *priv;
2527 ModestMailOperationState *state;
2530 g_return_if_fail (status != NULL);
2531 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2533 helper = (XFerMsgAsyncHelper *) user_data;
2534 g_return_if_fail (helper != NULL);
2536 self = helper->mail_op;
2537 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2539 priv->done = status->position;
2540 priv->total = status->of_total;
2542 state = modest_mail_operation_clone_state (self);
2544 /* This is not a GDK lock because we are a Tinymail callback and
2545 * Tinymail already acquires the Gdk lock */
2547 /* no gdk_threads_enter (), CHECKED */
2549 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2551 /* no gdk_threads_leave (), CHECKED */
2553 g_slice_free (ModestMailOperationState, state);
2558 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2560 XFerMsgAsyncHelper *helper;
2561 ModestMailOperation *self;
2562 ModestMailOperationPrivate *priv;
2563 TnyIterator *iter = NULL;
2564 TnyHeader *header = NULL;
2566 helper = (XFerMsgAsyncHelper *) user_data;
2567 self = helper->mail_op;
2569 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2572 priv->error = g_error_copy (err);
2574 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2575 } else if (cancelled) {
2576 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2577 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2578 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2579 _("Error trying to refresh the contents of %s"),
2580 tny_folder_get_name (folder));
2583 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2585 /* Update folder counts */
2586 tny_folder_poke_status (folder);
2587 tny_folder_poke_status (helper->dest_folder);
2591 /* Mark headers as deleted and seen */
2592 if ((helper->delete) &&
2593 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2594 iter = tny_list_create_iterator (helper->headers);
2595 while (!tny_iterator_is_done (iter)) {
2596 header = TNY_HEADER (tny_iterator_get_current (iter));
2597 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2598 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2599 g_object_unref (header);
2601 tny_iterator_next (iter);
2607 /* Notify about operation end */
2608 modest_mail_operation_notify_end (self);
2610 /* If user defined callback function was defined, call it */
2611 if (helper->user_callback) {
2612 /* This is not a GDK lock because we are a Tinymail callback and
2613 * Tinymail already acquires the Gdk lock */
2615 /* no gdk_threads_enter (), CHECKED */
2616 helper->user_callback (priv->source, helper->user_data);
2617 /* no gdk_threads_leave (), CHECKED */
2621 if (helper->headers)
2622 g_object_unref (helper->headers);
2623 if (helper->dest_folder)
2624 g_object_unref (helper->dest_folder);
2625 if (helper->mail_op)
2626 g_object_unref (helper->mail_op);
2628 g_object_unref (folder);
2630 g_object_unref (iter);
2631 g_slice_free (XFerMsgAsyncHelper, helper);
2636 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2639 gboolean delete_original,
2640 XferMsgsAsynUserCallback user_callback,
2643 ModestMailOperationPrivate *priv = NULL;
2644 TnyIterator *iter = NULL;
2645 TnyFolder *src_folder = NULL;
2646 XFerMsgAsyncHelper *helper = NULL;
2647 TnyHeader *header = NULL;
2648 ModestTnyFolderRules rules = 0;
2649 const gchar *id1 = NULL;
2650 const gchar *id2 = NULL;
2651 gboolean same_folder = FALSE;
2653 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2654 g_return_if_fail (TNY_IS_LIST (headers));
2655 g_return_if_fail (TNY_IS_FOLDER (folder));
2657 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2660 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2662 /* Apply folder rules */
2663 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2664 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2665 /* Set status failed and set an error */
2666 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2667 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2668 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2669 _CS("ckct_ib_unable_to_paste_here"));
2670 /* Notify the queue */
2671 modest_mail_operation_notify_end (self);
2675 /* Get source folder */
2676 iter = tny_list_create_iterator (headers);
2677 header = TNY_HEADER (tny_iterator_get_current (iter));
2679 src_folder = tny_header_get_folder (header);
2680 g_object_unref (header);
2683 g_object_unref (iter);
2685 /* Check folder source and destination */
2686 id1 = tny_folder_get_id (src_folder);
2687 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2688 same_folder = !g_ascii_strcasecmp (id1, id2);
2690 /* Set status failed and set an error */
2691 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2692 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2693 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2694 _("mcen_ib_unable_to_copy_samefolder"));
2696 /* Notify the queue */
2697 modest_mail_operation_notify_end (self);
2700 g_object_unref (src_folder);
2704 /* Create the helper */
2705 helper = g_slice_new0 (XFerMsgAsyncHelper);
2706 helper->mail_op = g_object_ref(self);
2707 helper->dest_folder = g_object_ref(folder);
2708 helper->headers = g_object_ref(headers);
2709 helper->user_callback = user_callback;
2710 helper->user_data = user_data;
2711 helper->delete = delete_original;
2713 /* Get account and set it into mail_operation */
2714 priv->account = modest_tny_folder_get_account (src_folder);
2716 /* Transfer messages */
2717 tny_folder_transfer_msgs_async (src_folder,
2722 transfer_msgs_status_cb,
2728 on_refresh_folder (TnyFolder *folder,
2733 RefreshAsyncHelper *helper = NULL;
2734 ModestMailOperation *self = NULL;
2735 ModestMailOperationPrivate *priv = NULL;
2737 helper = (RefreshAsyncHelper *) user_data;
2738 self = helper->mail_op;
2739 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2741 g_return_if_fail(priv!=NULL);
2744 priv->error = g_error_copy (error);
2745 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2746 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2752 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2753 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2754 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2755 _("Error trying to refresh the contents of %s"),
2756 tny_folder_get_name (folder));
2757 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2761 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2763 /* Call user defined callback, if it exists */
2764 if (helper->user_callback) {
2766 /* This is not a GDK lock because we are a Tinymail callback and
2767 * Tinymail already acquires the Gdk lock */
2769 /* no gdk_threads_enter (), CHECKED */
2770 helper->user_callback (self, folder, helper->user_data);
2771 /* no gdk_threads_leave (), CHECKED */
2776 g_slice_free (RefreshAsyncHelper, helper);
2778 /* Notify about operation end */
2779 modest_mail_operation_notify_end (self);
2780 g_object_unref(self);
2784 on_refresh_folder_status_update (GObject *obj,
2788 RefreshAsyncHelper *helper = NULL;
2789 ModestMailOperation *self = NULL;
2790 ModestMailOperationPrivate *priv = NULL;
2791 ModestMailOperationState *state;
2793 g_return_if_fail (user_data != NULL);
2794 g_return_if_fail (status != NULL);
2795 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2797 helper = (RefreshAsyncHelper *) user_data;
2798 self = helper->mail_op;
2799 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2801 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2803 priv->done = status->position;
2804 priv->total = status->of_total;
2806 state = modest_mail_operation_clone_state (self);
2808 /* This is not a GDK lock because we are a Tinymail callback and
2809 * Tinymail already acquires the Gdk lock */
2811 /* no gdk_threads_enter (), CHECKED */
2813 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2815 /* no gdk_threads_leave (), CHECKED */
2817 g_slice_free (ModestMailOperationState, state);
2821 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2823 RefreshAsyncUserCallback user_callback,
2826 ModestMailOperationPrivate *priv = NULL;
2827 RefreshAsyncHelper *helper = NULL;
2829 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2831 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2833 /* Get account and set it into mail_operation */
2834 priv->account = modest_tny_folder_get_account (folder);
2836 /* Create the helper */
2837 helper = g_slice_new0 (RefreshAsyncHelper);
2838 helper->mail_op = g_object_ref(self);
2839 helper->user_callback = user_callback;
2840 helper->user_data = user_data;
2842 /* Refresh the folder. TODO: tinymail could issue a status
2843 updates before the callback call then this could happen. We
2844 must review the design */
2845 tny_folder_refresh_async (folder,
2847 on_refresh_folder_status_update,
2853 * It's used by the mail operation queue to notify the observers
2854 * attached to that signal that the operation finished. We need to use
2855 * that because tinymail does not give us the progress of a given
2856 * operation when it finishes (it directly calls the operation
2860 modest_mail_operation_notify_end (ModestMailOperation *self)
2862 ModestMailOperationState *state;
2863 ModestMailOperationPrivate *priv = NULL;
2865 g_return_if_fail (self);
2867 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2869 /* Set the account back to not busy */
2870 if (priv->account_name) {
2871 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2872 priv->account_name, FALSE);
2873 g_free(priv->account_name);
2874 priv->account_name = NULL;
2877 /* Notify the observers about the mail operation end */
2878 /* We do not wrapp this emission because we assume that this
2879 function is always called from within the main lock */
2880 state = modest_mail_operation_clone_state (self);
2881 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2882 g_slice_free (ModestMailOperationState, state);