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-simple-list.h>
41 #include <tny-send-queue.h>
42 #include <tny-status.h>
43 #include <tny-folder-observer.h>
44 #include <camel/camel-stream-mem.h>
45 #include <glib/gi18n.h>
46 #include "modest-platform.h"
47 #include <modest-tny-account.h>
48 #include <modest-tny-send-queue.h>
49 #include <modest-runtime.h>
50 #include "modest-text-utils.h"
51 #include "modest-tny-msg.h"
52 #include "modest-tny-folder.h"
53 #include "modest-tny-platform-factory.h"
54 #include "modest-marshal.h"
55 #include "modest-error.h"
56 #include "modest-mail-operation.h"
60 /* 'private'/'protected' functions */
61 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
62 static void modest_mail_operation_init (ModestMailOperation *obj);
63 static void modest_mail_operation_finalize (GObject *obj);
65 static void get_msg_cb (TnyFolder *folder,
71 static void get_msg_status_cb (GObject *obj,
75 static void modest_mail_operation_notify_end (ModestMailOperation *self,
78 static gboolean did_a_cancel = FALSE;
80 enum _ModestMailOperationSignals
82 PROGRESS_CHANGED_SIGNAL,
87 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
88 struct _ModestMailOperationPrivate {
95 ErrorCheckingUserCallback error_checking;
96 gpointer error_checking_user_data;
97 ModestMailOperationStatus status;
98 ModestMailOperationTypeOperation op_type;
101 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
102 MODEST_TYPE_MAIL_OPERATION, \
103 ModestMailOperationPrivate))
105 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
106 priv->status = new_status;\
109 typedef struct _GetMsgAsyncHelper {
110 ModestMailOperation *mail_op;
112 GetMsgAsyncUserCallback user_callback;
116 typedef struct _RefreshAsyncHelper {
117 ModestMailOperation *mail_op;
118 RefreshAsyncUserCallback user_callback;
120 } RefreshAsyncHelper;
122 typedef struct _XFerMsgAsyncHelper
124 ModestMailOperation *mail_op;
126 TnyFolder *dest_folder;
127 XferMsgsAsynUserCallback user_callback;
129 } XFerMsgAsyncHelper;
132 static GObjectClass *parent_class = NULL;
134 static guint signals[NUM_SIGNALS] = {0};
137 modest_mail_operation_get_type (void)
139 static GType my_type = 0;
141 static const GTypeInfo my_info = {
142 sizeof(ModestMailOperationClass),
143 NULL, /* base init */
144 NULL, /* base finalize */
145 (GClassInitFunc) modest_mail_operation_class_init,
146 NULL, /* class finalize */
147 NULL, /* class data */
148 sizeof(ModestMailOperation),
150 (GInstanceInitFunc) modest_mail_operation_init,
153 my_type = g_type_register_static (G_TYPE_OBJECT,
154 "ModestMailOperation",
161 modest_mail_operation_class_init (ModestMailOperationClass *klass)
163 GObjectClass *gobject_class;
164 gobject_class = (GObjectClass*) klass;
166 parent_class = g_type_class_peek_parent (klass);
167 gobject_class->finalize = modest_mail_operation_finalize;
169 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
172 * ModestMailOperation::progress-changed
173 * @self: the #MailOperation that emits the signal
174 * @user_data: user data set when the signal handler was connected
176 * Emitted when the progress of a mail operation changes
178 signals[PROGRESS_CHANGED_SIGNAL] =
179 g_signal_new ("progress-changed",
180 G_TYPE_FROM_CLASS (gobject_class),
182 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
184 g_cclosure_marshal_VOID__POINTER,
185 G_TYPE_NONE, 1, G_TYPE_POINTER);
190 modest_mail_operation_init (ModestMailOperation *obj)
192 ModestMailOperationPrivate *priv;
194 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
196 priv->account = NULL;
197 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
198 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
203 priv->error_checking = NULL;
204 priv->error_checking_user_data = NULL;
208 modest_mail_operation_finalize (GObject *obj)
210 ModestMailOperationPrivate *priv;
212 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
217 g_error_free (priv->error);
221 g_object_unref (priv->source);
225 g_object_unref (priv->account);
226 priv->account = NULL;
230 G_OBJECT_CLASS(parent_class)->finalize (obj);
234 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
237 ModestMailOperation *obj;
238 ModestMailOperationPrivate *priv;
240 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
241 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
243 priv->op_type = op_type;
245 priv->source = g_object_ref(source);
251 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
253 ErrorCheckingUserCallback error_handler,
256 ModestMailOperation *obj;
257 ModestMailOperationPrivate *priv;
259 obj = modest_mail_operation_new (op_type, source);
260 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
262 g_return_val_if_fail (error_handler != NULL, obj);
263 priv->error_checking = error_handler;
269 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
271 ModestMailOperationPrivate *priv;
273 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
274 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
276 if (priv->error_checking != NULL)
277 priv->error_checking (self, priv->error_checking_user_data);
281 ModestMailOperationTypeOperation
282 modest_mail_operation_get_type_operation (ModestMailOperation *self)
284 ModestMailOperationPrivate *priv;
286 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
288 return priv->op_type;
292 modest_mail_operation_is_mine (ModestMailOperation *self,
295 ModestMailOperationPrivate *priv;
297 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
298 if (priv->source == NULL) return FALSE;
300 return priv->source == me;
304 modest_mail_operation_get_source (ModestMailOperation *self)
306 ModestMailOperationPrivate *priv;
308 g_return_val_if_fail (self, NULL);
310 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
312 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
316 return g_object_ref (priv->source);
319 ModestMailOperationStatus
320 modest_mail_operation_get_status (ModestMailOperation *self)
322 ModestMailOperationPrivate *priv;
324 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
325 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
326 MODEST_MAIL_OPERATION_STATUS_INVALID);
328 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
330 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
331 return MODEST_MAIL_OPERATION_STATUS_INVALID;
338 modest_mail_operation_get_error (ModestMailOperation *self)
340 ModestMailOperationPrivate *priv;
342 g_return_val_if_fail (self, NULL);
343 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
345 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
348 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
356 modest_mail_operation_cancel (ModestMailOperation *self)
358 ModestMailOperationPrivate *priv;
360 if (!MODEST_IS_MAIL_OPERATION (self)) {
361 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
365 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
367 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
374 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
376 /* This emits progress-changed on which the mail operation queue is
377 * listening, so the mail operation is correctly removed from the
378 * queue without further explicit calls. */
379 modest_mail_operation_notify_end (self, FALSE);
385 modest_mail_operation_get_task_done (ModestMailOperation *self)
387 ModestMailOperationPrivate *priv;
389 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
391 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
396 modest_mail_operation_get_task_total (ModestMailOperation *self)
398 ModestMailOperationPrivate *priv;
400 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
402 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
407 modest_mail_operation_is_finished (ModestMailOperation *self)
409 ModestMailOperationPrivate *priv;
410 gboolean retval = FALSE;
412 if (!MODEST_IS_MAIL_OPERATION (self)) {
413 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
417 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
419 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
420 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
421 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
422 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
432 modest_mail_operation_get_id (ModestMailOperation *self)
434 ModestMailOperationPrivate *priv;
436 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
438 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
443 modest_mail_operation_set_id (ModestMailOperation *self,
446 ModestMailOperationPrivate *priv;
448 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
450 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
455 * Creates an image of the current state of a mail operation, the
456 * caller must free it
458 static ModestMailOperationState *
459 modest_mail_operation_clone_state (ModestMailOperation *self)
461 ModestMailOperationState *state;
462 ModestMailOperationPrivate *priv;
464 /* FIXME: this should be fixed properly
466 * in some cases, priv was NULL, so checking here to
469 g_return_val_if_fail (self, NULL);
470 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
471 g_return_val_if_fail (priv, NULL);
476 state = g_slice_new (ModestMailOperationState);
478 state->status = priv->status;
479 state->op_type = priv->op_type;
480 state->done = priv->done;
481 state->total = priv->total;
482 state->finished = modest_mail_operation_is_finished (self);
483 state->bytes_done = 0;
484 state->bytes_total = 0;
489 /* ******************************************************************* */
490 /* ************************** SEND ACTIONS ************************* */
491 /* ******************************************************************* */
494 modest_mail_operation_send_mail (ModestMailOperation *self,
495 TnyTransportAccount *transport_account,
498 TnySendQueue *send_queue = NULL;
499 ModestMailOperationPrivate *priv;
501 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
502 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
503 g_return_if_fail (TNY_IS_MSG (msg));
505 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
507 /* Get account and set it into mail_operation */
508 priv->account = g_object_ref (transport_account);
512 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
513 if (!TNY_IS_SEND_QUEUE(send_queue)) {
514 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
515 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
516 "modest: could not find send queue for account\n");
518 /* TODO: connect to the msg-sent in order to know when
519 the mail operation is finished */
520 tny_send_queue_add (send_queue, msg, &(priv->error));
521 /* TODO: we're setting always success, do the check in
523 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
526 /* TODO: do this in the handler of the "msg-sent"
527 signal.Notify about operation end */
528 modest_mail_operation_notify_end (self, FALSE);
532 modest_mail_operation_send_new_mail (ModestMailOperation *self,
533 TnyTransportAccount *transport_account,
535 const gchar *from, const gchar *to,
536 const gchar *cc, const gchar *bcc,
537 const gchar *subject, const gchar *plain_body,
538 const gchar *html_body,
539 const GList *attachments_list,
540 TnyHeaderFlags priority_flags)
542 TnyMsg *new_msg = NULL;
543 TnyFolder *folder = NULL;
544 TnyHeader *header = NULL;
545 ModestMailOperationPrivate *priv = NULL;
547 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
548 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
550 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
552 /* Check parametters */
554 /* Set status failed and set an error */
555 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
556 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
557 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
558 _("Error trying to send a mail. You need to set at least one recipient"));
562 if (html_body == NULL) {
563 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
565 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
568 g_printerr ("modest: failed to create a new msg\n");
572 /* Set priority flags in message */
573 header = tny_msg_get_header (new_msg);
574 if (priority_flags != 0)
575 tny_header_set_flags (header, priority_flags);
577 /* Call mail operation */
578 modest_mail_operation_send_mail (self, transport_account, new_msg);
580 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
582 if (draft_msg != NULL) {
583 header = tny_msg_get_header (draft_msg);
584 /* Note: This can fail (with a warning) if the message is not really already in a folder,
585 * because this function requires it to have a UID. */
586 tny_folder_remove_msg (folder, header, NULL);
587 g_object_unref (header);
592 g_object_unref (G_OBJECT (new_msg));
596 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
597 TnyTransportAccount *transport_account,
599 const gchar *from, const gchar *to,
600 const gchar *cc, const gchar *bcc,
601 const gchar *subject, const gchar *plain_body,
602 const gchar *html_body,
603 const GList *attachments_list,
604 TnyHeaderFlags priority_flags)
607 TnyFolder *folder = NULL;
608 TnyHeader *header = NULL;
609 ModestMailOperationPrivate *priv = NULL;
611 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
612 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
614 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
616 /* Get account and set it into mail_operation */
617 priv->account = g_object_ref (transport_account);
619 if (html_body == NULL) {
620 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
622 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
625 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
626 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
627 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
628 "modest: failed to create a new msg\n");
632 /* add priority flags */
633 header = tny_msg_get_header (msg);
634 tny_header_set_flags (header, priority_flags);
636 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
638 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
639 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
640 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
641 "modest: failed to create a new msg\n");
645 if (draft_msg != NULL) {
646 header = tny_msg_get_header (draft_msg);
647 /* Remove the old draft expunging it */
648 tny_folder_remove_msg (folder, header, NULL);
649 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
650 g_object_unref (header);
654 tny_folder_add_msg (folder, msg, &(priv->error));
657 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
659 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
663 g_object_unref (G_OBJECT(msg));
665 g_object_unref (G_OBJECT(folder));
667 modest_mail_operation_notify_end (self, FALSE);
672 ModestMailOperation *mail_op;
673 TnyStoreAccount *account;
674 TnyTransportAccount *transport_account;
677 gchar *retrieve_type;
679 UpdateAccountCallback callback;
683 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
684 /* We use this folder observer to track the headers that have been
685 * added to a folder */
688 TnyList *new_headers;
689 } InternalFolderObserver;
693 } InternalFolderObserverClass;
695 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
697 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
698 internal_folder_observer,
700 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
704 foreach_add_item (gpointer header, gpointer user_data)
706 /* printf("DEBUG: %s: header subject=%s\n",
707 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
709 tny_list_prepend (TNY_LIST (user_data),
710 g_object_ref (G_OBJECT (header)));
713 /* This is the method that looks for new messages in a folder */
715 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
717 InternalFolderObserver *derived = (InternalFolderObserver *)self;
719 TnyFolderChangeChanged changed;
721 changed = tny_folder_change_get_changed (change);
723 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
726 /* Get added headers */
727 list = tny_simple_list_new ();
728 tny_folder_change_get_added_headers (change, list);
730 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
731 * __FUNCTION__, tny_list_get_length(list));
734 /* Add them to the folder observer */
735 tny_list_foreach (list, foreach_add_item,
736 derived->new_headers);
738 g_object_unref (G_OBJECT (list));
743 internal_folder_observer_init (InternalFolderObserver *self)
745 self->new_headers = tny_simple_list_new ();
748 internal_folder_observer_finalize (GObject *object)
750 InternalFolderObserver *self;
752 self = (InternalFolderObserver *) object;
753 g_object_unref (self->new_headers);
755 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
758 tny_folder_observer_init (TnyFolderObserverIface *iface)
760 iface->update_func = internal_folder_observer_update;
763 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
765 GObjectClass *object_class;
767 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
768 object_class = (GObjectClass*) klass;
769 object_class->finalize = internal_folder_observer_finalize;
775 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
778 TnyList *folders = tny_simple_list_new ();
780 tny_folder_store_get_folders (store, folders, query, NULL);
781 iter = tny_list_create_iterator (folders);
783 while (!tny_iterator_is_done (iter)) {
785 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
787 tny_list_prepend (all_folders, G_OBJECT (folder));
788 recurse_folders (folder, query, all_folders);
789 g_object_unref (G_OBJECT (folder));
791 tny_iterator_next (iter);
793 g_object_unref (G_OBJECT (iter));
794 g_object_unref (G_OBJECT (folders));
798 * Issues the "progress-changed" signal. The timer won't be removed,
799 * so you must call g_source_remove to stop the signal emission
802 idle_notify_progress (gpointer data)
804 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
805 ModestMailOperationState *state;
807 state = modest_mail_operation_clone_state (mail_op);
808 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
809 g_slice_free (ModestMailOperationState, state);
815 * Issues the "progress-changed" signal and removes the timer. It uses
816 * a lock to ensure that the progress information of the mail
817 * operation is not modified while there are notifications pending
820 idle_notify_progress_once (gpointer data)
824 pair = (ModestPair *) data;
826 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
828 /* Free the state and the reference to the mail operation */
829 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
830 g_object_unref (pair->first);
836 * Used by update_account_thread to notify the queue from the main
837 * loop. We call it inside an idle call to achieve that
840 idle_notify_update_account_queue (gpointer data)
842 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
843 ModestMailOperationPrivate *priv = NULL;
845 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
847 /* Do not need to block, the notify end will do it for us */
848 modest_mail_operation_notify_end (mail_op, TRUE);
849 g_object_unref (mail_op);
855 compare_headers_by_date (gconstpointer a,
858 TnyHeader **header1, **header2;
861 header1 = (TnyHeader **) a;
862 header2 = (TnyHeader **) b;
864 sent1 = tny_header_get_date_sent (*header1);
865 sent2 = tny_header_get_date_sent (*header2);
867 /* We want the most recent ones (greater time_t) at the
876 set_last_updated_idle (gpointer data)
878 gdk_threads_enter ();
880 /* It does not matter if the time is not exactly the same than
881 the time when this idle was called, it's just an
882 approximation and it won't be very different */
883 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
885 MODEST_ACCOUNT_LAST_UPDATED,
889 gdk_threads_leave ();
895 update_account_thread (gpointer thr_user_data)
897 static gboolean first_time = TRUE;
898 UpdateAccountInfo *info;
899 TnyList *all_folders = NULL;
900 GPtrArray *new_headers = NULL;
901 TnyIterator *iter = NULL;
902 TnyFolderStoreQuery *query = NULL;
903 ModestMailOperationPrivate *priv = NULL;
904 ModestTnySendQueue *send_queue = NULL;
906 info = (UpdateAccountInfo *) thr_user_data;
907 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
909 /* Get account and set it into mail_operation */
910 priv->account = g_object_ref (info->account);
913 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
914 * show any updates unless we do that
916 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
917 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
919 /* Get all the folders. We can do it synchronously because
920 we're already running in a different thread than the UI */
921 all_folders = tny_simple_list_new ();
922 query = tny_folder_store_query_new ();
923 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
924 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
929 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
933 iter = tny_list_create_iterator (all_folders);
934 while (!tny_iterator_is_done (iter)) {
935 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
937 recurse_folders (folder, query, all_folders);
938 tny_iterator_next (iter);
940 g_object_unref (G_OBJECT (iter));
942 /* Update status and notify. We need to call the notification
943 with a source function in order to call it from the main
944 loop. We need that in order not to get into trouble with
945 Gtk+. We use a timeout in order to provide more status
946 information, because the sync tinymail call does not
947 provide it for the moment */
948 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
950 /* Refresh folders */
951 new_headers = g_ptr_array_new ();
952 iter = tny_list_create_iterator (all_folders);
954 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
956 InternalFolderObserver *observer;
957 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
959 /* Refresh the folder */
960 /* Our observer receives notification of new emails during folder refreshes,
961 * so we can use observer->new_headers.
963 observer = g_object_new (internal_folder_observer_get_type (), NULL);
964 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
966 /* This gets the status information (headers) from the server.
967 * We use the blocking version, because we are already in a separate
971 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
972 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
975 /* If the retrieve type is full messages, refresh and get the messages */
976 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
978 iter = tny_list_create_iterator (observer->new_headers);
979 while (!tny_iterator_is_done (iter)) {
980 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
982 /* Apply per-message size limits */
983 if (tny_header_get_message_size (header) < info->max_size)
984 g_ptr_array_add (new_headers, g_object_ref (header));
986 g_object_unref (header);
987 tny_iterator_next (iter);
989 g_object_unref (iter);
991 /* We do not need to do it the first time
992 because it's automatically done by the tree
994 if (G_UNLIKELY (!first_time))
995 tny_folder_poke_status (TNY_FOLDER (folder));
997 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
998 g_object_unref (observer);
1001 g_object_unref (G_OBJECT (folder));
1004 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1008 tny_iterator_next (iter);
1011 did_a_cancel = FALSE;
1013 g_object_unref (G_OBJECT (iter));
1014 g_source_remove (timeout);
1016 if (new_headers->len > 0) {
1020 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1022 /* Apply message count limit */
1023 /* If the number of messages exceeds the maximum, ask the
1024 * user to download them all,
1025 * as per the UI spec "Retrieval Limits" section in 4.4:
1027 if (new_headers->len > info->retrieve_limit) {
1028 /* TODO: Ask the user, instead of just
1030 * mail_nc_msg_count_limit_exceeded, with 'Get
1031 * all' and 'Newest only' buttons. */
1032 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1033 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1034 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1035 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1036 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1041 priv->total = MIN (new_headers->len, info->retrieve_limit);
1042 while (msg_num < priv->total) {
1044 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1045 TnyFolder *folder = tny_header_get_folder (header);
1046 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1047 ModestMailOperationState *state;
1051 /* We can not just use the mail operation because the
1052 values of done and total could change before the
1054 state = modest_mail_operation_clone_state (info->mail_op);
1055 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1056 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1057 pair, (GDestroyNotify) modest_pair_free);
1059 g_object_unref (msg);
1060 g_object_unref (folder);
1064 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1065 g_ptr_array_free (new_headers, FALSE);
1069 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1072 if (priv->account != NULL)
1073 g_object_unref (priv->account);
1074 priv->account = g_object_ref (info->transport_account);
1076 send_queue = modest_runtime_get_send_queue (info->transport_account);
1078 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1079 modest_tny_send_queue_try_to_send (send_queue);
1080 g_source_remove (timeout);
1082 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1083 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1084 "cannot create a send queue for %s\n",
1085 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1086 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1089 /* Check if the operation was a success */
1091 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1093 /* Update the last updated key */
1094 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1095 set_last_updated_idle,
1096 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1097 (GDestroyNotify) g_free);
1101 /* Notify about operation end. Note that the info could be
1102 freed before this idle happens, but the mail operation will
1104 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1106 if (info->callback) {
1107 /* This thread is not in the main lock */
1108 gdk_threads_enter ();
1109 info->callback (info->mail_op,
1110 (new_headers) ? new_headers->len : 0,
1112 gdk_threads_leave ();
1116 g_object_unref (query);
1117 g_object_unref (all_folders);
1118 g_object_unref (info->account);
1119 g_object_unref (info->transport_account);
1120 g_free (info->retrieve_type);
1121 g_slice_free (UpdateAccountInfo, info);
1129 modest_mail_operation_update_account (ModestMailOperation *self,
1130 const gchar *account_name,
1131 UpdateAccountCallback callback,
1135 UpdateAccountInfo *info;
1136 ModestMailOperationPrivate *priv;
1137 ModestAccountMgr *mgr;
1138 TnyStoreAccount *modest_account;
1139 TnyTransportAccount *transport_account;
1141 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1142 g_return_val_if_fail (account_name, FALSE);
1144 /* Init mail operation. Set total and done to 0, and do not
1145 update them, this way the progress objects will know that
1146 we have no clue about the number of the objects */
1147 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1150 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1152 /* Get the Modest account */
1153 modest_account = (TnyStoreAccount *)
1154 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1156 TNY_ACCOUNT_TYPE_STORE);
1158 if (!modest_account) {
1159 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1160 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1161 "cannot get tny store account for %s\n", account_name);
1166 /* Get the transport account, we can not do it in the thread
1167 due to some problems with dbus */
1168 transport_account = (TnyTransportAccount *)
1169 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1171 if (!transport_account) {
1172 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1173 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1174 "cannot get tny transport account for %s\n", account_name);
1178 /* Create the helper object */
1179 info = g_slice_new (UpdateAccountInfo);
1180 info->mail_op = self;
1181 info->account = modest_account;
1182 info->transport_account = transport_account;
1183 info->callback = callback;
1184 info->user_data = user_data;
1186 /* Get the message size limit */
1187 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1188 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1189 if (info->max_size == 0)
1190 info->max_size = G_MAXINT;
1192 info->max_size = info->max_size * KB;
1194 /* Get per-account retrieval type */
1195 mgr = modest_runtime_get_account_mgr ();
1196 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1197 MODEST_ACCOUNT_RETRIEVE, FALSE);
1199 /* Get per-account message amount retrieval limit */
1200 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1201 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1202 if (info->retrieve_limit == 0)
1203 info->retrieve_limit = G_MAXINT;
1205 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1207 /* Set account busy */
1208 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1209 priv->account_name = g_strdup(account_name);
1211 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1216 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1218 callback (self, 0, user_data);
1219 modest_mail_operation_notify_end (self, FALSE);
1223 /* ******************************************************************* */
1224 /* ************************** STORE ACTIONS ************************* */
1225 /* ******************************************************************* */
1229 modest_mail_operation_create_folder (ModestMailOperation *self,
1230 TnyFolderStore *parent,
1233 ModestMailOperationPrivate *priv;
1234 TnyFolder *new_folder = NULL;
1236 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1237 g_return_val_if_fail (name, NULL);
1239 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1242 if (TNY_IS_FOLDER (parent)) {
1243 /* Check folder rules */
1244 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1245 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1246 /* Set status failed and set an error */
1247 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1248 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1249 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1250 _("mail_in_ui_folder_create_error"));
1254 if (!strcmp (name, " ") || strchr (name, '/')) {
1255 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1256 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1257 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1258 _("mail_in_ui_folder_create_error"));
1262 /* Create the folder */
1263 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1264 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1266 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1269 /* Notify about operation end */
1270 modest_mail_operation_notify_end (self, FALSE);
1276 modest_mail_operation_remove_folder (ModestMailOperation *self,
1278 gboolean remove_to_trash)
1280 TnyAccount *account;
1281 ModestMailOperationPrivate *priv;
1282 ModestTnyFolderRules rules;
1284 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1285 g_return_if_fail (TNY_IS_FOLDER (folder));
1287 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1289 /* Check folder rules */
1290 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1291 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1292 /* Set status failed and set an error */
1293 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1294 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1295 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1296 _("mail_in_ui_folder_delete_error"));
1300 /* Get the account */
1301 account = modest_tny_folder_get_account (folder);
1302 priv->account = g_object_ref(account);
1304 /* Delete folder or move to trash */
1305 if (remove_to_trash) {
1306 TnyFolder *trash_folder = NULL;
1307 trash_folder = modest_tny_account_get_special_folder (account,
1308 TNY_FOLDER_TYPE_TRASH);
1309 /* TODO: error_handling */
1310 modest_mail_operation_xfer_folder (self, folder,
1311 TNY_FOLDER_STORE (trash_folder), TRUE);
1313 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1315 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1316 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1319 g_object_unref (G_OBJECT (parent));
1321 g_object_unref (G_OBJECT (account));
1324 /* Notify about operation end */
1325 modest_mail_operation_notify_end (self, FALSE);
1329 transfer_folder_status_cb (GObject *obj,
1333 ModestMailOperation *self;
1334 ModestMailOperationPrivate *priv;
1335 ModestMailOperationState *state;
1337 g_return_if_fail (status != NULL);
1338 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1340 self = MODEST_MAIL_OPERATION (user_data);
1341 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1343 priv->done = status->position;
1344 priv->total = status->of_total;
1346 state = modest_mail_operation_clone_state (self);
1347 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1348 g_slice_free (ModestMailOperationState, state);
1353 transfer_folder_cb (TnyFolder *folder,
1354 TnyFolderStore *into,
1356 TnyFolder *new_folder,
1360 ModestMailOperation *self = NULL;
1361 ModestMailOperationPrivate *priv = NULL;
1363 self = MODEST_MAIL_OPERATION (user_data);
1365 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1368 priv->error = g_error_copy (*err);
1370 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1371 } else if (cancelled) {
1372 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1373 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1374 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1375 _("Transference of %s was cancelled."),
1376 tny_folder_get_name (folder));
1379 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1383 g_object_unref (folder);
1384 g_object_unref (into);
1386 /* Notify about operation end */
1387 modest_mail_operation_notify_end (self, TRUE);
1391 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1393 TnyFolderStore *parent,
1394 gboolean delete_original)
1396 ModestMailOperationPrivate *priv = NULL;
1397 ModestTnyFolderRules parent_rules = 0, rules;
1399 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1400 g_return_if_fail (TNY_IS_FOLDER (folder));
1402 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1404 /* Get account and set it into mail_operation */
1405 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1406 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1408 /* Get folder rules */
1409 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1410 if (TNY_IS_FOLDER (parent))
1411 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1413 /* The moveable restriction is applied also to copy operation */
1414 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1415 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1416 /* Set status failed and set an error */
1417 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1418 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1419 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1420 _("mail_in_ui_folder_move_target_error"));
1422 /* Notify the queue */
1423 modest_mail_operation_notify_end (self, FALSE);
1424 } else if (TNY_IS_FOLDER (parent) &&
1425 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1426 /* Set status failed and set an error */
1427 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1428 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1429 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1430 _("FIXME: parent folder does not accept new folders"));
1432 /* Notify the queue */
1433 modest_mail_operation_notify_end (self, FALSE);
1435 /* Pick references for async calls */
1436 g_object_ref (folder);
1437 g_object_ref (parent);
1439 /* Move/Copy folder */
1440 tny_folder_copy_async (folder,
1442 tny_folder_get_name (folder),
1445 transfer_folder_status_cb,
1451 modest_mail_operation_rename_folder (ModestMailOperation *self,
1455 ModestMailOperationPrivate *priv;
1456 ModestTnyFolderRules rules;
1458 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1459 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1460 g_return_if_fail (name);
1462 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1464 /* Get account and set it into mail_operation */
1465 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1467 /* Check folder rules */
1468 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1469 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1470 /* Set status failed and set an error */
1471 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1472 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1473 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1474 _("FIXME: unable to rename"));
1476 /* Notify about operation end */
1477 modest_mail_operation_notify_end (self, FALSE);
1478 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1479 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1480 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1481 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1482 _("FIXME: unable to rename"));
1483 /* Notify about operation end */
1484 modest_mail_operation_notify_end (self, FALSE);
1486 TnyFolderStore *into;
1488 /* Rename. Camel handles folder subscription/unsubscription */
1489 into = tny_folder_get_folder_store (folder);
1490 tny_folder_copy_async (folder, into, name, TRUE,
1492 transfer_folder_status_cb,
1495 g_object_unref (into);
1499 /* ******************************************************************* */
1500 /* ************************** MSG ACTIONS ************************* */
1501 /* ******************************************************************* */
1503 void modest_mail_operation_get_msg (ModestMailOperation *self,
1505 GetMsgAsyncUserCallback user_callback,
1508 GetMsgAsyncHelper *helper = NULL;
1510 ModestMailOperationPrivate *priv;
1512 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1513 g_return_if_fail (TNY_IS_HEADER (header));
1515 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1516 folder = tny_header_get_folder (header);
1518 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1520 /* Get message from folder */
1522 /* Get account and set it into mail_operation */
1523 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1525 helper = g_slice_new0 (GetMsgAsyncHelper);
1526 helper->mail_op = self;
1527 helper->user_callback = user_callback;
1528 helper->user_data = user_data;
1529 helper->header = g_object_ref (header);
1531 // The callback's reference so that the mail op is not
1532 // finalized until the async operation is completed even if
1533 // the user canceled the request meanwhile.
1534 g_object_ref (G_OBJECT (helper->mail_op));
1536 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1538 g_object_unref (G_OBJECT (folder));
1540 /* Set status failed and set an error */
1541 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1542 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1543 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1544 _("Error trying to get a message. No folder found for header"));
1546 /* Notify the queue */
1547 modest_mail_operation_notify_end (self, FALSE);
1552 get_msg_cb (TnyFolder *folder,
1558 GetMsgAsyncHelper *helper = NULL;
1559 ModestMailOperation *self = NULL;
1560 ModestMailOperationPrivate *priv = NULL;
1562 helper = (GetMsgAsyncHelper *) user_data;
1563 g_return_if_fail (helper != NULL);
1564 self = helper->mail_op;
1565 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1566 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1568 /* Check errors and cancel */
1570 priv->error = g_error_copy (*error);
1571 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1575 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1576 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1577 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1578 _("Error trying to refresh the contents of %s"),
1579 tny_folder_get_name (folder));
1583 /* The mail operation might have been canceled in which case we do not
1584 want to notify anyone anymore. */
1585 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1586 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1588 /* If user defined callback function was defined, call it */
1589 if (helper->user_callback) {
1590 /* This callback is called into an iddle by tinymail,
1591 and idles are not in the main lock */
1592 gdk_threads_enter ();
1593 helper->user_callback (self, helper->header, msg, helper->user_data);
1594 gdk_threads_leave ();
1600 g_object_unref (helper->header);
1601 g_slice_free (GetMsgAsyncHelper, helper);
1603 /* Notify about operation end */
1604 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED)
1605 modest_mail_operation_notify_end (self, TRUE);
1607 g_object_unref (G_OBJECT (self));
1611 get_msg_status_cb (GObject *obj,
1615 GetMsgAsyncHelper *helper = NULL;
1616 ModestMailOperation *self;
1617 ModestMailOperationPrivate *priv;
1618 ModestMailOperationState *state;
1620 g_return_if_fail (status != NULL);
1621 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1623 helper = (GetMsgAsyncHelper *) user_data;
1624 g_return_if_fail (helper != NULL);
1626 self = helper->mail_op;
1627 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1629 if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1635 state = modest_mail_operation_clone_state (self);
1636 state->bytes_done = status->position;
1637 state->bytes_total = status->of_total;
1638 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1639 g_slice_free (ModestMailOperationState, state);
1642 /****************************************************/
1644 ModestMailOperation *mail_op;
1646 GetMsgAsyncUserCallback user_callback;
1648 GDestroyNotify notify;
1652 GetMsgAsyncUserCallback user_callback;
1656 ModestMailOperation *mail_op;
1657 } NotifyGetMsgsInfo;
1661 * Used by get_msgs_full_thread to call the user_callback for each
1662 * message that has been read
1665 notify_get_msgs_full (gpointer data)
1667 NotifyGetMsgsInfo *info;
1669 info = (NotifyGetMsgsInfo *) data;
1671 /* Call the user callback. Idles are not in the main lock, so
1673 gdk_threads_enter ();
1674 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1675 gdk_threads_leave ();
1677 g_slice_free (NotifyGetMsgsInfo, info);
1683 * Used by get_msgs_full_thread to free al the thread resources and to
1684 * call the destroy function for the passed user_data
1687 get_msgs_full_destroyer (gpointer data)
1689 GetFullMsgsInfo *info;
1691 info = (GetFullMsgsInfo *) data;
1694 gdk_threads_enter ();
1695 info->notify (info->user_data);
1696 gdk_threads_leave ();
1700 g_object_unref (info->headers);
1701 g_slice_free (GetFullMsgsInfo, info);
1707 get_msgs_full_thread (gpointer thr_user_data)
1709 GetFullMsgsInfo *info;
1710 ModestMailOperationPrivate *priv = NULL;
1711 TnyIterator *iter = NULL;
1713 info = (GetFullMsgsInfo *) thr_user_data;
1714 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1716 iter = tny_list_create_iterator (info->headers);
1717 while (!tny_iterator_is_done (iter)) {
1721 header = TNY_HEADER (tny_iterator_get_current (iter));
1722 folder = tny_header_get_folder (header);
1724 /* Get message from folder */
1727 /* The callback will call it per each header */
1728 msg = tny_folder_get_msg (folder, header, &(priv->error));
1731 ModestMailOperationState *state;
1736 /* notify progress */
1737 state = modest_mail_operation_clone_state (info->mail_op);
1738 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1739 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1740 pair, (GDestroyNotify) modest_pair_free);
1742 /* The callback is the responsible for
1743 freeing the message */
1744 if (info->user_callback) {
1745 NotifyGetMsgsInfo *info_notify;
1746 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1747 info_notify->user_callback = info->user_callback;
1748 info_notify->mail_op = info->mail_op;
1749 info_notify->header = g_object_ref (header);
1750 info_notify->msg = g_object_ref (msg);
1751 info_notify->user_data = info->user_data;
1752 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1753 notify_get_msgs_full,
1756 g_object_unref (msg);
1759 /* Set status failed and set an error */
1760 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1761 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1762 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1763 "Error trying to get a message. No folder found for header");
1765 g_object_unref (header);
1766 tny_iterator_next (iter);
1769 /* Set operation status */
1770 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1771 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1773 /* Notify about operation end */
1774 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1776 /* Free thread resources. Will be called after all previous idles */
1777 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1783 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1784 TnyList *header_list,
1785 GetMsgAsyncUserCallback user_callback,
1787 GDestroyNotify notify)
1789 TnyHeader *header = NULL;
1790 TnyFolder *folder = NULL;
1792 ModestMailOperationPrivate *priv = NULL;
1793 GetFullMsgsInfo *info = NULL;
1794 gboolean size_ok = TRUE;
1796 TnyIterator *iter = NULL;
1798 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1800 /* Init mail operation */
1801 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1802 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1804 priv->total = tny_list_get_length(header_list);
1806 /* Get account and set it into mail_operation */
1807 if (tny_list_get_length (header_list) >= 1) {
1808 iter = tny_list_create_iterator (header_list);
1809 header = TNY_HEADER (tny_iterator_get_current (iter));
1810 folder = tny_header_get_folder (header);
1811 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1812 g_object_unref (header);
1813 g_object_unref (folder);
1815 if (tny_list_get_length (header_list) == 1) {
1816 g_object_unref (iter);
1821 /* Get msg size limit */
1822 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1823 MODEST_CONF_MSG_SIZE_LIMIT,
1826 g_clear_error (&(priv->error));
1827 max_size = G_MAXINT;
1829 max_size = max_size * KB;
1832 /* Check message size limits. If there is only one message
1833 always retrieve it */
1835 while (!tny_iterator_is_done (iter) && size_ok) {
1836 header = TNY_HEADER (tny_iterator_get_current (iter));
1837 if (tny_header_get_message_size (header) >= max_size)
1839 g_object_unref (header);
1840 tny_iterator_next (iter);
1842 g_object_unref (iter);
1846 /* Create the info */
1847 info = g_slice_new0 (GetFullMsgsInfo);
1848 info->mail_op = self;
1849 info->user_callback = user_callback;
1850 info->user_data = user_data;
1851 info->headers = g_object_ref (header_list);
1852 info->notify = notify;
1854 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1856 /* Set status failed and set an error */
1857 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1858 /* FIXME: the error msg is different for pop */
1859 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1860 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1861 _("emev_ni_ui_imap_msg_size_exceed_error"));
1862 /* Remove from queue and free resources */
1863 modest_mail_operation_notify_end (self, FALSE);
1871 modest_mail_operation_remove_msg (ModestMailOperation *self, TnyHeader *header,
1872 gboolean remove_to_trash /*ignored*/)
1875 ModestMailOperationPrivate *priv;
1877 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1878 g_return_if_fail (TNY_IS_HEADER (header));
1880 if (remove_to_trash)
1881 g_warning ("remove to trash is not implemented");
1883 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1884 folder = tny_header_get_folder (header);
1886 /* Get account and set it into mail_operation */
1887 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1889 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1892 tny_folder_remove_msg (folder, header, &(priv->error));
1894 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1896 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
1897 tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
1898 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
1899 tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1902 tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1908 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1910 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1913 g_object_unref (G_OBJECT (folder));
1915 /* Notify about operation end */
1916 modest_mail_operation_notify_end (self, FALSE);
1920 transfer_msgs_status_cb (GObject *obj,
1924 XFerMsgAsyncHelper *helper = NULL;
1925 ModestMailOperation *self;
1926 ModestMailOperationPrivate *priv;
1927 ModestMailOperationState *state;
1930 g_return_if_fail (status != NULL);
1931 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1933 helper = (XFerMsgAsyncHelper *) user_data;
1934 g_return_if_fail (helper != NULL);
1936 self = helper->mail_op;
1937 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1939 priv->done = status->position;
1940 priv->total = status->of_total;
1942 state = modest_mail_operation_clone_state (self);
1943 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1944 g_slice_free (ModestMailOperationState, state);
1949 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1951 XFerMsgAsyncHelper *helper;
1952 ModestMailOperation *self;
1953 ModestMailOperationPrivate *priv;
1955 helper = (XFerMsgAsyncHelper *) user_data;
1956 self = helper->mail_op;
1958 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1961 priv->error = g_error_copy (*err);
1963 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1964 } else if (cancelled) {
1965 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1966 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1967 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1968 _("Error trying to refresh the contents of %s"),
1969 tny_folder_get_name (folder));
1972 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1975 /* Notify about operation end */
1976 modest_mail_operation_notify_end (self, TRUE);
1978 /* If user defined callback function was defined, call it */
1979 if (helper->user_callback) {
1980 gdk_threads_enter ();
1981 helper->user_callback (priv->source, helper->user_data);
1982 gdk_threads_leave ();
1986 g_object_unref (helper->headers);
1987 g_object_unref (helper->dest_folder);
1988 g_object_unref (helper->mail_op);
1989 g_slice_free (XFerMsgAsyncHelper, helper);
1990 g_object_unref (folder);
1995 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1998 gboolean delete_original,
1999 XferMsgsAsynUserCallback user_callback,
2002 ModestMailOperationPrivate *priv;
2004 TnyFolder *src_folder;
2005 XFerMsgAsyncHelper *helper;
2007 ModestTnyFolderRules rules;
2008 const gchar *id1 = NULL;
2009 const gchar *id2 = NULL;
2010 gboolean same_folder = FALSE;
2012 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2013 g_return_if_fail (TNY_IS_LIST (headers));
2014 g_return_if_fail (TNY_IS_FOLDER (folder));
2016 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2019 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2021 /* Apply folder rules */
2022 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2023 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2024 /* Set status failed and set an error */
2025 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2026 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2027 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2028 _("ckct_ib_unable_to_paste_here"));
2029 /* Notify the queue */
2030 modest_mail_operation_notify_end (self, FALSE);
2034 /* Get source folder */
2035 iter = tny_list_create_iterator (headers);
2036 header = TNY_HEADER (tny_iterator_get_current (iter));
2037 src_folder = tny_header_get_folder (header);
2038 g_object_unref (header);
2039 g_object_unref (iter);
2041 /* Check folder source and destination */
2042 id1 = tny_folder_get_id (src_folder);
2043 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2044 same_folder = !g_ascii_strcasecmp (id1, id2);
2046 /* Set status failed and set an error */
2047 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2048 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2049 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2050 _("mcen_ib_unable_to_copy_samefolder"));
2052 /* Notify the queue */
2053 modest_mail_operation_notify_end (self, FALSE);
2056 g_object_unref (src_folder);
2060 /* Create the helper */
2061 helper = g_slice_new0 (XFerMsgAsyncHelper);
2062 helper->mail_op = g_object_ref(self);
2063 helper->dest_folder = g_object_ref(folder);
2064 helper->headers = g_object_ref(headers);
2065 helper->user_callback = user_callback;
2066 helper->user_data = user_data;
2068 /* Get account and set it into mail_operation */
2069 priv->account = modest_tny_folder_get_account (src_folder);
2071 /* Transfer messages */
2072 tny_folder_transfer_msgs_async (src_folder,
2077 transfer_msgs_status_cb,
2083 on_refresh_folder (TnyFolder *folder,
2088 RefreshAsyncHelper *helper = NULL;
2089 ModestMailOperation *self = NULL;
2090 ModestMailOperationPrivate *priv = NULL;
2092 helper = (RefreshAsyncHelper *) user_data;
2093 self = helper->mail_op;
2094 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2097 priv->error = g_error_copy (*error);
2098 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2103 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2104 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2105 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2106 _("Error trying to refresh the contents of %s"),
2107 tny_folder_get_name (folder));
2111 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2114 /* Call user defined callback, if it exists */
2115 if (helper->user_callback) {
2116 gdk_threads_enter ();
2117 helper->user_callback (priv->source, folder, helper->user_data);
2118 gdk_threads_leave ();
2122 g_object_unref (helper->mail_op);
2123 g_slice_free (RefreshAsyncHelper, helper);
2124 g_object_unref (folder);
2126 /* Notify about operation end */
2127 modest_mail_operation_notify_end (self, TRUE);
2131 on_refresh_folder_status_update (GObject *obj,
2135 RefreshAsyncHelper *helper = NULL;
2136 ModestMailOperation *self = NULL;
2137 ModestMailOperationPrivate *priv = NULL;
2138 ModestMailOperationState *state;
2140 g_return_if_fail (user_data != NULL);
2141 g_return_if_fail (status != NULL);
2142 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2144 helper = (RefreshAsyncHelper *) user_data;
2145 self = helper->mail_op;
2146 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2148 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2150 priv->done = status->position;
2151 priv->total = status->of_total;
2153 state = modest_mail_operation_clone_state (self);
2154 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2155 g_slice_free (ModestMailOperationState, state);
2159 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2161 RefreshAsyncUserCallback user_callback,
2164 ModestMailOperationPrivate *priv = NULL;
2165 RefreshAsyncHelper *helper = NULL;
2167 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2169 /* Pick a reference */
2170 g_object_ref (folder);
2172 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2174 /* Get account and set it into mail_operation */
2175 priv->account = modest_tny_folder_get_account (folder);
2177 /* Create the helper */
2178 helper = g_slice_new0 (RefreshAsyncHelper);
2179 helper->mail_op = g_object_ref(self);
2180 helper->user_callback = user_callback;
2181 helper->user_data = user_data;
2183 /* Refresh the folder. TODO: tinymail could issue a status
2184 updates before the callback call then this could happen. We
2185 must review the design */
2186 tny_folder_refresh_async (folder,
2188 on_refresh_folder_status_update,
2194 * It's used by the mail operation queue to notify the observers
2195 * attached to that signal that the operation finished. We need to use
2196 * that because tinymail does not give us the progress of a given
2197 * operation when it finishes (it directly calls the operation
2201 modest_mail_operation_notify_end (ModestMailOperation *self,
2204 ModestMailOperationState *state;
2205 ModestMailOperationPrivate *priv = NULL;
2207 g_return_if_fail (self);
2209 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2212 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2216 /* Set the account back to not busy */
2217 if (priv->account_name) {
2218 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2219 priv->account_name, FALSE);
2220 g_free(priv->account_name);
2221 priv->account_name = NULL;
2224 /* Notify the observers about the mail opertation end */
2225 state = modest_mail_operation_clone_state (self);
2227 gdk_threads_enter ();
2228 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2230 gdk_threads_leave ();
2231 g_slice_free (ModestMailOperationState, state);