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-account-mgr-helpers.h"
49 #include <modest-tny-account.h>
50 #include <modest-tny-send-queue.h>
51 #include <modest-runtime.h>
52 #include "modest-text-utils.h"
53 #include "modest-tny-msg.h"
54 #include "modest-tny-folder.h"
55 #include "modest-tny-account-store.h"
56 #include "modest-tny-platform-factory.h"
57 #include "modest-marshal.h"
58 #include "modest-error.h"
59 #include "modest-mail-operation.h"
64 * Remove all these #ifdef stuff when the tinymail's idle calls become
67 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
69 /* 'private'/'protected' functions */
70 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
71 static void modest_mail_operation_init (ModestMailOperation *obj);
72 static void modest_mail_operation_finalize (GObject *obj);
74 static void get_msg_cb (TnyFolder *folder,
80 static void get_msg_status_cb (GObject *obj,
84 static void modest_mail_operation_notify_start (ModestMailOperation *self);
85 static void modest_mail_operation_notify_end (ModestMailOperation *self);
87 enum _ModestMailOperationSignals
89 PROGRESS_CHANGED_SIGNAL,
90 OPERATION_STARTED_SIGNAL,
91 OPERATION_FINISHED_SIGNAL,
95 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
96 struct _ModestMailOperationPrivate {
102 ErrorCheckingUserCallback error_checking;
103 gpointer error_checking_user_data;
104 ErrorCheckingUserDataDestroyer error_checking_user_data_destroyer;
105 ModestMailOperationStatus status;
106 ModestMailOperationTypeOperation op_type;
109 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
110 MODEST_TYPE_MAIL_OPERATION, \
111 ModestMailOperationPrivate))
113 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
114 priv->status = new_status;\
117 typedef struct _GetMsgAsyncHelper {
118 ModestMailOperation *mail_op;
120 GetMsgAsyncUserCallback user_callback;
124 typedef struct _RefreshAsyncHelper {
125 ModestMailOperation *mail_op;
126 RefreshAsyncUserCallback user_callback;
128 } RefreshAsyncHelper;
130 typedef struct _XFerMsgAsyncHelper
132 ModestMailOperation *mail_op;
134 TnyFolder *dest_folder;
135 XferAsyncUserCallback user_callback;
138 gint last_total_bytes;
139 gint sum_total_bytes;
141 } XFerMsgAsyncHelper;
143 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
147 static void modest_mail_operation_create_msg (ModestMailOperation *self,
148 const gchar *from, const gchar *to,
149 const gchar *cc, const gchar *bcc,
150 const gchar *subject, const gchar *plain_body,
151 const gchar *html_body, const GList *attachments_list,
152 const GList *images_list,
153 TnyHeaderFlags priority_flags,
154 ModestMailOperationCreateMsgCallback callback,
157 static gboolean idle_notify_queue (gpointer data);
160 ModestMailOperation *mail_op;
168 GList *attachments_list;
170 TnyHeaderFlags priority_flags;
171 ModestMailOperationCreateMsgCallback callback;
177 ModestMailOperation *mail_op;
179 ModestMailOperationCreateMsgCallback callback;
184 static GObjectClass *parent_class = NULL;
186 static guint signals[NUM_SIGNALS] = {0};
189 modest_mail_operation_get_type (void)
191 static GType my_type = 0;
193 static const GTypeInfo my_info = {
194 sizeof(ModestMailOperationClass),
195 NULL, /* base init */
196 NULL, /* base finalize */
197 (GClassInitFunc) modest_mail_operation_class_init,
198 NULL, /* class finalize */
199 NULL, /* class data */
200 sizeof(ModestMailOperation),
202 (GInstanceInitFunc) modest_mail_operation_init,
205 my_type = g_type_register_static (G_TYPE_OBJECT,
206 "ModestMailOperation",
213 modest_mail_operation_class_init (ModestMailOperationClass *klass)
215 GObjectClass *gobject_class;
216 gobject_class = (GObjectClass*) klass;
218 parent_class = g_type_class_peek_parent (klass);
219 gobject_class->finalize = modest_mail_operation_finalize;
221 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
224 * ModestMailOperation::progress-changed
225 * @self: the #MailOperation that emits the signal
226 * @user_data: user data set when the signal handler was connected
228 * Emitted when the progress of a mail operation changes
230 signals[PROGRESS_CHANGED_SIGNAL] =
231 g_signal_new ("progress-changed",
232 G_TYPE_FROM_CLASS (gobject_class),
234 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
236 g_cclosure_marshal_VOID__POINTER,
237 G_TYPE_NONE, 1, G_TYPE_POINTER);
241 * This signal is issued whenever a mail operation starts, and
242 * starts mean when the tinymail operation is issued. This
243 * means that it could happen that something wrong happens and
244 * the tinymail function is never called. In this situation a
245 * operation-finished will be issued without any
248 signals[OPERATION_STARTED_SIGNAL] =
249 g_signal_new ("operation-started",
250 G_TYPE_FROM_CLASS (gobject_class),
252 G_STRUCT_OFFSET (ModestMailOperationClass, operation_started),
254 g_cclosure_marshal_VOID__VOID,
259 * This signal is issued whenever a mail operation
260 * finishes. Note that this signal could be issued without any
261 * previous "operation-started" signal, because this last one
262 * is only issued when the tinymail operation is successfully
265 signals[OPERATION_FINISHED_SIGNAL] =
266 g_signal_new ("operation-finished",
267 G_TYPE_FROM_CLASS (gobject_class),
269 G_STRUCT_OFFSET (ModestMailOperationClass, operation_finished),
271 g_cclosure_marshal_VOID__VOID,
276 modest_mail_operation_init (ModestMailOperation *obj)
278 ModestMailOperationPrivate *priv;
280 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
282 priv->account = NULL;
283 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
284 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
289 priv->error_checking = NULL;
290 priv->error_checking_user_data = NULL;
294 modest_mail_operation_finalize (GObject *obj)
296 ModestMailOperationPrivate *priv;
298 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
303 g_error_free (priv->error);
307 g_object_unref (priv->source);
311 g_object_unref (priv->account);
312 priv->account = NULL;
316 G_OBJECT_CLASS(parent_class)->finalize (obj);
320 modest_mail_operation_new (GObject *source)
322 ModestMailOperation *obj;
323 ModestMailOperationPrivate *priv;
325 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
326 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
329 priv->source = g_object_ref(source);
335 modest_mail_operation_new_with_error_handling (GObject *source,
336 ErrorCheckingUserCallback error_handler,
338 ErrorCheckingUserDataDestroyer error_handler_destroyer)
340 ModestMailOperation *obj;
341 ModestMailOperationPrivate *priv;
343 obj = modest_mail_operation_new (source);
344 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
346 g_return_val_if_fail (error_handler != NULL, obj);
347 priv->error_checking = error_handler;
348 priv->error_checking_user_data = user_data;
349 priv->error_checking_user_data_destroyer = error_handler_destroyer;
355 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
357 ModestMailOperationPrivate *priv;
359 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
360 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
362 /* Call the user callback */
363 if (priv->error_checking != NULL)
364 priv->error_checking (self, priv->error_checking_user_data);
368 ModestMailOperationTypeOperation
369 modest_mail_operation_get_type_operation (ModestMailOperation *self)
371 ModestMailOperationPrivate *priv;
373 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
375 return priv->op_type;
379 modest_mail_operation_is_mine (ModestMailOperation *self,
382 ModestMailOperationPrivate *priv;
384 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
385 if (priv->source == NULL) return FALSE;
387 return priv->source == me;
391 modest_mail_operation_get_source (ModestMailOperation *self)
393 ModestMailOperationPrivate *priv;
395 g_return_val_if_fail (self, NULL);
397 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
399 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
403 return (priv->source) ? g_object_ref (priv->source) : NULL;
406 ModestMailOperationStatus
407 modest_mail_operation_get_status (ModestMailOperation *self)
409 ModestMailOperationPrivate *priv;
411 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
412 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
413 MODEST_MAIL_OPERATION_STATUS_INVALID);
415 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
417 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
418 return MODEST_MAIL_OPERATION_STATUS_INVALID;
425 modest_mail_operation_get_error (ModestMailOperation *self)
427 ModestMailOperationPrivate *priv;
429 g_return_val_if_fail (self, NULL);
430 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
432 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
435 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
443 modest_mail_operation_cancel (ModestMailOperation *self)
445 ModestMailOperationPrivate *priv;
446 gboolean canceled = FALSE;
448 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
450 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
452 /* Note that if we call cancel with an already canceled mail
453 operation the progress changed signal won't be emitted */
454 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
458 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
460 /* Cancel the mail operation. We need to wrap it between this
461 start/stop operations to allow following calls to the
463 g_return_val_if_fail (priv->account, FALSE);
464 tny_account_cancel (priv->account);
470 modest_mail_operation_get_task_done (ModestMailOperation *self)
472 ModestMailOperationPrivate *priv;
474 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
476 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
481 modest_mail_operation_get_task_total (ModestMailOperation *self)
483 ModestMailOperationPrivate *priv;
485 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
487 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
492 modest_mail_operation_is_finished (ModestMailOperation *self)
494 ModestMailOperationPrivate *priv;
495 gboolean retval = FALSE;
497 if (!MODEST_IS_MAIL_OPERATION (self)) {
498 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
502 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
504 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
505 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
506 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
507 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
517 * Creates an image of the current state of a mail operation, the
518 * caller must free it
520 static ModestMailOperationState *
521 modest_mail_operation_clone_state (ModestMailOperation *self)
523 ModestMailOperationState *state;
524 ModestMailOperationPrivate *priv;
526 /* FIXME: this should be fixed properly
528 * in some cases, priv was NULL, so checking here to
531 g_return_val_if_fail (self, NULL);
532 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
533 g_return_val_if_fail (priv, NULL);
538 state = g_slice_new (ModestMailOperationState);
540 state->status = priv->status;
541 state->op_type = priv->op_type;
542 state->done = priv->done;
543 state->total = priv->total;
544 state->finished = modest_mail_operation_is_finished (self);
545 state->bytes_done = 0;
546 state->bytes_total = 0;
551 /* ******************************************************************* */
552 /* ************************** SEND ACTIONS ************************* */
553 /* ******************************************************************* */
556 modest_mail_operation_send_mail (ModestMailOperation *self,
557 TnyTransportAccount *transport_account,
560 TnySendQueue *send_queue = NULL;
561 ModestMailOperationPrivate *priv;
563 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
564 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
565 g_return_if_fail (TNY_IS_MSG (msg));
567 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
569 /* Get account and set it into mail_operation */
570 priv->account = g_object_ref (transport_account);
574 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
575 if (!TNY_IS_SEND_QUEUE(send_queue)) {
576 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
577 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
578 "modest: could not find send queue for account\n");
579 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
582 /* Add the msg to the queue */
583 modest_mail_operation_notify_start (self);
584 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue),
588 /* TODO: we're setting always success, do the check in
590 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
593 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)
594 modest_platform_information_banner (NULL, NULL, _("mcen_ib_outbox_waiting_to_be_sent"));
596 /* TODO: do this in the handler of the "msg-sent"
597 signal.Notify about operation end */
598 modest_mail_operation_notify_end (self);
602 idle_create_msg_cb (gpointer idle_data)
604 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
606 /* This is a GDK lock because we are an idle callback and
607 * info->callback can contain Gtk+ code */
609 gdk_threads_enter (); /* CHECKED */
610 info->callback (info->mail_op, info->msg, info->userdata);
612 g_object_unref (info->mail_op);
614 g_object_unref (info->msg);
615 g_slice_free (CreateMsgIdleInfo, info);
616 gdk_threads_leave (); /* CHECKED */
622 create_msg_thread (gpointer thread_data)
624 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
625 TnyMsg *new_msg = NULL;
626 ModestMailOperationPrivate *priv;
628 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
629 if (info->html_body == NULL) {
630 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
631 info->bcc, info->subject, info->plain_body,
632 info->attachments_list);
634 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
635 info->bcc, info->subject, info->html_body,
636 info->plain_body, info->attachments_list,
642 TnyHeaderFlags flags = 0;
644 /* Set priority flags in message */
645 header = tny_msg_get_header (new_msg);
646 if (info->priority_flags != 0)
647 flags |= info->priority_flags;
649 /* Set attachment flags in message */
650 if (info->attachments_list != NULL)
651 flags |= TNY_HEADER_FLAG_ATTACHMENTS;
653 tny_header_set_flags (header, flags);
654 g_object_unref (G_OBJECT(header));
656 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
657 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
658 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
659 "modest: failed to create a new msg\n");
667 g_free (info->plain_body);
668 g_free (info->html_body);
669 g_free (info->subject);
670 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
671 g_list_free (info->attachments_list);
672 g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
673 g_list_free (info->images_list);
675 if (info->callback) {
676 CreateMsgIdleInfo *idle_info;
677 idle_info = g_slice_new0 (CreateMsgIdleInfo);
678 idle_info->mail_op = g_object_ref (info->mail_op);
679 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
680 idle_info->callback = info->callback;
681 idle_info->userdata = info->userdata;
682 g_idle_add (idle_create_msg_cb, idle_info);
684 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
687 g_object_unref (info->mail_op);
688 g_slice_free (CreateMsgInfo, info);
693 modest_mail_operation_create_msg (ModestMailOperation *self,
694 const gchar *from, const gchar *to,
695 const gchar *cc, const gchar *bcc,
696 const gchar *subject, const gchar *plain_body,
697 const gchar *html_body,
698 const GList *attachments_list,
699 const GList *images_list,
700 TnyHeaderFlags priority_flags,
701 ModestMailOperationCreateMsgCallback callback,
704 CreateMsgInfo *info = NULL;
706 info = g_slice_new0 (CreateMsgInfo);
707 info->mail_op = g_object_ref (self);
709 info->from = g_strdup (from);
710 info->to = g_strdup (to);
711 info->cc = g_strdup (cc);
712 info->bcc = g_strdup (bcc);
713 info->subject = g_strdup (subject);
714 info->plain_body = g_strdup (plain_body);
715 info->html_body = g_strdup (html_body);
716 info->attachments_list = g_list_copy ((GList *) attachments_list);
717 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
718 info->images_list = g_list_copy ((GList *) images_list);
719 g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
720 info->priority_flags = priority_flags;
722 info->callback = callback;
723 info->userdata = userdata;
725 g_thread_create (create_msg_thread, info, FALSE, NULL);
730 TnyTransportAccount *transport_account;
735 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
739 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
740 TnyFolder *draft_folder = NULL;
741 TnyFolder *outbox_folder = NULL;
749 /* Call mail operation */
750 modest_mail_operation_send_mail (self, info->transport_account, msg);
752 /* Remove old mail from its source folder */
753 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_DRAFTS);
754 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account), TNY_FOLDER_TYPE_OUTBOX);
755 if (info->draft_msg != NULL) {
756 TnyFolder *folder = NULL;
757 TnyFolder *src_folder = NULL;
758 TnyFolderType folder_type;
759 folder = tny_msg_get_folder (info->draft_msg);
760 if (folder == NULL) goto end;
761 folder_type = modest_tny_folder_guess_folder_type (folder);
762 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
763 src_folder = outbox_folder;
765 src_folder = draft_folder;
767 /* Note: This can fail (with a warning) if the message is not really already in a folder,
768 * because this function requires it to have a UID. */
769 header = tny_msg_get_header (info->draft_msg);
770 tny_folder_remove_msg (src_folder, header, NULL);
772 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
773 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
775 g_object_unref (header);
776 g_object_unref (folder);
783 g_object_unref (info->draft_msg);
785 g_object_unref (draft_folder);
787 g_object_unref (outbox_folder);
788 if (info->transport_account)
789 g_object_unref (info->transport_account);
790 g_slice_free (SendNewMailInfo, info);
791 modest_mail_operation_notify_end (self);
795 modest_mail_operation_send_new_mail (ModestMailOperation *self,
796 TnyTransportAccount *transport_account,
798 const gchar *from, const gchar *to,
799 const gchar *cc, const gchar *bcc,
800 const gchar *subject, const gchar *plain_body,
801 const gchar *html_body,
802 const GList *attachments_list,
803 const GList *images_list,
804 TnyHeaderFlags priority_flags)
806 ModestMailOperationPrivate *priv = NULL;
807 SendNewMailInfo *info;
809 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
810 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
812 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
813 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
814 priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
816 /* Check parametters */
818 /* Set status failed and set an error */
819 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
820 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
821 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
822 _("Error trying to send a mail. You need to set at least one recipient"));
825 info = g_slice_new0 (SendNewMailInfo);
826 info->transport_account = transport_account;
827 if (transport_account)
828 g_object_ref (transport_account);
829 info->draft_msg = draft_msg;
831 g_object_ref (draft_msg);
834 modest_mail_operation_notify_start (self);
835 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
836 attachments_list, images_list, priority_flags,
837 modest_mail_operation_send_new_mail_cb, info);
843 TnyTransportAccount *transport_account;
845 SaveToDraftstCallback callback;
850 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
854 TnyFolder *src_folder = NULL;
855 TnyFolder *drafts = NULL;
856 TnyHeader *header = NULL;
857 ModestMailOperationPrivate *priv = NULL;
858 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
860 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
863 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
864 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
865 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
866 "modest: failed to create a new msg\n");
870 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
871 TNY_FOLDER_TYPE_DRAFTS);
873 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
874 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
875 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
876 "modest: failed to create a new msg\n");
881 tny_folder_add_msg (drafts, msg, &(priv->error));
883 if ((!priv->error) && (info->draft_msg != NULL)) {
884 header = tny_msg_get_header (info->draft_msg);
885 src_folder = tny_header_get_folder (header);
887 /* Remove the old draft */
888 tny_folder_remove_msg (src_folder, header, NULL);
890 /* Synchronize to expunge and to update the msg counts */
891 tny_folder_sync_async (drafts, TRUE, NULL, NULL, NULL);
892 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
894 g_object_unref (header);
898 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
900 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
903 /* Call the user callback */
905 info->callback (self, msg, info->user_data);
908 g_object_unref (G_OBJECT(drafts));
910 g_object_unref (G_OBJECT(src_folder));
912 g_object_unref (G_OBJECT (info->draft_msg));
913 if (info->transport_account)
914 g_object_unref (G_OBJECT(info->transport_account));
915 g_slice_free (SaveToDraftsInfo, info);
917 modest_mail_operation_notify_end (self);
921 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
922 TnyTransportAccount *transport_account,
924 const gchar *from, const gchar *to,
925 const gchar *cc, const gchar *bcc,
926 const gchar *subject, const gchar *plain_body,
927 const gchar *html_body,
928 const GList *attachments_list,
929 const GList *images_list,
930 TnyHeaderFlags priority_flags,
931 SaveToDraftstCallback callback,
934 ModestMailOperationPrivate *priv = NULL;
935 SaveToDraftsInfo *info = NULL;
937 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
938 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
940 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
942 /* Get account and set it into mail_operation */
943 priv->account = g_object_ref (transport_account);
944 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
946 info = g_slice_new0 (SaveToDraftsInfo);
947 info->transport_account = g_object_ref (transport_account);
948 info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
949 info->callback = callback;
950 info->user_data = user_data;
952 modest_mail_operation_notify_start (self);
953 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
954 attachments_list, images_list, priority_flags,
955 modest_mail_operation_save_to_drafts_cb, info);
960 ModestMailOperation *mail_op;
961 TnyStoreAccount *account;
962 TnyTransportAccount *transport_account;
965 gchar *retrieve_type;
967 UpdateAccountCallback callback;
969 TnyList *new_headers;
974 ModestMailOperation *mail_op;
975 TnyMimePart *mime_part;
977 GetMimePartSizeCallback callback;
979 } GetMimePartSizeInfo;
981 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
982 /* We use this folder observer to track the headers that have been
983 * added to a folder */
986 TnyList *new_headers;
987 } InternalFolderObserver;
991 } InternalFolderObserverClass;
993 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
995 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
996 internal_folder_observer,
998 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1002 foreach_add_item (gpointer header, gpointer user_data)
1004 tny_list_prepend (TNY_LIST (user_data),
1005 g_object_ref (G_OBJECT (header)));
1008 /* This is the method that looks for new messages in a folder */
1010 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1012 InternalFolderObserver *derived = (InternalFolderObserver *)self;
1014 TnyFolderChangeChanged changed;
1016 changed = tny_folder_change_get_changed (change);
1018 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1021 /* Get added headers */
1022 list = tny_simple_list_new ();
1023 tny_folder_change_get_added_headers (change, list);
1025 /* Add them to the folder observer */
1026 tny_list_foreach (list, foreach_add_item,
1027 derived->new_headers);
1029 g_object_unref (G_OBJECT (list));
1034 internal_folder_observer_init (InternalFolderObserver *self)
1036 self->new_headers = tny_simple_list_new ();
1039 internal_folder_observer_finalize (GObject *object)
1041 InternalFolderObserver *self;
1043 self = (InternalFolderObserver *) object;
1044 g_object_unref (self->new_headers);
1046 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1049 tny_folder_observer_init (TnyFolderObserverIface *iface)
1051 iface->update_func = internal_folder_observer_update;
1054 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1056 GObjectClass *object_class;
1058 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1059 object_class = (GObjectClass*) klass;
1060 object_class->finalize = internal_folder_observer_finalize;
1066 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1069 TnyList *folders = tny_simple_list_new ();
1071 tny_folder_store_get_folders (store, folders, query, NULL);
1072 iter = tny_list_create_iterator (folders);
1074 while (!tny_iterator_is_done (iter)) {
1076 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1078 tny_list_prepend (all_folders, G_OBJECT (folder));
1079 recurse_folders (folder, query, all_folders);
1080 g_object_unref (G_OBJECT (folder));
1083 tny_iterator_next (iter);
1085 g_object_unref (G_OBJECT (iter));
1086 g_object_unref (G_OBJECT (folders));
1090 * Issues the "progress-changed" signal. The timer won't be removed,
1091 * so you must call g_source_remove to stop the signal emission
1094 idle_notify_progress (gpointer data)
1096 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1097 ModestMailOperationState *state;
1099 state = modest_mail_operation_clone_state (mail_op);
1101 /* This is a GDK lock because we are an idle callback and
1102 * the handlers of this signal can contain Gtk+ code */
1104 gdk_threads_enter (); /* CHECKED */
1105 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1106 gdk_threads_leave (); /* CHECKED */
1108 g_slice_free (ModestMailOperationState, state);
1114 * Issues the "progress-changed" signal and removes the timer. It uses
1115 * a lock to ensure that the progress information of the mail
1116 * operation is not modified while there are notifications pending
1119 idle_notify_progress_once (gpointer data)
1123 pair = (ModestPair *) data;
1125 /* This is a GDK lock because we are an idle callback and
1126 * the handlers of this signal can contain Gtk+ code */
1128 gdk_threads_enter (); /* CHECKED */
1129 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1130 gdk_threads_leave (); /* CHECKED */
1132 /* Free the state and the reference to the mail operation */
1133 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1134 g_object_unref (pair->first);
1140 * Used to notify the queue from the main
1141 * loop. We call it inside an idle call to achieve that
1144 idle_notify_queue (gpointer data)
1146 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1148 /* Do not need to block, the notify end will do it for us */
1149 modest_mail_operation_notify_end (mail_op);
1150 g_object_unref (mail_op);
1156 compare_headers_by_date (gconstpointer a,
1159 TnyHeader **header1, **header2;
1160 time_t sent1, sent2;
1162 header1 = (TnyHeader **) a;
1163 header2 = (TnyHeader **) b;
1165 sent1 = tny_header_get_date_sent (*header1);
1166 sent2 = tny_header_get_date_sent (*header2);
1168 /* We want the most recent ones (greater time_t) at the
1177 set_last_updated_idle (gpointer data)
1180 /* This is a GDK lock because we are an idle callback and
1181 * modest_account_mgr_set_last_updated can issue Gtk+ code */
1183 gdk_threads_enter (); /* CHECKED - please recheck */
1185 /* It does not matter if the time is not exactly the same than
1186 the time when this idle was called, it's just an
1187 approximation and it won't be very different */
1189 modest_account_mgr_set_last_updated (modest_runtime_get_account_mgr (),
1193 gdk_threads_leave (); /* CHECKED - please recheck */
1199 idle_update_account_cb (gpointer data)
1201 UpdateAccountInfo *idle_info;
1203 idle_info = (UpdateAccountInfo *) data;
1205 /* This is a GDK lock because we are an idle callback and
1206 * idle_info->callback can contain Gtk+ code */
1208 gdk_threads_enter (); /* CHECKED */
1209 idle_info->callback (idle_info->mail_op,
1210 idle_info->new_headers,
1211 idle_info->user_data);
1212 gdk_threads_leave (); /* CHECKED */
1215 g_object_unref (idle_info->mail_op);
1216 if (idle_info->new_headers)
1217 g_object_unref (idle_info->new_headers);
1224 get_all_folders_from_account (TnyStoreAccount *account,
1227 TnyList *all_folders = NULL;
1228 TnyIterator *iter = NULL;
1229 TnyFolderStoreQuery *query = NULL;
1231 all_folders = tny_simple_list_new ();
1232 query = tny_folder_store_query_new ();
1233 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1234 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
1241 g_object_unref (all_folders);
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));
1261 update_account_thread (gpointer thr_user_data)
1263 static gboolean first_time = TRUE;
1264 UpdateAccountInfo *info = NULL;
1265 TnyList *all_folders = NULL, *new_headers = NULL;
1266 GPtrArray *new_headers_array = NULL;
1267 TnyIterator *iter = NULL;
1268 ModestMailOperationPrivate *priv = NULL;
1269 ModestTnySendQueue *send_queue = NULL;
1270 gint i = 0, timeout = 0;
1272 info = (UpdateAccountInfo *) thr_user_data;
1273 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1275 /* Get account and set it into mail_operation */
1276 priv->account = g_object_ref (info->account);
1278 /* Get all the folders. We can do it synchronously because
1279 we're already running in a different thread than the UI */
1280 all_folders = get_all_folders_from_account (info->account, &(priv->error));
1282 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1286 /* Update status and notify. We need to call the notification
1287 with a source function in order to call it from the main
1288 loop. We need that in order not to get into trouble with
1289 Gtk+. We use a timeout in order to provide more status
1290 information, because the sync tinymail call does not
1291 provide it for the moment */
1292 timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1294 new_headers_array = g_ptr_array_new ();
1295 iter = tny_list_create_iterator (all_folders);
1297 while (!tny_iterator_is_done (iter) && !priv->error &&
1298 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1300 TnyFolderType folder_type;
1301 TnyFolder *folder = NULL;
1303 folder = TNY_FOLDER (tny_iterator_get_current (iter));
1304 folder_type = tny_folder_get_folder_type (folder);
1306 /* Refresh it only if it's the INBOX */
1307 if (folder_type == TNY_FOLDER_TYPE_INBOX) {
1308 InternalFolderObserver *observer = NULL;
1309 TnyIterator *new_headers_iter = NULL;
1311 /* Refresh the folder. Our observer receives
1312 * the new emails during folder refreshes, so
1313 * we can use observer->new_headers
1315 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1316 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1318 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1320 new_headers_iter = tny_list_create_iterator (observer->new_headers);
1321 while (!tny_iterator_is_done (new_headers_iter)) {
1322 TnyHeader *header = NULL;
1324 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1325 /* Apply per-message size limits */
1326 if (tny_header_get_message_size (header) < info->max_size)
1327 g_ptr_array_add (new_headers_array, g_object_ref (header));
1329 g_object_unref (header);
1330 tny_iterator_next (new_headers_iter);
1332 g_object_unref (new_headers_iter);
1334 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1335 g_object_unref (observer);
1337 /* We no not need to do it the first time,
1338 because it's automatically done by the tree
1340 if (G_LIKELY (!first_time))
1341 tny_folder_poke_status (folder);
1343 g_object_unref (folder);
1345 tny_iterator_next (iter);
1347 g_object_unref (G_OBJECT (iter));
1348 g_source_remove (timeout);
1350 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1351 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1352 new_headers_array->len > 0) {
1356 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1358 /* TODO: Ask the user, instead of just failing,
1359 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1360 * all' and 'Newest only' buttons. */
1361 if (new_headers_array->len > info->retrieve_limit) {
1365 /* Should be get only the headers or the message as well? */
1366 if (g_ascii_strcasecmp (info->retrieve_type,
1367 MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {
1369 priv->total = MIN (new_headers_array->len, info->retrieve_limit);
1370 while (msg_num < priv->total) {
1372 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1373 TnyFolder *folder = tny_header_get_folder (header);
1374 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1375 ModestMailOperationState *state;
1379 /* We can not just use the mail operation because the
1380 values of done and total could change before the
1382 state = modest_mail_operation_clone_state (info->mail_op);
1383 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1384 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1385 pair, (GDestroyNotify) modest_pair_free);
1387 g_object_unref (msg);
1388 g_object_unref (folder);
1395 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1398 /* Copy the headers to a list and free the array */
1399 new_headers = tny_simple_list_new ();
1400 for (i=0; i < new_headers_array->len; i++) {
1401 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1402 tny_list_append (new_headers, G_OBJECT (header));
1404 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1405 g_ptr_array_free (new_headers_array, FALSE);
1408 /* Perform send (if operation was not cancelled) */
1411 if (priv->account != NULL)
1412 g_object_unref (priv->account);
1414 if (info->transport_account) {
1415 priv->account = g_object_ref (info->transport_account);
1417 send_queue = modest_runtime_get_send_queue (info->transport_account);
1419 modest_tny_send_queue_try_to_send (send_queue);
1421 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1422 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1423 "cannot create a send queue for %s\n",
1424 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1425 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1429 /* Check if the operation was a success */
1431 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1433 /* Update the last updated key */
1434 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1435 set_last_updated_idle,
1436 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1437 (GDestroyNotify) g_free);
1441 /* Set the account back to not busy */
1442 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
1443 info->account_name, FALSE);
1445 if (info->callback) {
1446 UpdateAccountInfo *idle_info;
1448 /* This thread is not in the main lock */
1449 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1450 idle_info->mail_op = g_object_ref (info->mail_op);
1451 idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL;
1452 idle_info->callback = info->callback;
1453 idle_info->user_data = info->user_data;
1454 g_idle_add (idle_update_account_cb, idle_info);
1457 /* Notify about operation end. Note that the info could be
1458 freed before this idle happens, but the mail operation will
1460 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1464 g_object_unref (new_headers);
1466 g_object_unref (all_folders);
1467 g_object_unref (info->account);
1468 if (info->transport_account)
1469 g_object_unref (info->transport_account);
1470 g_free (info->account_name);
1471 g_free (info->retrieve_type);
1472 g_slice_free (UpdateAccountInfo, info);
1480 modest_mail_operation_update_account (ModestMailOperation *self,
1481 const gchar *account_name,
1482 UpdateAccountCallback callback,
1485 GThread *thread = NULL;
1486 UpdateAccountInfo *info = NULL;
1487 ModestMailOperationPrivate *priv = NULL;
1488 ModestAccountMgr *mgr = NULL;
1489 TnyStoreAccount *store_account = NULL;
1490 TnyTransportAccount *transport_account = NULL;
1492 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1493 g_return_val_if_fail (account_name, FALSE);
1495 /* Init mail operation. Set total and done to 0, and do not
1496 update them, this way the progress objects will know that
1497 we have no clue about the number of the objects */
1498 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1501 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1502 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1504 /* Get the store account */
1505 store_account = (TnyStoreAccount *)
1506 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1508 TNY_ACCOUNT_TYPE_STORE);
1510 if (!store_account) {
1511 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1512 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1513 "cannot get tny store account for %s\n", account_name);
1517 priv->account = g_object_ref (store_account);
1519 /* Get the transport account, we can not do it in the thread
1520 due to some problems with dbus */
1521 transport_account = (TnyTransportAccount *)
1522 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1524 if (!transport_account) {
1525 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1526 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1527 "cannot get tny transport account for %s\n", account_name);
1531 /* Create the helper object */
1532 info = g_slice_new (UpdateAccountInfo);
1533 info->mail_op = self;
1534 info->account = store_account;
1535 info->transport_account = transport_account;
1536 info->callback = callback;
1537 info->account_name = g_strdup (account_name);
1538 info->user_data = user_data;
1540 /* Get the message size limit */
1541 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1542 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1543 if (info->max_size == 0)
1544 info->max_size = G_MAXINT;
1546 info->max_size = info->max_size * KB;
1548 /* Get per-account retrieval type */
1549 mgr = modest_runtime_get_account_mgr ();
1550 info->retrieve_type = modest_account_mgr_get_retrieve_type (mgr, account_name);
1552 /* Get per-account message amount retrieval limit */
1553 info->retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, account_name);
1554 if (info->retrieve_limit == 0)
1555 info->retrieve_limit = G_MAXINT;
1557 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1559 /* Set account busy */
1560 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1562 modest_mail_operation_notify_start (self);
1563 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1569 g_object_unref (store_account);
1570 if (transport_account)
1571 g_object_unref (transport_account);
1572 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1574 callback (self, NULL, user_data);
1576 modest_mail_operation_notify_end (self);
1580 /* ******************************************************************* */
1581 /* ************************** STORE ACTIONS ************************* */
1582 /* ******************************************************************* */
1586 modest_mail_operation_create_folder (ModestMailOperation *self,
1587 TnyFolderStore *parent,
1590 ModestMailOperationPrivate *priv;
1591 TnyFolder *new_folder = NULL;
1593 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1594 g_return_val_if_fail (name, NULL);
1596 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1597 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1598 priv->account = (TNY_IS_ACCOUNT (parent)) ?
1599 g_object_ref (parent) :
1600 modest_tny_folder_get_account (TNY_FOLDER (parent));
1602 /* Check for already existing folder */
1603 if (modest_tny_folder_has_subfolder_with_name (parent, name)) {
1604 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1605 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1606 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1607 _CS("ckdg_ib_folder_already_exists"));
1611 if (TNY_IS_FOLDER (parent)) {
1612 /* Check folder rules */
1613 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1614 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1615 /* Set status failed and set an error */
1616 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1617 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1618 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1619 _("mail_in_ui_folder_create_error"));
1623 if (!strcmp (name, " ") || strchr (name, '/')) {
1624 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1625 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1626 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1627 _("mail_in_ui_folder_create_error"));
1631 /* Create the folder */
1632 modest_mail_operation_notify_start (self);
1633 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1634 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1636 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1639 /* Notify about operation end */
1640 modest_mail_operation_notify_end (self);
1646 modest_mail_operation_remove_folder (ModestMailOperation *self,
1648 gboolean remove_to_trash)
1650 TnyAccount *account;
1651 ModestMailOperationPrivate *priv;
1652 ModestTnyFolderRules rules;
1654 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1655 g_return_if_fail (TNY_IS_FOLDER (folder));
1657 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1659 /* Check folder rules */
1660 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1661 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1662 /* Set status failed and set an error */
1663 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1664 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1665 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1666 _("mail_in_ui_folder_delete_error"));
1670 /* Get the account */
1671 account = modest_tny_folder_get_account (folder);
1672 priv->account = g_object_ref(account);
1673 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1675 /* Delete folder or move to trash */
1676 if (remove_to_trash) {
1677 TnyFolder *trash_folder = NULL;
1678 trash_folder = modest_tny_account_get_special_folder (account,
1679 TNY_FOLDER_TYPE_TRASH);
1680 /* TODO: error_handling */
1682 modest_mail_operation_notify_start (self);
1683 modest_mail_operation_xfer_folder (self, folder,
1684 TNY_FOLDER_STORE (trash_folder),
1686 g_object_unref (trash_folder);
1689 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1691 modest_mail_operation_notify_start (self);
1692 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1693 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1696 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1699 g_object_unref (G_OBJECT (parent));
1701 g_object_unref (G_OBJECT (account));
1704 /* Notify about operation end */
1705 modest_mail_operation_notify_end (self);
1709 transfer_folder_status_cb (GObject *obj,
1713 ModestMailOperation *self;
1714 ModestMailOperationPrivate *priv;
1715 ModestMailOperationState *state;
1716 XFerMsgAsyncHelper *helper;
1718 g_return_if_fail (status != NULL);
1719 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1721 helper = (XFerMsgAsyncHelper *) user_data;
1722 g_return_if_fail (helper != NULL);
1724 self = helper->mail_op;
1725 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1727 priv->done = status->position;
1728 priv->total = status->of_total;
1730 state = modest_mail_operation_clone_state (self);
1732 /* This is not a GDK lock because we are a Tinymail callback
1733 * which is already GDK locked by Tinymail */
1735 /* no gdk_threads_enter (), CHECKED */
1737 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1739 /* no gdk_threads_leave (), CHECKED */
1741 g_slice_free (ModestMailOperationState, state);
1746 transfer_folder_cb (TnyFolder *folder,
1748 TnyFolderStore *into,
1749 TnyFolder *new_folder,
1753 XFerMsgAsyncHelper *helper;
1754 ModestMailOperation *self = NULL;
1755 ModestMailOperationPrivate *priv = NULL;
1757 helper = (XFerMsgAsyncHelper *) user_data;
1758 g_return_if_fail (helper != NULL);
1760 self = helper->mail_op;
1761 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1764 priv->error = g_error_copy (err);
1766 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1767 } else if (cancelled) {
1768 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1769 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1770 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1771 _("Transference of %s was cancelled."),
1772 tny_folder_get_name (folder));
1775 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1778 /* Notify about operation end */
1779 modest_mail_operation_notify_end (self);
1781 /* If user defined callback function was defined, call it */
1782 if (helper->user_callback) {
1784 /* This is not a GDK lock because we are a Tinymail callback
1785 * which is already GDK locked by Tinymail */
1787 /* no gdk_threads_enter (), CHECKED */
1788 helper->user_callback (self, helper->user_data);
1789 /* no gdk_threads_leave () , CHECKED */
1793 g_object_unref (helper->mail_op);
1794 g_slice_free (XFerMsgAsyncHelper, helper);
1799 * This function checks if the new name is a valid name for our local
1800 * folders account. The new name could not be the same than then name
1801 * of any of the mandatory local folders
1803 * We can not rely on tinymail because tinymail does not check the
1804 * name of the virtual folders that the account could have in the case
1805 * that we're doing a rename (because it directly calls Camel which
1806 * knows nothing about our virtual folders).
1808 * In the case of an actual copy/move (i.e. move/copy a folder between
1809 * accounts) tinymail uses the tny_folder_store_create_account which
1810 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1811 * checks the new name of the folder, so this call in that case
1812 * wouldn't be needed. *But* NOTE that if tinymail changes its
1813 * implementation (if folder transfers within the same account is no
1814 * longer implemented as a rename) this call will allow Modest to work
1817 * If the new name is not valid, this function will set the status to
1818 * failed and will set also an error in the mail operation
1821 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1822 TnyFolderStore *into,
1823 const gchar *new_name)
1825 if (TNY_IS_ACCOUNT (into) &&
1826 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1827 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1829 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1830 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1831 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1832 _("ckdg_ib_folder_already_exists"));
1839 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1841 TnyFolderStore *parent,
1842 gboolean delete_original,
1843 XferAsyncUserCallback user_callback,
1846 ModestMailOperationPrivate *priv = NULL;
1847 ModestTnyFolderRules parent_rules = 0, rules;
1848 XFerMsgAsyncHelper *helper = NULL;
1849 const gchar *folder_name = NULL;
1850 const gchar *error_msg;
1852 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1853 g_return_if_fail (TNY_IS_FOLDER (folder));
1854 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1856 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1857 folder_name = tny_folder_get_name (folder);
1859 /* Set the error msg */
1860 error_msg = _("mail_in_ui_folder_move_target_error");
1862 /* Get account and set it into mail_operation */
1863 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1864 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1865 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1867 /* Get folder rules */
1868 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1869 if (TNY_IS_FOLDER (parent))
1870 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1872 /* Apply operation constraints */
1873 if ((gpointer) parent == (gpointer) folder ||
1874 (!TNY_IS_FOLDER_STORE (parent)) ||
1875 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1878 } else if (TNY_IS_FOLDER (parent) &&
1879 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1883 } else if (TNY_IS_FOLDER (parent) &&
1884 TNY_IS_FOLDER_STORE (folder) &&
1885 modest_tny_folder_is_ancestor (TNY_FOLDER (parent),
1886 TNY_FOLDER_STORE (folder))) {
1887 /* Do not move a parent into a child */
1889 } else if (TNY_IS_FOLDER_STORE (parent) &&
1890 modest_tny_folder_has_subfolder_with_name (parent, folder_name)) {
1891 /* Check that the new folder name is not used by any
1894 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1895 /* Check that the new folder name is not used by any
1896 special local folder */
1899 /* Create the helper */
1900 helper = g_slice_new0 (XFerMsgAsyncHelper);
1901 helper->mail_op = g_object_ref (self);
1902 helper->dest_folder = NULL;
1903 helper->headers = NULL;
1904 helper->user_callback = user_callback;
1905 helper->user_data = user_data;
1907 /* Move/Copy folder */
1908 modest_mail_operation_notify_start (self);
1909 tny_folder_copy_async (folder,
1911 tny_folder_get_name (folder),
1914 transfer_folder_status_cb,
1920 /* Set status failed and set an error */
1921 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1922 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1923 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1926 /* Call the user callback if exists */
1928 user_callback (self, user_data);
1930 /* Notify the queue */
1931 modest_mail_operation_notify_end (self);
1935 modest_mail_operation_rename_folder (ModestMailOperation *self,
1939 ModestMailOperationPrivate *priv;
1940 ModestTnyFolderRules rules;
1941 XFerMsgAsyncHelper *helper;
1943 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1944 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1945 g_return_if_fail (name);
1947 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1949 /* Get account and set it into mail_operation */
1950 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1951 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1953 /* Check folder rules */
1954 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1955 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1956 /* Set status failed and set an error */
1957 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1958 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1959 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1960 _("FIXME: unable to rename"));
1962 /* Notify about operation end */
1963 modest_mail_operation_notify_end (self);
1964 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1965 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1966 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1967 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1968 _("FIXME: unable to rename"));
1969 /* Notify about operation end */
1970 modest_mail_operation_notify_end (self);
1972 TnyFolderStore *into;
1974 into = tny_folder_get_folder_store (folder);
1976 /* Check that the new folder name is not used by any
1977 special local folder */
1978 if (new_name_valid_if_local_account (priv, into, name)) {
1979 /* Create the helper */
1980 helper = g_slice_new0 (XFerMsgAsyncHelper);
1981 helper->mail_op = g_object_ref(self);
1982 helper->dest_folder = NULL;
1983 helper->headers = NULL;
1984 helper->user_callback = NULL;
1985 helper->user_data = NULL;
1987 /* Rename. Camel handles folder subscription/unsubscription */
1988 modest_mail_operation_notify_start (self);
1989 tny_folder_copy_async (folder, into, name, TRUE,
1991 transfer_folder_status_cb,
1994 modest_mail_operation_notify_end (self);
1996 g_object_unref (into);
2000 /* ******************************************************************* */
2001 /* ************************** MSG ACTIONS ************************* */
2002 /* ******************************************************************* */
2005 modest_mail_operation_get_msg (ModestMailOperation *self,
2007 GetMsgAsyncUserCallback user_callback,
2010 GetMsgAsyncHelper *helper = NULL;
2012 ModestMailOperationPrivate *priv;
2014 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2015 g_return_if_fail (TNY_IS_HEADER (header));
2017 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2018 folder = tny_header_get_folder (header);
2020 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2021 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2023 /* Get account and set it into mail_operation */
2024 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2026 /* Check for cached messages */
2027 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2028 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2030 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2032 helper = g_slice_new0 (GetMsgAsyncHelper);
2033 helper->mail_op = self;
2034 helper->user_callback = user_callback;
2035 helper->user_data = user_data;
2036 helper->header = g_object_ref (header);
2038 /* The callback's reference so that the mail op is not
2039 * finalized until the async operation is completed even if
2040 * the user canceled the request meanwhile.
2042 g_object_ref (G_OBJECT (helper->mail_op));
2044 modest_mail_operation_notify_start (self);
2045 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2047 g_object_unref (G_OBJECT (folder));
2051 get_msg_cb (TnyFolder *folder,
2057 GetMsgAsyncHelper *helper = NULL;
2058 ModestMailOperation *self = NULL;
2059 ModestMailOperationPrivate *priv = NULL;
2061 helper = (GetMsgAsyncHelper *) user_data;
2062 g_return_if_fail (helper != NULL);
2063 self = helper->mail_op;
2064 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2065 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2067 /* Check errors and cancel */
2069 priv->error = g_error_copy (error);
2070 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2071 } else if (cancelled) {
2072 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2073 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2074 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2075 _("Error trying to refresh the contents of %s"),
2076 tny_folder_get_name (folder));
2078 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2081 /* If user defined callback function was defined, call it even
2082 if the operation failed*/
2083 if (helper->user_callback) {
2084 /* This is not a GDK lock because we are a Tinymail callback
2085 * which is already GDK locked by Tinymail */
2087 /* no gdk_threads_enter (), CHECKED */
2088 helper->user_callback (self, helper->header, msg, helper->user_data);
2089 /* no gdk_threads_leave (), CHECKED */
2092 /* Notify about operation end */
2093 modest_mail_operation_notify_end (self);
2095 g_object_unref (helper->mail_op);
2096 g_object_unref (helper->header);
2097 g_slice_free (GetMsgAsyncHelper, helper);
2102 get_msg_status_cb (GObject *obj,
2106 GetMsgAsyncHelper *helper = NULL;
2107 ModestMailOperation *self;
2108 ModestMailOperationPrivate *priv;
2109 ModestMailOperationState *state;
2111 g_return_if_fail (status != NULL);
2112 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2114 helper = (GetMsgAsyncHelper *) user_data;
2115 g_return_if_fail (helper != NULL);
2117 self = helper->mail_op;
2118 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2123 state = modest_mail_operation_clone_state (self);
2124 state->bytes_done = status->position;
2125 state->bytes_total = status->of_total;
2127 /* This is not a GDK lock because we are a Tinymail callback
2128 * which is already GDK locked by Tinymail */
2130 /* no gdk_threads_enter (), CHECKED */
2131 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2132 /* no gdk_threads_leave (), CHECKED */
2134 g_slice_free (ModestMailOperationState, state);
2137 /****************************************************/
2139 ModestMailOperation *mail_op;
2141 GetMsgAsyncUserCallback user_callback;
2143 GDestroyNotify notify;
2147 GetMsgAsyncUserCallback user_callback;
2151 ModestMailOperation *mail_op;
2152 } NotifyGetMsgsInfo;
2156 * Used by get_msgs_full_thread to call the user_callback for each
2157 * message that has been read
2160 notify_get_msgs_full (gpointer data)
2162 NotifyGetMsgsInfo *info;
2164 info = (NotifyGetMsgsInfo *) data;
2166 /* This is a GDK lock because we are an idle callback and
2167 * because info->user_callback can contain Gtk+ code */
2169 gdk_threads_enter (); /* CHECKED */
2170 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2171 gdk_threads_leave (); /* CHECKED */
2173 g_slice_free (NotifyGetMsgsInfo, info);
2179 * Used by get_msgs_full_thread to free al the thread resources and to
2180 * call the destroy function for the passed user_data
2183 get_msgs_full_destroyer (gpointer data)
2185 GetFullMsgsInfo *info;
2187 info = (GetFullMsgsInfo *) data;
2191 /* This is a GDK lock because we are an idle callback and
2192 * because info->notify can contain Gtk+ code */
2194 gdk_threads_enter (); /* CHECKED */
2195 info->notify (info->user_data);
2196 gdk_threads_leave (); /* CHECKED */
2200 g_object_unref (info->headers);
2201 g_slice_free (GetFullMsgsInfo, info);
2207 get_msgs_full_thread (gpointer thr_user_data)
2209 GetFullMsgsInfo *info;
2210 ModestMailOperationPrivate *priv = NULL;
2211 TnyIterator *iter = NULL;
2213 info = (GetFullMsgsInfo *) thr_user_data;
2214 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2216 iter = tny_list_create_iterator (info->headers);
2217 while (!tny_iterator_is_done (iter)) {
2221 header = TNY_HEADER (tny_iterator_get_current (iter));
2222 folder = tny_header_get_folder (header);
2224 /* Check for cached messages */
2225 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2226 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2228 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2230 /* Get message from folder */
2233 /* The callback will call it per each header */
2234 msg = tny_folder_get_msg (folder, header, &(priv->error));
2237 ModestMailOperationState *state;
2242 /* notify progress */
2243 state = modest_mail_operation_clone_state (info->mail_op);
2244 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2245 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2246 pair, (GDestroyNotify) modest_pair_free);
2248 /* The callback is the responsible for
2249 freeing the message */
2250 if (info->user_callback) {
2251 NotifyGetMsgsInfo *info_notify;
2252 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2253 info_notify->user_callback = info->user_callback;
2254 info_notify->mail_op = info->mail_op;
2255 info_notify->header = g_object_ref (header);
2256 info_notify->msg = g_object_ref (msg);
2257 info_notify->user_data = info->user_data;
2258 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2259 notify_get_msgs_full,
2262 g_object_unref (msg);
2265 /* Set status failed and set an error */
2266 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2267 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2268 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2269 "Error trying to get a message. No folder found for header");
2273 g_object_unref (header);
2275 tny_iterator_next (iter);
2278 /* Set operation status */
2279 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2280 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2282 /* Notify about operation end */
2283 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2285 /* Free thread resources. Will be called after all previous idles */
2286 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2292 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2293 TnyList *header_list,
2294 GetMsgAsyncUserCallback user_callback,
2296 GDestroyNotify notify)
2298 TnyHeader *header = NULL;
2299 TnyFolder *folder = NULL;
2301 ModestMailOperationPrivate *priv = NULL;
2302 GetFullMsgsInfo *info = NULL;
2303 gboolean size_ok = TRUE;
2305 TnyIterator *iter = NULL;
2307 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2309 /* Init mail operation */
2310 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2311 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2312 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2314 priv->total = tny_list_get_length(header_list);
2316 /* Get account and set it into mail_operation */
2317 if (tny_list_get_length (header_list) >= 1) {
2318 iter = tny_list_create_iterator (header_list);
2319 header = TNY_HEADER (tny_iterator_get_current (iter));
2321 folder = tny_header_get_folder (header);
2323 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2324 g_object_unref (folder);
2326 g_object_unref (header);
2329 if (tny_list_get_length (header_list) == 1) {
2330 g_object_unref (iter);
2335 /* Get msg size limit */
2336 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2337 MODEST_CONF_MSG_SIZE_LIMIT,
2340 g_clear_error (&(priv->error));
2341 max_size = G_MAXINT;
2343 max_size = max_size * KB;
2346 /* Check message size limits. If there is only one message
2347 always retrieve it */
2349 while (!tny_iterator_is_done (iter) && size_ok) {
2350 header = TNY_HEADER (tny_iterator_get_current (iter));
2352 if (tny_header_get_message_size (header) >= max_size)
2354 g_object_unref (header);
2357 tny_iterator_next (iter);
2359 g_object_unref (iter);
2363 /* Create the info */
2364 info = g_slice_new0 (GetFullMsgsInfo);
2365 info->mail_op = self;
2366 info->user_callback = user_callback;
2367 info->user_data = user_data;
2368 info->headers = g_object_ref (header_list);
2369 info->notify = notify;
2371 modest_mail_operation_notify_start (self);
2372 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2374 /* Set status failed and set an error */
2375 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2376 /* FIXME: the error msg is different for pop */
2377 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2378 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2379 _("emev_ni_ui_imap_msg_size_exceed_error"));
2380 /* Remove from queue and free resources */
2381 modest_mail_operation_notify_end (self);
2389 modest_mail_operation_remove_msg (ModestMailOperation *self,
2391 gboolean remove_to_trash /*ignored*/)
2394 ModestMailOperationPrivate *priv;
2396 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2397 g_return_if_fail (TNY_IS_HEADER (header));
2399 if (remove_to_trash)
2400 g_warning ("remove to trash is not implemented");
2402 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2403 folder = tny_header_get_folder (header);
2405 /* Get account and set it into mail_operation */
2406 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2407 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2408 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2410 /* remove message from folder */
2411 tny_folder_remove_msg (folder, header, &(priv->error));
2413 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2414 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2416 modest_mail_operation_notify_start (self);
2418 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2419 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2420 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2421 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2422 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2423 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2426 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2427 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2433 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2435 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2438 g_object_unref (G_OBJECT (folder));
2440 /* Notify about operation end */
2441 modest_mail_operation_notify_end (self);
2445 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2447 gboolean remove_to_trash /*ignored*/)
2450 ModestMailOperationPrivate *priv;
2451 TnyIterator *iter = NULL;
2452 TnyHeader *header = NULL;
2454 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2455 g_return_if_fail (TNY_IS_LIST (headers));
2457 if (remove_to_trash)
2458 g_warning ("remove to trash is not implemented");
2460 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2462 /* Get folder from first header and sync it */
2463 iter = tny_list_create_iterator (headers);
2464 header = TNY_HEADER (tny_iterator_get_current (iter));
2465 folder = tny_header_get_folder (header);
2467 /* Get account and set it into mail_operation */
2468 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2469 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2470 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2472 /* remove message from folder */
2473 modest_mail_operation_notify_start (self);
2475 tny_folder_remove_msgs (folder, headers, &(priv->error));
2477 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2478 TNY_IS_CAMEL_POP_FOLDER (folder))
2479 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */
2482 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2488 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2490 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2493 g_object_unref (header);
2494 g_object_unref (iter);
2495 g_object_unref (G_OBJECT (folder));
2497 /* Notify about operation end */
2498 modest_mail_operation_notify_end (self);
2503 transfer_msgs_status_cb (GObject *obj,
2507 XFerMsgAsyncHelper *helper = NULL;
2508 ModestMailOperation *self;
2509 ModestMailOperationPrivate *priv;
2510 ModestMailOperationState *state;
2511 gboolean is_num_bytes;
2513 g_return_if_fail (status != NULL);
2514 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2516 helper = (XFerMsgAsyncHelper *) user_data;
2517 g_return_if_fail (helper != NULL);
2519 self = helper->mail_op;
2520 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2522 /* We know that tinymail sends us information about
2523 transferred bytes with this particular message */
2524 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2526 state = modest_mail_operation_clone_state (self);
2527 if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2528 /* We know that we're in a different message when the
2529 total number of bytes to transfer is different. Of
2530 course it could fail if we're transferring messages
2531 of the same size, but this is a workarround */
2532 if (status->of_total != helper->last_total_bytes) {
2534 helper->sum_total_bytes += helper->last_total_bytes;
2535 helper->last_total_bytes = status->of_total;
2537 state->bytes_done += status->position + helper->sum_total_bytes;
2538 state->bytes_total = helper->total_bytes;
2541 /* Notify the status change. Only notify about changes
2542 referred to bytes */
2543 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2547 g_slice_free (ModestMailOperationState, state);
2552 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2554 XFerMsgAsyncHelper *helper;
2555 ModestMailOperation *self;
2556 ModestMailOperationPrivate *priv;
2557 TnyIterator *iter = NULL;
2558 TnyHeader *header = NULL;
2560 helper = (XFerMsgAsyncHelper *) user_data;
2561 self = helper->mail_op;
2563 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2566 priv->error = g_error_copy (err);
2568 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2569 } else if (cancelled) {
2570 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2571 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2572 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2573 _("Error trying to refresh the contents of %s"),
2574 tny_folder_get_name (folder));
2577 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2579 /* Update folder counts */
2580 tny_folder_poke_status (folder);
2581 tny_folder_poke_status (helper->dest_folder);
2585 /* Mark headers as deleted and seen */
2586 if ((helper->delete) &&
2587 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2588 iter = tny_list_create_iterator (helper->headers);
2589 while (!tny_iterator_is_done (iter)) {
2590 header = TNY_HEADER (tny_iterator_get_current (iter));
2591 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
2592 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
2593 g_object_unref (header);
2595 tny_iterator_next (iter);
2601 /* Notify about operation end */
2602 modest_mail_operation_notify_end (self);
2604 /* If user defined callback function was defined, call it */
2605 if (helper->user_callback) {
2606 /* This is not a GDK lock because we are a Tinymail callback and
2607 * Tinymail already acquires the Gdk lock */
2609 /* no gdk_threads_enter (), CHECKED */
2610 helper->user_callback (self, helper->user_data);
2611 /* no gdk_threads_leave (), CHECKED */
2615 if (helper->headers)
2616 g_object_unref (helper->headers);
2617 if (helper->dest_folder)
2618 g_object_unref (helper->dest_folder);
2619 if (helper->mail_op)
2620 g_object_unref (helper->mail_op);
2622 g_object_unref (folder);
2624 g_object_unref (iter);
2625 g_slice_free (XFerMsgAsyncHelper, helper);
2629 compute_message_list_size (TnyList *headers)
2634 iter = tny_list_create_iterator (headers);
2635 while (!tny_iterator_is_done (iter)) {
2636 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2637 size += tny_header_get_message_size (header);
2638 g_object_unref (header);
2639 tny_iterator_next (iter);
2641 g_object_unref (iter);
2647 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2650 gboolean delete_original,
2651 XferAsyncUserCallback user_callback,
2654 ModestMailOperationPrivate *priv = NULL;
2655 TnyIterator *iter = NULL;
2656 TnyFolder *src_folder = NULL;
2657 XFerMsgAsyncHelper *helper = NULL;
2658 TnyHeader *header = NULL;
2659 ModestTnyFolderRules rules = 0;
2661 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2662 g_return_if_fail (TNY_IS_LIST (headers));
2663 g_return_if_fail (TNY_IS_FOLDER (folder));
2665 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2666 priv->total = tny_list_get_length (headers);
2668 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2669 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2671 /* Apply folder rules */
2672 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2673 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2674 /* Set status failed and set an error */
2675 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2676 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2677 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2678 _CS("ckct_ib_unable_to_paste_here"));
2679 /* Notify the queue */
2680 modest_mail_operation_notify_end (self);
2684 /* Get source folder */
2685 iter = tny_list_create_iterator (headers);
2686 header = TNY_HEADER (tny_iterator_get_current (iter));
2688 src_folder = tny_header_get_folder (header);
2689 g_object_unref (header);
2692 g_object_unref (iter);
2694 /* Check folder source and destination */
2695 if (src_folder == folder) {
2696 /* Set status failed and set an error */
2697 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2698 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2699 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2700 _("mcen_ib_unable_to_copy_samefolder"));
2702 /* Notify the queue */
2703 modest_mail_operation_notify_end (self);
2706 g_object_unref (src_folder);
2710 /* Create the helper */
2711 helper = g_slice_new0 (XFerMsgAsyncHelper);
2712 helper->mail_op = g_object_ref(self);
2713 helper->dest_folder = g_object_ref(folder);
2714 helper->headers = g_object_ref(headers);
2715 helper->user_callback = user_callback;
2716 helper->user_data = user_data;
2717 helper->delete = delete_original;
2718 helper->last_total_bytes = 0;
2719 helper->sum_total_bytes = 0;
2720 helper->total_bytes = compute_message_list_size (headers);
2722 /* Get account and set it into mail_operation */
2723 priv->account = modest_tny_folder_get_account (src_folder);
2725 /* Transfer messages */
2726 modest_mail_operation_notify_start (self);
2727 tny_folder_transfer_msgs_async (src_folder,
2732 transfer_msgs_status_cb,
2738 on_refresh_folder (TnyFolder *folder,
2743 RefreshAsyncHelper *helper = NULL;
2744 ModestMailOperation *self = NULL;
2745 ModestMailOperationPrivate *priv = NULL;
2747 helper = (RefreshAsyncHelper *) user_data;
2748 self = helper->mail_op;
2749 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2751 g_return_if_fail(priv!=NULL);
2754 priv->error = g_error_copy (error);
2755 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2760 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2761 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2762 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2763 _("Error trying to refresh the contents of %s"),
2764 tny_folder_get_name (folder));
2768 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2771 /* Call user defined callback, if it exists */
2772 if (helper->user_callback) {
2774 /* This is not a GDK lock because we are a Tinymail callback and
2775 * Tinymail already acquires the Gdk lock */
2776 helper->user_callback (self, folder, helper->user_data);
2780 g_slice_free (RefreshAsyncHelper, helper);
2782 /* Notify about operation end */
2783 modest_mail_operation_notify_end (self);
2784 g_object_unref(self);
2788 on_refresh_folder_status_update (GObject *obj,
2792 RefreshAsyncHelper *helper = NULL;
2793 ModestMailOperation *self = NULL;
2794 ModestMailOperationPrivate *priv = NULL;
2795 ModestMailOperationState *state;
2797 g_return_if_fail (user_data != NULL);
2798 g_return_if_fail (status != NULL);
2799 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2801 helper = (RefreshAsyncHelper *) user_data;
2802 self = helper->mail_op;
2803 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2805 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2807 priv->done = status->position;
2808 priv->total = status->of_total;
2810 state = modest_mail_operation_clone_state (self);
2812 /* This is not a GDK lock because we are a Tinymail callback and
2813 * Tinymail already acquires the Gdk lock */
2814 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2816 g_slice_free (ModestMailOperationState, state);
2820 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2822 RefreshAsyncUserCallback user_callback,
2825 ModestMailOperationPrivate *priv = NULL;
2826 RefreshAsyncHelper *helper = NULL;
2828 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2830 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2832 /* Get account and set it into mail_operation */
2833 priv->account = modest_tny_folder_get_account (folder);
2834 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2836 /* Create the helper */
2837 helper = g_slice_new0 (RefreshAsyncHelper);
2838 helper->mail_op = g_object_ref(self);
2839 helper->user_callback = user_callback;
2840 helper->user_data = user_data;
2842 /* Refresh the folder. TODO: tinymail could issue a status
2843 updates before the callback call then this could happen. We
2844 must review the design */
2845 modest_mail_operation_notify_start (self);
2846 tny_folder_refresh_async (folder,
2848 on_refresh_folder_status_update,
2854 modest_mail_operation_notify_start (ModestMailOperation *self)
2856 ModestMailOperationPrivate *priv = NULL;
2858 g_return_if_fail (self);
2860 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2862 /* Ensure that all the fields are filled correctly */
2863 g_return_if_fail (priv->account != NULL);
2864 g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2866 /* Notify the observers about the mail operation. We do not
2867 wrapp this emission because we assume that this function is
2868 always called from within the main lock */
2869 g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2874 * It's used by the mail operation queue to notify the observers
2875 * attached to that signal that the operation finished. We need to use
2876 * that because tinymail does not give us the progress of a given
2877 * operation when it finishes (it directly calls the operation
2881 modest_mail_operation_notify_end (ModestMailOperation *self)
2883 ModestMailOperationPrivate *priv = NULL;
2885 g_return_if_fail (self);
2887 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2889 /* Notify the observers about the mail operation end. We do
2890 not wrapp this emission because we assume that this
2891 function is always called from within the main lock */
2892 g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2894 /* Remove the error user data */
2895 if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2896 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2900 modest_mail_operation_get_account (ModestMailOperation *self)
2902 ModestMailOperationPrivate *priv = NULL;
2904 g_return_val_if_fail (self, NULL);
2906 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2908 return (priv->account) ? g_object_ref (priv->account) : NULL;