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);
594 gdk_threads_leave (); /* CHECKED */
596 g_object_unref (info->mail_op);
598 g_object_unref (info->msg);
599 g_slice_free (CreateMsgIdleInfo, info);
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;
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);
1200 update_account_thread (gpointer thr_user_data)
1202 static gboolean first_time = TRUE;
1203 UpdateAccountInfo *info = NULL;
1204 TnyList *all_folders = NULL;
1205 GPtrArray *new_headers = NULL;
1206 TnyIterator *iter = NULL;
1207 TnyFolderStoreQuery *query = NULL;
1208 ModestMailOperationPrivate *priv = NULL;
1209 ModestTnySendQueue *send_queue = NULL;
1210 gint num_new_headers = 0;
1212 info = (UpdateAccountInfo *) thr_user_data;
1213 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1215 /* Get account and set it into mail_operation */
1216 priv->account = g_object_ref (info->account);
1219 * Previousl, we did this for POP3, to do a logout-login upon send/receive,
1220 * because many POP-servers (like Gmail) do not
1221 * show any updates unless we do that.
1222 * But that didn't work with gmail anyway,
1223 * and tinymail now takes care of this itself by disconnecting
1224 * automatically after using the connection.
1227 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT (priv->account))
1228 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
1231 /* Get all the folders. We can do it synchronously because
1232 we're already running in a different thread than the UI */
1233 all_folders = tny_simple_list_new ();
1234 query = tny_folder_store_query_new ();
1235 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1236 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
1241 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1245 iter = tny_list_create_iterator (all_folders);
1246 while (!tny_iterator_is_done (iter)) {
1247 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1249 recurse_folders (folder, query, all_folders);
1250 g_object_unref (folder);
1252 tny_iterator_next (iter);
1254 g_object_unref (G_OBJECT (iter));
1256 /* Update status and notify. We need to call the notification
1257 with a source function in order to call it from the main
1258 loop. We need that in order not to get into trouble with
1259 Gtk+. We use a timeout in order to provide more status
1260 information, because the sync tinymail call does not
1261 provide it for the moment */
1262 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1264 /* Refresh folders */
1265 num_new_headers = 0;
1266 new_headers = g_ptr_array_new ();
1267 iter = tny_list_create_iterator (all_folders);
1269 while (!tny_iterator_is_done (iter) && !priv->error &&
1270 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1272 InternalFolderObserver *observer;
1273 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1275 /* Refresh the folder */
1276 /* Our observer receives notification of new emails during folder refreshes,
1277 * so we can use observer->new_headers.
1279 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1280 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1282 /* This gets the status information (headers) from the server.
1283 * We use the blocking version, because we are already in a separate
1287 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
1288 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
1291 /* If the retrieve type is full messages, refresh and get the messages */
1292 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1294 iter = tny_list_create_iterator (observer->new_headers);
1295 while (!tny_iterator_is_done (iter)) {
1296 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1298 /* Apply per-message size limits */
1299 if (tny_header_get_message_size (header) < info->max_size)
1300 g_ptr_array_add (new_headers, g_object_ref (header));
1302 g_object_unref (header);
1303 tny_iterator_next (iter);
1305 g_object_unref (iter);
1307 /* We do not need to do it the first time
1308 because it's automatically done by the tree
1310 if (G_UNLIKELY (!first_time))
1311 tny_folder_poke_status (TNY_FOLDER (folder));
1314 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1315 g_object_unref (observer);
1319 g_object_unref (G_OBJECT (folder));
1322 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1324 tny_iterator_next (iter);
1327 g_object_unref (G_OBJECT (iter));
1328 g_source_remove (timeout);
1330 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1331 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1332 new_headers->len > 0) {
1336 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1338 /* Apply message count limit */
1339 /* If the number of messages exceeds the maximum, ask the
1340 * user to download them all,
1341 * as per the UI spec "Retrieval Limits" section in 4.4:
1343 if (new_headers->len > info->retrieve_limit) {
1344 /* TODO: Ask the user, instead of just
1346 * mail_nc_msg_count_limit_exceeded, with 'Get
1347 * all' and 'Newest only' buttons. */
1348 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1349 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1350 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1351 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1352 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1357 priv->total = MIN (new_headers->len, info->retrieve_limit);
1358 while (msg_num < priv->total) {
1360 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1361 TnyFolder *folder = tny_header_get_folder (header);
1362 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1363 ModestMailOperationState *state;
1367 /* We can not just use the mail operation because the
1368 values of done and total could change before the
1370 state = modest_mail_operation_clone_state (info->mail_op);
1371 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1372 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1373 pair, (GDestroyNotify) modest_pair_free);
1375 g_object_unref (msg);
1376 g_object_unref (folder);
1382 /* Get the number of new headers and free them */
1383 num_new_headers = new_headers->len;
1384 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1385 g_ptr_array_free (new_headers, FALSE);
1387 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1390 /* Perform send (if operation was not cancelled) */
1393 if (priv->account != NULL)
1394 g_object_unref (priv->account);
1395 priv->account = g_object_ref (info->transport_account);
1397 send_queue = modest_runtime_get_send_queue (info->transport_account);
1399 modest_tny_send_queue_try_to_send (send_queue);
1401 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1402 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1403 "cannot create a send queue for %s\n",
1404 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1405 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1408 /* Check if the operation was a success */
1410 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1412 /* Update the last updated key */
1413 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1414 set_last_updated_idle,
1415 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1416 (GDestroyNotify) g_free);
1421 if (info->callback) {
1422 UpdateAccountInfo *idle_info;
1424 /* This thread is not in the main lock */
1425 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1426 idle_info->mail_op = g_object_ref (info->mail_op);
1427 idle_info->new_headers = num_new_headers;
1428 idle_info->callback = info->callback;
1429 idle_info->user_data = info->user_data;
1430 g_idle_add (idle_update_account_cb, idle_info);
1433 /* Notify about operation end. Note that the info could be
1434 freed before this idle happens, but the mail operation will
1436 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1439 g_object_unref (query);
1440 g_object_unref (all_folders);
1441 g_object_unref (info->account);
1442 g_object_unref (info->transport_account);
1443 g_free (info->retrieve_type);
1444 g_slice_free (UpdateAccountInfo, info);
1452 modest_mail_operation_update_account (ModestMailOperation *self,
1453 const gchar *account_name,
1454 UpdateAccountCallback callback,
1457 GThread *thread = NULL;
1458 UpdateAccountInfo *info = NULL;
1459 ModestMailOperationPrivate *priv = NULL;
1460 ModestAccountMgr *mgr = NULL;
1461 TnyStoreAccount *store_account = NULL;
1462 TnyTransportAccount *transport_account = NULL;
1464 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1465 g_return_val_if_fail (account_name, FALSE);
1467 /* Init mail operation. Set total and done to 0, and do not
1468 update them, this way the progress objects will know that
1469 we have no clue about the number of the objects */
1470 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1473 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1475 /* Get the Modest account */
1476 store_account = (TnyStoreAccount *)
1477 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1479 TNY_ACCOUNT_TYPE_STORE);
1481 /* Make sure that we have a connection, and request one
1483 * TODO: Is there some way to trigger this for every attempt to
1484 * use the network? */
1485 if (!modest_platform_connect_and_wait (NULL, TNY_ACCOUNT (store_account)))
1488 if (!store_account) {
1489 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1490 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1491 "cannot get tny store account for %s\n", account_name);
1496 /* Get the transport account, we can not do it in the thread
1497 due to some problems with dbus */
1498 transport_account = (TnyTransportAccount *)
1499 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1501 if (!transport_account) {
1502 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1503 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1504 "cannot get tny transport account for %s\n", account_name);
1508 /* Create the helper object */
1509 info = g_slice_new (UpdateAccountInfo);
1510 info->mail_op = self;
1511 info->account = store_account;
1512 info->transport_account = transport_account;
1513 info->callback = callback;
1514 info->user_data = user_data;
1516 /* Get the message size limit */
1517 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1518 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1519 if (info->max_size == 0)
1520 info->max_size = G_MAXINT;
1522 info->max_size = info->max_size * KB;
1524 /* Get per-account retrieval type */
1525 mgr = modest_runtime_get_account_mgr ();
1526 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1527 MODEST_ACCOUNT_RETRIEVE, FALSE);
1529 /* Get per-account message amount retrieval limit */
1530 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1531 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1532 if (info->retrieve_limit == 0)
1533 info->retrieve_limit = G_MAXINT;
1535 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1537 /* Set account busy */
1538 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1539 priv->account_name = g_strdup(account_name);
1541 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1546 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1548 callback (self, 0, user_data);
1549 modest_mail_operation_notify_end (self);
1553 /* ******************************************************************* */
1554 /* ************************** STORE ACTIONS ************************* */
1555 /* ******************************************************************* */
1559 modest_mail_operation_create_folder (ModestMailOperation *self,
1560 TnyFolderStore *parent,
1563 ModestMailOperationPrivate *priv;
1564 TnyFolder *new_folder = NULL;
1566 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1567 g_return_val_if_fail (name, NULL);
1569 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1571 /* Check for already existing folder */
1572 if (modest_tny_folder_has_subfolder_with_name (parent, name)) {
1573 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1574 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1575 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1576 _CS("ckdg_ib_folder_already_exists"));
1580 if (TNY_IS_FOLDER (parent)) {
1581 /* Check folder rules */
1582 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1583 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1584 /* Set status failed and set an error */
1585 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1586 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1587 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1588 _("mail_in_ui_folder_create_error"));
1592 if (!strcmp (name, " ") || strchr (name, '/')) {
1593 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1594 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1595 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1596 _("mail_in_ui_folder_create_error"));
1600 /* Create the folder */
1601 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1602 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1604 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1607 /* Notify about operation end */
1608 modest_mail_operation_notify_end (self);
1614 modest_mail_operation_remove_folder (ModestMailOperation *self,
1616 gboolean remove_to_trash)
1618 TnyAccount *account;
1619 ModestMailOperationPrivate *priv;
1620 ModestTnyFolderRules rules;
1622 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1623 g_return_if_fail (TNY_IS_FOLDER (folder));
1625 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1627 /* Check folder rules */
1628 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1629 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1630 /* Set status failed and set an error */
1631 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1632 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1633 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1634 _("mail_in_ui_folder_delete_error"));
1638 /* Get the account */
1639 account = modest_tny_folder_get_account (folder);
1640 priv->account = g_object_ref(account);
1642 /* Delete folder or move to trash */
1643 if (remove_to_trash) {
1644 TnyFolder *trash_folder = NULL;
1645 trash_folder = modest_tny_account_get_special_folder (account,
1646 TNY_FOLDER_TYPE_TRASH);
1647 /* TODO: error_handling */
1649 modest_mail_operation_xfer_folder (self, folder,
1650 TNY_FOLDER_STORE (trash_folder),
1652 g_object_unref (trash_folder);
1655 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1657 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1658 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1661 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1664 g_object_unref (G_OBJECT (parent));
1666 g_object_unref (G_OBJECT (account));
1669 /* Notify about operation end */
1670 modest_mail_operation_notify_end (self);
1674 transfer_folder_status_cb (GObject *obj,
1678 ModestMailOperation *self;
1679 ModestMailOperationPrivate *priv;
1680 ModestMailOperationState *state;
1681 XFerMsgAsyncHelper *helper;
1683 g_return_if_fail (status != NULL);
1684 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1686 helper = (XFerMsgAsyncHelper *) user_data;
1687 g_return_if_fail (helper != NULL);
1689 self = helper->mail_op;
1690 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1692 priv->done = status->position;
1693 priv->total = status->of_total;
1695 state = modest_mail_operation_clone_state (self);
1697 /* This is not a GDK lock because we are a Tinymail callback
1698 * which is already GDK locked by Tinymail */
1700 /* no gdk_threads_enter (), CHECKED */
1702 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1704 /* no gdk_threads_leave (), CHECKED */
1706 g_slice_free (ModestMailOperationState, state);
1711 transfer_folder_cb (TnyFolder *folder,
1713 TnyFolderStore *into,
1714 TnyFolder *new_folder,
1718 XFerMsgAsyncHelper *helper;
1719 ModestMailOperation *self = NULL;
1720 ModestMailOperationPrivate *priv = NULL;
1722 helper = (XFerMsgAsyncHelper *) user_data;
1723 g_return_if_fail (helper != NULL);
1725 self = helper->mail_op;
1726 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1729 priv->error = g_error_copy (err);
1731 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1732 } else if (cancelled) {
1733 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1734 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1735 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1736 _("Transference of %s was cancelled."),
1737 tny_folder_get_name (folder));
1740 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1743 /* Notify about operation end */
1744 modest_mail_operation_notify_end (self);
1746 /* If user defined callback function was defined, call it */
1747 if (helper->user_callback) {
1749 /* This is not a GDK lock because we are a Tinymail callback
1750 * which is already GDK locked by Tinymail */
1752 /* no gdk_threads_enter (), CHECKED */
1753 helper->user_callback (priv->source, helper->user_data);
1754 /* no gdk_threads_leave () , CHECKED */
1758 g_object_unref (helper->mail_op);
1759 g_slice_free (XFerMsgAsyncHelper, helper);
1764 * This function checks if the new name is a valid name for our local
1765 * folders account. The new name could not be the same than then name
1766 * of any of the mandatory local folders
1768 * We can not rely on tinymail because tinymail does not check the
1769 * name of the virtual folders that the account could have in the case
1770 * that we're doing a rename (because it directly calls Camel which
1771 * knows nothing about our virtual folders).
1773 * In the case of an actual copy/move (i.e. move/copy a folder between
1774 * accounts) tinymail uses the tny_folder_store_create_account which
1775 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1776 * checks the new name of the folder, so this call in that case
1777 * wouldn't be needed. *But* NOTE that if tinymail changes its
1778 * implementation (if folder transfers within the same account is no
1779 * longer implemented as a rename) this call will allow Modest to work
1782 * If the new name is not valid, this function will set the status to
1783 * failed and will set also an error in the mail operation
1786 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1787 TnyFolderStore *into,
1788 const gchar *new_name)
1790 if (TNY_IS_ACCOUNT (into) &&
1791 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1792 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1794 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1795 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1796 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1797 _("ckdg_ib_folder_already_exists"));
1804 * This function checks if @ancestor is an acestor of @folder and
1805 * returns TRUE in that case
1808 folder_is_ancestor (TnyFolder *folder,
1809 TnyFolderStore *ancestor)
1811 TnyFolder *tmp = NULL;
1812 gboolean found = FALSE;
1815 while (!found && tmp && !TNY_IS_ACCOUNT (tmp)) {
1816 TnyFolderStore *folder_store;
1818 folder_store = tny_folder_get_folder_store (tmp);
1819 if (ancestor == folder_store)
1822 tmp = g_object_ref (folder_store);
1823 g_object_unref (folder_store);
1829 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1831 TnyFolderStore *parent,
1832 gboolean delete_original,
1833 XferMsgsAsynUserCallback user_callback,
1836 ModestMailOperationPrivate *priv = NULL;
1837 ModestTnyFolderRules parent_rules = 0, rules;
1838 XFerMsgAsyncHelper *helper = NULL;
1839 const gchar *folder_name = NULL;
1840 const gchar *error_msg;
1842 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1843 g_return_if_fail (TNY_IS_FOLDER (folder));
1844 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1846 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1847 folder_name = tny_folder_get_name (folder);
1849 /* Set the error msg */
1850 error_msg = (delete_original) ?
1851 _("mail_in_ui_folder_move_target_error") :
1852 _("mail_in_ui_folder_copy_target_error");
1854 /* Get account and set it into mail_operation */
1855 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1856 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1858 /* Get folder rules */
1859 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1860 if (TNY_IS_FOLDER (parent))
1861 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1863 /* The moveable restriction is applied also to copy operation */
1864 if ((gpointer) parent == (gpointer) folder ||
1865 (!TNY_IS_FOLDER_STORE (parent)) ||
1866 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1868 /* Set status failed and set an error */
1869 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1870 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1871 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1874 /* Notify the queue */
1875 modest_mail_operation_notify_end (self);
1877 } else if (TNY_IS_FOLDER (parent) &&
1878 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1880 /* Set status failed and set an error */
1881 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1882 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1883 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1886 /* Notify the queue */
1887 modest_mail_operation_notify_end (self);
1889 } else if (TNY_IS_FOLDER (parent) &&
1890 TNY_IS_FOLDER_STORE (folder) &&
1891 folder_is_ancestor (TNY_FOLDER (parent), TNY_FOLDER_STORE (folder))) {
1892 /* Set status failed and set an error */
1893 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1894 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1895 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1898 /* Notify the queue */
1899 modest_mail_operation_notify_end (self);
1901 } else if (TNY_IS_FOLDER_STORE (parent) &&
1902 modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1903 /* Check that the new folder name is not used by any
1906 /* Set status failed and set an error */
1907 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1908 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1909 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1912 /* Notify the queue */
1913 modest_mail_operation_notify_end (self);
1915 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1916 /* Check that the new folder name is not used by any
1917 special local folder */
1919 /* Set status failed and set an error */
1920 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1921 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1922 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1925 /* Notify the queue */
1926 modest_mail_operation_notify_end (self);
1928 /* Create the helper */
1929 helper = g_slice_new0 (XFerMsgAsyncHelper);
1930 helper->mail_op = g_object_ref(self);
1931 helper->dest_folder = NULL;
1932 helper->headers = NULL;
1933 helper->user_callback = user_callback;
1934 helper->user_data = user_data;
1936 /* Move/Copy folder */
1937 tny_folder_copy_async (folder,
1939 tny_folder_get_name (folder),
1942 transfer_folder_status_cb,
1949 modest_mail_operation_rename_folder (ModestMailOperation *self,
1953 ModestMailOperationPrivate *priv;
1954 ModestTnyFolderRules rules;
1955 XFerMsgAsyncHelper *helper;
1957 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1958 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1959 g_return_if_fail (name);
1961 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1963 /* Get account and set it into mail_operation */
1964 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1966 /* Check folder rules */
1967 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1968 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1969 /* Set status failed and set an error */
1970 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1971 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1972 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1973 _("FIXME: unable to rename"));
1975 /* Notify about operation end */
1976 modest_mail_operation_notify_end (self);
1977 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1978 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1979 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1980 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1981 _("FIXME: unable to rename"));
1982 /* Notify about operation end */
1983 modest_mail_operation_notify_end (self);
1985 TnyFolderStore *into;
1987 into = tny_folder_get_folder_store (folder);
1989 /* Check that the new folder name is not used by any
1990 special local folder */
1991 if (new_name_valid_if_local_account (priv, into, name)) {
1992 /* Create the helper */
1993 helper = g_slice_new0 (XFerMsgAsyncHelper);
1994 helper->mail_op = g_object_ref(self);
1995 helper->dest_folder = NULL;
1996 helper->headers = NULL;
1997 helper->user_callback = NULL;
1998 helper->user_data = NULL;
2000 /* Rename. Camel handles folder subscription/unsubscription */
2001 tny_folder_copy_async (folder, into, name, TRUE,
2003 transfer_folder_status_cb,
2006 modest_mail_operation_notify_end (self);
2008 g_object_unref (into);
2012 /* ******************************************************************* */
2013 /* ************************** MSG ACTIONS ************************* */
2014 /* ******************************************************************* */
2016 void modest_mail_operation_get_msg (ModestMailOperation *self,
2018 GetMsgAsyncUserCallback user_callback,
2021 GetMsgAsyncHelper *helper = NULL;
2023 ModestMailOperationPrivate *priv;
2025 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2026 g_return_if_fail (TNY_IS_HEADER (header));
2028 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2029 folder = tny_header_get_folder (header);
2031 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2033 /* Get message from folder */
2035 /* Get account and set it into mail_operation */
2036 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2038 /* Check for cached messages */
2039 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2040 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2042 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2044 helper = g_slice_new0 (GetMsgAsyncHelper);
2045 helper->mail_op = self;
2046 helper->user_callback = user_callback;
2047 helper->user_data = user_data;
2048 helper->header = g_object_ref (header);
2050 // The callback's reference so that the mail op is not
2051 // finalized until the async operation is completed even if
2052 // the user canceled the request meanwhile.
2053 g_object_ref (G_OBJECT (helper->mail_op));
2055 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2057 g_object_unref (G_OBJECT (folder));
2059 /* Set status failed and set an error */
2060 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2061 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2062 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2063 _("Error trying to get a message. No folder found for header"));
2065 /* Notify the queue */
2066 modest_mail_operation_notify_end (self);
2071 get_msg_cb (TnyFolder *folder,
2077 GetMsgAsyncHelper *helper = NULL;
2078 ModestMailOperation *self = NULL;
2079 ModestMailOperationPrivate *priv = NULL;
2081 helper = (GetMsgAsyncHelper *) user_data;
2082 g_return_if_fail (helper != NULL);
2083 self = helper->mail_op;
2084 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2085 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2087 /* Check errors and cancel */
2089 priv->error = g_error_copy (error);
2090 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2091 } else if (cancelled) {
2092 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2093 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2094 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2095 _("Error trying to refresh the contents of %s"),
2096 tny_folder_get_name (folder));
2098 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2101 /* If user defined callback function was defined, call it even
2102 if the operation failed*/
2103 if (helper->user_callback) {
2104 /* This is not a GDK lock because we are a Tinymail callback
2105 * which is already GDK locked by Tinymail */
2107 /* no gdk_threads_enter (), CHECKED */
2108 helper->user_callback (self, helper->header, msg, helper->user_data);
2109 /* no gdk_threads_leave (), CHECKED */
2112 /* Notify about operation end */
2113 modest_mail_operation_notify_end (self);
2115 g_object_unref (helper->mail_op);
2116 g_object_unref (helper->header);
2117 g_slice_free (GetMsgAsyncHelper, helper);
2122 get_msg_status_cb (GObject *obj,
2126 GetMsgAsyncHelper *helper = NULL;
2127 ModestMailOperation *self;
2128 ModestMailOperationPrivate *priv;
2129 ModestMailOperationState *state;
2131 g_return_if_fail (status != NULL);
2132 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2134 helper = (GetMsgAsyncHelper *) user_data;
2135 g_return_if_fail (helper != NULL);
2137 self = helper->mail_op;
2138 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2143 state = modest_mail_operation_clone_state (self);
2144 state->bytes_done = status->position;
2145 state->bytes_total = status->of_total;
2147 /* This is not a GDK lock because we are a Tinymail callback
2148 * which is already GDK locked by Tinymail */
2150 /* no gdk_threads_enter (), CHECKED */
2151 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2152 /* no gdk_threads_leave (), CHECKED */
2154 g_slice_free (ModestMailOperationState, state);
2157 /****************************************************/
2159 ModestMailOperation *mail_op;
2161 GetMsgAsyncUserCallback user_callback;
2163 GDestroyNotify notify;
2167 GetMsgAsyncUserCallback user_callback;
2171 ModestMailOperation *mail_op;
2172 } NotifyGetMsgsInfo;
2176 * Used by get_msgs_full_thread to call the user_callback for each
2177 * message that has been read
2180 notify_get_msgs_full (gpointer data)
2182 NotifyGetMsgsInfo *info;
2184 info = (NotifyGetMsgsInfo *) data;
2186 /* This is a GDK lock because we are an idle callback and
2187 * because info->user_callback can contain Gtk+ code */
2189 gdk_threads_enter (); /* CHECKED */
2190 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2191 gdk_threads_leave (); /* CHECKED */
2193 g_slice_free (NotifyGetMsgsInfo, info);
2199 * Used by get_msgs_full_thread to free al the thread resources and to
2200 * call the destroy function for the passed user_data
2203 get_msgs_full_destroyer (gpointer data)
2205 GetFullMsgsInfo *info;
2207 info = (GetFullMsgsInfo *) data;
2211 /* This is a GDK lock because we are an idle callback and
2212 * because info->notify can contain Gtk+ code */
2214 gdk_threads_enter (); /* CHECKED */
2215 info->notify (info->user_data);
2216 gdk_threads_leave (); /* CHECKED */
2220 g_object_unref (info->headers);
2221 g_slice_free (GetFullMsgsInfo, info);
2227 get_msgs_full_thread (gpointer thr_user_data)
2229 GetFullMsgsInfo *info;
2230 ModestMailOperationPrivate *priv = NULL;
2231 TnyIterator *iter = NULL;
2233 info = (GetFullMsgsInfo *) thr_user_data;
2234 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2236 iter = tny_list_create_iterator (info->headers);
2237 while (!tny_iterator_is_done (iter)) {
2241 header = TNY_HEADER (tny_iterator_get_current (iter));
2242 folder = tny_header_get_folder (header);
2244 /* Check for cached messages */
2245 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2246 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2248 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2250 /* Get message from folder */
2253 /* The callback will call it per each header */
2254 msg = tny_folder_get_msg (folder, header, &(priv->error));
2257 ModestMailOperationState *state;
2262 /* notify progress */
2263 state = modest_mail_operation_clone_state (info->mail_op);
2264 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2265 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2266 pair, (GDestroyNotify) modest_pair_free);
2268 /* The callback is the responsible for
2269 freeing the message */
2270 if (info->user_callback) {
2271 NotifyGetMsgsInfo *info_notify;
2272 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2273 info_notify->user_callback = info->user_callback;
2274 info_notify->mail_op = info->mail_op;
2275 info_notify->header = g_object_ref (header);
2276 info_notify->msg = g_object_ref (msg);
2277 info_notify->user_data = info->user_data;
2278 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2279 notify_get_msgs_full,
2282 g_object_unref (msg);
2285 /* Set status failed and set an error */
2286 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2287 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2288 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2289 "Error trying to get a message. No folder found for header");
2293 g_object_unref (header);
2295 tny_iterator_next (iter);
2298 /* Set operation status */
2299 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2300 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2302 /* Notify about operation end */
2303 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2305 /* Free thread resources. Will be called after all previous idles */
2306 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2312 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2313 TnyList *header_list,
2314 GetMsgAsyncUserCallback user_callback,
2316 GDestroyNotify notify)
2318 TnyHeader *header = NULL;
2319 TnyFolder *folder = NULL;
2321 ModestMailOperationPrivate *priv = NULL;
2322 GetFullMsgsInfo *info = NULL;
2323 gboolean size_ok = TRUE;
2325 TnyIterator *iter = NULL;
2327 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2329 /* Init mail operation */
2330 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2331 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2333 priv->total = tny_list_get_length(header_list);
2335 /* Get account and set it into mail_operation */
2336 if (tny_list_get_length (header_list) >= 1) {
2337 iter = tny_list_create_iterator (header_list);
2338 header = TNY_HEADER (tny_iterator_get_current (iter));
2340 folder = tny_header_get_folder (header);
2342 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2344 g_object_unref (folder);
2347 g_object_unref (header);
2350 if (tny_list_get_length (header_list) == 1) {
2351 g_object_unref (iter);
2356 /* Get msg size limit */
2357 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2358 MODEST_CONF_MSG_SIZE_LIMIT,
2361 g_clear_error (&(priv->error));
2362 max_size = G_MAXINT;
2364 max_size = max_size * KB;
2367 /* Check message size limits. If there is only one message
2368 always retrieve it */
2370 while (!tny_iterator_is_done (iter) && size_ok) {
2371 header = TNY_HEADER (tny_iterator_get_current (iter));
2373 if (tny_header_get_message_size (header) >= max_size)
2375 g_object_unref (header);
2378 tny_iterator_next (iter);
2380 g_object_unref (iter);
2384 /* Create the info */
2385 info = g_slice_new0 (GetFullMsgsInfo);
2386 info->mail_op = self;
2387 info->user_callback = user_callback;
2388 info->user_data = user_data;
2389 info->headers = g_object_ref (header_list);
2390 info->notify = notify;
2392 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2394 /* Set status failed and set an error */
2395 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2396 /* FIXME: the error msg is different for pop */
2397 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2398 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2399 _("emev_ni_ui_imap_msg_size_exceed_error"));
2400 /* Remove from queue and free resources */
2401 modest_mail_operation_notify_end (self);
2409 modest_mail_operation_remove_msg (ModestMailOperation *self,
2411 gboolean remove_to_trash /*ignored*/)
2414 ModestMailOperationPrivate *priv;
2416 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2417 g_return_if_fail (TNY_IS_HEADER (header));
2419 if (remove_to_trash)
2420 g_warning ("remove to trash is not implemented");
2422 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2423 folder = tny_header_get_folder (header);
2425 /* Get account and set it into mail_operation */
2426 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2428 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2430 /* remove message from folder */
2431 tny_folder_remove_msg (folder, header, &(priv->error));
2433 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2434 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2436 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2437 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2438 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2439 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2440 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2441 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2444 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2445 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2451 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2453 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2456 g_object_unref (G_OBJECT (folder));
2458 /* Notify about operation end */
2459 modest_mail_operation_notify_end (self);
2463 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2465 gboolean remove_to_trash /*ignored*/)
2468 ModestMailOperationPrivate *priv;
2469 TnyIterator *iter = NULL;
2470 TnyHeader *header = NULL;
2472 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2473 g_return_if_fail (TNY_IS_LIST (headers));
2475 if (remove_to_trash)
2476 g_warning ("remove to trash is not implemented");
2478 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2480 /* Get folder from first header and sync it */
2481 iter = tny_list_create_iterator (headers);
2482 header = TNY_HEADER (tny_iterator_get_current (iter));
2483 folder = tny_header_get_folder (header);
2485 /* Get account and set it into mail_operation */
2486 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2488 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2490 /* remove message from folder */
2491 tny_folder_remove_msgs (folder, headers, &(priv->error));
2493 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2494 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2495 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2496 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2497 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2498 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2501 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2502 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2508 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2510 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2513 g_object_unref (header);
2514 g_object_unref (iter);
2515 g_object_unref (G_OBJECT (folder));
2517 /* Notify about operation end */
2518 modest_mail_operation_notify_end (self);
2523 transfer_msgs_status_cb (GObject *obj,
2527 XFerMsgAsyncHelper *helper = NULL;
2528 ModestMailOperation *self;
2529 ModestMailOperationPrivate *priv;
2530 ModestMailOperationState *state;
2533 g_return_if_fail (status != NULL);
2534 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2536 helper = (XFerMsgAsyncHelper *) user_data;
2537 g_return_if_fail (helper != NULL);
2539 self = helper->mail_op;
2540 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2542 priv->done = status->position;
2543 priv->total = status->of_total;
2545 state = modest_mail_operation_clone_state (self);
2547 /* This is not a GDK lock because we are a Tinymail callback and
2548 * Tinymail already acquires the Gdk lock */
2550 /* no gdk_threads_enter (), CHECKED */
2552 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2554 /* no gdk_threads_leave (), CHECKED */
2556 g_slice_free (ModestMailOperationState, state);
2561 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2563 XFerMsgAsyncHelper *helper;
2564 ModestMailOperation *self;
2565 ModestMailOperationPrivate *priv;
2566 TnyIterator *iter = NULL;
2567 TnyHeader *header = NULL;
2569 helper = (XFerMsgAsyncHelper *) user_data;
2570 self = helper->mail_op;
2572 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2575 priv->error = g_error_copy (err);
2577 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2578 } else if (cancelled) {
2579 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2580 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2581 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2582 _("Error trying to refresh the contents of %s"),
2583 tny_folder_get_name (folder));
2586 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2588 /* Update folder counts */
2589 tny_folder_poke_status (folder);
2590 tny_folder_poke_status (helper->dest_folder);
2594 /* Mark headers as deleted and seen */
2595 if ((helper->delete) &&
2596 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2597 iter = tny_list_create_iterator (helper->headers);
2598 while (!tny_iterator_is_done (iter)) {
2599 header = TNY_HEADER (tny_iterator_get_current (iter));
2600 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2601 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2602 g_object_unref (header);
2604 tny_iterator_next (iter);
2610 /* Notify about operation end */
2611 modest_mail_operation_notify_end (self);
2613 /* If user defined callback function was defined, call it */
2614 if (helper->user_callback) {
2615 /* This is not a GDK lock because we are a Tinymail callback and
2616 * Tinymail already acquires the Gdk lock */
2618 /* no gdk_threads_enter (), CHECKED */
2619 helper->user_callback (priv->source, helper->user_data);
2620 /* no gdk_threads_leave (), CHECKED */
2624 if (helper->headers)
2625 g_object_unref (helper->headers);
2626 if (helper->dest_folder)
2627 g_object_unref (helper->dest_folder);
2628 if (helper->mail_op)
2629 g_object_unref (helper->mail_op);
2631 g_object_unref (folder);
2633 g_object_unref (iter);
2634 g_slice_free (XFerMsgAsyncHelper, helper);
2639 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2642 gboolean delete_original,
2643 XferMsgsAsynUserCallback user_callback,
2646 ModestMailOperationPrivate *priv = NULL;
2647 TnyIterator *iter = NULL;
2648 TnyFolder *src_folder = NULL;
2649 XFerMsgAsyncHelper *helper = NULL;
2650 TnyHeader *header = NULL;
2651 ModestTnyFolderRules rules = 0;
2652 const gchar *id1 = NULL;
2653 const gchar *id2 = NULL;
2654 gboolean same_folder = FALSE;
2656 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2657 g_return_if_fail (TNY_IS_LIST (headers));
2658 g_return_if_fail (TNY_IS_FOLDER (folder));
2660 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2663 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2665 /* Apply folder rules */
2666 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2667 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2668 /* Set status failed and set an error */
2669 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2670 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2671 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2672 _CS("ckct_ib_unable_to_paste_here"));
2673 /* Notify the queue */
2674 modest_mail_operation_notify_end (self);
2678 /* Get source folder */
2679 iter = tny_list_create_iterator (headers);
2680 header = TNY_HEADER (tny_iterator_get_current (iter));
2682 src_folder = tny_header_get_folder (header);
2683 g_object_unref (header);
2686 g_object_unref (iter);
2688 /* Check folder source and destination */
2689 id1 = tny_folder_get_id (src_folder);
2690 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2691 same_folder = !g_ascii_strcasecmp (id1, id2);
2693 /* Set status failed and set an error */
2694 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2695 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2696 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2697 _("mcen_ib_unable_to_copy_samefolder"));
2699 /* Notify the queue */
2700 modest_mail_operation_notify_end (self);
2703 g_object_unref (src_folder);
2707 /* Create the helper */
2708 helper = g_slice_new0 (XFerMsgAsyncHelper);
2709 helper->mail_op = g_object_ref(self);
2710 helper->dest_folder = g_object_ref(folder);
2711 helper->headers = g_object_ref(headers);
2712 helper->user_callback = user_callback;
2713 helper->user_data = user_data;
2714 helper->delete = delete_original;
2716 /* Get account and set it into mail_operation */
2717 priv->account = modest_tny_folder_get_account (src_folder);
2719 /* Transfer messages */
2720 tny_folder_transfer_msgs_async (src_folder,
2725 transfer_msgs_status_cb,
2731 on_refresh_folder (TnyFolder *folder,
2736 RefreshAsyncHelper *helper = NULL;
2737 ModestMailOperation *self = NULL;
2738 ModestMailOperationPrivate *priv = NULL;
2740 helper = (RefreshAsyncHelper *) user_data;
2741 self = helper->mail_op;
2742 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2744 g_return_if_fail(priv!=NULL);
2747 priv->error = g_error_copy (error);
2748 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2749 printf("DEBUG: %s: Operation error:\n %s", __FUNCTION__,
2755 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2756 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2757 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2758 _("Error trying to refresh the contents of %s"),
2759 tny_folder_get_name (folder));
2760 printf("DEBUG: %s: Operation cancelled.\n", __FUNCTION__);
2764 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2766 /* Call user defined callback, if it exists */
2767 if (helper->user_callback) {
2769 /* This is not a GDK lock because we are a Tinymail callback and
2770 * Tinymail already acquires the Gdk lock */
2771 helper->user_callback (self, folder, helper->user_data);
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 */
2810 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2812 g_slice_free (ModestMailOperationState, state);
2816 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2818 RefreshAsyncUserCallback user_callback,
2821 ModestMailOperationPrivate *priv = NULL;
2822 RefreshAsyncHelper *helper = NULL;
2824 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2826 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2828 /* Get account and set it into mail_operation */
2829 priv->account = modest_tny_folder_get_account (folder);
2831 /* Create the helper */
2832 helper = g_slice_new0 (RefreshAsyncHelper);
2833 helper->mail_op = g_object_ref(self);
2834 helper->user_callback = user_callback;
2835 helper->user_data = user_data;
2837 /* Refresh the folder. TODO: tinymail could issue a status
2838 updates before the callback call then this could happen. We
2839 must review the design */
2840 tny_folder_refresh_async (folder,
2842 on_refresh_folder_status_update,
2848 * It's used by the mail operation queue to notify the observers
2849 * attached to that signal that the operation finished. We need to use
2850 * that because tinymail does not give us the progress of a given
2851 * operation when it finishes (it directly calls the operation
2855 modest_mail_operation_notify_end (ModestMailOperation *self)
2857 ModestMailOperationState *state;
2858 ModestMailOperationPrivate *priv = NULL;
2860 g_return_if_fail (self);
2862 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2864 /* Set the account back to not busy */
2865 if (priv->account_name) {
2866 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2867 priv->account_name, FALSE);
2868 g_free(priv->account_name);
2869 priv->account_name = NULL;
2872 /* Notify the observers about the mail operation end */
2873 /* We do not wrapp this emission because we assume that this
2874 function is always called from within the main lock */
2875 state = modest_mail_operation_clone_state (self);
2876 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2877 g_slice_free (ModestMailOperationState, state);