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);
77 static gboolean did_a_cancel = FALSE;
79 enum _ModestMailOperationSignals
81 PROGRESS_CHANGED_SIGNAL,
86 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
87 struct _ModestMailOperationPrivate {
94 ErrorCheckingUserCallback error_checking;
95 gpointer error_checking_user_data;
96 ModestMailOperationStatus status;
97 ModestMailOperationTypeOperation op_type;
100 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
101 MODEST_TYPE_MAIL_OPERATION, \
102 ModestMailOperationPrivate))
104 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
105 priv->status = new_status;\
108 typedef struct _GetMsgAsyncHelper {
109 ModestMailOperation *mail_op;
111 GetMsgAsyncUserCallback user_callback;
115 typedef struct _RefreshAsyncHelper {
116 ModestMailOperation *mail_op;
117 RefreshAsyncUserCallback user_callback;
119 } RefreshAsyncHelper;
121 typedef struct _XFerMsgAsyncHelper
123 ModestMailOperation *mail_op;
125 TnyFolder *dest_folder;
126 XferMsgsAsynUserCallback user_callback;
128 } XFerMsgAsyncHelper;
131 static GObjectClass *parent_class = NULL;
133 static guint signals[NUM_SIGNALS] = {0};
136 modest_mail_operation_get_type (void)
138 static GType my_type = 0;
140 static const GTypeInfo my_info = {
141 sizeof(ModestMailOperationClass),
142 NULL, /* base init */
143 NULL, /* base finalize */
144 (GClassInitFunc) modest_mail_operation_class_init,
145 NULL, /* class finalize */
146 NULL, /* class data */
147 sizeof(ModestMailOperation),
149 (GInstanceInitFunc) modest_mail_operation_init,
152 my_type = g_type_register_static (G_TYPE_OBJECT,
153 "ModestMailOperation",
160 modest_mail_operation_class_init (ModestMailOperationClass *klass)
162 GObjectClass *gobject_class;
163 gobject_class = (GObjectClass*) klass;
165 parent_class = g_type_class_peek_parent (klass);
166 gobject_class->finalize = modest_mail_operation_finalize;
168 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
171 * ModestMailOperation::progress-changed
172 * @self: the #MailOperation that emits the signal
173 * @user_data: user data set when the signal handler was connected
175 * Emitted when the progress of a mail operation changes
177 signals[PROGRESS_CHANGED_SIGNAL] =
178 g_signal_new ("progress-changed",
179 G_TYPE_FROM_CLASS (gobject_class),
181 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
183 g_cclosure_marshal_VOID__POINTER,
184 G_TYPE_NONE, 1, G_TYPE_POINTER);
189 modest_mail_operation_init (ModestMailOperation *obj)
191 ModestMailOperationPrivate *priv;
193 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
195 priv->account = NULL;
196 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
197 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
202 priv->error_checking = NULL;
203 priv->error_checking_user_data = NULL;
207 modest_mail_operation_finalize (GObject *obj)
209 ModestMailOperationPrivate *priv;
211 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
216 g_error_free (priv->error);
220 g_object_unref (priv->source);
224 g_object_unref (priv->account);
225 priv->account = NULL;
229 G_OBJECT_CLASS(parent_class)->finalize (obj);
233 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
236 ModestMailOperation *obj;
237 ModestMailOperationPrivate *priv;
239 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
240 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
242 priv->op_type = op_type;
244 priv->source = g_object_ref(source);
250 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
252 ErrorCheckingUserCallback error_handler,
255 ModestMailOperation *obj;
256 ModestMailOperationPrivate *priv;
258 obj = modest_mail_operation_new (op_type, source);
259 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
261 g_return_val_if_fail (error_handler != NULL, obj);
262 priv->error_checking = error_handler;
268 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
270 ModestMailOperationPrivate *priv;
272 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
273 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
275 if (priv->error_checking != NULL)
276 priv->error_checking (self, priv->error_checking_user_data);
280 ModestMailOperationTypeOperation
281 modest_mail_operation_get_type_operation (ModestMailOperation *self)
283 ModestMailOperationPrivate *priv;
285 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
287 return priv->op_type;
291 modest_mail_operation_is_mine (ModestMailOperation *self,
294 ModestMailOperationPrivate *priv;
296 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
297 if (priv->source == NULL) return FALSE;
299 return priv->source == me;
303 modest_mail_operation_get_source (ModestMailOperation *self)
305 ModestMailOperationPrivate *priv;
307 g_return_val_if_fail (self, NULL);
309 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
311 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
315 return g_object_ref (priv->source);
318 ModestMailOperationStatus
319 modest_mail_operation_get_status (ModestMailOperation *self)
321 ModestMailOperationPrivate *priv;
323 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
324 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
325 MODEST_MAIL_OPERATION_STATUS_INVALID);
327 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
329 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
330 return MODEST_MAIL_OPERATION_STATUS_INVALID;
337 modest_mail_operation_get_error (ModestMailOperation *self)
339 ModestMailOperationPrivate *priv;
341 g_return_val_if_fail (self, NULL);
342 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
344 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
347 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
355 modest_mail_operation_cancel (ModestMailOperation *self)
357 ModestMailOperationPrivate *priv;
359 if (!MODEST_IS_MAIL_OPERATION (self)) {
360 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
364 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
366 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
373 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
375 /* This emits progress-changed on which the mail operation queue is
376 * listening, so the mail operation is correctly removed from the
377 * queue without further explicit calls. */
378 modest_mail_operation_notify_end (self);
384 modest_mail_operation_get_task_done (ModestMailOperation *self)
386 ModestMailOperationPrivate *priv;
388 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
390 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
395 modest_mail_operation_get_task_total (ModestMailOperation *self)
397 ModestMailOperationPrivate *priv;
399 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
401 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
406 modest_mail_operation_is_finished (ModestMailOperation *self)
408 ModestMailOperationPrivate *priv;
409 gboolean retval = FALSE;
411 if (!MODEST_IS_MAIL_OPERATION (self)) {
412 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
416 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
418 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
419 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
420 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
421 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
431 modest_mail_operation_get_id (ModestMailOperation *self)
433 ModestMailOperationPrivate *priv;
435 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
437 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
442 modest_mail_operation_set_id (ModestMailOperation *self,
445 ModestMailOperationPrivate *priv;
447 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
449 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
454 * Creates an image of the current state of a mail operation, the
455 * caller must free it
457 static ModestMailOperationState *
458 modest_mail_operation_clone_state (ModestMailOperation *self)
460 ModestMailOperationState *state;
461 ModestMailOperationPrivate *priv;
463 /* FIXME: this should be fixed properly
465 * in some cases, priv was NULL, so checking here to
468 g_return_val_if_fail (self, NULL);
469 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
470 g_return_val_if_fail (priv, NULL);
475 state = g_slice_new (ModestMailOperationState);
477 state->status = priv->status;
478 state->op_type = priv->op_type;
479 state->done = priv->done;
480 state->total = priv->total;
481 state->finished = modest_mail_operation_is_finished (self);
482 state->bytes_done = 0;
483 state->bytes_total = 0;
488 /* ******************************************************************* */
489 /* ************************** SEND ACTIONS ************************* */
490 /* ******************************************************************* */
493 modest_mail_operation_send_mail (ModestMailOperation *self,
494 TnyTransportAccount *transport_account,
497 TnySendQueue *send_queue = NULL;
498 ModestMailOperationPrivate *priv;
500 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
501 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
502 g_return_if_fail (TNY_IS_MSG (msg));
504 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
506 /* Get account and set it into mail_operation */
507 priv->account = g_object_ref (transport_account);
511 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
512 if (!TNY_IS_SEND_QUEUE(send_queue)) {
513 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
514 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
515 "modest: could not find send queue for account\n");
517 /* TODO: connect to the msg-sent in order to know when
518 the mail operation is finished */
519 tny_send_queue_add (send_queue, msg, &(priv->error));
520 /* TODO: we're setting always success, do the check in
522 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
525 /* TODO: do this in the handler of the "msg-sent"
526 signal.Notify about operation end */
527 modest_mail_operation_notify_end (self);
531 modest_mail_operation_send_new_mail (ModestMailOperation *self,
532 TnyTransportAccount *transport_account,
534 const gchar *from, const gchar *to,
535 const gchar *cc, const gchar *bcc,
536 const gchar *subject, const gchar *plain_body,
537 const gchar *html_body,
538 const GList *attachments_list,
539 TnyHeaderFlags priority_flags)
541 TnyMsg *new_msg = NULL;
542 TnyFolder *folder = NULL;
543 TnyHeader *header = NULL;
544 ModestMailOperationPrivate *priv = NULL;
546 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
547 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
549 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
551 /* Check parametters */
553 /* Set status failed and set an error */
554 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
555 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
556 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
557 _("Error trying to send a mail. You need to set at least one recipient"));
561 if (html_body == NULL) {
562 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
564 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
567 g_printerr ("modest: failed to create a new msg\n");
571 /* Set priority flags in message */
572 header = tny_msg_get_header (new_msg);
573 if (priority_flags != 0)
574 tny_header_set_flags (header, priority_flags);
576 /* Call mail operation */
577 modest_mail_operation_send_mail (self, transport_account, new_msg);
579 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
581 if (draft_msg != NULL) {
582 header = tny_msg_get_header (draft_msg);
583 /* Note: This can fail (with a warning) if the message is not really already in a folder,
584 * because this function requires it to have a UID. */
585 tny_folder_remove_msg (folder, header, NULL);
586 g_object_unref (header);
591 g_object_unref (G_OBJECT (new_msg));
595 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
596 TnyTransportAccount *transport_account,
598 const gchar *from, const gchar *to,
599 const gchar *cc, const gchar *bcc,
600 const gchar *subject, const gchar *plain_body,
601 const gchar *html_body,
602 const GList *attachments_list,
603 TnyHeaderFlags priority_flags)
606 TnyFolder *folder = NULL;
607 TnyHeader *header = NULL;
608 ModestMailOperationPrivate *priv = NULL;
610 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
611 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
613 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
615 /* Get account and set it into mail_operation */
616 priv->account = g_object_ref (transport_account);
618 if (html_body == NULL) {
619 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
621 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
624 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
625 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
626 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
627 "modest: failed to create a new msg\n");
631 /* add priority flags */
632 header = tny_msg_get_header (msg);
633 tny_header_set_flags (header, priority_flags);
635 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
637 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
638 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
639 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
640 "modest: failed to create a new msg\n");
644 if (draft_msg != NULL) {
645 header = tny_msg_get_header (draft_msg);
646 /* Remove the old draft expunging it */
647 tny_folder_remove_msg (folder, header, NULL);
648 tny_folder_sync (folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
649 g_object_unref (header);
653 tny_folder_add_msg (folder, msg, &(priv->error));
656 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
658 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
662 g_object_unref (G_OBJECT(msg));
664 g_object_unref (G_OBJECT(folder));
666 modest_mail_operation_notify_end (self);
671 ModestMailOperation *mail_op;
672 TnyStoreAccount *account;
673 TnyTransportAccount *transport_account;
676 gchar *retrieve_type;
678 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);
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 idle_update_account_cb (gpointer data)
897 UpdateAccountInfo *idle_info;
899 idle_info = (UpdateAccountInfo *) data;
901 gdk_threads_enter ();
902 idle_info->callback (idle_info->mail_op,
903 idle_info->new_headers,
904 idle_info->user_data);
905 gdk_threads_leave ();
908 g_object_unref (idle_info->mail_op);
916 update_account_thread (gpointer thr_user_data)
918 static gboolean first_time = TRUE;
919 UpdateAccountInfo *info;
920 TnyList *all_folders = NULL;
921 GPtrArray *new_headers = NULL;
922 TnyIterator *iter = NULL;
923 TnyFolderStoreQuery *query = NULL;
924 ModestMailOperationPrivate *priv = NULL;
925 ModestTnySendQueue *send_queue = NULL;
927 info = (UpdateAccountInfo *) thr_user_data;
928 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
930 /* Get account and set it into mail_operation */
931 priv->account = g_object_ref (info->account);
934 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
935 * show any updates unless we do that
937 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
938 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
940 /* Get all the folders. We can do it synchronously because
941 we're already running in a different thread than the UI */
942 all_folders = tny_simple_list_new ();
943 query = tny_folder_store_query_new ();
944 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
945 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
950 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
954 iter = tny_list_create_iterator (all_folders);
955 while (!tny_iterator_is_done (iter)) {
956 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
958 recurse_folders (folder, query, all_folders);
959 tny_iterator_next (iter);
961 g_object_unref (G_OBJECT (iter));
963 /* Update status and notify. We need to call the notification
964 with a source function in order to call it from the main
965 loop. We need that in order not to get into trouble with
966 Gtk+. We use a timeout in order to provide more status
967 information, because the sync tinymail call does not
968 provide it for the moment */
969 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
971 /* Refresh folders */
972 new_headers = g_ptr_array_new ();
973 iter = tny_list_create_iterator (all_folders);
975 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
977 InternalFolderObserver *observer;
978 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
980 /* Refresh the folder */
981 /* Our observer receives notification of new emails during folder refreshes,
982 * so we can use observer->new_headers.
984 observer = g_object_new (internal_folder_observer_get_type (), NULL);
985 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
987 /* This gets the status information (headers) from the server.
988 * We use the blocking version, because we are already in a separate
992 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
993 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
996 /* If the retrieve type is full messages, refresh and get the messages */
997 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
999 iter = tny_list_create_iterator (observer->new_headers);
1000 while (!tny_iterator_is_done (iter)) {
1001 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1003 /* Apply per-message size limits */
1004 if (tny_header_get_message_size (header) < info->max_size)
1005 g_ptr_array_add (new_headers, g_object_ref (header));
1007 g_object_unref (header);
1008 tny_iterator_next (iter);
1010 g_object_unref (iter);
1012 /* We do not need to do it the first time
1013 because it's automatically done by the tree
1015 if (G_UNLIKELY (!first_time))
1016 tny_folder_poke_status (TNY_FOLDER (folder));
1018 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1019 g_object_unref (observer);
1022 g_object_unref (G_OBJECT (folder));
1025 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1029 tny_iterator_next (iter);
1032 did_a_cancel = FALSE;
1034 g_object_unref (G_OBJECT (iter));
1035 g_source_remove (timeout);
1037 if (new_headers->len > 0) {
1041 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1043 /* Apply message count limit */
1044 /* If the number of messages exceeds the maximum, ask the
1045 * user to download them all,
1046 * as per the UI spec "Retrieval Limits" section in 4.4:
1048 if (new_headers->len > info->retrieve_limit) {
1049 /* TODO: Ask the user, instead of just
1051 * mail_nc_msg_count_limit_exceeded, with 'Get
1052 * all' and 'Newest only' buttons. */
1053 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1054 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1055 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1056 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1057 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1062 priv->total = MIN (new_headers->len, info->retrieve_limit);
1063 while (msg_num < priv->total) {
1065 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1066 TnyFolder *folder = tny_header_get_folder (header);
1067 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1068 ModestMailOperationState *state;
1072 /* We can not just use the mail operation because the
1073 values of done and total could change before the
1075 state = modest_mail_operation_clone_state (info->mail_op);
1076 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1077 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1078 pair, (GDestroyNotify) modest_pair_free);
1080 g_object_unref (msg);
1081 g_object_unref (folder);
1085 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1086 g_ptr_array_free (new_headers, FALSE);
1090 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1093 if (priv->account != NULL)
1094 g_object_unref (priv->account);
1095 priv->account = g_object_ref (info->transport_account);
1097 send_queue = modest_runtime_get_send_queue (info->transport_account);
1099 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1100 modest_tny_send_queue_try_to_send (send_queue);
1101 g_source_remove (timeout);
1103 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1104 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1105 "cannot create a send queue for %s\n",
1106 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1107 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1110 /* Check if the operation was a success */
1112 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1114 /* Update the last updated key */
1115 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1116 set_last_updated_idle,
1117 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1118 (GDestroyNotify) g_free);
1123 if (info->callback) {
1124 UpdateAccountInfo *idle_info;
1126 /* This thread is not in the main lock */
1127 idle_info = g_malloc0 (sizeof (UpdateAccountInfo));
1128 idle_info->mail_op = g_object_ref (info->mail_op);
1129 idle_info->new_headers = (new_headers) ? new_headers->len : 0;
1130 idle_info->callback = info->callback;
1131 g_idle_add (idle_update_account_cb, idle_info);
1134 /* Notify about operation end. Note that the info could be
1135 freed before this idle happens, but the mail operation will
1137 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1140 g_object_unref (query);
1141 g_object_unref (all_folders);
1142 g_object_unref (info->account);
1143 g_object_unref (info->transport_account);
1144 g_free (info->retrieve_type);
1145 g_slice_free (UpdateAccountInfo, info);
1153 modest_mail_operation_update_account (ModestMailOperation *self,
1154 const gchar *account_name,
1155 UpdateAccountCallback callback,
1159 UpdateAccountInfo *info;
1160 ModestMailOperationPrivate *priv;
1161 ModestAccountMgr *mgr;
1162 TnyStoreAccount *modest_account;
1163 TnyTransportAccount *transport_account;
1165 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1166 g_return_val_if_fail (account_name, FALSE);
1168 /* Init mail operation. Set total and done to 0, and do not
1169 update them, this way the progress objects will know that
1170 we have no clue about the number of the objects */
1171 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1174 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1176 /* Get the Modest account */
1177 modest_account = (TnyStoreAccount *)
1178 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1180 TNY_ACCOUNT_TYPE_STORE);
1182 if (!modest_account) {
1183 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1184 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1185 "cannot get tny store account for %s\n", account_name);
1190 /* Get the transport account, we can not do it in the thread
1191 due to some problems with dbus */
1192 transport_account = (TnyTransportAccount *)
1193 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1195 if (!transport_account) {
1196 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1197 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1198 "cannot get tny transport account for %s\n", account_name);
1202 /* Create the helper object */
1203 info = g_slice_new (UpdateAccountInfo);
1204 info->mail_op = self;
1205 info->account = modest_account;
1206 info->transport_account = transport_account;
1207 info->callback = callback;
1208 info->user_data = user_data;
1210 /* Get the message size limit */
1211 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1212 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1213 if (info->max_size == 0)
1214 info->max_size = G_MAXINT;
1216 info->max_size = info->max_size * KB;
1218 /* Get per-account retrieval type */
1219 mgr = modest_runtime_get_account_mgr ();
1220 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1221 MODEST_ACCOUNT_RETRIEVE, FALSE);
1223 /* Get per-account message amount retrieval limit */
1224 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1225 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1226 if (info->retrieve_limit == 0)
1227 info->retrieve_limit = G_MAXINT;
1229 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1231 /* Set account busy */
1232 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1233 priv->account_name = g_strdup(account_name);
1235 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1240 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1242 callback (self, 0, user_data);
1243 modest_mail_operation_notify_end (self);
1247 /* ******************************************************************* */
1248 /* ************************** STORE ACTIONS ************************* */
1249 /* ******************************************************************* */
1253 modest_mail_operation_create_folder (ModestMailOperation *self,
1254 TnyFolderStore *parent,
1257 ModestMailOperationPrivate *priv;
1258 TnyFolder *new_folder = NULL;
1260 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1261 g_return_val_if_fail (name, NULL);
1263 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1266 if (TNY_IS_FOLDER (parent)) {
1267 /* Check folder rules */
1268 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1269 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1270 /* Set status failed and set an error */
1271 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1272 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1273 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1274 _("mail_in_ui_folder_create_error"));
1278 if (!strcmp (name, " ") || strchr (name, '/')) {
1279 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1280 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1281 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1282 _("mail_in_ui_folder_create_error"));
1286 /* Create the folder */
1287 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1288 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1290 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1293 /* Notify about operation end */
1294 modest_mail_operation_notify_end (self);
1300 modest_mail_operation_remove_folder (ModestMailOperation *self,
1302 gboolean remove_to_trash)
1304 TnyAccount *account;
1305 ModestMailOperationPrivate *priv;
1306 ModestTnyFolderRules rules;
1308 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1309 g_return_if_fail (TNY_IS_FOLDER (folder));
1311 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1313 /* Check folder rules */
1314 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1315 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1316 /* Set status failed and set an error */
1317 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1318 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1319 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1320 _("mail_in_ui_folder_delete_error"));
1324 /* Get the account */
1325 account = modest_tny_folder_get_account (folder);
1326 priv->account = g_object_ref(account);
1328 /* Delete folder or move to trash */
1329 if (remove_to_trash) {
1330 TnyFolder *trash_folder = NULL;
1331 trash_folder = modest_tny_account_get_special_folder (account,
1332 TNY_FOLDER_TYPE_TRASH);
1333 /* TODO: error_handling */
1334 modest_mail_operation_xfer_folder (self, folder,
1335 TNY_FOLDER_STORE (trash_folder), TRUE);
1337 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1339 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1340 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1343 g_object_unref (G_OBJECT (parent));
1345 g_object_unref (G_OBJECT (account));
1348 /* Notify about operation end */
1349 modest_mail_operation_notify_end (self);
1353 transfer_folder_status_cb (GObject *obj,
1357 ModestMailOperation *self;
1358 ModestMailOperationPrivate *priv;
1359 ModestMailOperationState *state;
1361 g_return_if_fail (status != NULL);
1362 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1364 self = MODEST_MAIL_OPERATION (user_data);
1365 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1367 priv->done = status->position;
1368 priv->total = status->of_total;
1370 state = modest_mail_operation_clone_state (self);
1371 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1372 g_slice_free (ModestMailOperationState, state);
1377 transfer_folder_cb (TnyFolder *folder,
1378 TnyFolderStore *into,
1380 TnyFolder *new_folder,
1384 ModestMailOperation *self = NULL;
1385 ModestMailOperationPrivate *priv = NULL;
1387 self = MODEST_MAIL_OPERATION (user_data);
1389 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1392 priv->error = g_error_copy (*err);
1394 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1395 } else if (cancelled) {
1396 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1397 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1398 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1399 _("Transference of %s was cancelled."),
1400 tny_folder_get_name (folder));
1403 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1407 g_object_unref (folder);
1408 g_object_unref (into);
1410 /* Notify about operation end */
1411 modest_mail_operation_notify_end (self);
1415 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1417 TnyFolderStore *parent,
1418 gboolean delete_original)
1420 ModestMailOperationPrivate *priv = NULL;
1421 ModestTnyFolderRules parent_rules = 0, rules;
1423 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1424 g_return_if_fail (TNY_IS_FOLDER (folder));
1426 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1428 /* Get account and set it into mail_operation */
1429 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1430 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1432 /* Get folder rules */
1433 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1434 if (TNY_IS_FOLDER (parent))
1435 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1437 /* The moveable restriction is applied also to copy operation */
1438 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1439 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1440 /* Set status failed and set an error */
1441 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1442 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1443 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1444 _("mail_in_ui_folder_move_target_error"));
1446 /* Notify the queue */
1447 modest_mail_operation_notify_end (self);
1448 } else if (TNY_IS_FOLDER (parent) &&
1449 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1450 /* Set status failed and set an error */
1451 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1452 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1453 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1454 _("FIXME: parent folder does not accept new folders"));
1456 /* Notify the queue */
1457 modest_mail_operation_notify_end (self);
1459 /* Pick references for async calls */
1460 g_object_ref (folder);
1461 g_object_ref (parent);
1463 /* Move/Copy folder */
1464 tny_folder_copy_async (folder,
1466 tny_folder_get_name (folder),
1469 transfer_folder_status_cb,
1475 modest_mail_operation_rename_folder (ModestMailOperation *self,
1479 ModestMailOperationPrivate *priv;
1480 ModestTnyFolderRules rules;
1482 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1483 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1484 g_return_if_fail (name);
1486 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1488 /* Get account and set it into mail_operation */
1489 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1491 /* Check folder rules */
1492 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1493 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1494 /* Set status failed and set an error */
1495 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1496 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1497 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1498 _("FIXME: unable to rename"));
1500 /* Notify about operation end */
1501 modest_mail_operation_notify_end (self);
1502 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1503 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1504 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1505 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1506 _("FIXME: unable to rename"));
1507 /* Notify about operation end */
1508 modest_mail_operation_notify_end (self);
1510 TnyFolderStore *into;
1512 /* Rename. Camel handles folder subscription/unsubscription */
1513 into = tny_folder_get_folder_store (folder);
1514 tny_folder_copy_async (folder, into, name, TRUE,
1516 transfer_folder_status_cb,
1519 g_object_unref (into);
1523 /* ******************************************************************* */
1524 /* ************************** MSG ACTIONS ************************* */
1525 /* ******************************************************************* */
1527 void modest_mail_operation_get_msg (ModestMailOperation *self,
1529 GetMsgAsyncUserCallback user_callback,
1532 GetMsgAsyncHelper *helper = NULL;
1534 ModestMailOperationPrivate *priv;
1536 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1537 g_return_if_fail (TNY_IS_HEADER (header));
1539 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1540 folder = tny_header_get_folder (header);
1542 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1544 /* Get message from folder */
1546 /* Get account and set it into mail_operation */
1547 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1549 helper = g_slice_new0 (GetMsgAsyncHelper);
1550 helper->mail_op = self;
1551 helper->user_callback = user_callback;
1552 helper->user_data = user_data;
1553 helper->header = g_object_ref (header);
1555 // The callback's reference so that the mail op is not
1556 // finalized until the async operation is completed even if
1557 // the user canceled the request meanwhile.
1558 g_object_ref (G_OBJECT (helper->mail_op));
1560 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1562 g_object_unref (G_OBJECT (folder));
1564 /* Set status failed and set an error */
1565 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1566 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1567 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1568 _("Error trying to get a message. No folder found for header"));
1570 /* Notify the queue */
1571 modest_mail_operation_notify_end (self);
1576 get_msg_cb (TnyFolder *folder,
1582 GetMsgAsyncHelper *helper = NULL;
1583 ModestMailOperation *self = NULL;
1584 ModestMailOperationPrivate *priv = NULL;
1586 helper = (GetMsgAsyncHelper *) user_data;
1587 g_return_if_fail (helper != NULL);
1588 self = helper->mail_op;
1589 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1590 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1592 /* Check errors and cancel */
1594 priv->error = g_error_copy (*error);
1595 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1599 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1600 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1601 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1602 _("Error trying to refresh the contents of %s"),
1603 tny_folder_get_name (folder));
1607 /* The mail operation might have been canceled in which case we do not
1608 want to notify anyone anymore. */
1609 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1610 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1612 /* If user defined callback function was defined, call it */
1613 if (helper->user_callback) {
1614 /* This callback is called into an iddle by tinymail,
1615 and idles are not in the main lock */
1616 gdk_threads_enter ();
1617 helper->user_callback (self, helper->header, msg, helper->user_data);
1618 gdk_threads_leave ();
1624 g_object_unref (helper->header);
1625 g_slice_free (GetMsgAsyncHelper, helper);
1627 /* Notify about operation end */
1628 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED)
1629 modest_mail_operation_notify_end (self);
1631 g_object_unref (G_OBJECT (self));
1635 get_msg_status_cb (GObject *obj,
1639 GetMsgAsyncHelper *helper = NULL;
1640 ModestMailOperation *self;
1641 ModestMailOperationPrivate *priv;
1642 ModestMailOperationState *state;
1644 g_return_if_fail (status != NULL);
1645 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1647 helper = (GetMsgAsyncHelper *) user_data;
1648 g_return_if_fail (helper != NULL);
1650 self = helper->mail_op;
1651 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1653 if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1659 state = modest_mail_operation_clone_state (self);
1660 state->bytes_done = status->position;
1661 state->bytes_total = status->of_total;
1662 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1663 g_slice_free (ModestMailOperationState, state);
1666 /****************************************************/
1668 ModestMailOperation *mail_op;
1670 GetMsgAsyncUserCallback user_callback;
1672 GDestroyNotify notify;
1676 GetMsgAsyncUserCallback user_callback;
1680 ModestMailOperation *mail_op;
1681 } NotifyGetMsgsInfo;
1685 * Used by get_msgs_full_thread to call the user_callback for each
1686 * message that has been read
1689 notify_get_msgs_full (gpointer data)
1691 NotifyGetMsgsInfo *info;
1693 info = (NotifyGetMsgsInfo *) data;
1695 /* Call the user callback. Idles are not in the main lock, so
1697 gdk_threads_enter ();
1698 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1699 gdk_threads_leave ();
1701 g_slice_free (NotifyGetMsgsInfo, info);
1707 * Used by get_msgs_full_thread to free al the thread resources and to
1708 * call the destroy function for the passed user_data
1711 get_msgs_full_destroyer (gpointer data)
1713 GetFullMsgsInfo *info;
1715 info = (GetFullMsgsInfo *) data;
1718 gdk_threads_enter ();
1719 info->notify (info->user_data);
1720 gdk_threads_leave ();
1724 g_object_unref (info->headers);
1725 g_slice_free (GetFullMsgsInfo, info);
1731 get_msgs_full_thread (gpointer thr_user_data)
1733 GetFullMsgsInfo *info;
1734 ModestMailOperationPrivate *priv = NULL;
1735 TnyIterator *iter = NULL;
1737 info = (GetFullMsgsInfo *) thr_user_data;
1738 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1740 iter = tny_list_create_iterator (info->headers);
1741 while (!tny_iterator_is_done (iter)) {
1745 header = TNY_HEADER (tny_iterator_get_current (iter));
1746 folder = tny_header_get_folder (header);
1748 /* Get message from folder */
1751 /* The callback will call it per each header */
1752 msg = tny_folder_get_msg (folder, header, &(priv->error));
1755 ModestMailOperationState *state;
1760 /* notify progress */
1761 state = modest_mail_operation_clone_state (info->mail_op);
1762 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1763 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1764 pair, (GDestroyNotify) modest_pair_free);
1766 /* The callback is the responsible for
1767 freeing the message */
1768 if (info->user_callback) {
1769 NotifyGetMsgsInfo *info_notify;
1770 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1771 info_notify->user_callback = info->user_callback;
1772 info_notify->mail_op = info->mail_op;
1773 info_notify->header = g_object_ref (header);
1774 info_notify->msg = g_object_ref (msg);
1775 info_notify->user_data = info->user_data;
1776 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1777 notify_get_msgs_full,
1780 g_object_unref (msg);
1783 /* Set status failed and set an error */
1784 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1785 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1786 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1787 "Error trying to get a message. No folder found for header");
1789 g_object_unref (header);
1790 tny_iterator_next (iter);
1793 /* Set operation status */
1794 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1795 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1797 /* Notify about operation end */
1798 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1800 /* Free thread resources. Will be called after all previous idles */
1801 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1807 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1808 TnyList *header_list,
1809 GetMsgAsyncUserCallback user_callback,
1811 GDestroyNotify notify)
1813 TnyHeader *header = NULL;
1814 TnyFolder *folder = NULL;
1816 ModestMailOperationPrivate *priv = NULL;
1817 GetFullMsgsInfo *info = NULL;
1818 gboolean size_ok = TRUE;
1820 TnyIterator *iter = NULL;
1822 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1824 /* Init mail operation */
1825 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1826 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1828 priv->total = tny_list_get_length(header_list);
1830 /* Get account and set it into mail_operation */
1831 if (tny_list_get_length (header_list) >= 1) {
1832 iter = tny_list_create_iterator (header_list);
1833 header = TNY_HEADER (tny_iterator_get_current (iter));
1834 folder = tny_header_get_folder (header);
1835 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1836 g_object_unref (header);
1837 g_object_unref (folder);
1839 if (tny_list_get_length (header_list) == 1) {
1840 g_object_unref (iter);
1845 /* Get msg size limit */
1846 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1847 MODEST_CONF_MSG_SIZE_LIMIT,
1850 g_clear_error (&(priv->error));
1851 max_size = G_MAXINT;
1853 max_size = max_size * KB;
1856 /* Check message size limits. If there is only one message
1857 always retrieve it */
1859 while (!tny_iterator_is_done (iter) && size_ok) {
1860 header = TNY_HEADER (tny_iterator_get_current (iter));
1861 if (tny_header_get_message_size (header) >= max_size)
1863 g_object_unref (header);
1864 tny_iterator_next (iter);
1866 g_object_unref (iter);
1870 /* Create the info */
1871 info = g_slice_new0 (GetFullMsgsInfo);
1872 info->mail_op = self;
1873 info->user_callback = user_callback;
1874 info->user_data = user_data;
1875 info->headers = g_object_ref (header_list);
1876 info->notify = notify;
1878 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1880 /* Set status failed and set an error */
1881 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1882 /* FIXME: the error msg is different for pop */
1883 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1884 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1885 _("emev_ni_ui_imap_msg_size_exceed_error"));
1886 /* Remove from queue and free resources */
1887 modest_mail_operation_notify_end (self);
1895 modest_mail_operation_remove_msg (ModestMailOperation *self, TnyHeader *header,
1896 gboolean remove_to_trash /*ignored*/)
1899 ModestMailOperationPrivate *priv;
1901 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1902 g_return_if_fail (TNY_IS_HEADER (header));
1904 if (remove_to_trash)
1905 g_warning ("remove to trash is not implemented");
1907 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1908 folder = tny_header_get_folder (header);
1910 /* Get account and set it into mail_operation */
1911 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1913 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1916 tny_folder_remove_msg (folder, header, &(priv->error));
1918 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1920 if (TNY_IS_CAMEL_IMAP_FOLDER (folder))
1921 tny_folder_sync(folder, FALSE, &(priv->error)); /* FALSE --> don't expunge */
1922 else if (TNY_IS_CAMEL_POP_FOLDER (folder))
1923 tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1926 tny_folder_sync(folder, TRUE, &(priv->error)); /* TRUE --> expunge */
1932 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1934 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1937 g_object_unref (G_OBJECT (folder));
1939 /* Notify about operation end */
1940 modest_mail_operation_notify_end (self);
1944 transfer_msgs_status_cb (GObject *obj,
1948 XFerMsgAsyncHelper *helper = NULL;
1949 ModestMailOperation *self;
1950 ModestMailOperationPrivate *priv;
1951 ModestMailOperationState *state;
1954 g_return_if_fail (status != NULL);
1955 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1957 helper = (XFerMsgAsyncHelper *) user_data;
1958 g_return_if_fail (helper != NULL);
1960 self = helper->mail_op;
1961 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1963 priv->done = status->position;
1964 priv->total = status->of_total;
1966 state = modest_mail_operation_clone_state (self);
1967 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1968 g_slice_free (ModestMailOperationState, state);
1973 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1975 XFerMsgAsyncHelper *helper;
1976 ModestMailOperation *self;
1977 ModestMailOperationPrivate *priv;
1979 helper = (XFerMsgAsyncHelper *) user_data;
1980 self = helper->mail_op;
1982 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1985 priv->error = g_error_copy (*err);
1987 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1988 } else if (cancelled) {
1989 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1990 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1991 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1992 _("Error trying to refresh the contents of %s"),
1993 tny_folder_get_name (folder));
1996 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1999 /* Notify about operation end */
2000 modest_mail_operation_notify_end (self);
2002 /* If user defined callback function was defined, call it */
2003 if (helper->user_callback) {
2004 gdk_threads_enter ();
2005 helper->user_callback (priv->source, helper->user_data);
2006 gdk_threads_leave ();
2010 g_object_unref (helper->headers);
2011 g_object_unref (helper->dest_folder);
2012 g_object_unref (helper->mail_op);
2013 g_slice_free (XFerMsgAsyncHelper, helper);
2014 g_object_unref (folder);
2019 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2022 gboolean delete_original,
2023 XferMsgsAsynUserCallback user_callback,
2026 ModestMailOperationPrivate *priv;
2028 TnyFolder *src_folder;
2029 XFerMsgAsyncHelper *helper;
2031 ModestTnyFolderRules rules;
2032 const gchar *id1 = NULL;
2033 const gchar *id2 = NULL;
2034 gboolean same_folder = FALSE;
2036 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2037 g_return_if_fail (TNY_IS_LIST (headers));
2038 g_return_if_fail (TNY_IS_FOLDER (folder));
2040 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2043 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2045 /* Apply folder rules */
2046 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2047 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2048 /* Set status failed and set an error */
2049 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2050 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2051 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2052 _("ckct_ib_unable_to_paste_here"));
2053 /* Notify the queue */
2054 modest_mail_operation_notify_end (self);
2058 /* Get source folder */
2059 iter = tny_list_create_iterator (headers);
2060 header = TNY_HEADER (tny_iterator_get_current (iter));
2061 src_folder = tny_header_get_folder (header);
2062 g_object_unref (header);
2063 g_object_unref (iter);
2065 /* Check folder source and destination */
2066 id1 = tny_folder_get_id (src_folder);
2067 id2 = tny_folder_get_id (TNY_FOLDER(folder));
2068 same_folder = !g_ascii_strcasecmp (id1, id2);
2070 /* Set status failed and set an error */
2071 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2072 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2073 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2074 _("mcen_ib_unable_to_copy_samefolder"));
2076 /* Notify the queue */
2077 modest_mail_operation_notify_end (self);
2080 g_object_unref (src_folder);
2084 /* Create the helper */
2085 helper = g_slice_new0 (XFerMsgAsyncHelper);
2086 helper->mail_op = g_object_ref(self);
2087 helper->dest_folder = g_object_ref(folder);
2088 helper->headers = g_object_ref(headers);
2089 helper->user_callback = user_callback;
2090 helper->user_data = user_data;
2092 /* Get account and set it into mail_operation */
2093 priv->account = modest_tny_folder_get_account (src_folder);
2095 /* Transfer messages */
2096 tny_folder_transfer_msgs_async (src_folder,
2101 transfer_msgs_status_cb,
2107 on_refresh_folder (TnyFolder *folder,
2112 RefreshAsyncHelper *helper = NULL;
2113 ModestMailOperation *self = NULL;
2114 ModestMailOperationPrivate *priv = NULL;
2116 helper = (RefreshAsyncHelper *) user_data;
2117 self = helper->mail_op;
2118 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2121 priv->error = g_error_copy (*error);
2122 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2127 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2128 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2129 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2130 _("Error trying to refresh the contents of %s"),
2131 tny_folder_get_name (folder));
2135 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2138 /* Call user defined callback, if it exists */
2139 if (helper->user_callback) {
2140 gdk_threads_enter ();
2141 helper->user_callback (priv->source, folder, helper->user_data);
2142 gdk_threads_leave ();
2146 g_object_unref (helper->mail_op);
2147 g_slice_free (RefreshAsyncHelper, helper);
2148 g_object_unref (folder);
2150 /* Notify about operation end */
2151 modest_mail_operation_notify_end (self);
2155 on_refresh_folder_status_update (GObject *obj,
2159 RefreshAsyncHelper *helper = NULL;
2160 ModestMailOperation *self = NULL;
2161 ModestMailOperationPrivate *priv = NULL;
2162 ModestMailOperationState *state;
2164 g_return_if_fail (user_data != NULL);
2165 g_return_if_fail (status != NULL);
2166 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2168 helper = (RefreshAsyncHelper *) user_data;
2169 self = helper->mail_op;
2170 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2172 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2174 priv->done = status->position;
2175 priv->total = status->of_total;
2177 state = modest_mail_operation_clone_state (self);
2178 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2179 g_slice_free (ModestMailOperationState, state);
2183 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2185 RefreshAsyncUserCallback user_callback,
2188 ModestMailOperationPrivate *priv = NULL;
2189 RefreshAsyncHelper *helper = NULL;
2191 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2193 /* Pick a reference */
2194 g_object_ref (folder);
2196 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2198 /* Get account and set it into mail_operation */
2199 priv->account = modest_tny_folder_get_account (folder);
2201 /* Create the helper */
2202 helper = g_slice_new0 (RefreshAsyncHelper);
2203 helper->mail_op = g_object_ref(self);
2204 helper->user_callback = user_callback;
2205 helper->user_data = user_data;
2207 /* Refresh the folder. TODO: tinymail could issue a status
2208 updates before the callback call then this could happen. We
2209 must review the design */
2210 tny_folder_refresh_async (folder,
2212 on_refresh_folder_status_update,
2218 * It's used by the mail operation queue to notify the observers
2219 * attached to that signal that the operation finished. We need to use
2220 * that because tinymail does not give us the progress of a given
2221 * operation when it finishes (it directly calls the operation
2225 modest_mail_operation_notify_end (ModestMailOperation *self)
2227 ModestMailOperationState *state;
2228 ModestMailOperationPrivate *priv = NULL;
2230 g_return_if_fail (self);
2232 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2235 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2239 /* Set the account back to not busy */
2240 if (priv->account_name) {
2241 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2242 priv->account_name, FALSE);
2243 g_free(priv->account_name);
2244 priv->account_name = NULL;
2247 /* Notify the observers about the mail opertation end */
2248 state = modest_mail_operation_clone_state (self);
2249 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2250 g_slice_free (ModestMailOperationState, state);