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_flag (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),
754 TNY_FOLDER_TYPE_DRAFTS);
755 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
756 TNY_FOLDER_TYPE_OUTBOX);
757 if (info->draft_msg != NULL) {
758 TnyFolder *folder = NULL;
759 TnyFolder *src_folder = NULL;
760 TnyFolderType folder_type;
761 folder = tny_msg_get_folder (info->draft_msg);
762 if (folder == NULL) goto end;
763 folder_type = modest_tny_folder_guess_folder_type (folder);
765 if (folder_type == TNY_FOLDER_TYPE_INVALID)
766 g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
768 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
769 src_folder = outbox_folder;
771 src_folder = draft_folder;
773 /* Note: This can fail (with a warning) if the message is not really already in a folder,
774 * because this function requires it to have a UID. */
775 header = tny_msg_get_header (info->draft_msg);
776 tny_folder_remove_msg (src_folder, header, NULL);
778 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
779 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
781 g_object_unref (header);
782 g_object_unref (folder);
789 g_object_unref (info->draft_msg);
791 g_object_unref (draft_folder);
793 g_object_unref (outbox_folder);
794 if (info->transport_account)
795 g_object_unref (info->transport_account);
796 g_slice_free (SendNewMailInfo, info);
797 modest_mail_operation_notify_end (self);
801 modest_mail_operation_send_new_mail (ModestMailOperation *self,
802 TnyTransportAccount *transport_account,
804 const gchar *from, const gchar *to,
805 const gchar *cc, const gchar *bcc,
806 const gchar *subject, const gchar *plain_body,
807 const gchar *html_body,
808 const GList *attachments_list,
809 const GList *images_list,
810 TnyHeaderFlags priority_flags)
812 ModestMailOperationPrivate *priv = NULL;
813 SendNewMailInfo *info;
815 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
816 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
818 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
819 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
820 priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
822 /* Check parametters */
824 /* Set status failed and set an error */
825 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
826 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
827 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
828 _("Error trying to send a mail. You need to set at least one recipient"));
831 info = g_slice_new0 (SendNewMailInfo);
832 info->transport_account = transport_account;
833 if (transport_account)
834 g_object_ref (transport_account);
835 info->draft_msg = draft_msg;
837 g_object_ref (draft_msg);
840 modest_mail_operation_notify_start (self);
841 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
842 attachments_list, images_list, priority_flags,
843 modest_mail_operation_send_new_mail_cb, info);
849 TnyTransportAccount *transport_account;
851 SaveToDraftstCallback callback;
856 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
860 TnyFolder *src_folder = NULL;
861 TnyFolder *drafts = NULL;
862 TnyHeader *header = NULL;
863 ModestMailOperationPrivate *priv = NULL;
864 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
866 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
869 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
870 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
871 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
872 "modest: failed to create a new msg\n");
876 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
877 TNY_FOLDER_TYPE_DRAFTS);
879 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
880 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
881 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
882 "modest: failed to create a new msg\n");
887 tny_folder_add_msg (drafts, msg, &(priv->error));
889 if ((!priv->error) && (info->draft_msg != NULL)) {
890 header = tny_msg_get_header (info->draft_msg);
891 src_folder = tny_header_get_folder (header);
893 /* Remove the old draft */
894 tny_folder_remove_msg (src_folder, header, NULL);
896 /* Synchronize to expunge and to update the msg counts */
897 tny_folder_sync_async (drafts, TRUE, NULL, NULL, NULL);
898 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
900 g_object_unref (header);
904 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
906 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
909 /* Call the user callback */
911 info->callback (self, msg, info->user_data);
914 g_object_unref (G_OBJECT(drafts));
916 g_object_unref (G_OBJECT(src_folder));
918 g_object_unref (G_OBJECT (info->draft_msg));
919 if (info->transport_account)
920 g_object_unref (G_OBJECT(info->transport_account));
921 g_slice_free (SaveToDraftsInfo, info);
923 modest_mail_operation_notify_end (self);
927 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
928 TnyTransportAccount *transport_account,
930 const gchar *from, const gchar *to,
931 const gchar *cc, const gchar *bcc,
932 const gchar *subject, const gchar *plain_body,
933 const gchar *html_body,
934 const GList *attachments_list,
935 const GList *images_list,
936 TnyHeaderFlags priority_flags,
937 SaveToDraftstCallback callback,
940 ModestMailOperationPrivate *priv = NULL;
941 SaveToDraftsInfo *info = NULL;
943 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
944 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
946 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
948 /* Get account and set it into mail_operation */
949 priv->account = g_object_ref (transport_account);
950 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
952 info = g_slice_new0 (SaveToDraftsInfo);
953 info->transport_account = g_object_ref (transport_account);
954 info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
955 info->callback = callback;
956 info->user_data = user_data;
958 modest_mail_operation_notify_start (self);
959 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
960 attachments_list, images_list, priority_flags,
961 modest_mail_operation_save_to_drafts_cb, info);
966 ModestMailOperation *mail_op;
967 TnyStoreAccount *account;
968 TnyTransportAccount *transport_account;
971 gchar *retrieve_type;
973 UpdateAccountCallback callback;
975 TnyList *new_headers;
980 ModestMailOperation *mail_op;
981 TnyMimePart *mime_part;
983 GetMimePartSizeCallback callback;
985 } GetMimePartSizeInfo;
987 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
988 /* We use this folder observer to track the headers that have been
989 * added to a folder */
992 TnyList *new_headers;
993 } InternalFolderObserver;
997 } InternalFolderObserverClass;
999 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1001 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1002 internal_folder_observer,
1004 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1008 foreach_add_item (gpointer header, gpointer user_data)
1010 tny_list_prepend (TNY_LIST (user_data),
1011 g_object_ref (G_OBJECT (header)));
1014 /* This is the method that looks for new messages in a folder */
1016 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1018 InternalFolderObserver *derived = (InternalFolderObserver *)self;
1020 TnyFolderChangeChanged changed;
1022 changed = tny_folder_change_get_changed (change);
1024 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1027 /* Get added headers */
1028 list = tny_simple_list_new ();
1029 tny_folder_change_get_added_headers (change, list);
1031 /* Add them to the folder observer */
1032 tny_list_foreach (list, foreach_add_item,
1033 derived->new_headers);
1035 g_object_unref (G_OBJECT (list));
1040 internal_folder_observer_init (InternalFolderObserver *self)
1042 self->new_headers = tny_simple_list_new ();
1045 internal_folder_observer_finalize (GObject *object)
1047 InternalFolderObserver *self;
1049 self = (InternalFolderObserver *) object;
1050 g_object_unref (self->new_headers);
1052 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1055 tny_folder_observer_init (TnyFolderObserverIface *iface)
1057 iface->update_func = internal_folder_observer_update;
1060 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1062 GObjectClass *object_class;
1064 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1065 object_class = (GObjectClass*) klass;
1066 object_class->finalize = internal_folder_observer_finalize;
1072 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1075 TnyList *folders = tny_simple_list_new ();
1077 tny_folder_store_get_folders (store, folders, query, NULL);
1078 iter = tny_list_create_iterator (folders);
1080 while (!tny_iterator_is_done (iter)) {
1082 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1084 tny_list_prepend (all_folders, G_OBJECT (folder));
1085 recurse_folders (folder, query, all_folders);
1086 g_object_unref (G_OBJECT (folder));
1089 tny_iterator_next (iter);
1091 g_object_unref (G_OBJECT (iter));
1092 g_object_unref (G_OBJECT (folders));
1096 * Issues the "progress-changed" signal. The timer won't be removed,
1097 * so you must call g_source_remove to stop the signal emission
1100 idle_notify_progress (gpointer data)
1102 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1103 ModestMailOperationState *state;
1105 state = modest_mail_operation_clone_state (mail_op);
1107 /* This is a GDK lock because we are an idle callback and
1108 * the handlers of this signal can contain Gtk+ code */
1110 gdk_threads_enter (); /* CHECKED */
1111 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1112 gdk_threads_leave (); /* CHECKED */
1114 g_slice_free (ModestMailOperationState, state);
1120 * Issues the "progress-changed" signal and removes the timer. It uses
1121 * a lock to ensure that the progress information of the mail
1122 * operation is not modified while there are notifications pending
1125 idle_notify_progress_once (gpointer data)
1129 pair = (ModestPair *) data;
1131 /* This is a GDK lock because we are an idle callback and
1132 * the handlers of this signal can contain Gtk+ code */
1134 gdk_threads_enter (); /* CHECKED */
1135 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1136 gdk_threads_leave (); /* CHECKED */
1138 /* Free the state and the reference to the mail operation */
1139 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1140 g_object_unref (pair->first);
1146 * Used to notify the queue from the main
1147 * loop. We call it inside an idle call to achieve that
1150 idle_notify_queue (gpointer data)
1152 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1154 /* Do not need to block, the notify end will do it for us */
1155 modest_mail_operation_notify_end (mail_op);
1156 g_object_unref (mail_op);
1162 compare_headers_by_date (gconstpointer a,
1165 TnyHeader **header1, **header2;
1166 time_t sent1, sent2;
1168 header1 = (TnyHeader **) a;
1169 header2 = (TnyHeader **) b;
1171 sent1 = tny_header_get_date_sent (*header1);
1172 sent2 = tny_header_get_date_sent (*header2);
1174 /* We want the most recent ones (greater time_t) at the
1183 set_last_updated_idle (gpointer data)
1186 /* This is a GDK lock because we are an idle callback and
1187 * modest_account_mgr_set_last_updated can issue Gtk+ code */
1189 gdk_threads_enter (); /* CHECKED - please recheck */
1191 /* It does not matter if the time is not exactly the same than
1192 the time when this idle was called, it's just an
1193 approximation and it won't be very different */
1195 modest_account_mgr_set_last_updated (modest_runtime_get_account_mgr (),
1199 gdk_threads_leave (); /* CHECKED - please recheck */
1205 idle_update_account_cb (gpointer data)
1207 UpdateAccountInfo *idle_info;
1209 idle_info = (UpdateAccountInfo *) data;
1211 /* This is a GDK lock because we are an idle callback and
1212 * idle_info->callback can contain Gtk+ code */
1214 gdk_threads_enter (); /* CHECKED */
1215 idle_info->callback (idle_info->mail_op,
1216 idle_info->new_headers,
1217 idle_info->user_data);
1218 gdk_threads_leave (); /* CHECKED */
1221 g_object_unref (idle_info->mail_op);
1222 if (idle_info->new_headers)
1223 g_object_unref (idle_info->new_headers);
1230 get_all_folders_from_account (TnyStoreAccount *account,
1233 TnyList *all_folders = NULL;
1234 TnyIterator *iter = NULL;
1235 TnyFolderStoreQuery *query = NULL;
1237 all_folders = tny_simple_list_new ();
1238 query = tny_folder_store_query_new ();
1239 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1240 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
1247 g_object_unref (all_folders);
1251 iter = tny_list_create_iterator (all_folders);
1252 while (!tny_iterator_is_done (iter)) {
1253 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1255 recurse_folders (folder, query, all_folders);
1256 g_object_unref (folder);
1258 tny_iterator_next (iter);
1260 g_object_unref (G_OBJECT (iter));
1267 update_account_thread (gpointer thr_user_data)
1269 static gboolean first_time = TRUE;
1270 UpdateAccountInfo *info = NULL;
1271 TnyList *all_folders = NULL, *new_headers = NULL;
1272 GPtrArray *new_headers_array = NULL;
1273 TnyIterator *iter = NULL;
1274 ModestMailOperationPrivate *priv = NULL;
1275 ModestTnySendQueue *send_queue = NULL;
1276 gint i = 0, timeout = 0;
1278 info = (UpdateAccountInfo *) thr_user_data;
1279 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1281 /* Get account and set it into mail_operation */
1282 priv->account = g_object_ref (info->account);
1284 /* Get all the folders. We can do it synchronously because
1285 we're already running in a different thread than the UI */
1286 all_folders = get_all_folders_from_account (info->account, &(priv->error));
1288 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1292 /* Update status and notify. We need to call the notification
1293 with a source function in order to call it from the main
1294 loop. We need that in order not to get into trouble with
1295 Gtk+. We use a timeout in order to provide more status
1296 information, because the sync tinymail call does not
1297 provide it for the moment */
1298 timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1300 new_headers_array = g_ptr_array_new ();
1301 iter = tny_list_create_iterator (all_folders);
1303 while (!tny_iterator_is_done (iter) && !priv->error &&
1304 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1306 TnyFolderType folder_type;
1307 TnyFolder *folder = NULL;
1309 folder = TNY_FOLDER (tny_iterator_get_current (iter));
1310 folder_type = tny_folder_get_folder_type (folder);
1312 /* Refresh it only if it's the INBOX */
1313 if (folder_type == TNY_FOLDER_TYPE_INBOX) {
1314 InternalFolderObserver *observer = NULL;
1315 TnyIterator *new_headers_iter = NULL;
1317 /* Refresh the folder. Our observer receives
1318 * the new emails during folder refreshes, so
1319 * we can use observer->new_headers
1321 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1322 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1324 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1326 new_headers_iter = tny_list_create_iterator (observer->new_headers);
1327 while (!tny_iterator_is_done (new_headers_iter)) {
1328 TnyHeader *header = NULL;
1330 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1331 /* Apply per-message size limits */
1332 if (tny_header_get_message_size (header) < info->max_size)
1333 g_ptr_array_add (new_headers_array, g_object_ref (header));
1335 g_object_unref (header);
1336 tny_iterator_next (new_headers_iter);
1338 g_object_unref (new_headers_iter);
1340 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1341 g_object_unref (observer);
1343 /* We no not need to do it the first time,
1344 because it's automatically done by the tree
1346 if (G_LIKELY (!first_time))
1347 tny_folder_poke_status (folder);
1349 g_object_unref (folder);
1351 tny_iterator_next (iter);
1353 g_object_unref (G_OBJECT (iter));
1354 g_source_remove (timeout);
1356 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1357 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1358 new_headers_array->len > 0) {
1362 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1364 /* TODO: Ask the user, instead of just failing,
1365 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1366 * all' and 'Newest only' buttons. */
1367 if (new_headers_array->len > info->retrieve_limit) {
1371 /* Should be get only the headers or the message as well? */
1372 if (g_ascii_strcasecmp (info->retrieve_type,
1373 MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {
1375 priv->total = MIN (new_headers_array->len, info->retrieve_limit);
1376 while (msg_num < priv->total) {
1378 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1379 TnyFolder *folder = tny_header_get_folder (header);
1380 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1381 ModestMailOperationState *state;
1385 /* We can not just use the mail operation because the
1386 values of done and total could change before the
1388 state = modest_mail_operation_clone_state (info->mail_op);
1389 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1390 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1391 pair, (GDestroyNotify) modest_pair_free);
1393 g_object_unref (msg);
1394 g_object_unref (folder);
1401 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1404 /* Copy the headers to a list and free the array */
1405 new_headers = tny_simple_list_new ();
1406 for (i=0; i < new_headers_array->len; i++) {
1407 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1408 tny_list_append (new_headers, G_OBJECT (header));
1410 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1411 g_ptr_array_free (new_headers_array, FALSE);
1414 /* Perform send (if operation was not cancelled) */
1417 if (priv->account != NULL)
1418 g_object_unref (priv->account);
1420 if (info->transport_account) {
1421 priv->account = g_object_ref (info->transport_account);
1423 send_queue = modest_runtime_get_send_queue (info->transport_account);
1425 modest_tny_send_queue_try_to_send (send_queue);
1427 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1428 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1429 "cannot create a send queue for %s\n",
1430 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1431 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1435 /* Check if the operation was a success */
1437 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1439 /* Update the last updated key */
1440 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1441 set_last_updated_idle,
1442 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1443 (GDestroyNotify) g_free);
1447 /* Set the account back to not busy */
1448 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
1449 info->account_name, FALSE);
1451 if (info->callback) {
1452 UpdateAccountInfo *idle_info;
1454 /* This thread is not in the main lock */
1455 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1456 idle_info->mail_op = g_object_ref (info->mail_op);
1457 idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL;
1458 idle_info->callback = info->callback;
1459 idle_info->user_data = info->user_data;
1460 g_idle_add (idle_update_account_cb, idle_info);
1463 /* Notify about operation end. Note that the info could be
1464 freed before this idle happens, but the mail operation will
1466 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1470 g_object_unref (new_headers);
1472 g_object_unref (all_folders);
1473 g_object_unref (info->account);
1474 if (info->transport_account)
1475 g_object_unref (info->transport_account);
1476 g_free (info->account_name);
1477 g_free (info->retrieve_type);
1478 g_slice_free (UpdateAccountInfo, info);
1486 modest_mail_operation_update_account (ModestMailOperation *self,
1487 const gchar *account_name,
1488 UpdateAccountCallback callback,
1491 GThread *thread = NULL;
1492 UpdateAccountInfo *info = NULL;
1493 ModestMailOperationPrivate *priv = NULL;
1494 ModestAccountMgr *mgr = NULL;
1495 TnyStoreAccount *store_account = NULL;
1496 TnyTransportAccount *transport_account = NULL;
1498 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1499 g_return_val_if_fail (account_name, FALSE);
1501 /* Init mail operation. Set total and done to 0, and do not
1502 update them, this way the progress objects will know that
1503 we have no clue about the number of the objects */
1504 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1507 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1508 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1510 /* Get the store account */
1511 store_account = (TnyStoreAccount *)
1512 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1514 TNY_ACCOUNT_TYPE_STORE);
1516 if (!store_account) {
1517 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1518 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1519 "cannot get tny store account for %s\n", account_name);
1523 priv->account = g_object_ref (store_account);
1525 /* Get the transport account, we can not do it in the thread
1526 due to some problems with dbus */
1527 transport_account = (TnyTransportAccount *)
1528 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1530 if (!transport_account) {
1531 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1532 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1533 "cannot get tny transport account for %s\n", account_name);
1537 /* Create the helper object */
1538 info = g_slice_new (UpdateAccountInfo);
1539 info->mail_op = self;
1540 info->account = store_account;
1541 info->transport_account = transport_account;
1542 info->callback = callback;
1543 info->account_name = g_strdup (account_name);
1544 info->user_data = user_data;
1546 /* Get the message size limit */
1547 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1548 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1549 if (info->max_size == 0)
1550 info->max_size = G_MAXINT;
1552 info->max_size = info->max_size * KB;
1554 /* Get per-account retrieval type */
1555 mgr = modest_runtime_get_account_mgr ();
1556 info->retrieve_type = modest_account_mgr_get_retrieve_type (mgr, account_name);
1558 /* Get per-account message amount retrieval limit */
1559 info->retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, account_name);
1560 if (info->retrieve_limit == 0)
1561 info->retrieve_limit = G_MAXINT;
1563 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1565 /* Set account busy */
1566 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1568 modest_mail_operation_notify_start (self);
1569 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1575 g_object_unref (store_account);
1576 if (transport_account)
1577 g_object_unref (transport_account);
1578 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1580 callback (self, NULL, user_data);
1582 modest_mail_operation_notify_end (self);
1586 /* ******************************************************************* */
1587 /* ************************** STORE ACTIONS ************************* */
1588 /* ******************************************************************* */
1592 modest_mail_operation_create_folder (ModestMailOperation *self,
1593 TnyFolderStore *parent,
1596 ModestMailOperationPrivate *priv;
1597 TnyFolder *new_folder = NULL;
1599 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1600 g_return_val_if_fail (name, NULL);
1602 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1603 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1604 priv->account = (TNY_IS_ACCOUNT (parent)) ?
1605 g_object_ref (parent) :
1606 modest_tny_folder_get_account (TNY_FOLDER (parent));
1608 /* Check for already existing folder */
1609 if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1610 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1611 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1612 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1613 _CS("ckdg_ib_folder_already_exists"));
1617 if (TNY_IS_FOLDER (parent)) {
1618 /* Check folder rules */
1619 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1620 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1621 /* Set status failed and set an error */
1622 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1623 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1624 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1625 _("mail_in_ui_folder_create_error"));
1629 if (!strcmp (name, " ") || strchr (name, '/')) {
1630 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1631 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1632 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1633 _("mail_in_ui_folder_create_error"));
1637 /* Create the folder */
1638 modest_mail_operation_notify_start (self);
1639 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1640 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1642 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1645 /* Notify about operation end */
1646 modest_mail_operation_notify_end (self);
1652 modest_mail_operation_remove_folder (ModestMailOperation *self,
1654 gboolean remove_to_trash)
1656 TnyAccount *account;
1657 ModestMailOperationPrivate *priv;
1658 ModestTnyFolderRules rules;
1660 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1661 g_return_if_fail (TNY_IS_FOLDER (folder));
1663 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1665 /* Check folder rules */
1666 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1667 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1668 /* Set status failed and set an error */
1669 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1670 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1671 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1672 _("mail_in_ui_folder_delete_error"));
1676 /* Get the account */
1677 account = modest_tny_folder_get_account (folder);
1678 priv->account = g_object_ref(account);
1679 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1681 /* Delete folder or move to trash */
1682 if (remove_to_trash) {
1683 TnyFolder *trash_folder = NULL;
1684 trash_folder = modest_tny_account_get_special_folder (account,
1685 TNY_FOLDER_TYPE_TRASH);
1686 /* TODO: error_handling */
1688 modest_mail_operation_notify_start (self);
1689 modest_mail_operation_xfer_folder (self, folder,
1690 TNY_FOLDER_STORE (trash_folder),
1692 g_object_unref (trash_folder);
1695 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1697 modest_mail_operation_notify_start (self);
1698 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1699 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1702 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1705 g_object_unref (G_OBJECT (parent));
1707 g_object_unref (G_OBJECT (account));
1710 /* Notify about operation end */
1711 modest_mail_operation_notify_end (self);
1715 transfer_folder_status_cb (GObject *obj,
1719 ModestMailOperation *self;
1720 ModestMailOperationPrivate *priv;
1721 ModestMailOperationState *state;
1722 XFerMsgAsyncHelper *helper;
1724 g_return_if_fail (status != NULL);
1725 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1727 helper = (XFerMsgAsyncHelper *) user_data;
1728 g_return_if_fail (helper != NULL);
1730 self = helper->mail_op;
1731 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1733 priv->done = status->position;
1734 priv->total = status->of_total;
1736 state = modest_mail_operation_clone_state (self);
1738 /* This is not a GDK lock because we are a Tinymail callback
1739 * which is already GDK locked by Tinymail */
1741 /* no gdk_threads_enter (), CHECKED */
1743 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1745 /* no gdk_threads_leave (), CHECKED */
1747 g_slice_free (ModestMailOperationState, state);
1752 transfer_folder_cb (TnyFolder *folder,
1754 TnyFolderStore *into,
1755 TnyFolder *new_folder,
1759 XFerMsgAsyncHelper *helper;
1760 ModestMailOperation *self = NULL;
1761 ModestMailOperationPrivate *priv = NULL;
1763 helper = (XFerMsgAsyncHelper *) user_data;
1764 g_return_if_fail (helper != NULL);
1766 self = helper->mail_op;
1767 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1770 priv->error = g_error_copy (err);
1772 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1773 } else if (cancelled) {
1774 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1775 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1776 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1777 _("Transference of %s was cancelled."),
1778 tny_folder_get_name (folder));
1781 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1784 /* Notify about operation end */
1785 modest_mail_operation_notify_end (self);
1787 /* If user defined callback function was defined, call it */
1788 if (helper->user_callback) {
1790 /* This is not a GDK lock because we are a Tinymail callback
1791 * which is already GDK locked by Tinymail */
1793 /* no gdk_threads_enter (), CHECKED */
1794 helper->user_callback (self, helper->user_data);
1795 /* no gdk_threads_leave () , CHECKED */
1799 g_object_unref (helper->mail_op);
1800 g_slice_free (XFerMsgAsyncHelper, helper);
1805 * This function checks if the new name is a valid name for our local
1806 * folders account. The new name could not be the same than then name
1807 * of any of the mandatory local folders
1809 * We can not rely on tinymail because tinymail does not check the
1810 * name of the virtual folders that the account could have in the case
1811 * that we're doing a rename (because it directly calls Camel which
1812 * knows nothing about our virtual folders).
1814 * In the case of an actual copy/move (i.e. move/copy a folder between
1815 * accounts) tinymail uses the tny_folder_store_create_account which
1816 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1817 * checks the new name of the folder, so this call in that case
1818 * wouldn't be needed. *But* NOTE that if tinymail changes its
1819 * implementation (if folder transfers within the same account is no
1820 * longer implemented as a rename) this call will allow Modest to work
1823 * If the new name is not valid, this function will set the status to
1824 * failed and will set also an error in the mail operation
1827 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1828 TnyFolderStore *into,
1829 const gchar *new_name)
1831 if (TNY_IS_ACCOUNT (into) &&
1832 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1833 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1835 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1836 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1837 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1838 _CS("ckdg_ib_folder_already_exists"));
1845 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1847 TnyFolderStore *parent,
1848 gboolean delete_original,
1849 XferAsyncUserCallback user_callback,
1852 ModestMailOperationPrivate *priv = NULL;
1853 ModestTnyFolderRules parent_rules = 0, rules;
1854 XFerMsgAsyncHelper *helper = NULL;
1855 const gchar *folder_name = NULL;
1856 const gchar *error_msg;
1858 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1859 g_return_if_fail (TNY_IS_FOLDER (folder));
1860 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1862 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1863 folder_name = tny_folder_get_name (folder);
1865 /* Set the error msg */
1866 error_msg = _("mail_in_ui_folder_move_target_error");
1868 /* Get account and set it into mail_operation */
1869 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1870 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1871 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1873 /* Get folder rules */
1874 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1875 if (TNY_IS_FOLDER (parent))
1876 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1878 /* Apply operation constraints */
1879 if ((gpointer) parent == (gpointer) folder ||
1880 (!TNY_IS_FOLDER_STORE (parent)) ||
1881 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1884 } else if (TNY_IS_FOLDER (parent) &&
1885 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1889 } else if (TNY_IS_FOLDER (parent) &&
1890 TNY_IS_FOLDER_STORE (folder) &&
1891 modest_tny_folder_is_ancestor (TNY_FOLDER (parent),
1892 TNY_FOLDER_STORE (folder))) {
1893 /* Do not move a parent into a child */
1895 } else if (TNY_IS_FOLDER_STORE (parent) &&
1896 modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1897 /* Check that the new folder name is not used by any
1900 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1901 /* Check that the new folder name is not used by any
1902 special local folder */
1905 /* Create the helper */
1906 helper = g_slice_new0 (XFerMsgAsyncHelper);
1907 helper->mail_op = g_object_ref (self);
1908 helper->dest_folder = NULL;
1909 helper->headers = NULL;
1910 helper->user_callback = user_callback;
1911 helper->user_data = user_data;
1913 /* Move/Copy folder */
1914 modest_mail_operation_notify_start (self);
1915 tny_folder_copy_async (folder,
1917 tny_folder_get_name (folder),
1920 transfer_folder_status_cb,
1926 /* Set status failed and set an error */
1927 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1928 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1929 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1932 /* Call the user callback if exists */
1934 user_callback (self, user_data);
1936 /* Notify the queue */
1937 modest_mail_operation_notify_end (self);
1941 modest_mail_operation_rename_folder (ModestMailOperation *self,
1945 ModestMailOperationPrivate *priv;
1946 ModestTnyFolderRules rules;
1947 XFerMsgAsyncHelper *helper;
1949 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1950 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1951 g_return_if_fail (name);
1953 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1955 /* Get account and set it into mail_operation */
1956 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1957 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1959 /* Check folder rules */
1960 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1961 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1962 /* Set status failed and set an error */
1963 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1964 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1965 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1966 _("FIXME: unable to rename"));
1968 /* Notify about operation end */
1969 modest_mail_operation_notify_end (self);
1970 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1971 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1972 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1973 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1974 _("FIXME: unable to rename"));
1975 /* Notify about operation end */
1976 modest_mail_operation_notify_end (self);
1978 TnyFolderStore *into;
1980 into = tny_folder_get_folder_store (folder);
1982 /* Check that the new folder name is not used by any
1983 special local folder */
1984 if (new_name_valid_if_local_account (priv, into, name)) {
1985 /* Create the helper */
1986 helper = g_slice_new0 (XFerMsgAsyncHelper);
1987 helper->mail_op = g_object_ref(self);
1988 helper->dest_folder = NULL;
1989 helper->headers = NULL;
1990 helper->user_callback = NULL;
1991 helper->user_data = NULL;
1993 /* Rename. Camel handles folder subscription/unsubscription */
1994 modest_mail_operation_notify_start (self);
1995 tny_folder_copy_async (folder, into, name, TRUE,
1997 transfer_folder_status_cb,
2000 modest_mail_operation_notify_end (self);
2002 g_object_unref (into);
2006 /* ******************************************************************* */
2007 /* ************************** MSG ACTIONS ************************* */
2008 /* ******************************************************************* */
2011 modest_mail_operation_get_msg (ModestMailOperation *self,
2013 GetMsgAsyncUserCallback user_callback,
2016 GetMsgAsyncHelper *helper = NULL;
2018 ModestMailOperationPrivate *priv;
2020 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2021 g_return_if_fail (TNY_IS_HEADER (header));
2023 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2024 folder = tny_header_get_folder (header);
2026 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2027 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2029 /* Get account and set it into mail_operation */
2030 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2032 /* Check for cached messages */
2033 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2034 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2036 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2038 helper = g_slice_new0 (GetMsgAsyncHelper);
2039 helper->mail_op = self;
2040 helper->user_callback = user_callback;
2041 helper->user_data = user_data;
2042 helper->header = g_object_ref (header);
2044 /* The callback's reference so that the mail op is not
2045 * finalized until the async operation is completed even if
2046 * the user canceled the request meanwhile.
2048 g_object_ref (G_OBJECT (helper->mail_op));
2050 modest_mail_operation_notify_start (self);
2051 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2053 g_object_unref (G_OBJECT (folder));
2057 get_msg_cb (TnyFolder *folder,
2063 GetMsgAsyncHelper *helper = NULL;
2064 ModestMailOperation *self = NULL;
2065 ModestMailOperationPrivate *priv = NULL;
2067 helper = (GetMsgAsyncHelper *) user_data;
2068 g_return_if_fail (helper != NULL);
2069 self = helper->mail_op;
2070 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2071 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2073 /* Check errors and cancel */
2075 priv->error = g_error_copy (error);
2076 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2077 } else if (cancelled) {
2078 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2079 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2080 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2081 _("Error trying to refresh the contents of %s"),
2082 tny_folder_get_name (folder));
2084 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2087 /* If user defined callback function was defined, call it even
2088 if the operation failed*/
2089 if (helper->user_callback) {
2090 /* This is not a GDK lock because we are a Tinymail callback
2091 * which is already GDK locked by Tinymail */
2093 /* no gdk_threads_enter (), CHECKED */
2094 helper->user_callback (self, helper->header, msg, helper->user_data);
2095 /* no gdk_threads_leave (), CHECKED */
2098 /* Notify about operation end */
2099 modest_mail_operation_notify_end (self);
2101 g_object_unref (helper->mail_op);
2102 g_object_unref (helper->header);
2103 g_slice_free (GetMsgAsyncHelper, helper);
2108 get_msg_status_cb (GObject *obj,
2112 GetMsgAsyncHelper *helper = NULL;
2113 ModestMailOperation *self;
2114 ModestMailOperationPrivate *priv;
2115 ModestMailOperationState *state;
2117 g_return_if_fail (status != NULL);
2118 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2120 helper = (GetMsgAsyncHelper *) user_data;
2121 g_return_if_fail (helper != NULL);
2123 self = helper->mail_op;
2124 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2129 state = modest_mail_operation_clone_state (self);
2130 state->bytes_done = status->position;
2131 state->bytes_total = status->of_total;
2133 /* This is not a GDK lock because we are a Tinymail callback
2134 * which is already GDK locked by Tinymail */
2136 /* no gdk_threads_enter (), CHECKED */
2137 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2138 /* no gdk_threads_leave (), CHECKED */
2140 g_slice_free (ModestMailOperationState, state);
2143 /****************************************************/
2145 ModestMailOperation *mail_op;
2147 GetMsgAsyncUserCallback user_callback;
2149 GDestroyNotify notify;
2153 GetMsgAsyncUserCallback user_callback;
2157 ModestMailOperation *mail_op;
2158 } NotifyGetMsgsInfo;
2162 * Used by get_msgs_full_thread to call the user_callback for each
2163 * message that has been read
2166 notify_get_msgs_full (gpointer data)
2168 NotifyGetMsgsInfo *info;
2170 info = (NotifyGetMsgsInfo *) data;
2172 /* This is a GDK lock because we are an idle callback and
2173 * because info->user_callback can contain Gtk+ code */
2175 gdk_threads_enter (); /* CHECKED */
2176 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2177 gdk_threads_leave (); /* CHECKED */
2179 g_slice_free (NotifyGetMsgsInfo, info);
2185 * Used by get_msgs_full_thread to free al the thread resources and to
2186 * call the destroy function for the passed user_data
2189 get_msgs_full_destroyer (gpointer data)
2191 GetFullMsgsInfo *info;
2193 info = (GetFullMsgsInfo *) data;
2197 /* This is a GDK lock because we are an idle callback and
2198 * because info->notify can contain Gtk+ code */
2200 gdk_threads_enter (); /* CHECKED */
2201 info->notify (info->user_data);
2202 gdk_threads_leave (); /* CHECKED */
2206 g_object_unref (info->headers);
2207 g_slice_free (GetFullMsgsInfo, info);
2213 get_msgs_full_thread (gpointer thr_user_data)
2215 GetFullMsgsInfo *info;
2216 ModestMailOperationPrivate *priv = NULL;
2217 TnyIterator *iter = NULL;
2219 info = (GetFullMsgsInfo *) thr_user_data;
2220 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2222 iter = tny_list_create_iterator (info->headers);
2223 while (!tny_iterator_is_done (iter)) {
2227 header = TNY_HEADER (tny_iterator_get_current (iter));
2228 folder = tny_header_get_folder (header);
2230 /* Check for cached messages */
2231 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2232 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2234 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2236 /* Get message from folder */
2239 /* The callback will call it per each header */
2240 msg = tny_folder_get_msg (folder, header, &(priv->error));
2243 ModestMailOperationState *state;
2248 /* notify progress */
2249 state = modest_mail_operation_clone_state (info->mail_op);
2250 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2251 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2252 pair, (GDestroyNotify) modest_pair_free);
2254 /* The callback is the responsible for
2255 freeing the message */
2256 if (info->user_callback) {
2257 NotifyGetMsgsInfo *info_notify;
2258 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2259 info_notify->user_callback = info->user_callback;
2260 info_notify->mail_op = info->mail_op;
2261 info_notify->header = g_object_ref (header);
2262 info_notify->msg = g_object_ref (msg);
2263 info_notify->user_data = info->user_data;
2264 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2265 notify_get_msgs_full,
2268 g_object_unref (msg);
2271 /* Set status failed and set an error */
2272 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2273 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2274 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2275 "Error trying to get a message. No folder found for header");
2279 g_object_unref (header);
2281 tny_iterator_next (iter);
2284 /* Set operation status */
2285 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2286 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2288 /* Notify about operation end */
2289 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2291 /* Free thread resources. Will be called after all previous idles */
2292 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2298 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2299 TnyList *header_list,
2300 GetMsgAsyncUserCallback user_callback,
2302 GDestroyNotify notify)
2304 TnyHeader *header = NULL;
2305 TnyFolder *folder = NULL;
2307 ModestMailOperationPrivate *priv = NULL;
2308 GetFullMsgsInfo *info = NULL;
2309 gboolean size_ok = TRUE;
2311 TnyIterator *iter = NULL;
2313 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2315 /* Init mail operation */
2316 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2317 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2318 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2320 priv->total = tny_list_get_length(header_list);
2322 /* Get account and set it into mail_operation */
2323 if (tny_list_get_length (header_list) >= 1) {
2324 iter = tny_list_create_iterator (header_list);
2325 header = TNY_HEADER (tny_iterator_get_current (iter));
2327 folder = tny_header_get_folder (header);
2329 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2330 g_object_unref (folder);
2332 g_object_unref (header);
2335 if (tny_list_get_length (header_list) == 1) {
2336 g_object_unref (iter);
2341 /* Get msg size limit */
2342 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2343 MODEST_CONF_MSG_SIZE_LIMIT,
2346 g_clear_error (&(priv->error));
2347 max_size = G_MAXINT;
2349 max_size = max_size * KB;
2352 /* Check message size limits. If there is only one message
2353 always retrieve it */
2355 while (!tny_iterator_is_done (iter) && size_ok) {
2356 header = TNY_HEADER (tny_iterator_get_current (iter));
2358 if (tny_header_get_message_size (header) >= max_size)
2360 g_object_unref (header);
2363 tny_iterator_next (iter);
2365 g_object_unref (iter);
2369 /* Create the info */
2370 info = g_slice_new0 (GetFullMsgsInfo);
2371 info->mail_op = self;
2372 info->user_callback = user_callback;
2373 info->user_data = user_data;
2374 info->headers = g_object_ref (header_list);
2375 info->notify = notify;
2377 modest_mail_operation_notify_start (self);
2378 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2380 /* Set status failed and set an error */
2381 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2382 /* FIXME: the error msg is different for pop */
2383 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2384 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2385 _("emev_ni_ui_imap_msg_size_exceed_error"));
2386 /* Remove from queue and free resources */
2387 modest_mail_operation_notify_end (self);
2395 modest_mail_operation_remove_msg (ModestMailOperation *self,
2397 gboolean remove_to_trash /*ignored*/)
2400 ModestMailOperationPrivate *priv;
2402 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2403 g_return_if_fail (TNY_IS_HEADER (header));
2405 if (remove_to_trash)
2406 g_warning ("remove to trash is not implemented");
2408 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2409 folder = tny_header_get_folder (header);
2411 /* Get account and set it into mail_operation */
2412 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2413 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2414 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2416 /* remove message from folder */
2417 tny_folder_remove_msg (folder, header, &(priv->error));
2419 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2420 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2422 modest_mail_operation_notify_start (self);
2424 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2425 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2426 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2427 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2428 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2429 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2432 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2433 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2439 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2441 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2444 g_object_unref (G_OBJECT (folder));
2446 /* Notify about operation end */
2447 modest_mail_operation_notify_end (self);
2451 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2453 gboolean remove_to_trash /*ignored*/)
2456 ModestMailOperationPrivate *priv;
2457 TnyIterator *iter = NULL;
2458 TnyHeader *header = NULL;
2460 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2461 g_return_if_fail (TNY_IS_LIST (headers));
2463 if (remove_to_trash)
2464 g_warning ("remove to trash is not implemented");
2466 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2468 /* Get folder from first header and sync it */
2469 iter = tny_list_create_iterator (headers);
2470 header = TNY_HEADER (tny_iterator_get_current (iter));
2471 folder = tny_header_get_folder (header);
2473 /* Get account and set it into mail_operation */
2474 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2475 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2476 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2478 /* remove message from folder */
2479 modest_mail_operation_notify_start (self);
2481 tny_folder_remove_msgs (folder, headers, &(priv->error));
2483 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2484 TNY_IS_CAMEL_POP_FOLDER (folder))
2485 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */
2488 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2494 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2496 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2499 g_object_unref (header);
2500 g_object_unref (iter);
2501 g_object_unref (G_OBJECT (folder));
2503 /* Notify about operation end */
2504 modest_mail_operation_notify_end (self);
2509 transfer_msgs_status_cb (GObject *obj,
2513 XFerMsgAsyncHelper *helper = NULL;
2514 ModestMailOperation *self;
2515 ModestMailOperationPrivate *priv;
2516 ModestMailOperationState *state;
2517 gboolean is_num_bytes;
2519 g_return_if_fail (status != NULL);
2520 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2522 helper = (XFerMsgAsyncHelper *) user_data;
2523 g_return_if_fail (helper != NULL);
2525 self = helper->mail_op;
2526 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2528 /* We know that tinymail sends us information about
2529 transferred bytes with this particular message */
2530 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2532 state = modest_mail_operation_clone_state (self);
2533 if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2534 /* We know that we're in a different message when the
2535 total number of bytes to transfer is different. Of
2536 course it could fail if we're transferring messages
2537 of the same size, but this is a workarround */
2538 if (status->of_total != helper->last_total_bytes) {
2540 helper->sum_total_bytes += helper->last_total_bytes;
2541 helper->last_total_bytes = status->of_total;
2543 state->bytes_done += status->position + helper->sum_total_bytes;
2544 state->bytes_total = helper->total_bytes;
2547 /* Notify the status change. Only notify about changes
2548 referred to bytes */
2549 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2553 g_slice_free (ModestMailOperationState, state);
2558 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2560 XFerMsgAsyncHelper *helper;
2561 ModestMailOperation *self;
2562 ModestMailOperationPrivate *priv;
2563 TnyIterator *iter = NULL;
2564 TnyHeader *header = NULL;
2566 helper = (XFerMsgAsyncHelper *) user_data;
2567 self = helper->mail_op;
2569 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2572 priv->error = g_error_copy (err);
2574 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2575 } else if (cancelled) {
2576 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2577 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2578 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2579 _("Error trying to refresh the contents of %s"),
2580 tny_folder_get_name (folder));
2583 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2585 /* Update folder counts */
2586 tny_folder_poke_status (folder);
2587 tny_folder_poke_status (helper->dest_folder);
2591 /* Mark headers as deleted and seen */
2592 if ((helper->delete) &&
2593 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2594 iter = tny_list_create_iterator (helper->headers);
2595 while (!tny_iterator_is_done (iter)) {
2596 header = TNY_HEADER (tny_iterator_get_current (iter));
2597 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2598 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2599 g_object_unref (header);
2601 tny_iterator_next (iter);
2607 /* Notify about operation end */
2608 modest_mail_operation_notify_end (self);
2610 /* If user defined callback function was defined, call it */
2611 if (helper->user_callback) {
2612 /* This is not a GDK lock because we are a Tinymail callback and
2613 * Tinymail already acquires the Gdk lock */
2615 /* no gdk_threads_enter (), CHECKED */
2616 helper->user_callback (self, helper->user_data);
2617 /* no gdk_threads_leave (), CHECKED */
2621 if (helper->headers)
2622 g_object_unref (helper->headers);
2623 if (helper->dest_folder)
2624 g_object_unref (helper->dest_folder);
2625 if (helper->mail_op)
2626 g_object_unref (helper->mail_op);
2628 g_object_unref (folder);
2630 g_object_unref (iter);
2631 g_slice_free (XFerMsgAsyncHelper, helper);
2635 compute_message_list_size (TnyList *headers)
2640 iter = tny_list_create_iterator (headers);
2641 while (!tny_iterator_is_done (iter)) {
2642 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2643 size += tny_header_get_message_size (header);
2644 g_object_unref (header);
2645 tny_iterator_next (iter);
2647 g_object_unref (iter);
2653 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2656 gboolean delete_original,
2657 XferAsyncUserCallback user_callback,
2660 ModestMailOperationPrivate *priv = NULL;
2661 TnyIterator *iter = NULL;
2662 TnyFolder *src_folder = NULL;
2663 XFerMsgAsyncHelper *helper = NULL;
2664 TnyHeader *header = NULL;
2665 ModestTnyFolderRules rules = 0;
2667 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2668 g_return_if_fail (headers && TNY_IS_LIST (headers));
2669 g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2671 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2672 priv->total = tny_list_get_length (headers);
2674 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2675 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2677 /* Apply folder rules */
2678 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2679 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2680 /* Set status failed and set an error */
2681 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2682 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2683 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2684 _CS("ckct_ib_unable_to_paste_here"));
2685 /* Notify the queue */
2686 modest_mail_operation_notify_end (self);
2690 /* Get source folder */
2691 iter = tny_list_create_iterator (headers);
2692 header = TNY_HEADER (tny_iterator_get_current (iter));
2694 src_folder = tny_header_get_folder (header);
2695 g_object_unref (header);
2697 g_object_unref (iter);
2699 if (src_folder == NULL) {
2700 /* Notify the queue */
2701 modest_mail_operation_notify_end (self);
2703 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2708 /* Check folder source and destination */
2709 if (src_folder == folder) {
2710 /* Set status failed and set an error */
2711 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2712 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2713 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2714 _("mcen_ib_unable_to_copy_samefolder"));
2716 /* Notify the queue */
2717 modest_mail_operation_notify_end (self);
2720 g_object_unref (src_folder);
2724 /* Create the helper */
2725 helper = g_slice_new0 (XFerMsgAsyncHelper);
2726 helper->mail_op = g_object_ref(self);
2727 helper->dest_folder = g_object_ref(folder);
2728 helper->headers = g_object_ref(headers);
2729 helper->user_callback = user_callback;
2730 helper->user_data = user_data;
2731 helper->delete = delete_original;
2732 helper->last_total_bytes = 0;
2733 helper->sum_total_bytes = 0;
2734 helper->total_bytes = compute_message_list_size (headers);
2736 /* Get account and set it into mail_operation */
2737 priv->account = modest_tny_folder_get_account (src_folder);
2739 /* Transfer messages */
2740 modest_mail_operation_notify_start (self);
2741 tny_folder_transfer_msgs_async (src_folder,
2746 transfer_msgs_status_cb,
2752 on_refresh_folder (TnyFolder *folder,
2757 RefreshAsyncHelper *helper = NULL;
2758 ModestMailOperation *self = NULL;
2759 ModestMailOperationPrivate *priv = NULL;
2761 helper = (RefreshAsyncHelper *) user_data;
2762 self = helper->mail_op;
2763 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2765 g_return_if_fail(priv!=NULL);
2768 priv->error = g_error_copy (error);
2769 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2774 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2775 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2776 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2777 _("Error trying to refresh the contents of %s"),
2778 tny_folder_get_name (folder));
2782 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2785 /* Call user defined callback, if it exists */
2786 if (helper->user_callback) {
2788 /* This is not a GDK lock because we are a Tinymail callback and
2789 * Tinymail already acquires the Gdk lock */
2790 helper->user_callback (self, folder, helper->user_data);
2794 g_slice_free (RefreshAsyncHelper, helper);
2796 /* Notify about operation end */
2797 modest_mail_operation_notify_end (self);
2798 g_object_unref(self);
2802 on_refresh_folder_status_update (GObject *obj,
2806 RefreshAsyncHelper *helper = NULL;
2807 ModestMailOperation *self = NULL;
2808 ModestMailOperationPrivate *priv = NULL;
2809 ModestMailOperationState *state;
2811 g_return_if_fail (user_data != NULL);
2812 g_return_if_fail (status != NULL);
2813 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2815 helper = (RefreshAsyncHelper *) user_data;
2816 self = helper->mail_op;
2817 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2819 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2821 priv->done = status->position;
2822 priv->total = status->of_total;
2824 state = modest_mail_operation_clone_state (self);
2826 /* This is not a GDK lock because we are a Tinymail callback and
2827 * Tinymail already acquires the Gdk lock */
2828 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2830 g_slice_free (ModestMailOperationState, state);
2834 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2836 RefreshAsyncUserCallback user_callback,
2839 ModestMailOperationPrivate *priv = NULL;
2840 RefreshAsyncHelper *helper = NULL;
2842 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2844 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2846 /* Get account and set it into mail_operation */
2847 priv->account = modest_tny_folder_get_account (folder);
2848 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2850 /* Create the helper */
2851 helper = g_slice_new0 (RefreshAsyncHelper);
2852 helper->mail_op = g_object_ref(self);
2853 helper->user_callback = user_callback;
2854 helper->user_data = user_data;
2856 /* Refresh the folder. TODO: tinymail could issue a status
2857 updates before the callback call then this could happen. We
2858 must review the design */
2859 modest_mail_operation_notify_start (self);
2860 tny_folder_refresh_async (folder,
2862 on_refresh_folder_status_update,
2868 modest_mail_operation_notify_start (ModestMailOperation *self)
2870 ModestMailOperationPrivate *priv = NULL;
2872 g_return_if_fail (self);
2874 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2876 /* Ensure that all the fields are filled correctly */
2877 g_return_if_fail (priv->account != NULL);
2878 g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2880 /* Notify the observers about the mail operation. We do not
2881 wrapp this emission because we assume that this function is
2882 always called from within the main lock */
2883 g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2888 * It's used by the mail operation queue to notify the observers
2889 * attached to that signal that the operation finished. We need to use
2890 * that because tinymail does not give us the progress of a given
2891 * operation when it finishes (it directly calls the operation
2895 modest_mail_operation_notify_end (ModestMailOperation *self)
2897 ModestMailOperationPrivate *priv = NULL;
2899 g_return_if_fail (self);
2901 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2903 /* Notify the observers about the mail operation end. We do
2904 not wrapp this emission because we assume that this
2905 function is always called from within the main lock */
2906 g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2908 /* Remove the error user data */
2909 if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2910 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2914 modest_mail_operation_get_account (ModestMailOperation *self)
2916 ModestMailOperationPrivate *priv = NULL;
2918 g_return_val_if_fail (self, NULL);
2920 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2922 return (priv->account) ? g_object_ref (priv->account) : NULL;