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;
855 ModestMailOperation *mailop;
856 } SaveToDraftsAddMsgInfo;
859 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
864 ModestMailOperationPrivate *priv = NULL;
865 SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
867 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
870 g_warning ("%s: priv->error != NULL", __FUNCTION__);
871 g_error_free(priv->error);
874 priv->error = g_error_copy(err);
876 if ((!priv->error) && (info->draft_msg != NULL)) {
877 TnyHeader *header = tny_msg_get_header (info->draft_msg);
878 TnyFolder *src_folder = tny_header_get_folder (header);
880 /* Remove the old draft */
881 tny_folder_remove_msg (src_folder, header, NULL);
883 /* Synchronize to expunge and to update the msg counts */
884 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
885 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
887 g_object_unref (G_OBJECT(header));
888 g_object_unref (G_OBJECT(src_folder));
892 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
894 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
896 /* Call the user callback */
898 info->callback (info->mailop, info->msg, info->user_data);
900 if (info->transport_account)
901 g_object_unref (G_OBJECT(info->transport_account));
903 g_object_unref (G_OBJECT (info->draft_msg));
905 g_object_unref (G_OBJECT(info->drafts));
907 g_object_unref (G_OBJECT (info->msg));
908 g_slice_free (SaveToDraftsAddMsgInfo, info);
910 modest_mail_operation_notify_end (info->mailop);
911 g_object_unref(info->mailop);
916 TnyTransportAccount *transport_account;
918 SaveToDraftstCallback callback;
923 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
927 TnyFolder *drafts = NULL;
928 ModestMailOperationPrivate *priv = NULL;
929 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
931 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
934 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
935 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
936 "modest: failed to create a new msg\n");
938 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
939 TNY_FOLDER_TYPE_DRAFTS);
941 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
942 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
943 "modest: failed to create a new msg\n");
948 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
949 cb_info->transport_account = g_object_ref(info->transport_account);
950 cb_info->draft_msg = g_object_ref(info->draft_msg);
951 cb_info->callback = info->callback;
952 cb_info->user_data = info->user_data;
953 cb_info->drafts = g_object_ref(drafts);
954 cb_info->msg = g_object_ref(msg);
955 cb_info->mailop = g_object_ref(self);
956 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
959 /* Call the user callback */
960 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
962 info->callback (self, msg, info->user_data);
963 modest_mail_operation_notify_end (self);
967 g_object_unref (G_OBJECT(drafts));
969 g_object_unref (G_OBJECT (info->draft_msg));
970 if (info->transport_account)
971 g_object_unref (G_OBJECT(info->transport_account));
972 g_slice_free (SaveToDraftsInfo, info);
976 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
977 TnyTransportAccount *transport_account,
979 const gchar *from, const gchar *to,
980 const gchar *cc, const gchar *bcc,
981 const gchar *subject, const gchar *plain_body,
982 const gchar *html_body,
983 const GList *attachments_list,
984 const GList *images_list,
985 TnyHeaderFlags priority_flags,
986 SaveToDraftstCallback callback,
989 ModestMailOperationPrivate *priv = NULL;
990 SaveToDraftsInfo *info = NULL;
992 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
993 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
995 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
997 /* Get account and set it into mail_operation */
998 priv->account = g_object_ref (transport_account);
999 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1001 info = g_slice_new0 (SaveToDraftsInfo);
1002 info->transport_account = g_object_ref (transport_account);
1003 info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1004 info->callback = callback;
1005 info->user_data = user_data;
1007 modest_mail_operation_notify_start (self);
1008 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1009 attachments_list, images_list, priority_flags,
1010 modest_mail_operation_save_to_drafts_cb, info);
1015 ModestMailOperation *mail_op;
1016 TnyStoreAccount *account;
1017 TnyTransportAccount *transport_account;
1019 gint retrieve_limit;
1020 gchar *retrieve_type;
1021 gchar *account_name;
1022 UpdateAccountCallback callback;
1024 TnyList *new_headers;
1025 } UpdateAccountInfo;
1029 ModestMailOperation *mail_op;
1030 TnyMimePart *mime_part;
1032 GetMimePartSizeCallback callback;
1034 } GetMimePartSizeInfo;
1036 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
1037 /* We use this folder observer to track the headers that have been
1038 * added to a folder */
1041 TnyList *new_headers;
1042 } InternalFolderObserver;
1045 GObjectClass parent;
1046 } InternalFolderObserverClass;
1048 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1050 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1051 internal_folder_observer,
1053 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1057 foreach_add_item (gpointer header, gpointer user_data)
1059 tny_list_prepend (TNY_LIST (user_data),
1060 g_object_ref (G_OBJECT (header)));
1063 /* This is the method that looks for new messages in a folder */
1065 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1067 InternalFolderObserver *derived = (InternalFolderObserver *)self;
1069 TnyFolderChangeChanged changed;
1071 changed = tny_folder_change_get_changed (change);
1073 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1076 /* Get added headers */
1077 list = tny_simple_list_new ();
1078 tny_folder_change_get_added_headers (change, list);
1080 /* Add them to the folder observer */
1081 tny_list_foreach (list, foreach_add_item,
1082 derived->new_headers);
1084 g_object_unref (G_OBJECT (list));
1089 internal_folder_observer_init (InternalFolderObserver *self)
1091 self->new_headers = tny_simple_list_new ();
1094 internal_folder_observer_finalize (GObject *object)
1096 InternalFolderObserver *self;
1098 self = (InternalFolderObserver *) object;
1099 g_object_unref (self->new_headers);
1101 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1104 tny_folder_observer_init (TnyFolderObserverIface *iface)
1106 iface->update_func = internal_folder_observer_update;
1109 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1111 GObjectClass *object_class;
1113 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1114 object_class = (GObjectClass*) klass;
1115 object_class->finalize = internal_folder_observer_finalize;
1121 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
1124 TnyList *folders = tny_simple_list_new ();
1126 tny_folder_store_get_folders (store, folders, query, NULL);
1127 iter = tny_list_create_iterator (folders);
1129 while (!tny_iterator_is_done (iter)) {
1131 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1133 tny_list_prepend (all_folders, G_OBJECT (folder));
1134 recurse_folders (folder, query, all_folders);
1135 g_object_unref (G_OBJECT (folder));
1138 tny_iterator_next (iter);
1140 g_object_unref (G_OBJECT (iter));
1141 g_object_unref (G_OBJECT (folders));
1145 * Issues the "progress-changed" signal. The timer won't be removed,
1146 * so you must call g_source_remove to stop the signal emission
1149 idle_notify_progress (gpointer data)
1151 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1152 ModestMailOperationState *state;
1154 state = modest_mail_operation_clone_state (mail_op);
1156 /* This is a GDK lock because we are an idle callback and
1157 * the handlers of this signal can contain Gtk+ code */
1159 gdk_threads_enter (); /* CHECKED */
1160 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1161 gdk_threads_leave (); /* CHECKED */
1163 g_slice_free (ModestMailOperationState, state);
1169 * Issues the "progress-changed" signal and removes the timer. It uses
1170 * a lock to ensure that the progress information of the mail
1171 * operation is not modified while there are notifications pending
1174 idle_notify_progress_once (gpointer data)
1178 pair = (ModestPair *) data;
1180 /* This is a GDK lock because we are an idle callback and
1181 * the handlers of this signal can contain Gtk+ code */
1183 gdk_threads_enter (); /* CHECKED */
1184 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
1185 gdk_threads_leave (); /* CHECKED */
1187 /* Free the state and the reference to the mail operation */
1188 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
1189 g_object_unref (pair->first);
1195 * Used to notify the queue from the main
1196 * loop. We call it inside an idle call to achieve that
1199 idle_notify_queue (gpointer data)
1201 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1203 /* Do not need to block, the notify end will do it for us */
1204 modest_mail_operation_notify_end (mail_op);
1205 g_object_unref (mail_op);
1211 compare_headers_by_date (gconstpointer a,
1214 TnyHeader **header1, **header2;
1215 time_t sent1, sent2;
1217 header1 = (TnyHeader **) a;
1218 header2 = (TnyHeader **) b;
1220 sent1 = tny_header_get_date_sent (*header1);
1221 sent2 = tny_header_get_date_sent (*header2);
1223 /* We want the most recent ones (greater time_t) at the
1232 set_last_updated_idle (gpointer data)
1235 /* This is a GDK lock because we are an idle callback and
1236 * modest_account_mgr_set_last_updated can issue Gtk+ code */
1238 gdk_threads_enter (); /* CHECKED - please recheck */
1240 /* It does not matter if the time is not exactly the same than
1241 the time when this idle was called, it's just an
1242 approximation and it won't be very different */
1244 modest_account_mgr_set_last_updated (modest_runtime_get_account_mgr (),
1248 gdk_threads_leave (); /* CHECKED - please recheck */
1254 idle_update_account_cb (gpointer data)
1256 UpdateAccountInfo *idle_info;
1258 idle_info = (UpdateAccountInfo *) data;
1260 /* This is a GDK lock because we are an idle callback and
1261 * idle_info->callback can contain Gtk+ code */
1263 gdk_threads_enter (); /* CHECKED */
1264 idle_info->callback (idle_info->mail_op,
1265 idle_info->new_headers,
1266 idle_info->user_data);
1267 gdk_threads_leave (); /* CHECKED */
1270 g_object_unref (idle_info->mail_op);
1271 if (idle_info->new_headers)
1272 g_object_unref (idle_info->new_headers);
1279 get_all_folders_from_account (TnyStoreAccount *account,
1282 TnyList *all_folders = NULL;
1283 TnyIterator *iter = NULL;
1284 TnyFolderStoreQuery *query = NULL;
1286 all_folders = tny_simple_list_new ();
1287 query = tny_folder_store_query_new ();
1288 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1289 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
1296 g_object_unref (all_folders);
1300 iter = tny_list_create_iterator (all_folders);
1301 while (!tny_iterator_is_done (iter)) {
1302 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1304 recurse_folders (folder, query, all_folders);
1305 g_object_unref (folder);
1307 tny_iterator_next (iter);
1309 g_object_unref (G_OBJECT (iter));
1316 update_account_thread (gpointer thr_user_data)
1318 static gboolean first_time = TRUE;
1319 UpdateAccountInfo *info = NULL;
1320 TnyList *all_folders = NULL, *new_headers = NULL;
1321 GPtrArray *new_headers_array = NULL;
1322 TnyIterator *iter = NULL;
1323 ModestMailOperationPrivate *priv = NULL;
1324 ModestTnySendQueue *send_queue = NULL;
1325 gint i = 0, timeout = 0;
1327 info = (UpdateAccountInfo *) thr_user_data;
1328 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1330 /* Get account and set it into mail_operation */
1331 priv->account = g_object_ref (info->account);
1333 /* Get all the folders. We can do it synchronously because
1334 we're already running in a different thread than the UI */
1335 all_folders = get_all_folders_from_account (info->account, &(priv->error));
1337 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1341 /* Update status and notify. We need to call the notification
1342 with a source function in order to call it from the main
1343 loop. We need that in order not to get into trouble with
1344 Gtk+. We use a timeout in order to provide more status
1345 information, because the sync tinymail call does not
1346 provide it for the moment */
1347 timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1349 new_headers_array = g_ptr_array_new ();
1350 iter = tny_list_create_iterator (all_folders);
1352 while (!tny_iterator_is_done (iter) && !priv->error &&
1353 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1355 TnyFolderType folder_type;
1356 TnyFolder *folder = NULL;
1358 folder = TNY_FOLDER (tny_iterator_get_current (iter));
1359 folder_type = tny_folder_get_folder_type (folder);
1361 /* Refresh it only if it's the INBOX */
1362 if (folder_type == TNY_FOLDER_TYPE_INBOX) {
1363 InternalFolderObserver *observer = NULL;
1364 TnyIterator *new_headers_iter = NULL;
1366 /* Refresh the folder. Our observer receives
1367 * the new emails during folder refreshes, so
1368 * we can use observer->new_headers
1370 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1371 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1373 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1375 new_headers_iter = tny_list_create_iterator (observer->new_headers);
1376 while (!tny_iterator_is_done (new_headers_iter)) {
1377 TnyHeader *header = NULL;
1379 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1380 /* Apply per-message size limits */
1381 if (tny_header_get_message_size (header) < info->max_size)
1382 g_ptr_array_add (new_headers_array, g_object_ref (header));
1384 g_object_unref (header);
1385 tny_iterator_next (new_headers_iter);
1387 g_object_unref (new_headers_iter);
1389 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1390 g_object_unref (observer);
1392 /* We no not need to do it the first time,
1393 because it's automatically done by the tree
1395 if (G_LIKELY (!first_time))
1396 tny_folder_poke_status (folder);
1398 g_object_unref (folder);
1400 tny_iterator_next (iter);
1402 g_object_unref (G_OBJECT (iter));
1403 g_source_remove (timeout);
1405 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1406 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1407 new_headers_array->len > 0) {
1411 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1413 /* TODO: Ask the user, instead of just failing,
1414 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1415 * all' and 'Newest only' buttons. */
1416 if (new_headers_array->len > info->retrieve_limit) {
1420 /* Should be get only the headers or the message as well? */
1421 if (g_ascii_strcasecmp (info->retrieve_type,
1422 MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {
1424 priv->total = MIN (new_headers_array->len, info->retrieve_limit);
1425 while (msg_num < priv->total) {
1427 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1428 TnyFolder *folder = tny_header_get_folder (header);
1429 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1430 ModestMailOperationState *state;
1434 /* We can not just use the mail operation because the
1435 values of done and total could change before the
1437 state = modest_mail_operation_clone_state (info->mail_op);
1438 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1439 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1440 pair, (GDestroyNotify) modest_pair_free);
1442 g_object_unref (msg);
1443 g_object_unref (folder);
1450 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1453 /* Copy the headers to a list and free the array */
1454 new_headers = tny_simple_list_new ();
1455 for (i=0; i < new_headers_array->len; i++) {
1456 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1457 tny_list_append (new_headers, G_OBJECT (header));
1459 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1460 g_ptr_array_free (new_headers_array, FALSE);
1463 /* Perform send (if operation was not cancelled) */
1466 if (priv->account != NULL)
1467 g_object_unref (priv->account);
1469 if (info->transport_account) {
1470 priv->account = g_object_ref (info->transport_account);
1472 send_queue = modest_runtime_get_send_queue (info->transport_account);
1474 modest_tny_send_queue_try_to_send (send_queue);
1476 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1477 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1478 "cannot create a send queue for %s\n",
1479 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1480 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1484 /* Check if the operation was a success */
1486 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1488 /* Update the last updated key */
1489 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1490 set_last_updated_idle,
1491 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1492 (GDestroyNotify) g_free);
1496 /* Set the account back to not busy */
1497 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
1498 info->account_name, FALSE);
1500 if (info->callback) {
1501 UpdateAccountInfo *idle_info;
1503 /* This thread is not in the main lock */
1504 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1505 idle_info->mail_op = g_object_ref (info->mail_op);
1506 idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL;
1507 idle_info->callback = info->callback;
1508 idle_info->user_data = info->user_data;
1509 g_idle_add (idle_update_account_cb, idle_info);
1512 /* Notify about operation end. Note that the info could be
1513 freed before this idle happens, but the mail operation will
1515 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1519 g_object_unref (new_headers);
1521 g_object_unref (all_folders);
1522 g_object_unref (info->account);
1523 if (info->transport_account)
1524 g_object_unref (info->transport_account);
1525 g_free (info->account_name);
1526 g_free (info->retrieve_type);
1527 g_slice_free (UpdateAccountInfo, info);
1535 modest_mail_operation_update_account (ModestMailOperation *self,
1536 const gchar *account_name,
1537 UpdateAccountCallback callback,
1540 GThread *thread = NULL;
1541 UpdateAccountInfo *info = NULL;
1542 ModestMailOperationPrivate *priv = NULL;
1543 ModestAccountMgr *mgr = NULL;
1544 TnyStoreAccount *store_account = NULL;
1545 TnyTransportAccount *transport_account = NULL;
1547 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1548 g_return_val_if_fail (account_name, FALSE);
1550 /* Init mail operation. Set total and done to 0, and do not
1551 update them, this way the progress objects will know that
1552 we have no clue about the number of the objects */
1553 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1556 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1557 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1559 /* Get the store account */
1560 store_account = (TnyStoreAccount *)
1561 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1563 TNY_ACCOUNT_TYPE_STORE);
1565 if (!store_account) {
1566 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1567 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1568 "cannot get tny store account for %s\n", account_name);
1572 priv->account = g_object_ref (store_account);
1574 /* Get the transport account, we can not do it in the thread
1575 due to some problems with dbus */
1576 transport_account = (TnyTransportAccount *)
1577 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1579 if (!transport_account) {
1580 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1581 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1582 "cannot get tny transport account for %s\n", account_name);
1586 /* Create the helper object */
1587 info = g_slice_new (UpdateAccountInfo);
1588 info->mail_op = self;
1589 info->account = store_account;
1590 info->transport_account = transport_account;
1591 info->callback = callback;
1592 info->account_name = g_strdup (account_name);
1593 info->user_data = user_data;
1595 /* Get the message size limit */
1596 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1597 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1598 if (info->max_size == 0)
1599 info->max_size = G_MAXINT;
1601 info->max_size = info->max_size * KB;
1603 /* Get per-account retrieval type */
1604 mgr = modest_runtime_get_account_mgr ();
1605 info->retrieve_type = modest_account_mgr_get_retrieve_type (mgr, account_name);
1607 /* Get per-account message amount retrieval limit */
1608 info->retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, account_name);
1609 if (info->retrieve_limit == 0)
1610 info->retrieve_limit = G_MAXINT;
1612 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1614 /* Set account busy */
1615 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1617 modest_mail_operation_notify_start (self);
1618 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1624 g_object_unref (store_account);
1625 if (transport_account)
1626 g_object_unref (transport_account);
1627 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1629 callback (self, NULL, user_data);
1631 modest_mail_operation_notify_end (self);
1635 /* ******************************************************************* */
1636 /* ************************** STORE ACTIONS ************************* */
1637 /* ******************************************************************* */
1641 modest_mail_operation_create_folder (ModestMailOperation *self,
1642 TnyFolderStore *parent,
1645 ModestMailOperationPrivate *priv;
1646 TnyFolder *new_folder = NULL;
1648 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1649 g_return_val_if_fail (name, NULL);
1651 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1652 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1653 priv->account = (TNY_IS_ACCOUNT (parent)) ?
1654 g_object_ref (parent) :
1655 modest_tny_folder_get_account (TNY_FOLDER (parent));
1657 /* Check for already existing folder */
1658 if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1659 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1660 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1661 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1662 _CS("ckdg_ib_folder_already_exists"));
1666 if (TNY_IS_FOLDER (parent)) {
1667 /* Check folder rules */
1668 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1669 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1670 /* Set status failed and set an error */
1671 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1672 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1673 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1674 _("mail_in_ui_folder_create_error"));
1678 if (!strcmp (name, " ") || strchr (name, '/')) {
1679 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1680 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1681 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1682 _("mail_in_ui_folder_create_error"));
1686 /* Create the folder */
1687 modest_mail_operation_notify_start (self);
1688 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1689 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1691 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1694 /* Notify about operation end */
1695 modest_mail_operation_notify_end (self);
1701 modest_mail_operation_remove_folder (ModestMailOperation *self,
1703 gboolean remove_to_trash)
1705 TnyAccount *account;
1706 ModestMailOperationPrivate *priv;
1707 ModestTnyFolderRules rules;
1709 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1710 g_return_if_fail (TNY_IS_FOLDER (folder));
1712 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1714 /* Check folder rules */
1715 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1716 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1717 /* Set status failed and set an error */
1718 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1719 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1720 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1721 _("mail_in_ui_folder_delete_error"));
1725 /* Get the account */
1726 account = modest_tny_folder_get_account (folder);
1727 priv->account = g_object_ref(account);
1728 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1730 /* Delete folder or move to trash */
1731 if (remove_to_trash) {
1732 TnyFolder *trash_folder = NULL;
1733 trash_folder = modest_tny_account_get_special_folder (account,
1734 TNY_FOLDER_TYPE_TRASH);
1735 /* TODO: error_handling */
1737 modest_mail_operation_notify_start (self);
1738 modest_mail_operation_xfer_folder (self, folder,
1739 TNY_FOLDER_STORE (trash_folder),
1741 g_object_unref (trash_folder);
1744 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1746 modest_mail_operation_notify_start (self);
1747 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1748 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1751 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1754 g_object_unref (G_OBJECT (parent));
1756 g_object_unref (G_OBJECT (account));
1759 /* Notify about operation end */
1760 modest_mail_operation_notify_end (self);
1764 transfer_folder_status_cb (GObject *obj,
1768 ModestMailOperation *self;
1769 ModestMailOperationPrivate *priv;
1770 ModestMailOperationState *state;
1771 XFerMsgAsyncHelper *helper;
1773 g_return_if_fail (status != NULL);
1774 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1776 helper = (XFerMsgAsyncHelper *) user_data;
1777 g_return_if_fail (helper != NULL);
1779 self = helper->mail_op;
1780 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1782 priv->done = status->position;
1783 priv->total = status->of_total;
1785 state = modest_mail_operation_clone_state (self);
1787 /* This is not a GDK lock because we are a Tinymail callback
1788 * which is already GDK locked by Tinymail */
1790 /* no gdk_threads_enter (), CHECKED */
1792 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1794 /* no gdk_threads_leave (), CHECKED */
1796 g_slice_free (ModestMailOperationState, state);
1801 transfer_folder_cb (TnyFolder *folder,
1803 TnyFolderStore *into,
1804 TnyFolder *new_folder,
1808 XFerMsgAsyncHelper *helper;
1809 ModestMailOperation *self = NULL;
1810 ModestMailOperationPrivate *priv = NULL;
1812 helper = (XFerMsgAsyncHelper *) user_data;
1813 g_return_if_fail (helper != NULL);
1815 self = helper->mail_op;
1816 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1819 priv->error = g_error_copy (err);
1821 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1822 } else if (cancelled) {
1823 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1824 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1825 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1826 _("Transference of %s was cancelled."),
1827 tny_folder_get_name (folder));
1830 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1833 /* Notify about operation end */
1834 modest_mail_operation_notify_end (self);
1836 /* If user defined callback function was defined, call it */
1837 if (helper->user_callback) {
1839 /* This is not a GDK lock because we are a Tinymail callback
1840 * which is already GDK locked by Tinymail */
1842 /* no gdk_threads_enter (), CHECKED */
1843 helper->user_callback (self, helper->user_data);
1844 /* no gdk_threads_leave () , CHECKED */
1848 g_object_unref (helper->mail_op);
1849 g_slice_free (XFerMsgAsyncHelper, helper);
1854 * This function checks if the new name is a valid name for our local
1855 * folders account. The new name could not be the same than then name
1856 * of any of the mandatory local folders
1858 * We can not rely on tinymail because tinymail does not check the
1859 * name of the virtual folders that the account could have in the case
1860 * that we're doing a rename (because it directly calls Camel which
1861 * knows nothing about our virtual folders).
1863 * In the case of an actual copy/move (i.e. move/copy a folder between
1864 * accounts) tinymail uses the tny_folder_store_create_account which
1865 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1866 * checks the new name of the folder, so this call in that case
1867 * wouldn't be needed. *But* NOTE that if tinymail changes its
1868 * implementation (if folder transfers within the same account is no
1869 * longer implemented as a rename) this call will allow Modest to work
1872 * If the new name is not valid, this function will set the status to
1873 * failed and will set also an error in the mail operation
1876 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1877 TnyFolderStore *into,
1878 const gchar *new_name)
1880 if (TNY_IS_ACCOUNT (into) &&
1881 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1882 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1884 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1885 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1886 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1887 _CS("ckdg_ib_folder_already_exists"));
1894 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1896 TnyFolderStore *parent,
1897 gboolean delete_original,
1898 XferAsyncUserCallback user_callback,
1901 ModestMailOperationPrivate *priv = NULL;
1902 ModestTnyFolderRules parent_rules = 0, rules;
1903 XFerMsgAsyncHelper *helper = NULL;
1904 const gchar *folder_name = NULL;
1905 const gchar *error_msg;
1907 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1908 g_return_if_fail (TNY_IS_FOLDER (folder));
1909 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1911 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1912 folder_name = tny_folder_get_name (folder);
1914 /* Set the error msg */
1915 error_msg = _("mail_in_ui_folder_move_target_error");
1917 /* Get account and set it into mail_operation */
1918 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1919 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1920 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1922 /* Get folder rules */
1923 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1924 if (TNY_IS_FOLDER (parent))
1925 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1927 /* Apply operation constraints */
1928 if ((gpointer) parent == (gpointer) folder ||
1929 (!TNY_IS_FOLDER_STORE (parent)) ||
1930 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1933 } else if (TNY_IS_FOLDER (parent) &&
1934 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1938 } else if (TNY_IS_FOLDER (parent) &&
1939 TNY_IS_FOLDER_STORE (folder) &&
1940 modest_tny_folder_is_ancestor (TNY_FOLDER (parent),
1941 TNY_FOLDER_STORE (folder))) {
1942 /* Do not move a parent into a child */
1944 } else if (TNY_IS_FOLDER_STORE (parent) &&
1945 modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1946 /* Check that the new folder name is not used by any
1949 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1950 /* Check that the new folder name is not used by any
1951 special local folder */
1954 /* Create the helper */
1955 helper = g_slice_new0 (XFerMsgAsyncHelper);
1956 helper->mail_op = g_object_ref (self);
1957 helper->dest_folder = NULL;
1958 helper->headers = NULL;
1959 helper->user_callback = user_callback;
1960 helper->user_data = user_data;
1962 /* Move/Copy folder */
1963 modest_mail_operation_notify_start (self);
1964 tny_folder_copy_async (folder,
1966 tny_folder_get_name (folder),
1969 transfer_folder_status_cb,
1975 /* Set status failed and set an error */
1976 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1977 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1978 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1981 /* Call the user callback if exists */
1983 user_callback (self, user_data);
1985 /* Notify the queue */
1986 modest_mail_operation_notify_end (self);
1990 modest_mail_operation_rename_folder (ModestMailOperation *self,
1994 ModestMailOperationPrivate *priv;
1995 ModestTnyFolderRules rules;
1996 XFerMsgAsyncHelper *helper;
1998 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1999 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2000 g_return_if_fail (name);
2002 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2004 /* Get account and set it into mail_operation */
2005 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2006 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2008 /* Check folder rules */
2009 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2010 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2011 /* Set status failed and set an error */
2012 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2013 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2014 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2015 _("FIXME: unable to rename"));
2017 /* Notify about operation end */
2018 modest_mail_operation_notify_end (self);
2019 } else if (!strcmp (name, " ") || strchr (name, '/')) {
2020 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2021 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2022 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2023 _("FIXME: unable to rename"));
2024 /* Notify about operation end */
2025 modest_mail_operation_notify_end (self);
2027 TnyFolderStore *into;
2029 into = tny_folder_get_folder_store (folder);
2031 /* Check that the new folder name is not used by any
2032 special local folder */
2033 if (new_name_valid_if_local_account (priv, into, name)) {
2034 /* Create the helper */
2035 helper = g_slice_new0 (XFerMsgAsyncHelper);
2036 helper->mail_op = g_object_ref(self);
2037 helper->dest_folder = NULL;
2038 helper->headers = NULL;
2039 helper->user_callback = NULL;
2040 helper->user_data = NULL;
2042 /* Rename. Camel handles folder subscription/unsubscription */
2043 modest_mail_operation_notify_start (self);
2044 tny_folder_copy_async (folder, into, name, TRUE,
2046 transfer_folder_status_cb,
2049 modest_mail_operation_notify_end (self);
2051 g_object_unref (into);
2055 /* ******************************************************************* */
2056 /* ************************** MSG ACTIONS ************************* */
2057 /* ******************************************************************* */
2060 modest_mail_operation_get_msg (ModestMailOperation *self,
2062 GetMsgAsyncUserCallback user_callback,
2065 GetMsgAsyncHelper *helper = NULL;
2067 ModestMailOperationPrivate *priv;
2069 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2070 g_return_if_fail (TNY_IS_HEADER (header));
2072 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2073 folder = tny_header_get_folder (header);
2075 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2076 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2078 /* Get account and set it into mail_operation */
2079 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2081 /* Check for cached messages */
2082 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2083 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2085 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2087 helper = g_slice_new0 (GetMsgAsyncHelper);
2088 helper->mail_op = self;
2089 helper->user_callback = user_callback;
2090 helper->user_data = user_data;
2091 helper->header = g_object_ref (header);
2093 /* The callback's reference so that the mail op is not
2094 * finalized until the async operation is completed even if
2095 * the user canceled the request meanwhile.
2097 g_object_ref (G_OBJECT (helper->mail_op));
2099 modest_mail_operation_notify_start (self);
2100 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2102 g_object_unref (G_OBJECT (folder));
2106 get_msg_cb (TnyFolder *folder,
2112 GetMsgAsyncHelper *helper = NULL;
2113 ModestMailOperation *self = NULL;
2114 ModestMailOperationPrivate *priv = NULL;
2116 helper = (GetMsgAsyncHelper *) user_data;
2117 g_return_if_fail (helper != NULL);
2118 self = helper->mail_op;
2119 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2120 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2122 /* Check errors and cancel */
2124 priv->error = g_error_copy (error);
2125 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2126 } else if (cancelled) {
2127 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2128 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2129 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2130 _("Error trying to refresh the contents of %s"),
2131 tny_folder_get_name (folder));
2133 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2136 /* If user defined callback function was defined, call it even
2137 if the operation failed*/
2138 if (helper->user_callback) {
2139 /* This is not a GDK lock because we are a Tinymail callback
2140 * which is already GDK locked by Tinymail */
2142 /* no gdk_threads_enter (), CHECKED */
2143 helper->user_callback (self, helper->header, msg, helper->user_data);
2144 /* no gdk_threads_leave (), CHECKED */
2147 /* Notify about operation end */
2148 modest_mail_operation_notify_end (self);
2150 g_object_unref (helper->mail_op);
2151 g_object_unref (helper->header);
2152 g_slice_free (GetMsgAsyncHelper, helper);
2157 get_msg_status_cb (GObject *obj,
2161 GetMsgAsyncHelper *helper = NULL;
2162 ModestMailOperation *self;
2163 ModestMailOperationPrivate *priv;
2164 ModestMailOperationState *state;
2166 g_return_if_fail (status != NULL);
2167 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2169 helper = (GetMsgAsyncHelper *) user_data;
2170 g_return_if_fail (helper != NULL);
2172 self = helper->mail_op;
2173 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2178 state = modest_mail_operation_clone_state (self);
2179 state->bytes_done = status->position;
2180 state->bytes_total = status->of_total;
2182 /* This is not a GDK lock because we are a Tinymail callback
2183 * which is already GDK locked by Tinymail */
2185 /* no gdk_threads_enter (), CHECKED */
2186 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2187 /* no gdk_threads_leave (), CHECKED */
2189 g_slice_free (ModestMailOperationState, state);
2192 /****************************************************/
2194 ModestMailOperation *mail_op;
2196 GetMsgAsyncUserCallback user_callback;
2198 GDestroyNotify notify;
2202 GetMsgAsyncUserCallback user_callback;
2206 ModestMailOperation *mail_op;
2207 } NotifyGetMsgsInfo;
2211 * Used by get_msgs_full_thread to call the user_callback for each
2212 * message that has been read
2215 notify_get_msgs_full (gpointer data)
2217 NotifyGetMsgsInfo *info;
2219 info = (NotifyGetMsgsInfo *) data;
2221 /* This is a GDK lock because we are an idle callback and
2222 * because info->user_callback can contain Gtk+ code */
2224 gdk_threads_enter (); /* CHECKED */
2225 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2226 gdk_threads_leave (); /* CHECKED */
2228 g_slice_free (NotifyGetMsgsInfo, info);
2234 * Used by get_msgs_full_thread to free al the thread resources and to
2235 * call the destroy function for the passed user_data
2238 get_msgs_full_destroyer (gpointer data)
2240 GetFullMsgsInfo *info;
2242 info = (GetFullMsgsInfo *) data;
2246 /* This is a GDK lock because we are an idle callback and
2247 * because info->notify can contain Gtk+ code */
2249 gdk_threads_enter (); /* CHECKED */
2250 info->notify (info->user_data);
2251 gdk_threads_leave (); /* CHECKED */
2255 g_object_unref (info->headers);
2256 g_slice_free (GetFullMsgsInfo, info);
2262 get_msgs_full_thread (gpointer thr_user_data)
2264 GetFullMsgsInfo *info;
2265 ModestMailOperationPrivate *priv = NULL;
2266 TnyIterator *iter = NULL;
2268 info = (GetFullMsgsInfo *) thr_user_data;
2269 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2271 iter = tny_list_create_iterator (info->headers);
2272 while (!tny_iterator_is_done (iter)) {
2276 header = TNY_HEADER (tny_iterator_get_current (iter));
2277 folder = tny_header_get_folder (header);
2279 /* Check for cached messages */
2280 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2281 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2283 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2285 /* Get message from folder */
2288 /* The callback will call it per each header */
2289 msg = tny_folder_get_msg (folder, header, &(priv->error));
2292 ModestMailOperationState *state;
2297 /* notify progress */
2298 state = modest_mail_operation_clone_state (info->mail_op);
2299 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2300 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2301 pair, (GDestroyNotify) modest_pair_free);
2303 /* The callback is the responsible for
2304 freeing the message */
2305 if (info->user_callback) {
2306 NotifyGetMsgsInfo *info_notify;
2307 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2308 info_notify->user_callback = info->user_callback;
2309 info_notify->mail_op = info->mail_op;
2310 info_notify->header = g_object_ref (header);
2311 info_notify->msg = g_object_ref (msg);
2312 info_notify->user_data = info->user_data;
2313 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2314 notify_get_msgs_full,
2317 g_object_unref (msg);
2320 /* Set status failed and set an error */
2321 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2322 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2323 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2324 "Error trying to get a message. No folder found for header");
2328 g_object_unref (header);
2330 tny_iterator_next (iter);
2333 /* Set operation status */
2334 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2335 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2337 /* Notify about operation end */
2338 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2340 /* Free thread resources. Will be called after all previous idles */
2341 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2347 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2348 TnyList *header_list,
2349 GetMsgAsyncUserCallback user_callback,
2351 GDestroyNotify notify)
2353 TnyHeader *header = NULL;
2354 TnyFolder *folder = NULL;
2356 ModestMailOperationPrivate *priv = NULL;
2357 GetFullMsgsInfo *info = NULL;
2358 gboolean size_ok = TRUE;
2360 TnyIterator *iter = NULL;
2362 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2364 /* Init mail operation */
2365 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2366 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2367 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2369 priv->total = tny_list_get_length(header_list);
2371 /* Get account and set it into mail_operation */
2372 if (tny_list_get_length (header_list) >= 1) {
2373 iter = tny_list_create_iterator (header_list);
2374 header = TNY_HEADER (tny_iterator_get_current (iter));
2376 folder = tny_header_get_folder (header);
2378 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2379 g_object_unref (folder);
2381 g_object_unref (header);
2384 if (tny_list_get_length (header_list) == 1) {
2385 g_object_unref (iter);
2390 /* Get msg size limit */
2391 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2392 MODEST_CONF_MSG_SIZE_LIMIT,
2395 g_clear_error (&(priv->error));
2396 max_size = G_MAXINT;
2398 max_size = max_size * KB;
2401 /* Check message size limits. If there is only one message
2402 always retrieve it */
2404 while (!tny_iterator_is_done (iter) && size_ok) {
2405 header = TNY_HEADER (tny_iterator_get_current (iter));
2407 if (tny_header_get_message_size (header) >= max_size)
2409 g_object_unref (header);
2412 tny_iterator_next (iter);
2414 g_object_unref (iter);
2418 /* Create the info */
2419 info = g_slice_new0 (GetFullMsgsInfo);
2420 info->mail_op = self;
2421 info->user_callback = user_callback;
2422 info->user_data = user_data;
2423 info->headers = g_object_ref (header_list);
2424 info->notify = notify;
2426 modest_mail_operation_notify_start (self);
2427 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2429 /* Set status failed and set an error */
2430 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2431 /* FIXME: the error msg is different for pop */
2432 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2433 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2434 _("emev_ni_ui_imap_msg_size_exceed_error"));
2435 /* Remove from queue and free resources */
2436 modest_mail_operation_notify_end (self);
2444 modest_mail_operation_remove_msg (ModestMailOperation *self,
2446 gboolean remove_to_trash /*ignored*/)
2449 ModestMailOperationPrivate *priv;
2451 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2452 g_return_if_fail (TNY_IS_HEADER (header));
2454 if (remove_to_trash)
2455 g_warning ("remove to trash is not implemented");
2457 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2458 folder = tny_header_get_folder (header);
2460 /* Get account and set it into mail_operation */
2461 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2462 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2463 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2465 /* remove message from folder */
2466 tny_folder_remove_msg (folder, header, &(priv->error));
2468 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2469 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2471 modest_mail_operation_notify_start (self);
2473 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2474 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2475 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2476 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2477 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2478 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2481 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2482 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2488 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2490 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2493 g_object_unref (G_OBJECT (folder));
2495 /* Notify about operation end */
2496 modest_mail_operation_notify_end (self);
2500 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2502 gboolean remove_to_trash /*ignored*/)
2505 ModestMailOperationPrivate *priv;
2506 TnyIterator *iter = NULL;
2507 TnyHeader *header = NULL;
2509 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2510 g_return_if_fail (TNY_IS_LIST (headers));
2512 if (remove_to_trash)
2513 g_warning ("remove to trash is not implemented");
2515 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2517 /* Get folder from first header and sync it */
2518 iter = tny_list_create_iterator (headers);
2519 header = TNY_HEADER (tny_iterator_get_current (iter));
2520 folder = tny_header_get_folder (header);
2522 /* Get account and set it into mail_operation */
2523 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2524 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2525 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2527 /* remove message from folder */
2528 modest_mail_operation_notify_start (self);
2530 tny_folder_remove_msgs (folder, headers, &(priv->error));
2532 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2533 TNY_IS_CAMEL_POP_FOLDER (folder))
2534 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */
2537 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2543 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2545 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2548 g_object_unref (header);
2549 g_object_unref (iter);
2550 g_object_unref (G_OBJECT (folder));
2552 /* Notify about operation end */
2553 modest_mail_operation_notify_end (self);
2558 transfer_msgs_status_cb (GObject *obj,
2562 XFerMsgAsyncHelper *helper = NULL;
2563 ModestMailOperation *self;
2564 ModestMailOperationPrivate *priv;
2565 ModestMailOperationState *state;
2566 gboolean is_num_bytes;
2568 g_return_if_fail (status != NULL);
2569 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2571 helper = (XFerMsgAsyncHelper *) user_data;
2572 g_return_if_fail (helper != NULL);
2574 self = helper->mail_op;
2575 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2577 /* We know that tinymail sends us information about
2578 transferred bytes with this particular message */
2579 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2581 state = modest_mail_operation_clone_state (self);
2582 if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2583 /* We know that we're in a different message when the
2584 total number of bytes to transfer is different. Of
2585 course it could fail if we're transferring messages
2586 of the same size, but this is a workarround */
2587 if (status->of_total != helper->last_total_bytes) {
2589 helper->sum_total_bytes += helper->last_total_bytes;
2590 helper->last_total_bytes = status->of_total;
2592 state->bytes_done += status->position + helper->sum_total_bytes;
2593 state->bytes_total = helper->total_bytes;
2596 /* Notify the status change. Only notify about changes
2597 referred to bytes */
2598 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2602 g_slice_free (ModestMailOperationState, state);
2607 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2609 XFerMsgAsyncHelper *helper;
2610 ModestMailOperation *self;
2611 ModestMailOperationPrivate *priv;
2612 TnyIterator *iter = NULL;
2613 TnyHeader *header = NULL;
2615 helper = (XFerMsgAsyncHelper *) user_data;
2616 self = helper->mail_op;
2618 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2621 priv->error = g_error_copy (err);
2623 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2624 } else if (cancelled) {
2625 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2626 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2627 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2628 _("Error trying to refresh the contents of %s"),
2629 tny_folder_get_name (folder));
2632 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2634 /* Update folder counts */
2635 tny_folder_poke_status (folder);
2636 tny_folder_poke_status (helper->dest_folder);
2640 /* Mark headers as deleted and seen */
2641 if ((helper->delete) &&
2642 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2643 iter = tny_list_create_iterator (helper->headers);
2644 while (!tny_iterator_is_done (iter)) {
2645 header = TNY_HEADER (tny_iterator_get_current (iter));
2646 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2647 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2648 g_object_unref (header);
2650 tny_iterator_next (iter);
2656 /* Notify about operation end */
2657 modest_mail_operation_notify_end (self);
2659 /* If user defined callback function was defined, call it */
2660 if (helper->user_callback) {
2661 /* This is not a GDK lock because we are a Tinymail callback and
2662 * Tinymail already acquires the Gdk lock */
2664 /* no gdk_threads_enter (), CHECKED */
2665 helper->user_callback (self, helper->user_data);
2666 /* no gdk_threads_leave (), CHECKED */
2670 if (helper->headers)
2671 g_object_unref (helper->headers);
2672 if (helper->dest_folder)
2673 g_object_unref (helper->dest_folder);
2674 if (helper->mail_op)
2675 g_object_unref (helper->mail_op);
2677 g_object_unref (folder);
2679 g_object_unref (iter);
2680 g_slice_free (XFerMsgAsyncHelper, helper);
2684 compute_message_list_size (TnyList *headers)
2689 iter = tny_list_create_iterator (headers);
2690 while (!tny_iterator_is_done (iter)) {
2691 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2692 size += tny_header_get_message_size (header);
2693 g_object_unref (header);
2694 tny_iterator_next (iter);
2696 g_object_unref (iter);
2702 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2705 gboolean delete_original,
2706 XferAsyncUserCallback user_callback,
2709 ModestMailOperationPrivate *priv = NULL;
2710 TnyIterator *iter = NULL;
2711 TnyFolder *src_folder = NULL;
2712 XFerMsgAsyncHelper *helper = NULL;
2713 TnyHeader *header = NULL;
2714 ModestTnyFolderRules rules = 0;
2716 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2717 g_return_if_fail (headers && TNY_IS_LIST (headers));
2718 g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2720 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2721 priv->total = tny_list_get_length (headers);
2723 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2724 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2726 /* Apply folder rules */
2727 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2728 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2729 /* Set status failed and set an error */
2730 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2731 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2732 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2733 _CS("ckct_ib_unable_to_paste_here"));
2734 /* Notify the queue */
2735 modest_mail_operation_notify_end (self);
2739 /* Get source folder */
2740 iter = tny_list_create_iterator (headers);
2741 header = TNY_HEADER (tny_iterator_get_current (iter));
2743 src_folder = tny_header_get_folder (header);
2744 g_object_unref (header);
2746 g_object_unref (iter);
2748 if (src_folder == NULL) {
2749 /* Notify the queue */
2750 modest_mail_operation_notify_end (self);
2752 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2757 /* Check folder source and destination */
2758 if (src_folder == folder) {
2759 /* Set status failed and set an error */
2760 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2761 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2762 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2763 _("mcen_ib_unable_to_copy_samefolder"));
2765 /* Notify the queue */
2766 modest_mail_operation_notify_end (self);
2769 g_object_unref (src_folder);
2773 /* Create the helper */
2774 helper = g_slice_new0 (XFerMsgAsyncHelper);
2775 helper->mail_op = g_object_ref(self);
2776 helper->dest_folder = g_object_ref(folder);
2777 helper->headers = g_object_ref(headers);
2778 helper->user_callback = user_callback;
2779 helper->user_data = user_data;
2780 helper->delete = delete_original;
2781 helper->last_total_bytes = 0;
2782 helper->sum_total_bytes = 0;
2783 helper->total_bytes = compute_message_list_size (headers);
2785 /* Get account and set it into mail_operation */
2786 priv->account = modest_tny_folder_get_account (src_folder);
2788 /* Transfer messages */
2789 modest_mail_operation_notify_start (self);
2790 tny_folder_transfer_msgs_async (src_folder,
2795 transfer_msgs_status_cb,
2801 on_refresh_folder (TnyFolder *folder,
2806 RefreshAsyncHelper *helper = NULL;
2807 ModestMailOperation *self = NULL;
2808 ModestMailOperationPrivate *priv = NULL;
2810 helper = (RefreshAsyncHelper *) user_data;
2811 self = helper->mail_op;
2812 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2814 g_return_if_fail(priv!=NULL);
2817 priv->error = g_error_copy (error);
2818 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2823 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2824 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2825 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2826 _("Error trying to refresh the contents of %s"),
2827 tny_folder_get_name (folder));
2831 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2834 /* Call user defined callback, if it exists */
2835 if (helper->user_callback) {
2837 /* This is not a GDK lock because we are a Tinymail callback and
2838 * Tinymail already acquires the Gdk lock */
2839 helper->user_callback (self, folder, helper->user_data);
2843 g_slice_free (RefreshAsyncHelper, helper);
2845 /* Notify about operation end */
2846 modest_mail_operation_notify_end (self);
2847 g_object_unref(self);
2851 on_refresh_folder_status_update (GObject *obj,
2855 RefreshAsyncHelper *helper = NULL;
2856 ModestMailOperation *self = NULL;
2857 ModestMailOperationPrivate *priv = NULL;
2858 ModestMailOperationState *state;
2860 g_return_if_fail (user_data != NULL);
2861 g_return_if_fail (status != NULL);
2862 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2864 helper = (RefreshAsyncHelper *) user_data;
2865 self = helper->mail_op;
2866 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2868 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2870 priv->done = status->position;
2871 priv->total = status->of_total;
2873 state = modest_mail_operation_clone_state (self);
2875 /* This is not a GDK lock because we are a Tinymail callback and
2876 * Tinymail already acquires the Gdk lock */
2877 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2879 g_slice_free (ModestMailOperationState, state);
2883 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2885 RefreshAsyncUserCallback user_callback,
2888 ModestMailOperationPrivate *priv = NULL;
2889 RefreshAsyncHelper *helper = NULL;
2891 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2893 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2895 /* Get account and set it into mail_operation */
2896 priv->account = modest_tny_folder_get_account (folder);
2897 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2899 /* Create the helper */
2900 helper = g_slice_new0 (RefreshAsyncHelper);
2901 helper->mail_op = g_object_ref(self);
2902 helper->user_callback = user_callback;
2903 helper->user_data = user_data;
2905 /* Refresh the folder. TODO: tinymail could issue a status
2906 updates before the callback call then this could happen. We
2907 must review the design */
2908 modest_mail_operation_notify_start (self);
2909 tny_folder_refresh_async (folder,
2911 on_refresh_folder_status_update,
2917 modest_mail_operation_notify_start (ModestMailOperation *self)
2919 ModestMailOperationPrivate *priv = NULL;
2921 g_return_if_fail (self);
2923 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2925 /* Ensure that all the fields are filled correctly */
2926 g_return_if_fail (priv->account != NULL);
2927 g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2929 /* Notify the observers about the mail operation. We do not
2930 wrapp this emission because we assume that this function is
2931 always called from within the main lock */
2932 g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2937 * It's used by the mail operation queue to notify the observers
2938 * attached to that signal that the operation finished. We need to use
2939 * that because tinymail does not give us the progress of a given
2940 * operation when it finishes (it directly calls the operation
2944 modest_mail_operation_notify_end (ModestMailOperation *self)
2946 ModestMailOperationPrivate *priv = NULL;
2948 g_return_if_fail (self);
2950 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2952 /* Notify the observers about the mail operation end. We do
2953 not wrapp this emission because we assume that this
2954 function is always called from within the main lock */
2955 g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2957 /* Remove the error user data */
2958 if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2959 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2963 modest_mail_operation_get_account (ModestMailOperation *self)
2965 ModestMailOperationPrivate *priv = NULL;
2967 g_return_val_if_fail (self, NULL);
2969 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2971 return (priv->account) ? g_object_ref (priv->account) : NULL;