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)); */
567 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue),
571 /* TODO: we're setting always success, do the check in
573 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
576 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)
577 modest_platform_information_banner (NULL, NULL, _("mcen_ib_outbox_waiting_to_be_sent"));
579 /* TODO: do this in the handler of the "msg-sent"
580 signal.Notify about operation end */
581 modest_mail_operation_notify_end (self);
585 idle_create_msg_cb (gpointer idle_data)
587 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
589 /* This is a GDK lock because we are an idle callback and
590 * info->callback can contain Gtk+ code */
592 gdk_threads_enter (); /* CHECKED */
593 info->callback (info->mail_op, info->msg, info->userdata);
595 g_object_unref (info->mail_op);
597 g_object_unref (info->msg);
598 g_slice_free (CreateMsgIdleInfo, info);
599 gdk_threads_leave (); /* CHECKED */
605 create_msg_thread (gpointer thread_data)
607 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
608 TnyMsg *new_msg = NULL;
609 ModestMailOperationPrivate *priv;
611 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
612 if (info->html_body == NULL) {
613 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
614 info->bcc, info->subject, info->plain_body,
615 info->attachments_list); /* FIXME: attachments */
617 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
618 info->bcc, info->subject, info->html_body,
619 info->plain_body, info->attachments_list);
624 TnyHeaderFlags flags = 0;
626 /* Set priority flags in message */
627 header = tny_msg_get_header (new_msg);
628 if (info->priority_flags != 0)
629 flags |= info->priority_flags;
631 /* Set attachment flags in message */
632 if (info->attachments_list != NULL)
633 flags |= TNY_HEADER_FLAG_ATTACHMENTS;
635 tny_header_set_flags (header, flags);
636 g_object_unref (G_OBJECT(header));
638 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
639 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
640 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
641 "modest: failed to create a new msg\n");
649 g_free (info->plain_body);
650 g_free (info->html_body);
651 g_free (info->subject);
652 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
653 g_list_free (info->attachments_list);
655 if (info->callback) {
656 CreateMsgIdleInfo *idle_info;
657 idle_info = g_slice_new0 (CreateMsgIdleInfo);
658 idle_info->mail_op = info->mail_op;
659 g_object_ref (info->mail_op);
660 idle_info->msg = new_msg;
662 g_object_ref (new_msg);
663 idle_info->callback = info->callback;
664 idle_info->userdata = info->userdata;
665 g_idle_add (idle_create_msg_cb, idle_info);
667 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
670 g_object_unref (info->mail_op);
671 g_slice_free (CreateMsgInfo, info);
676 modest_mail_operation_create_msg (ModestMailOperation *self,
677 const gchar *from, const gchar *to,
678 const gchar *cc, const gchar *bcc,
679 const gchar *subject, const gchar *plain_body,
680 const gchar *html_body,
681 const GList *attachments_list,
682 TnyHeaderFlags priority_flags,
683 ModestMailOperationCreateMsgCallback callback,
686 CreateMsgInfo *info = NULL;
688 info = g_slice_new0 (CreateMsgInfo);
689 info->mail_op = self;
692 info->from = g_strdup (from);
693 info->to = g_strdup (to);
694 info->cc = g_strdup (cc);
695 info->bcc = g_strdup (bcc);
696 info->subject = g_strdup (subject);
697 info->plain_body = g_strdup (plain_body);
698 info->html_body = g_strdup (html_body);
699 info->attachments_list = g_list_copy ((GList *) attachments_list);
700 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
701 info->priority_flags = priority_flags;
703 info->callback = callback;
704 info->userdata = userdata;
706 g_thread_create (create_msg_thread, info, FALSE, NULL);
711 TnyTransportAccount *transport_account;
716 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
720 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
721 TnyFolder *draft_folder = NULL;
722 TnyFolder *outbox_folder = NULL;
730 /* Call mail operation */
731 modest_mail_operation_send_mail (self, info->transport_account, msg);
733 /* Remove old mail from its source folder */
734 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
735 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
736 if (info->draft_msg != NULL) {
737 TnyFolder *folder = NULL;
738 TnyFolder *src_folder = NULL;
739 TnyFolderType folder_type;
740 folder = tny_msg_get_folder (info->draft_msg);
741 if (folder == NULL) goto end;
742 folder_type = modest_tny_folder_guess_folder_type (folder);
743 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
744 src_folder = outbox_folder;
746 src_folder = draft_folder;
748 /* Note: This can fail (with a warning) if the message is not really already in a folder,
749 * because this function requires it to have a UID. */
750 header = tny_msg_get_header (info->draft_msg);
751 tny_folder_remove_msg (src_folder, header, NULL);
753 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
754 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
756 g_object_unref (header);
757 g_object_unref (folder);
764 g_object_unref (info->draft_msg);
766 g_object_unref (draft_folder);
768 g_object_unref (outbox_folder);
769 if (info->transport_account)
770 g_object_unref (info->transport_account);
771 g_slice_free (SendNewMailInfo, info);
772 modest_mail_operation_notify_end (self);
776 modest_mail_operation_send_new_mail (ModestMailOperation *self,
777 TnyTransportAccount *transport_account,
779 const gchar *from, const gchar *to,
780 const gchar *cc, const gchar *bcc,
781 const gchar *subject, const gchar *plain_body,
782 const gchar *html_body,
783 const GList *attachments_list,
784 TnyHeaderFlags priority_flags)
786 ModestMailOperationPrivate *priv = NULL;
787 SendNewMailInfo *info;
789 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
790 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
792 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
794 /* Check parametters */
796 /* Set status failed and set an error */
797 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
798 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
799 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
800 _("Error trying to send a mail. You need to set at least one recipient"));
803 info = g_slice_new0 (SendNewMailInfo);
804 info->transport_account = transport_account;
805 if (transport_account)
806 g_object_ref (transport_account);
807 info->draft_msg = draft_msg;
809 g_object_ref (draft_msg);
810 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
811 attachments_list, priority_flags,
812 modest_mail_operation_send_new_mail_cb, info);
818 TnyTransportAccount *transport_account;
820 ModestMsgEditWindow *edit_window;
824 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
828 TnyFolder *src_folder = NULL;
829 TnyFolder *folder = NULL;
830 TnyHeader *header = NULL;
831 ModestMailOperationPrivate *priv = NULL;
832 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
834 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
836 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
837 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
838 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
839 "modest: failed to create a new msg\n");
843 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
845 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
846 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
847 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
848 "modest: failed to create a new msg\n");
853 tny_folder_add_msg (folder, msg, &(priv->error));
855 if ((!priv->error) && (info->draft_msg != NULL)) {
856 header = tny_msg_get_header (info->draft_msg);
857 src_folder = tny_header_get_folder (header);
859 /* Remove the old draft expunging it */
860 tny_folder_remove_msg (src_folder, header, NULL);
861 /* tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED); */
862 /* tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); */
864 tny_folder_sync (folder, TRUE, &(priv->error)); /* FALSE --> don't expunge */
865 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /* expunge */
867 g_object_unref (header);
871 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
873 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
875 if (info->edit_window)
876 modest_msg_edit_window_set_draft (info->edit_window, msg);
881 g_object_unref (G_OBJECT(folder));
883 g_object_unref (G_OBJECT(src_folder));
884 if (info->edit_window)
885 g_object_unref (G_OBJECT(info->edit_window));
887 g_object_unref (G_OBJECT (info->draft_msg));
888 if (info->transport_account)
889 g_object_unref (G_OBJECT(info->transport_account));
890 g_slice_free (SaveToDraftsInfo, info);
892 modest_mail_operation_notify_end (self);
896 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
897 TnyTransportAccount *transport_account,
899 ModestMsgEditWindow *edit_window,
900 const gchar *from, const gchar *to,
901 const gchar *cc, const gchar *bcc,
902 const gchar *subject, const gchar *plain_body,
903 const gchar *html_body,
904 const GList *attachments_list,
905 TnyHeaderFlags priority_flags)
907 ModestMailOperationPrivate *priv = NULL;
908 SaveToDraftsInfo *info = NULL;
910 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
911 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
913 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
915 /* Get account and set it into mail_operation */
916 priv->account = g_object_ref (transport_account);
918 info = g_slice_new0 (SaveToDraftsInfo);
919 info->transport_account = g_object_ref (transport_account);
920 info->draft_msg = draft_msg;
922 g_object_ref (draft_msg);
923 info->edit_window = edit_window;
925 g_object_ref (edit_window);
927 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
928 attachments_list, priority_flags,
929 modest_mail_operation_save_to_drafts_cb, info);
935 ModestMailOperation *mail_op;
936 TnyStoreAccount *account;
937 TnyTransportAccount *transport_account;
940 gchar *retrieve_type;
942 UpdateAccountCallback callback;
944 TnyList *new_headers;
949 ModestMailOperation *mail_op;
950 TnyMimePart *mime_part;
952 GetMimePartSizeCallback callback;
954 } GetMimePartSizeInfo;
956 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
957 /* We use this folder observer to track the headers that have been
958 * added to a folder */
961 TnyList *new_headers;
962 } InternalFolderObserver;
966 } InternalFolderObserverClass;
968 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
970 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
971 internal_folder_observer,
973 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
977 foreach_add_item (gpointer header, gpointer user_data)
979 tny_list_prepend (TNY_LIST (user_data),
980 g_object_ref (G_OBJECT (header)));
983 /* This is the method that looks for new messages in a folder */
985 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
987 InternalFolderObserver *derived = (InternalFolderObserver *)self;
989 TnyFolderChangeChanged changed;
991 changed = tny_folder_change_get_changed (change);
993 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
996 /* Get added headers */
997 list = tny_simple_list_new ();
998 tny_folder_change_get_added_headers (change, list);
1000 /* Add them to the folder observer */
1001 tny_list_foreach (list, foreach_add_item,
1002 derived->new_headers);
1004 g_object_unref (G_OBJECT (list));
1009 internal_folder_observer_init (InternalFolderObserver *self)
1011 self->new_headers = tny_simple_list_new ();
1014 internal_folder_observer_finalize (GObject *object)
1016 InternalFolderObserver *self;
1018 self = (InternalFolderObserver *) object;
1019 g_object_unref (self->new_headers);
1021 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1024 tny_folder_observer_init (TnyFolderObserverIface *iface)
1026 iface->update_func = internal_folder_observer_update;
1029 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1031 GObjectClass *object_class;
1033 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1034 object_class = (GObjectClass*) klass;
1035 object_class->finalize = internal_folder_observer_finalize;
1041 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1044 TnyList *folders = tny_simple_list_new ();
1046 tny_folder_store_get_folders (store, folders, query, NULL);
1047 iter = tny_list_create_iterator (folders);
1049 while (!tny_iterator_is_done (iter)) {
1051 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1053 tny_list_prepend (all_folders, G_OBJECT (folder));
1054 recurse_folders (folder, query, all_folders);
1055 g_object_unref (G_OBJECT (folder));
1058 tny_iterator_next (iter);
1060 g_object_unref (G_OBJECT (iter));
1061 g_object_unref (G_OBJECT (folders));
1065 * Issues the "progress-changed" signal. The timer won't be removed,
1066 * so you must call g_source_remove to stop the signal emission
1069 idle_notify_progress (gpointer data)
1071 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1072 ModestMailOperationState *state;
1074 state = modest_mail_operation_clone_state (mail_op);
1076 /* This is a GDK lock because we are an idle callback and
1077 * the handlers of this signal can contain Gtk+ code */
1079 gdk_threads_enter (); /* CHECKED */
1080 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1081 gdk_threads_leave (); /* CHECKED */
1083 g_slice_free (ModestMailOperationState, state);
1089 * Issues the "progress-changed" signal and removes the timer. It uses
1090 * a lock to ensure that the progress information of the mail
1091 * operation is not modified while there are notifications pending
1094 idle_notify_progress_once (gpointer data)
1098 pair = (ModestPair *) data;
1100 /* This is a GDK lock because we are an idle callback and
1101 * the handlers of this signal can contain Gtk+ code */
1103 gdk_threads_enter (); /* CHECKED */
1104 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1105 gdk_threads_leave (); /* CHECKED */
1107 /* Free the state and the reference to the mail operation */
1108 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1109 g_object_unref (pair->first);
1115 * Used to notify the queue from the main
1116 * loop. We call it inside an idle call to achieve that
1119 idle_notify_queue (gpointer data)
1121 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1123 /* Do not need to block, the notify end will do it for us */
1124 modest_mail_operation_notify_end (mail_op);
1125 g_object_unref (mail_op);
1131 compare_headers_by_date (gconstpointer a,
1134 TnyHeader **header1, **header2;
1135 time_t sent1, sent2;
1137 header1 = (TnyHeader **) a;
1138 header2 = (TnyHeader **) b;
1140 sent1 = tny_header_get_date_sent (*header1);
1141 sent2 = tny_header_get_date_sent (*header2);
1143 /* We want the most recent ones (greater time_t) at the
1152 set_last_updated_idle (gpointer data)
1155 /* This is a GDK lock because we are an idle callback and
1156 * modest_account_mgr_set_int can contain Gtk+ code */
1158 gdk_threads_enter (); /* CHECKED - please recheck */
1160 /* It does not matter if the time is not exactly the same than
1161 the time when this idle was called, it's just an
1162 approximation and it won't be very different */
1164 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
1166 MODEST_ACCOUNT_LAST_UPDATED,
1170 gdk_threads_leave (); /* CHECKED - please recheck */
1176 idle_update_account_cb (gpointer data)
1178 UpdateAccountInfo *idle_info;
1180 idle_info = (UpdateAccountInfo *) data;
1182 /* This is a GDK lock because we are an idle callback and
1183 * idle_info->callback can contain Gtk+ code */
1185 gdk_threads_enter (); /* CHECKED */
1186 idle_info->callback (idle_info->mail_op,
1187 idle_info->new_headers,
1188 idle_info->user_data);
1189 gdk_threads_leave (); /* CHECKED */
1192 g_object_unref (idle_info->mail_op);
1193 if (idle_info->new_headers)
1194 g_object_unref (idle_info->new_headers);
1201 get_all_folders_from_account (TnyStoreAccount *account,
1204 TnyList *all_folders = NULL;
1205 TnyIterator *iter = NULL;
1206 TnyFolderStoreQuery *query = NULL;
1208 all_folders = tny_simple_list_new ();
1209 query = tny_folder_store_query_new ();
1210 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1211 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
1218 g_object_unref (all_folders);
1222 iter = tny_list_create_iterator (all_folders);
1223 while (!tny_iterator_is_done (iter)) {
1224 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1226 recurse_folders (folder, query, all_folders);
1227 g_object_unref (folder);
1229 tny_iterator_next (iter);
1231 g_object_unref (G_OBJECT (iter));
1238 update_account_thread (gpointer thr_user_data)
1240 static gboolean first_time = TRUE;
1241 UpdateAccountInfo *info = NULL;
1242 TnyList *all_folders = NULL, *new_headers = NULL;
1243 GPtrArray *new_headers_array = NULL;
1244 TnyIterator *iter = NULL;
1245 ModestMailOperationPrivate *priv = NULL;
1246 ModestTnySendQueue *send_queue = NULL;
1247 gint i = 0, timeout = 0;
1249 info = (UpdateAccountInfo *) thr_user_data;
1250 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1252 /* Get account and set it into mail_operation */
1253 priv->account = g_object_ref (info->account);
1255 /* Get all the folders. We can do it synchronously because
1256 we're already running in a different thread than the UI */
1257 all_folders = get_all_folders_from_account (info->account, &(priv->error));
1259 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1263 /* Update status and notify. We need to call the notification
1264 with a source function in order to call it from the main
1265 loop. We need that in order not to get into trouble with
1266 Gtk+. We use a timeout in order to provide more status
1267 information, because the sync tinymail call does not
1268 provide it for the moment */
1269 timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1271 new_headers_array = g_ptr_array_new ();
1272 iter = tny_list_create_iterator (all_folders);
1274 while (!tny_iterator_is_done (iter) && !priv->error &&
1275 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1277 TnyFolderType folder_type;
1278 TnyFolder *folder = NULL;
1280 folder = TNY_FOLDER (tny_iterator_get_current (iter));
1281 folder_type = tny_folder_get_folder_type (folder);
1283 /* Refresh it only if it's the INBOX */
1284 if (folder_type == TNY_FOLDER_TYPE_INBOX) {
1285 InternalFolderObserver *observer = NULL;
1286 TnyIterator *new_headers_iter = NULL;
1288 /* Refresh the folder. Our observer receives
1289 * the new emails during folder refreshes, so
1290 * we can use observer->new_headers
1292 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1293 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1295 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1297 new_headers_iter = tny_list_create_iterator (observer->new_headers);
1298 while (!tny_iterator_is_done (new_headers_iter)) {
1299 TnyHeader *header = NULL;
1301 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1302 /* Apply per-message size limits */
1303 if (tny_header_get_message_size (header) < info->max_size)
1304 g_ptr_array_add (new_headers_array, g_object_ref (header));
1306 g_object_unref (header);
1307 tny_iterator_next (new_headers_iter);
1309 g_object_unref (new_headers_iter);
1311 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1312 g_object_unref (observer);
1314 /* We no not need to do it the first time,
1315 because it's automatically done by the tree
1317 if (G_LIKELY (!first_time))
1318 tny_folder_poke_status (folder);
1320 g_object_unref (folder);
1322 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_array->len > 0) {
1333 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1335 /* TODO: Ask the user, instead of just failing,
1336 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1337 * all' and 'Newest only' buttons. */
1338 if (new_headers_array->len > info->retrieve_limit) {
1342 /* Should be get only the headers or the message as well? */
1343 if (g_ascii_strcasecmp (info->retrieve_type,
1344 MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {
1346 priv->total = MIN (new_headers_array->len, info->retrieve_limit);
1347 while (msg_num < priv->total) {
1349 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1350 TnyFolder *folder = tny_header_get_folder (header);
1351 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1352 ModestMailOperationState *state;
1356 /* We can not just use the mail operation because the
1357 values of done and total could change before the
1359 state = modest_mail_operation_clone_state (info->mail_op);
1360 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1361 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1362 pair, (GDestroyNotify) modest_pair_free);
1364 g_object_unref (msg);
1365 g_object_unref (folder);
1372 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1375 /* Copy the headers to a list and free the array */
1376 new_headers = tny_simple_list_new ();
1377 for (i=0; i < new_headers_array->len; i++) {
1378 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1379 tny_list_append (new_headers, G_OBJECT (header));
1381 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1382 g_ptr_array_free (new_headers_array, FALSE);
1385 /* Perform send (if operation was not cancelled) */
1388 if (priv->account != NULL)
1389 g_object_unref (priv->account);
1390 priv->account = g_object_ref (info->transport_account);
1392 send_queue = modest_runtime_get_send_queue (info->transport_account);
1394 modest_tny_send_queue_try_to_send (send_queue);
1396 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1397 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1398 "cannot create a send queue for %s\n",
1399 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1400 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1403 /* Check if the operation was a success */
1405 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1407 /* Update the last updated key */
1408 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1409 set_last_updated_idle,
1410 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1411 (GDestroyNotify) g_free);
1416 if (info->callback) {
1417 UpdateAccountInfo *idle_info;
1419 /* This thread is not in the main lock */
1420 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1421 idle_info->mail_op = g_object_ref (info->mail_op);
1422 idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL;
1423 idle_info->callback = info->callback;
1424 idle_info->user_data = info->user_data;
1425 g_idle_add (idle_update_account_cb, idle_info);
1428 /* Notify about operation end. Note that the info could be
1429 freed before this idle happens, but the mail operation will
1431 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1435 g_object_unref (new_headers);
1436 g_object_unref (all_folders);
1437 g_object_unref (info->account);
1438 g_object_unref (info->transport_account);
1439 g_free (info->retrieve_type);
1440 g_slice_free (UpdateAccountInfo, info);
1448 modest_mail_operation_update_account (ModestMailOperation *self,
1449 const gchar *account_name,
1450 UpdateAccountCallback callback,
1453 GThread *thread = NULL;
1454 UpdateAccountInfo *info = NULL;
1455 ModestMailOperationPrivate *priv = NULL;
1456 ModestAccountMgr *mgr = NULL;
1457 TnyStoreAccount *store_account = NULL;
1458 TnyTransportAccount *transport_account = NULL;
1460 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1461 g_return_val_if_fail (account_name, FALSE);
1463 /* Init mail operation. Set total and done to 0, and do not
1464 update them, this way the progress objects will know that
1465 we have no clue about the number of the objects */
1466 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1469 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1471 /* Get the Modest account */
1472 store_account = (TnyStoreAccount *)
1473 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1475 TNY_ACCOUNT_TYPE_STORE);
1477 /* Make sure that we have a connection, and request one
1479 * TODO: Is there some way to trigger this for every attempt to
1480 * use the network? */
1481 if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1484 if (!store_account) {
1485 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1486 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1487 "cannot get tny store account for %s\n", account_name);
1492 /* Get the transport account, we can not do it in the thread
1493 due to some problems with dbus */
1494 transport_account = (TnyTransportAccount *)
1495 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1497 if (!transport_account) {
1498 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1499 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1500 "cannot get tny transport account for %s\n", account_name);
1504 /* Create the helper object */
1505 info = g_slice_new (UpdateAccountInfo);
1506 info->mail_op = self;
1507 info->account = store_account;
1508 info->transport_account = transport_account;
1509 info->callback = callback;
1510 info->user_data = user_data;
1512 /* Get the message size limit */
1513 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1514 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1515 if (info->max_size == 0)
1516 info->max_size = G_MAXINT;
1518 info->max_size = info->max_size * KB;
1520 /* Get per-account retrieval type */
1521 mgr = modest_runtime_get_account_mgr ();
1522 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1523 MODEST_ACCOUNT_RETRIEVE, FALSE);
1525 /* Get per-account message amount retrieval limit */
1526 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1527 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1528 if (info->retrieve_limit == 0)
1529 info->retrieve_limit = G_MAXINT;
1531 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1533 /* Set account busy */
1534 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1535 priv->account_name = g_strdup(account_name);
1537 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1542 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1544 callback (self, 0, user_data);
1545 modest_mail_operation_notify_end (self);
1549 /* ******************************************************************* */
1550 /* ************************** STORE ACTIONS ************************* */
1551 /* ******************************************************************* */
1555 modest_mail_operation_create_folder (ModestMailOperation *self,
1556 TnyFolderStore *parent,
1559 ModestMailOperationPrivate *priv;
1560 TnyFolder *new_folder = NULL;
1562 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1563 g_return_val_if_fail (name, NULL);
1565 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1567 /* Check for already existing folder */
1568 if (modest_tny_folder_has_subfolder_with_name (parent, name)) {
1569 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1570 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1571 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1572 _CS("ckdg_ib_folder_already_exists"));
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 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1660 g_object_unref (G_OBJECT (parent));
1662 g_object_unref (G_OBJECT (account));
1665 /* Notify about operation end */
1666 modest_mail_operation_notify_end (self);
1670 transfer_folder_status_cb (GObject *obj,
1674 ModestMailOperation *self;
1675 ModestMailOperationPrivate *priv;
1676 ModestMailOperationState *state;
1677 XFerMsgAsyncHelper *helper;
1679 g_return_if_fail (status != NULL);
1680 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1682 helper = (XFerMsgAsyncHelper *) user_data;
1683 g_return_if_fail (helper != NULL);
1685 self = helper->mail_op;
1686 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1688 priv->done = status->position;
1689 priv->total = status->of_total;
1691 state = modest_mail_operation_clone_state (self);
1693 /* This is not a GDK lock because we are a Tinymail callback
1694 * which is already GDK locked by Tinymail */
1696 /* no gdk_threads_enter (), CHECKED */
1698 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1700 /* no gdk_threads_leave (), CHECKED */
1702 g_slice_free (ModestMailOperationState, state);
1707 transfer_folder_cb (TnyFolder *folder,
1709 TnyFolderStore *into,
1710 TnyFolder *new_folder,
1714 XFerMsgAsyncHelper *helper;
1715 ModestMailOperation *self = NULL;
1716 ModestMailOperationPrivate *priv = NULL;
1718 helper = (XFerMsgAsyncHelper *) user_data;
1719 g_return_if_fail (helper != NULL);
1721 self = helper->mail_op;
1722 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1725 priv->error = g_error_copy (err);
1727 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1728 } else if (cancelled) {
1729 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1730 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1731 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1732 _("Transference of %s was cancelled."),
1733 tny_folder_get_name (folder));
1736 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1739 /* Notify about operation end */
1740 modest_mail_operation_notify_end (self);
1742 /* If user defined callback function was defined, call it */
1743 if (helper->user_callback) {
1745 /* This is not a GDK lock because we are a Tinymail callback
1746 * which is already GDK locked by Tinymail */
1748 /* no gdk_threads_enter (), CHECKED */
1749 helper->user_callback (priv->source, helper->user_data);
1750 /* no gdk_threads_leave () , CHECKED */
1754 g_object_unref (helper->mail_op);
1755 g_slice_free (XFerMsgAsyncHelper, helper);
1760 * This function checks if the new name is a valid name for our local
1761 * folders account. The new name could not be the same than then name
1762 * of any of the mandatory local folders
1764 * We can not rely on tinymail because tinymail does not check the
1765 * name of the virtual folders that the account could have in the case
1766 * that we're doing a rename (because it directly calls Camel which
1767 * knows nothing about our virtual folders).
1769 * In the case of an actual copy/move (i.e. move/copy a folder between
1770 * accounts) tinymail uses the tny_folder_store_create_account which
1771 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1772 * checks the new name of the folder, so this call in that case
1773 * wouldn't be needed. *But* NOTE that if tinymail changes its
1774 * implementation (if folder transfers within the same account is no
1775 * longer implemented as a rename) this call will allow Modest to work
1778 * If the new name is not valid, this function will set the status to
1779 * failed and will set also an error in the mail operation
1782 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1783 TnyFolderStore *into,
1784 const gchar *new_name)
1786 if (TNY_IS_ACCOUNT (into) &&
1787 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1788 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1790 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1791 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1792 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1793 _("ckdg_ib_folder_already_exists"));
1800 * This function checks if @ancestor is an acestor of @folder and
1801 * returns TRUE in that case
1804 folder_is_ancestor (TnyFolder *folder,
1805 TnyFolderStore *ancestor)
1807 TnyFolder *tmp = NULL;
1808 gboolean found = FALSE;
1811 while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1812 TnyFolderStore *folder_store;
1814 folder_store = tny_folder_get_folder_store (tmp);
1815 if (ancestor == folder_store)
1818 tmp = g_object_ref (folder_store);
1819 g_object_unref (folder_store);
1825 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1827 TnyFolderStore *parent,
1828 gboolean delete_original,
1829 XferMsgsAsynUserCallback user_callback,
1832 ModestMailOperationPrivate *priv = NULL;
1833 ModestTnyFolderRules parent_rules = 0, rules;
1834 XFerMsgAsyncHelper *helper = NULL;
1835 const gchar *folder_name = NULL;
1836 const gchar *error_msg;
1838 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1839 g_return_if_fail (TNY_IS_FOLDER (folder));
1840 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1842 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1843 folder_name = tny_folder_get_name (folder);
1845 /* Set the error msg */
1846 error_msg = _("mail_in_ui_folder_move_target_error");
1848 /* Get account and set it into mail_operation */
1849 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1850 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1852 /* Get folder rules */
1853 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1854 if (TNY_IS_FOLDER (parent))
1855 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1857 /* The moveable restriction is applied also to copy operation */
1858 if ((gpointer) parent == (gpointer) folder ||
1859 (!TNY_IS_FOLDER_STORE (parent)) ||
1860 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1862 /* Set status failed and set an error */
1863 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1864 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1865 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1868 /* Notify the queue */
1869 modest_mail_operation_notify_end (self);
1871 } else if (TNY_IS_FOLDER (parent) &&
1872 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1874 /* Set status failed and set an error */
1875 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1876 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1877 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1880 /* Notify the queue */
1881 modest_mail_operation_notify_end (self);
1883 } else if (TNY_IS_FOLDER (parent) &&
1884 TNY_IS_FOLDER_STORE (folder) &&
1885 folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1886 /* Set status failed and set an error */
1887 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1888 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1889 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1892 /* Notify the queue */
1893 modest_mail_operation_notify_end (self);
1895 } else if (TNY_IS_FOLDER_STORE (parent) &&
1896 modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1897 /* Check that the new folder name is not used by any
1900 /* Set status failed and set an error */
1901 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1902 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1903 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1906 /* Notify the queue */
1907 modest_mail_operation_notify_end (self);
1909 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1910 /* Check that the new folder name is not used by any
1911 special local folder */
1913 /* Set status failed and set an error */
1914 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1915 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1916 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1919 /* Notify the queue */
1920 modest_mail_operation_notify_end (self);
1922 /* Create the helper */
1923 helper = g_slice_new0 (XFerMsgAsyncHelper);
1924 helper->mail_op = g_object_ref(self);
1925 helper->dest_folder = NULL;
1926 helper->headers = NULL;
1927 helper->user_callback = user_callback;
1928 helper->user_data = user_data;
1930 /* Move/Copy folder */
1931 tny_folder_copy_async (folder,
1933 tny_folder_get_name (folder),
1936 transfer_folder_status_cb,
1943 modest_mail_operation_rename_folder (ModestMailOperation *self,
1947 ModestMailOperationPrivate *priv;
1948 ModestTnyFolderRules rules;
1949 XFerMsgAsyncHelper *helper;
1951 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1952 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1953 g_return_if_fail (name);
1955 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1957 /* Get account and set it into mail_operation */
1958 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1960 /* Check folder rules */
1961 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1962 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1963 /* Set status failed and set an error */
1964 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1965 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1966 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1967 _("FIXME: unable to rename"));
1969 /* Notify about operation end */
1970 modest_mail_operation_notify_end (self);
1971 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1972 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1973 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1974 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1975 _("FIXME: unable to rename"));
1976 /* Notify about operation end */
1977 modest_mail_operation_notify_end (self);
1979 TnyFolderStore *into;
1981 into = tny_folder_get_folder_store (folder);
1983 /* Check that the new folder name is not used by any
1984 special local folder */
1985 if (new_name_valid_if_local_account (priv, into, name)) {
1986 /* Create the helper */
1987 helper = g_slice_new0 (XFerMsgAsyncHelper);
1988 helper->mail_op = g_object_ref(self);
1989 helper->dest_folder = NULL;
1990 helper->headers = NULL;
1991 helper->user_callback = NULL;
1992 helper->user_data = NULL;
1994 /* Rename. Camel handles folder subscription/unsubscription */
1995 tny_folder_copy_async (folder, into, name, TRUE,
1997 transfer_folder_status_cb,
2000 modest_mail_operation_notify_end (self);
2002 g_object_unref (into);
2006 /* ******************************************************************* */
2007 /* ************************** MSG ACTIONS ************************* */
2008 /* ******************************************************************* */
2010 void modest_mail_operation_get_msg (ModestMailOperation *self,
2012 GetMsgAsyncUserCallback user_callback,
2015 GetMsgAsyncHelper *helper = NULL;
2017 ModestMailOperationPrivate *priv;
2019 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2020 g_return_if_fail (TNY_IS_HEADER (header));
2022 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2023 folder = tny_header_get_folder (header);
2025 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2027 /* Get message from folder */
2029 /* Get account and set it into mail_operation */
2030 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2032 /* Check for cached messages */
2033 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2034 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2036 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2038 helper = g_slice_new0 (GetMsgAsyncHelper);
2039 helper->mail_op = self;
2040 helper->user_callback = user_callback;
2041 helper->user_data = user_data;
2042 helper->header = g_object_ref (header);
2044 // The callback's reference so that the mail op is not
2045 // finalized until the async operation is completed even if
2046 // the user canceled the request meanwhile.
2047 g_object_ref (G_OBJECT (helper->mail_op));
2049 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2051 g_object_unref (G_OBJECT (folder));
2053 /* Set status failed and set an error */
2054 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2055 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2056 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2057 _("Error trying to get a message. No folder found for header"));
2059 /* Notify the queue */
2060 modest_mail_operation_notify_end (self);
2065 get_msg_cb (TnyFolder *folder,
2071 GetMsgAsyncHelper *helper = NULL;
2072 ModestMailOperation *self = NULL;
2073 ModestMailOperationPrivate *priv = NULL;
2075 helper = (GetMsgAsyncHelper *) user_data;
2076 g_return_if_fail (helper != NULL);
2077 self = helper->mail_op;
2078 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2079 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2081 /* Check errors and cancel */
2083 priv->error = g_error_copy (error);
2084 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2085 } else if (cancelled) {
2086 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2087 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2088 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2089 _("Error trying to refresh the contents of %s"),
2090 tny_folder_get_name (folder));
2092 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2095 /* If user defined callback function was defined, call it even
2096 if the operation failed*/
2097 if (helper->user_callback) {
2098 /* This is not a GDK lock because we are a Tinymail callback
2099 * which is already GDK locked by Tinymail */
2101 /* no gdk_threads_enter (), CHECKED */
2102 helper->user_callback (self, helper->header, msg, helper->user_data);
2103 /* no gdk_threads_leave (), CHECKED */
2106 /* Notify about operation end */
2107 modest_mail_operation_notify_end (self);
2109 g_object_unref (helper->mail_op);
2110 g_object_unref (helper->header);
2111 g_slice_free (GetMsgAsyncHelper, helper);
2116 get_msg_status_cb (GObject *obj,
2120 GetMsgAsyncHelper *helper = NULL;
2121 ModestMailOperation *self;
2122 ModestMailOperationPrivate *priv;
2123 ModestMailOperationState *state;
2125 g_return_if_fail (status != NULL);
2126 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2128 helper = (GetMsgAsyncHelper *) user_data;
2129 g_return_if_fail (helper != NULL);
2131 self = helper->mail_op;
2132 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2137 state = modest_mail_operation_clone_state (self);
2138 state->bytes_done = status->position;
2139 state->bytes_total = status->of_total;
2141 /* This is not a GDK lock because we are a Tinymail callback
2142 * which is already GDK locked by Tinymail */
2144 /* no gdk_threads_enter (), CHECKED */
2145 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2146 /* no gdk_threads_leave (), CHECKED */
2148 g_slice_free (ModestMailOperationState, state);
2151 /****************************************************/
2153 ModestMailOperation *mail_op;
2155 GetMsgAsyncUserCallback user_callback;
2157 GDestroyNotify notify;
2161 GetMsgAsyncUserCallback user_callback;
2165 ModestMailOperation *mail_op;
2166 } NotifyGetMsgsInfo;
2170 * Used by get_msgs_full_thread to call the user_callback for each
2171 * message that has been read
2174 notify_get_msgs_full (gpointer data)
2176 NotifyGetMsgsInfo *info;
2178 info = (NotifyGetMsgsInfo *) data;
2180 /* This is a GDK lock because we are an idle callback and
2181 * because info->user_callback can contain Gtk+ code */
2183 gdk_threads_enter (); /* CHECKED */
2184 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2185 gdk_threads_leave (); /* CHECKED */
2187 g_slice_free (NotifyGetMsgsInfo, info);
2193 * Used by get_msgs_full_thread to free al the thread resources and to
2194 * call the destroy function for the passed user_data
2197 get_msgs_full_destroyer (gpointer data)
2199 GetFullMsgsInfo *info;
2201 info = (GetFullMsgsInfo *) data;
2205 /* This is a GDK lock because we are an idle callback and
2206 * because info->notify can contain Gtk+ code */
2208 gdk_threads_enter (); /* CHECKED */
2209 info->notify (info->user_data);
2210 gdk_threads_leave (); /* CHECKED */
2214 g_object_unref (info->headers);
2215 g_slice_free (GetFullMsgsInfo, info);
2221 get_msgs_full_thread (gpointer thr_user_data)
2223 GetFullMsgsInfo *info;
2224 ModestMailOperationPrivate *priv = NULL;
2225 TnyIterator *iter = NULL;
2227 info = (GetFullMsgsInfo *) thr_user_data;
2228 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2230 iter = tny_list_create_iterator (info->headers);
2231 while (!tny_iterator_is_done (iter)) {
2235 header = TNY_HEADER (tny_iterator_get_current (iter));
2236 folder = tny_header_get_folder (header);
2238 /* Check for cached messages */
2239 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2240 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2242 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2244 /* Get message from folder */
2247 /* The callback will call it per each header */
2248 msg = tny_folder_get_msg (folder, header, &(priv->error));
2251 ModestMailOperationState *state;
2256 /* notify progress */
2257 state = modest_mail_operation_clone_state (info->mail_op);
2258 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2259 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2260 pair, (GDestroyNotify) modest_pair_free);
2262 /* The callback is the responsible for
2263 freeing the message */
2264 if (info->user_callback) {
2265 NotifyGetMsgsInfo *info_notify;
2266 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2267 info_notify->user_callback = info->user_callback;
2268 info_notify->mail_op = info->mail_op;
2269 info_notify->header = g_object_ref (header);
2270 info_notify->msg = g_object_ref (msg);
2271 info_notify->user_data = info->user_data;
2272 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2273 notify_get_msgs_full,
2276 g_object_unref (msg);
2279 /* Set status failed and set an error */
2280 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2281 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2282 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2283 "Error trying to get a message. No folder found for header");
2287 g_object_unref (header);
2289 tny_iterator_next (iter);
2292 /* Set operation status */
2293 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2294 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2296 /* Notify about operation end */
2297 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2299 /* Free thread resources. Will be called after all previous idles */
2300 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2306 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2307 TnyList *header_list,
2308 GetMsgAsyncUserCallback user_callback,
2310 GDestroyNotify notify)
2312 TnyHeader *header = NULL;
2313 TnyFolder *folder = NULL;
2315 ModestMailOperationPrivate *priv = NULL;
2316 GetFullMsgsInfo *info = NULL;
2317 gboolean size_ok = TRUE;
2319 TnyIterator *iter = NULL;
2321 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2323 /* Init mail operation */
2324 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2325 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2327 priv->total = tny_list_get_length(header_list);
2329 /* Get account and set it into mail_operation */
2330 if (tny_list_get_length (header_list) >= 1) {
2331 iter = tny_list_create_iterator (header_list);
2332 header = TNY_HEADER (tny_iterator_get_current (iter));
2334 folder = tny_header_get_folder (header);
2336 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2338 g_object_unref (folder);
2341 g_object_unref (header);
2344 if (tny_list_get_length (header_list) == 1) {
2345 g_object_unref (iter);
2350 /* Get msg size limit */
2351 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2352 MODEST_CONF_MSG_SIZE_LIMIT,
2355 g_clear_error (&(priv->error));
2356 max_size = G_MAXINT;
2358 max_size = max_size * KB;
2361 /* Check message size limits. If there is only one message
2362 always retrieve it */
2364 while (!tny_iterator_is_done (iter) && size_ok) {
2365 header = TNY_HEADER (tny_iterator_get_current (iter));
2367 if (tny_header_get_message_size (header) >= max_size)
2369 g_object_unref (header);
2372 tny_iterator_next (iter);
2374 g_object_unref (iter);
2378 /* Create the info */
2379 info = g_slice_new0 (GetFullMsgsInfo);
2380 info->mail_op = self;
2381 info->user_callback = user_callback;
2382 info->user_data = user_data;
2383 info->headers = g_object_ref (header_list);
2384 info->notify = notify;
2386 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2388 /* Set status failed and set an error */
2389 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2390 /* FIXME: the error msg is different for pop */
2391 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2392 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2393 _("emev_ni_ui_imap_msg_size_exceed_error"));
2394 /* Remove from queue and free resources */
2395 modest_mail_operation_notify_end (self);
2403 modest_mail_operation_remove_msg (ModestMailOperation *self,
2405 gboolean remove_to_trash /*ignored*/)
2408 ModestMailOperationPrivate *priv;
2410 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2411 g_return_if_fail (TNY_IS_HEADER (header));
2413 if (remove_to_trash)
2414 g_warning ("remove to trash is not implemented");
2416 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2417 folder = tny_header_get_folder (header);
2419 /* Get account and set it into mail_operation */
2420 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2422 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2424 /* remove message from folder */
2425 tny_folder_remove_msg (folder, header, &(priv->error));
2427 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2428 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2430 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2431 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2432 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2433 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2434 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2435 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2438 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2439 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2445 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2447 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2450 g_object_unref (G_OBJECT (folder));
2452 /* Notify about operation end */
2453 modest_mail_operation_notify_end (self);
2457 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2459 gboolean remove_to_trash /*ignored*/)
2462 ModestMailOperationPrivate *priv;
2463 TnyIterator *iter = NULL;
2464 TnyHeader *header = NULL;
2466 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2467 g_return_if_fail (TNY_IS_LIST (headers));
2469 if (remove_to_trash)
2470 g_warning ("remove to trash is not implemented");
2472 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2474 /* Get folder from first header and sync it */
2475 iter = tny_list_create_iterator (headers);
2476 header = TNY_HEADER (tny_iterator_get_current (iter));
2477 folder = tny_header_get_folder (header);
2479 /* Get account and set it into mail_operation */
2480 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2482 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2484 /* remove message from folder */
2485 tny_folder_remove_msgs (folder, headers, &(priv->error));
2487 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2488 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2489 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2490 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2491 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2492 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2495 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2496 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2502 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2504 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2507 g_object_unref (header);
2508 g_object_unref (iter);
2509 g_object_unref (G_OBJECT (folder));
2511 /* Notify about operation end */
2512 modest_mail_operation_notify_end (self);
2517 transfer_msgs_status_cb (GObject *obj,
2521 XFerMsgAsyncHelper *helper = NULL;
2522 ModestMailOperation *self;
2523 ModestMailOperationPrivate *priv;
2524 ModestMailOperationState *state;
2527 g_return_if_fail (status != NULL);
2528 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2530 helper = (XFerMsgAsyncHelper *) user_data;
2531 g_return_if_fail (helper != NULL);
2533 self = helper->mail_op;
2534 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2536 priv->done = status->position;
2537 priv->total = status->of_total;
2539 state = modest_mail_operation_clone_state (self);
2541 /* This is not a GDK lock because we are a Tinymail callback and
2542 * Tinymail already acquires the Gdk lock */
2544 /* no gdk_threads_enter (), CHECKED */
2546 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2548 /* no gdk_threads_leave (), CHECKED */
2550 g_slice_free (ModestMailOperationState, state);
2555 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2557 XFerMsgAsyncHelper *helper;
2558 ModestMailOperation *self;
2559 ModestMailOperationPrivate *priv;
2560 TnyIterator *iter = NULL;
2561 TnyHeader *header = NULL;
2563 helper = (XFerMsgAsyncHelper *) user_data;
2564 self = helper->mail_op;
2566 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2569 priv->error = g_error_copy (err);
2571 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2572 } else if (cancelled) {
2573 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2574 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2575 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2576 _("Error trying to refresh the contents of %s"),
2577 tny_folder_get_name (folder));
2580 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2582 /* Update folder counts */
2583 tny_folder_poke_status (folder);
2584 tny_folder_poke_status (helper->dest_folder);
2588 /* Mark headers as deleted and seen */
2589 if ((helper->delete) &&
2590 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2591 iter = tny_list_create_iterator (helper->headers);
2592 while (!tny_iterator_is_done (iter)) {
2593 header = TNY_HEADER (tny_iterator_get_current (iter));
2594 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2595 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2596 g_object_unref (header);
2598 tny_iterator_next (iter);
2604 /* Notify about operation end */
2605 modest_mail_operation_notify_end (self);
2607 /* If user defined callback function was defined, call it */
2608 if (helper->user_callback) {
2609 /* This is not a GDK lock because we are a Tinymail callback and
2610 * Tinymail already acquires the Gdk lock */
2612 /* no gdk_threads_enter (), CHECKED */
2613 helper->user_callback (priv->source, helper->user_data);
2614 /* no gdk_threads_leave (), CHECKED */
2618 if (helper->headers)
2619 g_object_unref (helper->headers);
2620 if (helper->dest_folder)
2621 g_object_unref (helper->dest_folder);
2622 if (helper->mail_op)
2623 g_object_unref (helper->mail_op);
2625 g_object_unref (folder);
2627 g_object_unref (iter);
2628 g_slice_free (XFerMsgAsyncHelper, helper);
2633 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2636 gboolean delete_original,
2637 XferMsgsAsynUserCallback user_callback,
2640 ModestMailOperationPrivate *priv = NULL;
2641 TnyIterator *iter = NULL;
2642 TnyFolder *src_folder = NULL;
2643 XFerMsgAsyncHelper *helper = NULL;
2644 TnyHeader *header = NULL;
2645 ModestTnyFolderRules rules = 0;
2646 const gchar *id1 = NULL;
2647 const gchar *id2 = NULL;
2648 gboolean same_folder = FALSE;
2650 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2651 g_return_if_fail (TNY_IS_LIST (headers));
2652 g_return_if_fail (TNY_IS_FOLDER (folder));
2654 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2657 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2659 /* Apply folder rules */
2660 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2661 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2662 /* Set status failed and set an error */
2663 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2664 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2665 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2666 _CS("ckct_ib_unable_to_paste_here"));
2667 /* Notify the queue */
2668 modest_mail_operation_notify_end (self);
2672 /* Get source folder */
2673 iter = tny_list_create_iterator (headers);
2674 header = TNY_HEADER (tny_iterator_get_current (iter));
2676 src_folder = tny_header_get_folder (header);
2677 g_object_unref (header);
2680 g_object_unref (iter);
2682 /* Check folder source and destination */
2683 id1 = tny_folder_get_id (src_folder);
2684 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2685 same_folder = !g_ascii_strcasecmp (id1, id2);
2687 /* Set status failed and set an error */
2688 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2689 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2690 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2691 _("mcen_ib_unable_to_copy_samefolder"));
2693 /* Notify the queue */
2694 modest_mail_operation_notify_end (self);
2697 g_object_unref (src_folder);
2701 /* Create the helper */
2702 helper = g_slice_new0 (XFerMsgAsyncHelper);
2703 helper->mail_op = g_object_ref(self);
2704 helper->dest_folder = g_object_ref(folder);
2705 helper->headers = g_object_ref(headers);
2706 helper->user_callback = user_callback;
2707 helper->user_data = user_data;
2708 helper->delete = delete_original;
2710 /* Get account and set it into mail_operation */
2711 priv->account = modest_tny_folder_get_account (src_folder);
2713 /* Transfer messages */
2714 tny_folder_transfer_msgs_async (src_folder,
2719 transfer_msgs_status_cb,
2725 on_refresh_folder (TnyFolder *folder,
2730 RefreshAsyncHelper *helper = NULL;
2731 ModestMailOperation *self = NULL;
2732 ModestMailOperationPrivate *priv = NULL;
2734 helper = (RefreshAsyncHelper *) user_data;
2735 self = helper->mail_op;
2736 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2738 g_return_if_fail(priv!=NULL);
2741 priv->error = g_error_copy (error);
2742 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2743 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2749 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2750 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2751 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2752 _("Error trying to refresh the contents of %s"),
2753 tny_folder_get_name (folder));
2754 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2758 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2760 /* Call user defined callback, if it exists */
2761 if (helper->user_callback) {
2763 /* This is not a GDK lock because we are a Tinymail callback and
2764 * Tinymail already acquires the Gdk lock */
2765 helper->user_callback (self, folder, helper->user_data);
2770 g_slice_free (RefreshAsyncHelper, helper);
2772 /* Notify about operation end */
2773 modest_mail_operation_notify_end (self);
2774 g_object_unref(self);
2778 on_refresh_folder_status_update (GObject *obj,
2782 RefreshAsyncHelper *helper = NULL;
2783 ModestMailOperation *self = NULL;
2784 ModestMailOperationPrivate *priv = NULL;
2785 ModestMailOperationState *state;
2787 g_return_if_fail (user_data != NULL);
2788 g_return_if_fail (status != NULL);
2789 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2791 helper = (RefreshAsyncHelper *) user_data;
2792 self = helper->mail_op;
2793 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2795 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2797 priv->done = status->position;
2798 priv->total = status->of_total;
2800 state = modest_mail_operation_clone_state (self);
2802 /* This is not a GDK lock because we are a Tinymail callback and
2803 * Tinymail already acquires the Gdk lock */
2804 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2806 g_slice_free (ModestMailOperationState, state);
2810 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2812 RefreshAsyncUserCallback user_callback,
2815 ModestMailOperationPrivate *priv = NULL;
2816 RefreshAsyncHelper *helper = NULL;
2818 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2820 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2822 /* Get account and set it into mail_operation */
2823 priv->account = modest_tny_folder_get_account (folder);
2825 /* Create the helper */
2826 helper = g_slice_new0 (RefreshAsyncHelper);
2827 helper->mail_op = g_object_ref(self);
2828 helper->user_callback = user_callback;
2829 helper->user_data = user_data;
2831 /* Refresh the folder. TODO: tinymail could issue a status
2832 updates before the callback call then this could happen. We
2833 must review the design */
2834 tny_folder_refresh_async (folder,
2836 on_refresh_folder_status_update,
2842 * It's used by the mail operation queue to notify the observers
2843 * attached to that signal that the operation finished. We need to use
2844 * that because tinymail does not give us the progress of a given
2845 * operation when it finishes (it directly calls the operation
2849 modest_mail_operation_notify_end (ModestMailOperation *self)
2851 ModestMailOperationState *state;
2852 ModestMailOperationPrivate *priv = NULL;
2854 g_return_if_fail (self);
2856 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2858 /* Set the account back to not busy */
2859 if (priv->account_name) {
2860 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2861 priv->account_name, FALSE);
2862 g_free(priv->account_name);
2863 priv->account_name = NULL;
2866 /* Notify the observers about the mail operation end */
2867 /* We do not wrapp this emission because we assume that this
2868 function is always called from within the main lock */
2869 state = modest_mail_operation_clone_state (self);
2870 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2871 g_slice_free (ModestMailOperationState, state);