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 = (err == NULL) ? NULL : 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 gdk_threads_enter ();
1204 modest_mail_operation_notify_end (mail_op);
1205 gdk_threads_leave ();
1206 g_object_unref (mail_op);
1212 compare_headers_by_date (gconstpointer a,
1215 TnyHeader **header1, **header2;
1216 time_t sent1, sent2;
1218 header1 = (TnyHeader **) a;
1219 header2 = (TnyHeader **) b;
1221 sent1 = tny_header_get_date_sent (*header1);
1222 sent2 = tny_header_get_date_sent (*header2);
1224 /* We want the most recent ones (greater time_t) at the
1233 set_last_updated_idle (gpointer data)
1236 /* This is a GDK lock because we are an idle callback and
1237 * modest_account_mgr_set_last_updated can issue Gtk+ code */
1239 gdk_threads_enter (); /* CHECKED - please recheck */
1241 /* It does not matter if the time is not exactly the same than
1242 the time when this idle was called, it's just an
1243 approximation and it won't be very different */
1245 modest_account_mgr_set_last_updated (modest_runtime_get_account_mgr (),
1249 gdk_threads_leave (); /* CHECKED - please recheck */
1255 idle_update_account_cb (gpointer data)
1257 UpdateAccountInfo *idle_info;
1259 idle_info = (UpdateAccountInfo *) data;
1261 /* This is a GDK lock because we are an idle callback and
1262 * idle_info->callback can contain Gtk+ code */
1264 gdk_threads_enter (); /* CHECKED */
1265 idle_info->callback (idle_info->mail_op,
1266 idle_info->new_headers,
1267 idle_info->user_data);
1268 gdk_threads_leave (); /* CHECKED */
1271 g_object_unref (idle_info->mail_op);
1272 if (idle_info->new_headers)
1273 g_object_unref (idle_info->new_headers);
1280 get_all_folders_from_account (TnyStoreAccount *account,
1283 TnyList *all_folders = NULL;
1284 TnyIterator *iter = NULL;
1285 TnyFolderStoreQuery *query = NULL;
1287 all_folders = tny_simple_list_new ();
1288 query = tny_folder_store_query_new ();
1289 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
1290 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
1297 g_object_unref (all_folders);
1301 iter = tny_list_create_iterator (all_folders);
1302 while (!tny_iterator_is_done (iter)) {
1303 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
1305 recurse_folders (folder, query, all_folders);
1306 g_object_unref (folder);
1308 tny_iterator_next (iter);
1310 g_object_unref (G_OBJECT (iter));
1317 update_account_thread (gpointer thr_user_data)
1319 static gboolean first_time = TRUE;
1320 UpdateAccountInfo *info = NULL;
1321 TnyList *all_folders = NULL, *new_headers = NULL;
1322 GPtrArray *new_headers_array = NULL;
1323 TnyIterator *iter = NULL;
1324 ModestMailOperationPrivate *priv = NULL;
1325 ModestTnySendQueue *send_queue = NULL;
1326 gint i = 0, timeout = 0;
1328 info = (UpdateAccountInfo *) thr_user_data;
1329 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
1331 /* Get account and set it into mail_operation */
1332 priv->account = g_object_ref (info->account);
1334 /* Get all the folders. We can do it synchronously because
1335 we're already running in a different thread than the UI */
1336 all_folders = get_all_folders_from_account (info->account, &(priv->error));
1338 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1342 /* Update status and notify. We need to call the notification
1343 with a source function in order to call it from the main
1344 loop. We need that in order not to get into trouble with
1345 Gtk+. We use a timeout in order to provide more status
1346 information, because the sync tinymail call does not
1347 provide it for the moment */
1348 timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
1350 new_headers_array = g_ptr_array_new ();
1351 iter = tny_list_create_iterator (all_folders);
1353 while (!tny_iterator_is_done (iter) && !priv->error &&
1354 priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1356 TnyFolderType folder_type;
1357 TnyFolder *folder = NULL;
1359 folder = TNY_FOLDER (tny_iterator_get_current (iter));
1360 folder_type = tny_folder_get_folder_type (folder);
1362 /* Refresh it only if it's the INBOX */
1363 if (folder_type == TNY_FOLDER_TYPE_INBOX) {
1364 InternalFolderObserver *observer = NULL;
1365 TnyIterator *new_headers_iter = NULL;
1367 /* Refresh the folder. Our observer receives
1368 * the new emails during folder refreshes, so
1369 * we can use observer->new_headers
1371 observer = g_object_new (internal_folder_observer_get_type (), NULL);
1372 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1374 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
1376 new_headers_iter = tny_list_create_iterator (observer->new_headers);
1377 while (!tny_iterator_is_done (new_headers_iter)) {
1378 TnyHeader *header = NULL;
1380 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1381 /* Apply per-message size limits */
1382 if (tny_header_get_message_size (header) < info->max_size)
1383 g_ptr_array_add (new_headers_array, g_object_ref (header));
1385 g_object_unref (header);
1386 tny_iterator_next (new_headers_iter);
1388 g_object_unref (new_headers_iter);
1390 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1391 g_object_unref (observer);
1393 /* We no not need to do it the first time,
1394 because it's automatically done by the tree
1396 if (G_LIKELY (!first_time))
1397 tny_folder_poke_status (folder);
1399 g_object_unref (folder);
1401 tny_iterator_next (iter);
1403 g_object_unref (G_OBJECT (iter));
1404 g_source_remove (timeout);
1406 if (priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED &&
1407 priv->status != MODEST_MAIL_OPERATION_STATUS_FAILED &&
1408 new_headers_array->len > 0) {
1412 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1414 /* TODO: Ask the user, instead of just failing,
1415 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1416 * all' and 'Newest only' buttons. */
1417 if (new_headers_array->len > info->retrieve_limit) {
1421 /* Should be get only the headers or the message as well? */
1422 if (g_ascii_strcasecmp (info->retrieve_type,
1423 MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY) != 0) {
1425 priv->total = MIN (new_headers_array->len, info->retrieve_limit);
1426 while (msg_num < priv->total) {
1428 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1429 TnyFolder *folder = tny_header_get_folder (header);
1430 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1431 ModestMailOperationState *state;
1435 /* We can not just use the mail operation because the
1436 values of done and total could change before the
1438 state = modest_mail_operation_clone_state (info->mail_op);
1439 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1440 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1441 pair, (GDestroyNotify) modest_pair_free);
1443 g_object_unref (msg);
1444 g_object_unref (folder);
1451 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1454 /* Copy the headers to a list and free the array */
1455 new_headers = tny_simple_list_new ();
1456 for (i=0; i < new_headers_array->len; i++) {
1457 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1458 tny_list_append (new_headers, G_OBJECT (header));
1460 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1461 g_ptr_array_free (new_headers_array, FALSE);
1464 /* Perform send (if operation was not cancelled) */
1467 if (priv->account != NULL)
1468 g_object_unref (priv->account);
1470 if (info->transport_account) {
1471 priv->account = g_object_ref (info->transport_account);
1473 send_queue = modest_runtime_get_send_queue (info->transport_account);
1475 modest_tny_send_queue_try_to_send (send_queue);
1477 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1478 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1479 "cannot create a send queue for %s\n",
1480 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1481 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1485 /* Check if the operation was a success */
1487 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1489 /* Update the last updated key */
1490 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1491 set_last_updated_idle,
1492 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1493 (GDestroyNotify) g_free);
1497 /* Set the account back to not busy */
1498 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
1499 info->account_name, FALSE);
1501 if (info->callback) {
1502 UpdateAccountInfo *idle_info;
1504 /* This thread is not in the main lock */
1505 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1506 idle_info->mail_op = g_object_ref (info->mail_op);
1507 idle_info->new_headers = (new_headers) ? g_object_ref (new_headers) : NULL;
1508 idle_info->callback = info->callback;
1509 idle_info->user_data = info->user_data;
1510 g_idle_add (idle_update_account_cb, idle_info);
1513 /* Notify about operation end. Note that the info could be
1514 freed before this idle happens, but the mail operation will
1516 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
1520 g_object_unref (new_headers);
1522 g_object_unref (all_folders);
1523 g_object_unref (info->account);
1524 if (info->transport_account)
1525 g_object_unref (info->transport_account);
1526 g_free (info->account_name);
1527 g_free (info->retrieve_type);
1528 g_slice_free (UpdateAccountInfo, info);
1536 modest_mail_operation_update_account (ModestMailOperation *self,
1537 const gchar *account_name,
1538 UpdateAccountCallback callback,
1541 GThread *thread = NULL;
1542 UpdateAccountInfo *info = NULL;
1543 ModestMailOperationPrivate *priv = NULL;
1544 ModestAccountMgr *mgr = NULL;
1545 TnyStoreAccount *store_account = NULL;
1546 TnyTransportAccount *transport_account = NULL;
1548 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1549 g_return_val_if_fail (account_name, FALSE);
1551 /* Init mail operation. Set total and done to 0, and do not
1552 update them, this way the progress objects will know that
1553 we have no clue about the number of the objects */
1554 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1557 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1558 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1560 /* Get the store account */
1561 store_account = (TnyStoreAccount *)
1562 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1564 TNY_ACCOUNT_TYPE_STORE);
1566 if (!store_account) {
1567 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1568 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1569 "cannot get tny store account for %s\n", account_name);
1573 priv->account = g_object_ref (store_account);
1575 /* Get the transport account, we can not do it in the thread
1576 due to some problems with dbus */
1577 transport_account = (TnyTransportAccount *)
1578 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1580 if (!transport_account) {
1581 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1582 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1583 "cannot get tny transport account for %s\n", account_name);
1587 /* Create the helper object */
1588 info = g_slice_new (UpdateAccountInfo);
1589 info->mail_op = self;
1590 info->account = store_account;
1591 info->transport_account = transport_account;
1592 info->callback = callback;
1593 info->account_name = g_strdup (account_name);
1594 info->user_data = user_data;
1596 /* Get the message size limit */
1597 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1598 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1599 if (info->max_size == 0)
1600 info->max_size = G_MAXINT;
1602 info->max_size = info->max_size * KB;
1604 /* Get per-account retrieval type */
1605 mgr = modest_runtime_get_account_mgr ();
1606 info->retrieve_type = modest_account_mgr_get_retrieve_type (mgr, account_name);
1608 /* Get per-account message amount retrieval limit */
1609 info->retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, account_name);
1610 if (info->retrieve_limit == 0)
1611 info->retrieve_limit = G_MAXINT;
1613 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1615 /* Set account busy */
1616 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1618 modest_mail_operation_notify_start (self);
1619 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1625 g_object_unref (store_account);
1626 if (transport_account)
1627 g_object_unref (transport_account);
1628 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1630 callback (self, NULL, user_data);
1632 modest_mail_operation_notify_end (self);
1636 /* ******************************************************************* */
1637 /* ************************** STORE ACTIONS ************************* */
1638 /* ******************************************************************* */
1642 modest_mail_operation_create_folder (ModestMailOperation *self,
1643 TnyFolderStore *parent,
1646 ModestMailOperationPrivate *priv;
1647 TnyFolder *new_folder = NULL;
1649 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1650 g_return_val_if_fail (name, NULL);
1652 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1653 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1654 priv->account = (TNY_IS_ACCOUNT (parent)) ?
1655 g_object_ref (parent) :
1656 modest_tny_folder_get_account (TNY_FOLDER (parent));
1658 /* Check for already existing folder */
1659 if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1660 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1661 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1662 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1663 _CS("ckdg_ib_folder_already_exists"));
1667 if (TNY_IS_FOLDER (parent)) {
1668 /* Check folder rules */
1669 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1670 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1671 /* Set status failed and set an error */
1672 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1673 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1674 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1675 _("mail_in_ui_folder_create_error"));
1679 if (!strcmp (name, " ") || strchr (name, '/')) {
1680 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1681 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1682 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1683 _("mail_in_ui_folder_create_error"));
1687 /* Create the folder */
1688 modest_mail_operation_notify_start (self);
1689 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1690 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1692 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1695 /* Notify about operation end */
1696 modest_mail_operation_notify_end (self);
1702 modest_mail_operation_remove_folder (ModestMailOperation *self,
1704 gboolean remove_to_trash)
1706 TnyAccount *account;
1707 ModestMailOperationPrivate *priv;
1708 ModestTnyFolderRules rules;
1710 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1711 g_return_if_fail (TNY_IS_FOLDER (folder));
1713 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1715 /* Check folder rules */
1716 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1717 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1718 /* Set status failed and set an error */
1719 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1720 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1721 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1722 _("mail_in_ui_folder_delete_error"));
1726 /* Get the account */
1727 account = modest_tny_folder_get_account (folder);
1728 priv->account = g_object_ref(account);
1729 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1731 /* Delete folder or move to trash */
1732 if (remove_to_trash) {
1733 TnyFolder *trash_folder = NULL;
1734 trash_folder = modest_tny_account_get_special_folder (account,
1735 TNY_FOLDER_TYPE_TRASH);
1736 /* TODO: error_handling */
1738 modest_mail_operation_notify_start (self);
1739 modest_mail_operation_xfer_folder (self, folder,
1740 TNY_FOLDER_STORE (trash_folder),
1742 g_object_unref (trash_folder);
1745 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1747 modest_mail_operation_notify_start (self);
1748 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1749 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1752 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1755 g_object_unref (G_OBJECT (parent));
1757 g_object_unref (G_OBJECT (account));
1760 /* Notify about operation end */
1761 modest_mail_operation_notify_end (self);
1765 transfer_folder_status_cb (GObject *obj,
1769 ModestMailOperation *self;
1770 ModestMailOperationPrivate *priv;
1771 ModestMailOperationState *state;
1772 XFerMsgAsyncHelper *helper;
1774 g_return_if_fail (status != NULL);
1775 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1777 helper = (XFerMsgAsyncHelper *) user_data;
1778 g_return_if_fail (helper != NULL);
1780 self = helper->mail_op;
1781 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1783 priv->done = status->position;
1784 priv->total = status->of_total;
1786 state = modest_mail_operation_clone_state (self);
1788 /* This is not a GDK lock because we are a Tinymail callback
1789 * which is already GDK locked by Tinymail */
1791 /* no gdk_threads_enter (), CHECKED */
1793 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1795 /* no gdk_threads_leave (), CHECKED */
1797 g_slice_free (ModestMailOperationState, state);
1802 transfer_folder_cb (TnyFolder *folder,
1804 TnyFolderStore *into,
1805 TnyFolder *new_folder,
1809 XFerMsgAsyncHelper *helper;
1810 ModestMailOperation *self = NULL;
1811 ModestMailOperationPrivate *priv = NULL;
1813 helper = (XFerMsgAsyncHelper *) user_data;
1814 g_return_if_fail (helper != NULL);
1816 self = helper->mail_op;
1817 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1820 priv->error = g_error_copy (err);
1822 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1823 } else if (cancelled) {
1824 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1825 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1826 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1827 _("Transference of %s was cancelled."),
1828 tny_folder_get_name (folder));
1831 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1834 /* Notify about operation end */
1835 modest_mail_operation_notify_end (self);
1837 /* If user defined callback function was defined, call it */
1838 if (helper->user_callback) {
1840 /* This is not a GDK lock because we are a Tinymail callback
1841 * which is already GDK locked by Tinymail */
1843 /* no gdk_threads_enter (), CHECKED */
1844 helper->user_callback (self, helper->user_data);
1845 /* no gdk_threads_leave () , CHECKED */
1849 g_object_unref (helper->mail_op);
1850 g_slice_free (XFerMsgAsyncHelper, helper);
1855 * This function checks if the new name is a valid name for our local
1856 * folders account. The new name could not be the same than then name
1857 * of any of the mandatory local folders
1859 * We can not rely on tinymail because tinymail does not check the
1860 * name of the virtual folders that the account could have in the case
1861 * that we're doing a rename (because it directly calls Camel which
1862 * knows nothing about our virtual folders).
1864 * In the case of an actual copy/move (i.e. move/copy a folder between
1865 * accounts) tinymail uses the tny_folder_store_create_account which
1866 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1867 * checks the new name of the folder, so this call in that case
1868 * wouldn't be needed. *But* NOTE that if tinymail changes its
1869 * implementation (if folder transfers within the same account is no
1870 * longer implemented as a rename) this call will allow Modest to work
1873 * If the new name is not valid, this function will set the status to
1874 * failed and will set also an error in the mail operation
1877 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1878 TnyFolderStore *into,
1879 const gchar *new_name)
1881 if (TNY_IS_ACCOUNT (into) &&
1882 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1883 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1885 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1886 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1887 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1888 _CS("ckdg_ib_folder_already_exists"));
1895 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1897 TnyFolderStore *parent,
1898 gboolean delete_original,
1899 XferAsyncUserCallback user_callback,
1902 ModestMailOperationPrivate *priv = NULL;
1903 ModestTnyFolderRules parent_rules = 0, rules;
1904 XFerMsgAsyncHelper *helper = NULL;
1905 const gchar *folder_name = NULL;
1906 const gchar *error_msg;
1908 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1909 g_return_if_fail (TNY_IS_FOLDER (folder));
1910 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1912 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1913 folder_name = tny_folder_get_name (folder);
1915 /* Set the error msg */
1916 error_msg = _("mail_in_ui_folder_move_target_error");
1918 /* Get account and set it into mail_operation */
1919 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1920 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1921 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1923 /* Get folder rules */
1924 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1925 if (TNY_IS_FOLDER (parent))
1926 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1928 /* Apply operation constraints */
1929 if ((gpointer) parent == (gpointer) folder ||
1930 (!TNY_IS_FOLDER_STORE (parent)) ||
1931 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1934 } else if (TNY_IS_FOLDER (parent) &&
1935 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1939 } else if (TNY_IS_FOLDER (parent) &&
1940 TNY_IS_FOLDER_STORE (folder) &&
1941 modest_tny_folder_is_ancestor (TNY_FOLDER (parent),
1942 TNY_FOLDER_STORE (folder))) {
1943 /* Do not move a parent into a child */
1945 } else if (TNY_IS_FOLDER_STORE (parent) &&
1946 modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1947 /* Check that the new folder name is not used by any
1950 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1951 /* Check that the new folder name is not used by any
1952 special local folder */
1955 /* Create the helper */
1956 helper = g_slice_new0 (XFerMsgAsyncHelper);
1957 helper->mail_op = g_object_ref (self);
1958 helper->dest_folder = NULL;
1959 helper->headers = NULL;
1960 helper->user_callback = user_callback;
1961 helper->user_data = user_data;
1963 /* Move/Copy folder */
1964 modest_mail_operation_notify_start (self);
1965 tny_folder_copy_async (folder,
1967 tny_folder_get_name (folder),
1970 transfer_folder_status_cb,
1976 /* Set status failed and set an error */
1977 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1978 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1979 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1982 /* Call the user callback if exists */
1984 user_callback (self, user_data);
1986 /* Notify the queue */
1987 modest_mail_operation_notify_end (self);
1991 modest_mail_operation_rename_folder (ModestMailOperation *self,
1995 ModestMailOperationPrivate *priv;
1996 ModestTnyFolderRules rules;
1997 XFerMsgAsyncHelper *helper;
1999 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2000 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
2001 g_return_if_fail (name);
2003 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2005 /* Get account and set it into mail_operation */
2006 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2007 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2009 /* Check folder rules */
2010 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2011 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2012 /* Set status failed and set an error */
2013 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2014 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2015 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2016 _("FIXME: unable to rename"));
2018 /* Notify about operation end */
2019 modest_mail_operation_notify_end (self);
2020 } else if (!strcmp (name, " ") || strchr (name, '/')) {
2021 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2022 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2023 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2024 _("FIXME: unable to rename"));
2025 /* Notify about operation end */
2026 modest_mail_operation_notify_end (self);
2028 TnyFolderStore *into;
2030 into = tny_folder_get_folder_store (folder);
2032 /* Check that the new folder name is not used by any
2033 special local folder */
2034 if (new_name_valid_if_local_account (priv, into, name)) {
2035 /* Create the helper */
2036 helper = g_slice_new0 (XFerMsgAsyncHelper);
2037 helper->mail_op = g_object_ref(self);
2038 helper->dest_folder = NULL;
2039 helper->headers = NULL;
2040 helper->user_callback = NULL;
2041 helper->user_data = NULL;
2043 /* Rename. Camel handles folder subscription/unsubscription */
2044 modest_mail_operation_notify_start (self);
2045 tny_folder_copy_async (folder, into, name, TRUE,
2047 transfer_folder_status_cb,
2050 modest_mail_operation_notify_end (self);
2052 g_object_unref (into);
2056 /* ******************************************************************* */
2057 /* ************************** MSG ACTIONS ************************* */
2058 /* ******************************************************************* */
2061 modest_mail_operation_get_msg (ModestMailOperation *self,
2063 GetMsgAsyncUserCallback user_callback,
2066 GetMsgAsyncHelper *helper = NULL;
2068 ModestMailOperationPrivate *priv;
2070 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2071 g_return_if_fail (TNY_IS_HEADER (header));
2073 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2074 folder = tny_header_get_folder (header);
2076 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2077 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2079 /* Get account and set it into mail_operation */
2080 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2082 /* Check for cached messages */
2083 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2084 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2086 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2088 helper = g_slice_new0 (GetMsgAsyncHelper);
2089 helper->mail_op = self;
2090 helper->user_callback = user_callback;
2091 helper->user_data = user_data;
2092 helper->header = g_object_ref (header);
2094 /* The callback's reference so that the mail op is not
2095 * finalized until the async operation is completed even if
2096 * the user canceled the request meanwhile.
2098 g_object_ref (G_OBJECT (helper->mail_op));
2100 modest_mail_operation_notify_start (self);
2101 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
2103 g_object_unref (G_OBJECT (folder));
2107 get_msg_cb (TnyFolder *folder,
2113 GetMsgAsyncHelper *helper = NULL;
2114 ModestMailOperation *self = NULL;
2115 ModestMailOperationPrivate *priv = NULL;
2117 helper = (GetMsgAsyncHelper *) user_data;
2118 g_return_if_fail (helper != NULL);
2119 self = helper->mail_op;
2120 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2121 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2123 /* Check errors and cancel */
2125 priv->error = g_error_copy (error);
2126 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2127 } else if (cancelled) {
2128 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2129 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2130 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2131 _("Error trying to refresh the contents of %s"),
2132 tny_folder_get_name (folder));
2134 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2137 /* If user defined callback function was defined, call it even
2138 if the operation failed*/
2139 if (helper->user_callback) {
2140 /* This is not a GDK lock because we are a Tinymail callback
2141 * which is already GDK locked by Tinymail */
2143 /* no gdk_threads_enter (), CHECKED */
2144 helper->user_callback (self, helper->header, msg, helper->user_data);
2145 /* no gdk_threads_leave (), CHECKED */
2148 /* Notify about operation end */
2149 modest_mail_operation_notify_end (self);
2151 g_object_unref (helper->mail_op);
2152 g_object_unref (helper->header);
2153 g_slice_free (GetMsgAsyncHelper, helper);
2158 get_msg_status_cb (GObject *obj,
2162 GetMsgAsyncHelper *helper = NULL;
2163 ModestMailOperation *self;
2164 ModestMailOperationPrivate *priv;
2165 ModestMailOperationState *state;
2167 g_return_if_fail (status != NULL);
2168 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2170 helper = (GetMsgAsyncHelper *) user_data;
2171 g_return_if_fail (helper != NULL);
2173 self = helper->mail_op;
2174 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2179 state = modest_mail_operation_clone_state (self);
2180 state->bytes_done = status->position;
2181 state->bytes_total = status->of_total;
2183 /* This is not a GDK lock because we are a Tinymail callback
2184 * which is already GDK locked by Tinymail */
2186 /* no gdk_threads_enter (), CHECKED */
2187 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2188 /* no gdk_threads_leave (), CHECKED */
2190 g_slice_free (ModestMailOperationState, state);
2193 /****************************************************/
2195 ModestMailOperation *mail_op;
2197 GetMsgAsyncUserCallback user_callback;
2199 GDestroyNotify notify;
2203 GetMsgAsyncUserCallback user_callback;
2207 ModestMailOperation *mail_op;
2208 } NotifyGetMsgsInfo;
2212 * Used by get_msgs_full_thread to call the user_callback for each
2213 * message that has been read
2216 notify_get_msgs_full (gpointer data)
2218 NotifyGetMsgsInfo *info;
2220 info = (NotifyGetMsgsInfo *) data;
2222 /* This is a GDK lock because we are an idle callback and
2223 * because info->user_callback can contain Gtk+ code */
2225 gdk_threads_enter (); /* CHECKED */
2226 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
2227 gdk_threads_leave (); /* CHECKED */
2229 g_slice_free (NotifyGetMsgsInfo, info);
2235 * Used by get_msgs_full_thread to free al the thread resources and to
2236 * call the destroy function for the passed user_data
2239 get_msgs_full_destroyer (gpointer data)
2241 GetFullMsgsInfo *info;
2243 info = (GetFullMsgsInfo *) data;
2247 /* This is a GDK lock because we are an idle callback and
2248 * because info->notify can contain Gtk+ code */
2250 gdk_threads_enter (); /* CHECKED */
2251 info->notify (info->user_data);
2252 gdk_threads_leave (); /* CHECKED */
2256 g_object_unref (info->headers);
2257 g_slice_free (GetFullMsgsInfo, info);
2263 get_msgs_full_thread (gpointer thr_user_data)
2265 GetFullMsgsInfo *info;
2266 ModestMailOperationPrivate *priv = NULL;
2267 TnyIterator *iter = NULL;
2269 info = (GetFullMsgsInfo *) thr_user_data;
2270 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2272 iter = tny_list_create_iterator (info->headers);
2273 while (!tny_iterator_is_done (iter)) {
2277 header = TNY_HEADER (tny_iterator_get_current (iter));
2278 folder = tny_header_get_folder (header);
2280 /* Check for cached messages */
2281 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2282 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2284 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2286 /* Get message from folder */
2289 /* The callback will call it per each header */
2290 msg = tny_folder_get_msg (folder, header, &(priv->error));
2293 ModestMailOperationState *state;
2298 /* notify progress */
2299 state = modest_mail_operation_clone_state (info->mail_op);
2300 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
2301 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
2302 pair, (GDestroyNotify) modest_pair_free);
2304 /* The callback is the responsible for
2305 freeing the message */
2306 if (info->user_callback) {
2307 NotifyGetMsgsInfo *info_notify;
2308 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
2309 info_notify->user_callback = info->user_callback;
2310 info_notify->mail_op = info->mail_op;
2311 info_notify->header = g_object_ref (header);
2312 info_notify->msg = g_object_ref (msg);
2313 info_notify->user_data = info->user_data;
2314 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
2315 notify_get_msgs_full,
2318 g_object_unref (msg);
2321 /* Set status failed and set an error */
2322 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2323 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2324 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2325 "Error trying to get a message. No folder found for header");
2329 g_object_unref (header);
2331 tny_iterator_next (iter);
2334 /* Set operation status */
2335 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2336 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2338 /* Notify about operation end */
2339 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
2341 /* Free thread resources. Will be called after all previous idles */
2342 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
2348 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2349 TnyList *header_list,
2350 GetMsgAsyncUserCallback user_callback,
2352 GDestroyNotify notify)
2354 TnyHeader *header = NULL;
2355 TnyFolder *folder = NULL;
2357 ModestMailOperationPrivate *priv = NULL;
2358 GetFullMsgsInfo *info = NULL;
2359 gboolean size_ok = TRUE;
2361 TnyIterator *iter = NULL;
2363 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2365 /* Init mail operation */
2366 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2367 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2368 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2370 priv->total = tny_list_get_length(header_list);
2372 /* Get account and set it into mail_operation */
2373 if (tny_list_get_length (header_list) >= 1) {
2374 iter = tny_list_create_iterator (header_list);
2375 header = TNY_HEADER (tny_iterator_get_current (iter));
2377 folder = tny_header_get_folder (header);
2379 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2380 g_object_unref (folder);
2382 g_object_unref (header);
2385 if (tny_list_get_length (header_list) == 1) {
2386 g_object_unref (iter);
2391 /* Get msg size limit */
2392 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2393 MODEST_CONF_MSG_SIZE_LIMIT,
2396 g_clear_error (&(priv->error));
2397 max_size = G_MAXINT;
2399 max_size = max_size * KB;
2402 /* Check message size limits. If there is only one message
2403 always retrieve it */
2405 while (!tny_iterator_is_done (iter) && size_ok) {
2406 header = TNY_HEADER (tny_iterator_get_current (iter));
2408 if (tny_header_get_message_size (header) >= max_size)
2410 g_object_unref (header);
2413 tny_iterator_next (iter);
2415 g_object_unref (iter);
2419 /* Create the info */
2420 info = g_slice_new0 (GetFullMsgsInfo);
2421 info->mail_op = self;
2422 info->user_callback = user_callback;
2423 info->user_data = user_data;
2424 info->headers = g_object_ref (header_list);
2425 info->notify = notify;
2427 modest_mail_operation_notify_start (self);
2428 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
2430 /* Set status failed and set an error */
2431 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2432 /* FIXME: the error msg is different for pop */
2433 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2434 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2435 _("emev_ni_ui_imap_msg_size_exceed_error"));
2436 /* Remove from queue and free resources */
2437 modest_mail_operation_notify_end (self);
2445 modest_mail_operation_remove_msg (ModestMailOperation *self,
2447 gboolean remove_to_trash /*ignored*/)
2450 ModestMailOperationPrivate *priv;
2452 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2453 g_return_if_fail (TNY_IS_HEADER (header));
2455 if (remove_to_trash)
2456 g_warning ("remove to trash is not implemented");
2458 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2459 folder = tny_header_get_folder (header);
2461 /* Get account and set it into mail_operation */
2462 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2463 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2464 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2466 /* remove message from folder */
2467 tny_folder_remove_msg (folder, header, &(priv->error));
2469 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2470 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2472 modest_mail_operation_notify_start (self);
2474 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
2475 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* FALSE --> don't expunge *\/ */
2476 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
2477 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
2478 /* tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /\* TRUE --> dont expunge *\/ */
2479 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2482 /* tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /\* TRUE --> expunge *\/ */
2483 tny_folder_sync (folder, TRUE, &(priv->error)); /* TRUE --> expunge */
2489 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2491 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2494 g_object_unref (G_OBJECT (folder));
2496 /* Notify about operation end */
2497 modest_mail_operation_notify_end (self);
2501 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2503 gboolean remove_to_trash /*ignored*/)
2506 ModestMailOperationPrivate *priv;
2507 TnyIterator *iter = NULL;
2508 TnyHeader *header = NULL;
2510 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2511 g_return_if_fail (TNY_IS_LIST (headers));
2513 if (remove_to_trash)
2514 g_warning ("remove to trash is not implemented");
2516 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2518 /* Get folder from first header and sync it */
2519 iter = tny_list_create_iterator (headers);
2520 header = TNY_HEADER (tny_iterator_get_current (iter));
2521 folder = tny_header_get_folder (header);
2523 /* Get account and set it into mail_operation */
2524 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2525 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2526 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2528 /* remove message from folder */
2529 modest_mail_operation_notify_start (self);
2531 tny_folder_remove_msgs (folder, headers, &(priv->error));
2533 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2534 TNY_IS_CAMEL_POP_FOLDER (folder))
2535 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */
2538 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2544 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2546 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2549 g_object_unref (header);
2550 g_object_unref (iter);
2551 g_object_unref (G_OBJECT (folder));
2553 /* Notify about operation end */
2554 modest_mail_operation_notify_end (self);
2559 transfer_msgs_status_cb (GObject *obj,
2563 XFerMsgAsyncHelper *helper = NULL;
2564 ModestMailOperation *self;
2565 ModestMailOperationPrivate *priv;
2566 ModestMailOperationState *state;
2567 gboolean is_num_bytes;
2569 g_return_if_fail (status != NULL);
2570 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2572 helper = (XFerMsgAsyncHelper *) user_data;
2573 g_return_if_fail (helper != NULL);
2575 self = helper->mail_op;
2576 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2578 /* We know that tinymail sends us information about
2579 transferred bytes with this particular message */
2580 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2582 state = modest_mail_operation_clone_state (self);
2583 if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2584 /* We know that we're in a different message when the
2585 total number of bytes to transfer is different. Of
2586 course it could fail if we're transferring messages
2587 of the same size, but this is a workarround */
2588 if (status->of_total != helper->last_total_bytes) {
2590 helper->sum_total_bytes += helper->last_total_bytes;
2591 helper->last_total_bytes = status->of_total;
2593 state->bytes_done += status->position + helper->sum_total_bytes;
2594 state->bytes_total = helper->total_bytes;
2597 /* Notify the status change. Only notify about changes
2598 referred to bytes */
2599 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2603 g_slice_free (ModestMailOperationState, state);
2608 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2610 XFerMsgAsyncHelper *helper;
2611 ModestMailOperation *self;
2612 ModestMailOperationPrivate *priv;
2613 TnyIterator *iter = NULL;
2614 TnyHeader *header = NULL;
2616 helper = (XFerMsgAsyncHelper *) user_data;
2617 self = helper->mail_op;
2619 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2622 priv->error = g_error_copy (err);
2624 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2625 } else if (cancelled) {
2626 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2627 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2628 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2629 _("Error trying to refresh the contents of %s"),
2630 tny_folder_get_name (folder));
2633 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2635 /* Update folder counts */
2636 tny_folder_poke_status (folder);
2637 tny_folder_poke_status (helper->dest_folder);
2641 /* Mark headers as deleted and seen */
2642 if ((helper->delete) &&
2643 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2644 iter = tny_list_create_iterator (helper->headers);
2645 while (!tny_iterator_is_done (iter)) {
2646 header = TNY_HEADER (tny_iterator_get_current (iter));
2647 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2648 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2649 g_object_unref (header);
2651 tny_iterator_next (iter);
2657 /* Notify about operation end */
2658 modest_mail_operation_notify_end (self);
2660 /* If user defined callback function was defined, call it */
2661 if (helper->user_callback) {
2662 /* This is not a GDK lock because we are a Tinymail callback and
2663 * Tinymail already acquires the Gdk lock */
2665 /* no gdk_threads_enter (), CHECKED */
2666 helper->user_callback (self, helper->user_data);
2667 /* no gdk_threads_leave (), CHECKED */
2671 if (helper->headers)
2672 g_object_unref (helper->headers);
2673 if (helper->dest_folder)
2674 g_object_unref (helper->dest_folder);
2675 if (helper->mail_op)
2676 g_object_unref (helper->mail_op);
2678 g_object_unref (folder);
2680 g_object_unref (iter);
2681 g_slice_free (XFerMsgAsyncHelper, helper);
2685 compute_message_list_size (TnyList *headers)
2690 iter = tny_list_create_iterator (headers);
2691 while (!tny_iterator_is_done (iter)) {
2692 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2693 size += tny_header_get_message_size (header);
2694 g_object_unref (header);
2695 tny_iterator_next (iter);
2697 g_object_unref (iter);
2703 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2706 gboolean delete_original,
2707 XferAsyncUserCallback user_callback,
2710 ModestMailOperationPrivate *priv = NULL;
2711 TnyIterator *iter = NULL;
2712 TnyFolder *src_folder = NULL;
2713 XFerMsgAsyncHelper *helper = NULL;
2714 TnyHeader *header = NULL;
2715 ModestTnyFolderRules rules = 0;
2717 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2718 g_return_if_fail (headers && TNY_IS_LIST (headers));
2719 g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2721 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2722 priv->total = tny_list_get_length (headers);
2724 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2725 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2727 /* Apply folder rules */
2728 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2729 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2730 /* Set status failed and set an error */
2731 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2732 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2733 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2734 _CS("ckct_ib_unable_to_paste_here"));
2735 /* Notify the queue */
2736 modest_mail_operation_notify_end (self);
2740 /* Get source folder */
2741 iter = tny_list_create_iterator (headers);
2742 header = TNY_HEADER (tny_iterator_get_current (iter));
2744 src_folder = tny_header_get_folder (header);
2745 g_object_unref (header);
2747 g_object_unref (iter);
2749 if (src_folder == NULL) {
2750 /* Notify the queue */
2751 modest_mail_operation_notify_end (self);
2753 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2758 /* Check folder source and destination */
2759 if (src_folder == folder) {
2760 /* Set status failed and set an error */
2761 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2762 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2763 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2764 _("mcen_ib_unable_to_copy_samefolder"));
2766 /* Notify the queue */
2767 modest_mail_operation_notify_end (self);
2770 g_object_unref (src_folder);
2774 /* Create the helper */
2775 helper = g_slice_new0 (XFerMsgAsyncHelper);
2776 helper->mail_op = g_object_ref(self);
2777 helper->dest_folder = g_object_ref(folder);
2778 helper->headers = g_object_ref(headers);
2779 helper->user_callback = user_callback;
2780 helper->user_data = user_data;
2781 helper->delete = delete_original;
2782 helper->last_total_bytes = 0;
2783 helper->sum_total_bytes = 0;
2784 helper->total_bytes = compute_message_list_size (headers);
2786 /* Get account and set it into mail_operation */
2787 priv->account = modest_tny_folder_get_account (src_folder);
2789 /* Transfer messages */
2790 modest_mail_operation_notify_start (self);
2791 tny_folder_transfer_msgs_async (src_folder,
2796 transfer_msgs_status_cb,
2802 on_refresh_folder (TnyFolder *folder,
2807 RefreshAsyncHelper *helper = NULL;
2808 ModestMailOperation *self = NULL;
2809 ModestMailOperationPrivate *priv = NULL;
2811 helper = (RefreshAsyncHelper *) user_data;
2812 self = helper->mail_op;
2813 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2815 g_return_if_fail(priv!=NULL);
2818 priv->error = g_error_copy (error);
2819 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2824 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2825 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2826 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2827 _("Error trying to refresh the contents of %s"),
2828 tny_folder_get_name (folder));
2832 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2835 /* Call user defined callback, if it exists */
2836 if (helper->user_callback) {
2838 /* This is not a GDK lock because we are a Tinymail callback and
2839 * Tinymail already acquires the Gdk lock */
2840 helper->user_callback (self, folder, helper->user_data);
2844 g_slice_free (RefreshAsyncHelper, helper);
2846 /* Notify about operation end */
2847 modest_mail_operation_notify_end (self);
2848 g_object_unref(self);
2852 on_refresh_folder_status_update (GObject *obj,
2856 RefreshAsyncHelper *helper = NULL;
2857 ModestMailOperation *self = NULL;
2858 ModestMailOperationPrivate *priv = NULL;
2859 ModestMailOperationState *state;
2861 g_return_if_fail (user_data != NULL);
2862 g_return_if_fail (status != NULL);
2863 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2865 helper = (RefreshAsyncHelper *) user_data;
2866 self = helper->mail_op;
2867 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2869 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2871 priv->done = status->position;
2872 priv->total = status->of_total;
2874 state = modest_mail_operation_clone_state (self);
2876 /* This is not a GDK lock because we are a Tinymail callback and
2877 * Tinymail already acquires the Gdk lock */
2878 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2880 g_slice_free (ModestMailOperationState, state);
2884 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2886 RefreshAsyncUserCallback user_callback,
2889 ModestMailOperationPrivate *priv = NULL;
2890 RefreshAsyncHelper *helper = NULL;
2892 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2894 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2896 /* Get account and set it into mail_operation */
2897 priv->account = modest_tny_folder_get_account (folder);
2898 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2900 /* Create the helper */
2901 helper = g_slice_new0 (RefreshAsyncHelper);
2902 helper->mail_op = g_object_ref(self);
2903 helper->user_callback = user_callback;
2904 helper->user_data = user_data;
2906 /* Refresh the folder. TODO: tinymail could issue a status
2907 updates before the callback call then this could happen. We
2908 must review the design */
2909 modest_mail_operation_notify_start (self);
2910 tny_folder_refresh_async (folder,
2912 on_refresh_folder_status_update,
2918 modest_mail_operation_notify_start (ModestMailOperation *self)
2920 ModestMailOperationPrivate *priv = NULL;
2922 g_return_if_fail (self);
2924 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2926 /* Ensure that all the fields are filled correctly */
2927 g_return_if_fail (priv->account != NULL);
2928 g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2930 /* Notify the observers about the mail operation. We do not
2931 wrapp this emission because we assume that this function is
2932 always called from within the main lock */
2933 g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2938 * It's used by the mail operation queue to notify the observers
2939 * attached to that signal that the operation finished. We need to use
2940 * that because tinymail does not give us the progress of a given
2941 * operation when it finishes (it directly calls the operation
2945 modest_mail_operation_notify_end (ModestMailOperation *self)
2947 ModestMailOperationPrivate *priv = NULL;
2949 g_return_if_fail (self);
2951 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2953 /* Notify the observers about the mail operation end. We do
2954 not wrapp this emission because we assume that this
2955 function is always called from within the main lock */
2956 g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2958 /* Remove the error user data */
2959 if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2960 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2964 modest_mail_operation_get_account (ModestMailOperation *self)
2966 ModestMailOperationPrivate *priv = NULL;
2968 g_return_val_if_fail (self, NULL);
2970 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2972 return (priv->account) ? g_object_ref (priv->account) : NULL;