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-simple-list.h>
38 #include <tny-send-queue.h>
39 #include <tny-status.h>
40 #include <tny-folder-observer.h>
41 #include <camel/camel-stream-mem.h>
42 #include <glib/gi18n.h>
43 #include "modest-platform.h"
44 #include <modest-tny-account.h>
45 #include <modest-tny-send-queue.h>
46 #include <modest-runtime.h>
47 #include "modest-text-utils.h"
48 #include "modest-tny-msg.h"
49 #include "modest-tny-folder.h"
50 #include "modest-tny-platform-factory.h"
51 #include "modest-marshal.h"
52 #include "modest-error.h"
53 #include "modest-mail-operation.h"
57 /* 'private'/'protected' functions */
58 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
59 static void modest_mail_operation_init (ModestMailOperation *obj);
60 static void modest_mail_operation_finalize (GObject *obj);
62 static void get_msg_cb (TnyFolder *folder,
68 static void get_msg_status_cb (GObject *obj,
72 static void modest_mail_operation_notify_end (ModestMailOperation *self);
74 static gboolean did_a_cancel = FALSE;
76 enum _ModestMailOperationSignals
78 PROGRESS_CHANGED_SIGNAL,
83 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
84 struct _ModestMailOperationPrivate {
90 ErrorCheckingUserCallback error_checking;
91 gpointer error_checking_user_data;
92 ModestMailOperationStatus status;
93 ModestMailOperationTypeOperation op_type;
96 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
97 MODEST_TYPE_MAIL_OPERATION, \
98 ModestMailOperationPrivate))
100 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
101 priv->status = new_status;\
104 typedef struct _GetMsgAsyncHelper {
105 ModestMailOperation *mail_op;
106 GetMsgAsyncUserCallback user_callback;
111 typedef struct _XFerMsgAsyncHelper
113 ModestMailOperation *mail_op;
115 TnyFolder *dest_folder;
116 XferMsgsAsynUserCallback user_callback;
118 } XFerMsgAsyncHelper;
121 static GObjectClass *parent_class = NULL;
123 static guint signals[NUM_SIGNALS] = {0};
126 modest_mail_operation_get_type (void)
128 static GType my_type = 0;
130 static const GTypeInfo my_info = {
131 sizeof(ModestMailOperationClass),
132 NULL, /* base init */
133 NULL, /* base finalize */
134 (GClassInitFunc) modest_mail_operation_class_init,
135 NULL, /* class finalize */
136 NULL, /* class data */
137 sizeof(ModestMailOperation),
139 (GInstanceInitFunc) modest_mail_operation_init,
142 my_type = g_type_register_static (G_TYPE_OBJECT,
143 "ModestMailOperation",
150 modest_mail_operation_class_init (ModestMailOperationClass *klass)
152 GObjectClass *gobject_class;
153 gobject_class = (GObjectClass*) klass;
155 parent_class = g_type_class_peek_parent (klass);
156 gobject_class->finalize = modest_mail_operation_finalize;
158 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
161 * ModestMailOperation::progress-changed
162 * @self: the #MailOperation that emits the signal
163 * @user_data: user data set when the signal handler was connected
165 * Emitted when the progress of a mail operation changes
167 signals[PROGRESS_CHANGED_SIGNAL] =
168 g_signal_new ("progress-changed",
169 G_TYPE_FROM_CLASS (gobject_class),
171 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
173 g_cclosure_marshal_VOID__POINTER,
174 G_TYPE_NONE, 1, G_TYPE_POINTER);
179 modest_mail_operation_init (ModestMailOperation *obj)
181 ModestMailOperationPrivate *priv;
183 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
185 priv->account = NULL;
186 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
187 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
192 priv->error_checking = NULL;
193 priv->error_checking_user_data = NULL;
197 modest_mail_operation_finalize (GObject *obj)
199 ModestMailOperationPrivate *priv;
201 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
204 g_error_free (priv->error);
208 g_object_unref (priv->source);
212 g_object_unref (priv->account);
213 priv->account = NULL;
217 G_OBJECT_CLASS(parent_class)->finalize (obj);
221 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
224 ModestMailOperation *obj;
225 ModestMailOperationPrivate *priv;
227 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
228 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
230 priv->op_type = op_type;
232 priv->source = g_object_ref(source);
238 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
240 ErrorCheckingUserCallback error_handler,
243 ModestMailOperation *obj;
244 ModestMailOperationPrivate *priv;
246 obj = modest_mail_operation_new (op_type, source);
247 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
249 g_return_val_if_fail (error_handler != NULL, obj);
250 priv->error_checking = error_handler;
256 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
258 ModestMailOperationPrivate *priv;
260 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
261 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
263 if (priv->error_checking != NULL)
264 priv->error_checking (self, priv->error_checking_user_data);
268 ModestMailOperationTypeOperation
269 modest_mail_operation_get_type_operation (ModestMailOperation *self)
271 ModestMailOperationPrivate *priv;
273 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
275 return priv->op_type;
279 modest_mail_operation_is_mine (ModestMailOperation *self,
282 ModestMailOperationPrivate *priv;
284 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
285 if (priv->source == NULL) return FALSE;
287 return priv->source == me;
291 modest_mail_operation_get_source (ModestMailOperation *self)
293 ModestMailOperationPrivate *priv;
295 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
297 return g_object_ref (priv->source);
300 ModestMailOperationStatus
301 modest_mail_operation_get_status (ModestMailOperation *self)
303 ModestMailOperationPrivate *priv;
305 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
306 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
307 MODEST_MAIL_OPERATION_STATUS_INVALID);
309 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
314 modest_mail_operation_get_error (ModestMailOperation *self)
316 ModestMailOperationPrivate *priv;
318 g_return_val_if_fail (self, NULL);
319 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
321 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
326 modest_mail_operation_cancel (ModestMailOperation *self)
328 ModestMailOperationPrivate *priv;
330 if (!MODEST_IS_MAIL_OPERATION (self)) {
331 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
335 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
337 /* cancel current operation in account */
338 tny_account_cancel (priv->account);
343 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
345 /* Notify about operation end */
346 modest_mail_operation_notify_end (self);
352 modest_mail_operation_get_task_done (ModestMailOperation *self)
354 ModestMailOperationPrivate *priv;
356 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
358 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
363 modest_mail_operation_get_task_total (ModestMailOperation *self)
365 ModestMailOperationPrivate *priv;
367 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
369 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
374 modest_mail_operation_is_finished (ModestMailOperation *self)
376 ModestMailOperationPrivate *priv;
377 gboolean retval = FALSE;
379 if (!MODEST_IS_MAIL_OPERATION (self)) {
380 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
384 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
386 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
387 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
388 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
389 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
399 modest_mail_operation_get_id (ModestMailOperation *self)
401 ModestMailOperationPrivate *priv;
403 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
405 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
410 modest_mail_operation_set_id (ModestMailOperation *self,
413 ModestMailOperationPrivate *priv;
415 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
417 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
422 * Creates an image of the current state of a mail operation, the
423 * caller must free it
425 static ModestMailOperationState *
426 modest_mail_operation_clone_state (ModestMailOperation *self)
428 ModestMailOperationState *state;
429 ModestMailOperationPrivate *priv;
431 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
433 state = g_slice_new (ModestMailOperationState);
435 state->status = priv->status;
436 state->op_type = priv->op_type;
437 state->done = priv->done;
438 state->total = priv->total;
439 state->finished = modest_mail_operation_is_finished (self);
444 /* ******************************************************************* */
445 /* ************************** SEND ACTIONS ************************* */
446 /* ******************************************************************* */
449 modest_mail_operation_send_mail (ModestMailOperation *self,
450 TnyTransportAccount *transport_account,
453 TnySendQueue *send_queue = NULL;
454 ModestMailOperationPrivate *priv;
456 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
457 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
458 g_return_if_fail (TNY_IS_MSG (msg));
460 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
462 /* Get account and set it into mail_operation */
463 priv->account = g_object_ref (transport_account);
465 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
466 if (!TNY_IS_SEND_QUEUE(send_queue)) {
467 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
468 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
469 "modest: could not find send queue for account\n");
471 tny_send_queue_add (send_queue, msg, &(priv->error));
474 /* Notify about operation end */
475 modest_mail_operation_notify_end (self);
479 modest_mail_operation_send_new_mail (ModestMailOperation *self,
480 TnyTransportAccount *transport_account,
482 const gchar *from, const gchar *to,
483 const gchar *cc, const gchar *bcc,
484 const gchar *subject, const gchar *plain_body,
485 const gchar *html_body,
486 const GList *attachments_list,
487 TnyHeaderFlags priority_flags)
489 TnyMsg *new_msg = NULL;
490 TnyFolder *folder = NULL;
491 TnyHeader *header = NULL;
492 ModestMailOperationPrivate *priv = NULL;
494 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
495 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
497 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
499 /* Get account and set it into mail_operation */
500 priv->account = g_object_ref (transport_account);
502 /* Check parametters */
504 /* Set status failed and set an error */
505 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
506 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
507 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
508 _("Error trying to send a mail. You need to set at least one recipient"));
512 if (html_body == NULL) {
513 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
515 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
518 g_printerr ("modest: failed to create a new msg\n");
522 /* Set priority flags in message */
523 header = tny_msg_get_header (new_msg);
524 tny_header_set_flags (header, priority_flags);
526 /* Call mail operation */
527 modest_mail_operation_send_mail (self, transport_account, new_msg);
529 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
531 if (draft_msg != NULL) {
532 header = tny_msg_get_header (draft_msg);
533 /* Note: This can fail (with a warning) if the message is not really already in a folder,
534 * because this function requires it to have a UID. */
535 tny_folder_remove_msg (folder, header, NULL);
536 g_object_unref (header);
541 g_object_unref (G_OBJECT (new_msg));
545 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
546 TnyTransportAccount *transport_account,
548 const gchar *from, const gchar *to,
549 const gchar *cc, const gchar *bcc,
550 const gchar *subject, const gchar *plain_body,
551 const gchar *html_body,
552 const GList *attachments_list,
553 TnyHeaderFlags priority_flags)
556 TnyFolder *folder = NULL;
557 TnyHeader *header = NULL;
558 ModestMailOperationPrivate *priv = NULL;
560 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
561 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
563 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
565 /* Get account and set it into mail_operation */
566 priv->account = g_object_ref (transport_account);
568 if (html_body == NULL) {
569 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
571 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
574 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
575 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
576 "modest: failed to create a new msg\n");
580 /* add priority flags */
581 header = tny_msg_get_header (msg);
582 tny_header_set_flags (header, priority_flags);
584 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
586 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
587 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
588 "modest: failed to create a new msg\n");
592 if (draft_msg != NULL) {
593 header = tny_msg_get_header (draft_msg);
594 tny_folder_remove_msg (folder, header, NULL);
595 g_object_unref (header);
598 tny_folder_add_msg (folder, msg, &(priv->error));
604 g_object_unref (G_OBJECT(msg));
606 g_object_unref (G_OBJECT(folder));
608 modest_mail_operation_notify_end (self);
613 ModestMailOperation *mail_op;
614 TnyStoreAccount *account;
615 TnyTransportAccount *transport_account;
618 gchar *retrieve_type;
621 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
622 /* We use this folder observer to track the headers that have been
623 * added to a folder */
626 TnyList *new_headers;
627 } InternalFolderObserver;
631 } InternalFolderObserverClass;
633 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
635 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
636 internal_folder_observer,
638 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
642 foreach_add_item (gpointer header, gpointer user_data)
644 /* printf("DEBUG: %s: header subject=%s\n",
645 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
647 tny_list_prepend (TNY_LIST (user_data),
648 g_object_ref (G_OBJECT (header)));
651 /* This is the method that looks for new messages in a folder */
653 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
655 InternalFolderObserver *derived = (InternalFolderObserver *)self;
657 TnyFolderChangeChanged changed;
659 changed = tny_folder_change_get_changed (change);
661 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
664 /* Get added headers */
665 list = tny_simple_list_new ();
666 tny_folder_change_get_added_headers (change, list);
668 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
669 * __FUNCTION__, tny_list_get_length(list));
672 /* Add them to the folder observer */
673 tny_list_foreach (list, foreach_add_item,
674 derived->new_headers);
676 g_object_unref (G_OBJECT (list));
681 internal_folder_observer_init (InternalFolderObserver *self)
683 self->new_headers = tny_simple_list_new ();
686 internal_folder_observer_finalize (GObject *object)
688 InternalFolderObserver *self;
690 self = (InternalFolderObserver *) object;
691 g_object_unref (self->new_headers);
693 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
696 tny_folder_observer_init (TnyFolderObserverIface *iface)
698 iface->update_func = internal_folder_observer_update;
701 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
703 GObjectClass *object_class;
705 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
706 object_class = (GObjectClass*) klass;
707 object_class->finalize = internal_folder_observer_finalize;
713 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
716 TnyList *folders = tny_simple_list_new ();
718 tny_folder_store_get_folders (store, folders, query, NULL);
719 iter = tny_list_create_iterator (folders);
721 while (!tny_iterator_is_done (iter)) {
723 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
725 tny_list_prepend (all_folders, G_OBJECT (folder));
726 recurse_folders (folder, query, all_folders);
727 g_object_unref (G_OBJECT (folder));
729 tny_iterator_next (iter);
731 g_object_unref (G_OBJECT (iter));
732 g_object_unref (G_OBJECT (folders));
736 * Issues the "progress-changed" signal. The timer won't be removed,
737 * so you must call g_source_remove to stop the signal emission
740 idle_notify_progress (gpointer data)
742 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
743 ModestMailOperationState *state;
745 state = modest_mail_operation_clone_state (mail_op);
746 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
747 g_slice_free (ModestMailOperationState, state);
753 * Issues the "progress-changed" signal and removes the timer. It uses
754 * a lock to ensure that the progress information of the mail
755 * operation is not modified while there are notifications pending
758 idle_notify_progress_once (gpointer data)
762 pair = (ModestPair *) data;
764 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
766 /* Free the state and the reference to the mail operation */
767 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
768 g_object_unref (pair->first);
774 * Used by update_account_thread to notify the queue from the main
775 * loop. We call it inside an idle call to achieve that
778 notify_update_account_queue (gpointer data)
780 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
781 ModestMailOperationPrivate *priv = NULL;
783 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
785 modest_mail_operation_notify_end (mail_op);
786 g_object_unref (mail_op);
792 compare_headers_by_date (gconstpointer a,
795 TnyHeader **header1, **header2;
798 header1 = (TnyHeader **) a;
799 header2 = (TnyHeader **) b;
801 sent1 = tny_header_get_date_sent (*header1);
802 sent2 = tny_header_get_date_sent (*header2);
804 /* We want the most recent ones (greater time_t) at the
813 set_last_updated_idle (gpointer data)
815 /* It does not matter if the time is not exactly the same than
816 the time when this idle was called, it's just an
817 approximation and it won't be very different */
818 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
820 MODEST_ACCOUNT_LAST_UPDATED,
828 update_account_thread (gpointer thr_user_data)
830 UpdateAccountInfo *info;
831 TnyList *all_folders = NULL;
832 GPtrArray *new_headers;
833 TnyIterator *iter = NULL;
834 TnyFolderStoreQuery *query = NULL;
835 ModestMailOperationPrivate *priv;
836 ModestTnySendQueue *send_queue;
839 info = (UpdateAccountInfo *) thr_user_data;
840 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
842 /* Get account and set it into mail_operation */
843 priv->account = g_object_ref (info->account);
845 /* Get all the folders. We can do it synchronously because
846 we're already running in a different thread than the UI */
847 all_folders = tny_simple_list_new ();
848 query = tny_folder_store_query_new ();
849 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
850 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
855 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
859 iter = tny_list_create_iterator (all_folders);
860 while (!tny_iterator_is_done (iter)) {
861 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
863 recurse_folders (folder, query, all_folders);
864 tny_iterator_next (iter);
866 g_object_unref (G_OBJECT (iter));
868 /* Update status and notify. We need to call the notification
869 with a source function in order to call it from the main
870 loop. We need that in order not to get into trouble with
871 Gtk+. We use a timeout in order to provide more status
872 information, because the sync tinymail call does not
873 provide it for the moment */
874 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
876 /* Refresh folders */
877 new_headers = g_ptr_array_new ();
878 iter = tny_list_create_iterator (all_folders);
880 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
882 InternalFolderObserver *observer;
883 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
885 /* Refresh the folder */
886 /* Our observer receives notification of new emails during folder refreshes,
887 * so we can use observer->new_headers.
888 * TODO: This does not seem to be providing accurate numbers.
889 * Possibly the observer is notified asynchronously.
891 observer = g_object_new (internal_folder_observer_get_type (), NULL);
892 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
894 /* This gets the status information (headers) from the server.
895 * We use the blocking version, because we are already in a separate
899 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
900 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
903 /* If the retrieve type is full messages, refresh and get the messages */
904 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
906 iter = tny_list_create_iterator (observer->new_headers);
907 while (!tny_iterator_is_done (iter)) {
908 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
909 /* printf (" DEBUG1.2 %s: checking size: account=%s, subject=%s\n",
910 * __FUNCTION__, tny_account_get_id (priv->account),
911 * tny_header_get_subject (header));
914 /* Apply per-message size limits */
915 if (tny_header_get_message_size (header) < info->max_size)
916 g_ptr_array_add (new_headers, g_object_ref (header));
918 g_object_unref (header);
919 tny_iterator_next (iter);
921 g_object_unref (iter);
922 } else /* If it's headers only, then just poke the folder status (this will update the unread and total count of folder observers, like the folder list model*/
923 tny_folder_poke_status (TNY_FOLDER (folder));
925 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
926 g_object_unref (observer);
930 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
932 g_object_unref (G_OBJECT (folder));
933 tny_iterator_next (iter);
936 did_a_cancel = FALSE;
938 g_object_unref (G_OBJECT (iter));
939 g_source_remove (timeout);
941 if (new_headers->len > 0) {
945 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
947 /* Apply message count limit */
948 /* If the number of messages exceeds the maximum, ask the
949 * user to download them all,
950 * as per the UI spec "Retrieval Limits" section in 4.4:
952 printf ("DEBUG: %s: account=%s, len=%d, retrieve_limit = %d\n", __FUNCTION__,
953 tny_account_get_id (priv->account), new_headers->len, info->retrieve_limit);
954 if (new_headers->len > info->retrieve_limit) {
955 /* TODO: Ask the user, instead of just failing, showing mail_nc_msg_count_limit_exceeded,
956 * with 'Get all' and 'Newest only' buttons. */
957 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
958 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
959 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
960 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
961 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
966 priv->total = MIN (new_headers->len, info->retrieve_limit);
967 while (msg_num < priv->total) {
969 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
970 TnyFolder *folder = tny_header_get_folder (header);
971 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
972 ModestMailOperationState *state;
976 /* We can not just use the mail operation because the
977 values of done and total could change before the
979 state = modest_mail_operation_clone_state (info->mail_op);
980 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
981 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
982 pair, (GDestroyNotify) modest_pair_free);
984 g_object_unref (msg);
985 g_object_unref (folder);
989 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
990 g_ptr_array_free (new_headers, FALSE);
994 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
997 if (priv->account != NULL)
998 g_object_unref (priv->account);
999 priv->account = g_object_ref (info->transport_account);
1001 send_queue = modest_runtime_get_send_queue (info->transport_account);
1003 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1004 modest_tny_send_queue_try_to_send (send_queue);
1005 g_source_remove (timeout);
1007 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1008 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1009 "cannot create a send queue for %s\n",
1010 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1011 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1014 /* Check if the operation was a success */
1016 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1018 /* Update the last updated key. TODO: this causes
1019 sometimes an error in dbus, in order to fix this we
1020 must call gconf from the main loop */
1021 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1022 set_last_updated_idle,
1023 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1024 (GDestroyNotify) g_free);
1028 /* Notify about operation end. Note that the info could be
1029 freed before this idle happens, but the mail operation will
1031 g_idle_add (notify_update_account_queue, info->mail_op);
1034 g_object_unref (query);
1035 g_object_unref (all_folders);
1036 g_object_unref (info->account);
1037 g_object_unref (info->transport_account);
1038 g_free (info->retrieve_type);
1039 g_slice_free (UpdateAccountInfo, info);
1045 modest_mail_operation_update_account (ModestMailOperation *self,
1046 const gchar *account_name)
1049 UpdateAccountInfo *info;
1050 ModestMailOperationPrivate *priv;
1051 ModestAccountMgr *mgr;
1052 TnyStoreAccount *modest_account;
1053 TnyTransportAccount *transport_account;
1055 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1056 g_return_val_if_fail (account_name, FALSE);
1058 /* Make sure that we have a connection, and request one
1060 * TODO: Is there some way to trigger this for every attempt to
1061 * use the network? */
1062 if (!modest_platform_connect_and_wait(NULL))
1065 /* Init mail operation. Set total and done to 0, and do not
1066 update them, this way the progress objects will know that
1067 we have no clue about the number of the objects */
1068 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1071 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1073 /* Get the Modest account */
1074 modest_account = (TnyStoreAccount *)
1075 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1077 TNY_ACCOUNT_TYPE_STORE);
1079 if (!modest_account) {
1080 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1081 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1082 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1083 "cannot get tny store account for %s\n", account_name);
1084 modest_mail_operation_notify_end (self);
1089 /* Get the transport account, we can not do it in the thread
1090 due to some problems with dbus */
1091 transport_account = (TnyTransportAccount *)
1092 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1094 if (!transport_account) {
1095 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1096 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1097 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1098 "cannot get tny transport account for %s\n", account_name);
1099 modest_mail_operation_notify_end (self);
1104 /* Create the helper object */
1105 info = g_slice_new (UpdateAccountInfo);
1106 info->mail_op = self;
1107 info->account = modest_account;
1108 info->transport_account = transport_account;
1110 /* Get the message size limit */
1111 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1112 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1113 if (info->max_size == 0)
1114 info->max_size = G_MAXINT;
1116 info->max_size = info->max_size * KB;
1118 /* Get per-account retrieval type */
1119 mgr = modest_runtime_get_account_mgr ();
1120 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1121 MODEST_ACCOUNT_RETRIEVE, FALSE);
1123 /* Get per-account message amount retrieval limit */
1124 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1125 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1126 if (info->retrieve_limit == 0)
1127 info->retrieve_limit = G_MAXINT;
1129 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1131 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1136 /* ******************************************************************* */
1137 /* ************************** STORE ACTIONS ************************* */
1138 /* ******************************************************************* */
1142 modest_mail_operation_create_folder (ModestMailOperation *self,
1143 TnyFolderStore *parent,
1146 ModestMailOperationPrivate *priv;
1147 TnyFolder *new_folder = NULL;
1149 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1150 g_return_val_if_fail (name, NULL);
1152 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1155 if (TNY_IS_FOLDER (parent)) {
1156 /* Check folder rules */
1157 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1158 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1159 /* Set status failed and set an error */
1160 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1161 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1162 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1163 _("mail_in_ui_folder_create_error"));
1168 /* Create the folder */
1169 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1170 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1172 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1175 /* Notify about operation end */
1176 modest_mail_operation_notify_end (self);
1182 modest_mail_operation_remove_folder (ModestMailOperation *self,
1184 gboolean remove_to_trash)
1186 TnyAccount *account;
1187 ModestMailOperationPrivate *priv;
1188 ModestTnyFolderRules rules;
1190 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1191 g_return_if_fail (TNY_IS_FOLDER (folder));
1193 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1195 /* Check folder rules */
1196 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1197 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1198 /* Set status failed and set an error */
1199 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1200 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1201 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1202 _("mail_in_ui_folder_delete_error"));
1206 /* Get the account */
1207 account = modest_tny_folder_get_account (folder);
1208 priv->account = g_object_ref(account);
1210 /* Delete folder or move to trash */
1211 if (remove_to_trash) {
1212 TnyFolder *trash_folder = NULL;
1213 trash_folder = modest_tny_account_get_special_folder (account,
1214 TNY_FOLDER_TYPE_TRASH);
1215 /* TODO: error_handling */
1216 modest_mail_operation_xfer_folder (self, folder,
1217 TNY_FOLDER_STORE (trash_folder), TRUE);
1219 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1221 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1222 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1225 g_object_unref (G_OBJECT (parent));
1227 g_object_unref (G_OBJECT (account));
1230 /* Notify about operation end */
1231 modest_mail_operation_notify_end (self);
1235 transfer_folder_status_cb (GObject *obj,
1239 ModestMailOperation *self;
1240 ModestMailOperationPrivate *priv;
1241 ModestMailOperationState *state;
1243 g_return_if_fail (status != NULL);
1244 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1246 self = MODEST_MAIL_OPERATION (user_data);
1247 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1249 if ((status->position == 1) && (status->of_total == 100))
1252 priv->done = status->position;
1253 priv->total = status->of_total;
1255 state = modest_mail_operation_clone_state (self);
1256 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1257 g_slice_free (ModestMailOperationState, state);
1262 transfer_folder_cb (TnyFolder *folder,
1263 TnyFolderStore *into,
1265 TnyFolder *new_folder, GError **err,
1268 ModestMailOperation *self = NULL;
1269 ModestMailOperationPrivate *priv = NULL;
1271 self = MODEST_MAIL_OPERATION (user_data);
1273 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1276 priv->error = g_error_copy (*err);
1278 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1279 } else if (cancelled) {
1280 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1281 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1282 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1283 _("Transference of %s was cancelled."),
1284 tny_folder_get_name (folder));
1287 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1291 g_object_unref (folder);
1292 g_object_unref (into);
1293 if (new_folder != NULL)
1294 g_object_unref (new_folder);
1296 /* Notify about operation end */
1297 modest_mail_operation_notify_end (self);
1301 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1303 TnyFolderStore *parent,
1304 gboolean delete_original)
1306 ModestMailOperationPrivate *priv = NULL;
1307 ModestTnyFolderRules parent_rules, rules;
1309 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1310 g_return_if_fail (TNY_IS_FOLDER (folder));
1311 g_return_if_fail (TNY_IS_FOLDER (parent));
1313 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1315 /* Get account and set it into mail_operation */
1316 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1317 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1319 /* Get folder rules */
1320 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1321 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1323 if (!TNY_IS_FOLDER_STORE (parent)) {
1327 /* The moveable restriction is applied also to copy operation */
1328 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1329 /* Set status failed and set an error */
1330 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1331 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1332 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1333 _("mail_in_ui_folder_move_target_error"));
1335 /* Notify the queue */
1336 modest_mail_operation_notify_end (self);
1337 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1338 /* Set status failed and set an error */
1339 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1340 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1341 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1342 _("FIXME: parent folder does not accept new folders"));
1344 /* Notify the queue */
1345 modest_mail_operation_notify_end (self);
1347 /* Pick references for async calls */
1348 g_object_ref (folder);
1349 g_object_ref (parent);
1351 /* Move/Copy folder */
1352 tny_folder_copy_async (folder,
1354 tny_folder_get_name (folder),
1357 transfer_folder_status_cb,
1363 modest_mail_operation_rename_folder (ModestMailOperation *self,
1367 ModestMailOperationPrivate *priv;
1368 ModestTnyFolderRules rules;
1370 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1371 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1372 g_return_if_fail (name);
1374 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1376 /* Get account and set it into mail_operation */
1377 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1379 /* Check folder rules */
1380 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1381 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1382 /* Set status failed and set an error */
1383 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1384 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1385 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1386 _("FIXME: unable to rename"));
1388 /* Notify about operation end */
1389 modest_mail_operation_notify_end (self);
1391 /* Rename. Camel handles folder subscription/unsubscription */
1392 TnyFolderStore *into;
1394 into = tny_folder_get_folder_store (folder);
1395 tny_folder_copy_async (folder, into, name, TRUE,
1397 transfer_folder_status_cb,
1400 g_object_unref (into);
1405 /* ******************************************************************* */
1406 /* ************************** MSG ACTIONS ************************* */
1407 /* ******************************************************************* */
1409 void modest_mail_operation_get_msg (ModestMailOperation *self,
1411 GetMsgAsyncUserCallback user_callback,
1414 GetMsgAsyncHelper *helper = NULL;
1416 ModestMailOperationPrivate *priv;
1418 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1419 g_return_if_fail (TNY_IS_HEADER (header));
1421 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1422 folder = tny_header_get_folder (header);
1424 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1426 /* Get message from folder */
1428 /* Get account and set it into mail_operation */
1429 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1431 helper = g_slice_new0 (GetMsgAsyncHelper);
1432 helper->mail_op = self;
1433 helper->user_callback = user_callback;
1434 helper->pending_ops = 1;
1435 helper->user_data = user_data;
1437 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1439 g_object_unref (G_OBJECT (folder));
1441 /* Set status failed and set an error */
1442 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1443 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1444 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1445 _("Error trying to get a message. No folder found for header"));
1447 /* Notify the queue */
1448 modest_mail_operation_notify_end (self);
1453 get_msg_cb (TnyFolder *folder,
1459 GetMsgAsyncHelper *helper = NULL;
1460 ModestMailOperation *self = NULL;
1461 ModestMailOperationPrivate *priv = NULL;
1463 helper = (GetMsgAsyncHelper *) user_data;
1464 g_return_if_fail (helper != NULL);
1465 self = helper->mail_op;
1466 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1467 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1469 helper->pending_ops--;
1471 /* Check errors and cancel */
1473 priv->error = g_error_copy (*error);
1474 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1478 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1479 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1480 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1481 _("Error trying to refresh the contents of %s"),
1482 tny_folder_get_name (folder));
1486 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1488 /* If user defined callback function was defined, call it */
1489 if (helper->user_callback) {
1490 helper->user_callback (self, NULL, msg, helper->user_data);
1495 if (helper->pending_ops == 0) {
1496 g_slice_free (GetMsgAsyncHelper, helper);
1498 /* Notify about operation end */
1499 modest_mail_operation_notify_end (self);
1504 get_msg_status_cb (GObject *obj,
1508 GetMsgAsyncHelper *helper = NULL;
1509 ModestMailOperation *self;
1510 ModestMailOperationPrivate *priv;
1511 ModestMailOperationState *state;
1513 g_return_if_fail (status != NULL);
1514 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1516 helper = (GetMsgAsyncHelper *) user_data;
1517 g_return_if_fail (helper != NULL);
1519 self = helper->mail_op;
1520 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1522 if ((status->position == 1) && (status->of_total == 100))
1528 state = modest_mail_operation_clone_state (self);
1529 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1530 g_slice_free (ModestMailOperationState, state);
1533 /****************************************************/
1535 ModestMailOperation *mail_op;
1537 GetMsgAsyncUserCallback user_callback;
1539 GDestroyNotify notify;
1543 GetMsgAsyncUserCallback user_callback;
1547 ModestMailOperation *mail_op;
1548 } NotifyGetMsgsInfo;
1552 * Used by get_msgs_full_thread to call the user_callback for each
1553 * message that has been read
1556 notify_get_msgs_full (gpointer data)
1558 NotifyGetMsgsInfo *info;
1560 info = (NotifyGetMsgsInfo *) data;
1562 /* Call the user callback */
1563 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1565 g_slice_free (NotifyGetMsgsInfo, info);
1571 * Used by get_msgs_full_thread to free al the thread resources and to
1572 * call the destroy function for the passed user_data
1575 get_msgs_full_destroyer (gpointer data)
1577 GetFullMsgsInfo *info;
1579 info = (GetFullMsgsInfo *) data;
1582 info->notify (info->user_data);
1585 g_object_unref (info->headers);
1586 g_slice_free (GetFullMsgsInfo, info);
1592 get_msgs_full_thread (gpointer thr_user_data)
1594 GetFullMsgsInfo *info;
1595 ModestMailOperationPrivate *priv = NULL;
1596 TnyIterator *iter = NULL;
1598 info = (GetFullMsgsInfo *) thr_user_data;
1599 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1601 iter = tny_list_create_iterator (info->headers);
1602 while (!tny_iterator_is_done (iter)) {
1606 header = TNY_HEADER (tny_iterator_get_current (iter));
1607 folder = tny_header_get_folder (header);
1609 /* Get message from folder */
1612 /* The callback will call it per each header */
1613 msg = tny_folder_get_msg (folder, header, &(priv->error));
1616 ModestMailOperationState *state;
1621 /* notify progress */
1622 state = modest_mail_operation_clone_state (info->mail_op);
1623 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1624 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1625 pair, (GDestroyNotify) modest_pair_free);
1627 /* The callback is the responsible for
1628 freeing the message */
1629 if (info->user_callback) {
1630 NotifyGetMsgsInfo *info_notify;
1631 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1632 info_notify->user_callback = info->user_callback;
1633 info_notify->mail_op = info->mail_op;
1634 info_notify->header = g_object_ref (header);
1635 info_notify->msg = g_object_ref (msg);
1636 info_notify->user_data = info->user_data;
1637 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1638 notify_get_msgs_full,
1641 g_object_unref (msg);
1644 /* Set status failed and set an error */
1645 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1646 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1647 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1648 "Error trying to get a message. No folder found for header");
1650 g_object_unref (header);
1651 tny_iterator_next (iter);
1654 /* Set operation status */
1655 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1656 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1658 /* Notify about operation end */
1659 g_idle_add (notify_update_account_queue, info->mail_op);
1661 /* Free thread resources. Will be called after all previous idles */
1662 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1668 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1669 TnyList *header_list,
1670 GetMsgAsyncUserCallback user_callback,
1672 GDestroyNotify notify)
1674 TnyHeader *header = NULL;
1675 TnyFolder *folder = NULL;
1677 ModestMailOperationPrivate *priv = NULL;
1678 GetFullMsgsInfo *info = NULL;
1679 gboolean size_ok = TRUE;
1681 TnyIterator *iter = NULL;
1683 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1685 /* Init mail operation */
1686 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1687 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1689 priv->total = tny_list_get_length(header_list);
1691 /* Get account and set it into mail_operation */
1692 if (tny_list_get_length (header_list) >= 1) {
1693 iter = tny_list_create_iterator (header_list);
1694 header = TNY_HEADER (tny_iterator_get_current (iter));
1695 folder = tny_header_get_folder (header);
1696 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1697 g_object_unref (header);
1698 g_object_unref (folder);
1700 if (tny_list_get_length (header_list) == 1) {
1701 g_object_unref (iter);
1706 /* Get msg size limit */
1707 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1708 MODEST_CONF_MSG_SIZE_LIMIT,
1711 g_clear_error (&(priv->error));
1712 max_size = G_MAXINT;
1714 max_size = max_size * KB;
1717 /* Check message size limits. If there is only one message
1718 always retrieve it */
1720 while (!tny_iterator_is_done (iter) && size_ok) {
1721 header = TNY_HEADER (tny_iterator_get_current (iter));
1722 if (tny_header_get_message_size (header) >= max_size)
1724 g_object_unref (header);
1725 tny_iterator_next (iter);
1727 g_object_unref (iter);
1731 /* Create the info */
1732 info = g_slice_new0 (GetFullMsgsInfo);
1733 info->mail_op = self;
1734 info->user_callback = user_callback;
1735 info->user_data = user_data;
1736 info->headers = g_object_ref (header_list);
1737 info->notify = notify;
1739 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1741 /* Set status failed and set an error */
1742 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1743 /* FIXME: the error msg is different for pop */
1744 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1745 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1746 _("emev_ni_ui_imap_msg_size_exceed_error"));
1747 /* Remove from queue and free resources */
1748 modest_mail_operation_notify_end (self);
1756 modest_mail_operation_remove_msg (ModestMailOperation *self,
1758 gboolean remove_to_trash)
1761 ModestMailOperationPrivate *priv;
1763 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1764 g_return_if_fail (TNY_IS_HEADER (header));
1766 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1767 folder = tny_header_get_folder (header);
1769 /* Get account and set it into mail_operation */
1770 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1772 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1774 /* Delete or move to trash */
1775 if (remove_to_trash) {
1776 TnyFolder *trash_folder;
1777 TnyStoreAccount *store_account;
1779 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1780 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1781 TNY_FOLDER_TYPE_TRASH);
1786 headers = tny_simple_list_new ();
1787 tny_list_append (headers, G_OBJECT (header));
1788 g_object_unref (header);
1791 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1792 g_object_unref (headers);
1793 /* g_object_unref (trash_folder); */
1795 ModestMailOperationPrivate *priv;
1797 /* Set status failed and set an error */
1798 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1799 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1800 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1801 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1802 _("Error trying to delete a message. Trash folder not found"));
1805 g_object_unref (G_OBJECT (store_account));
1807 tny_folder_remove_msg (folder, header, &(priv->error));
1809 tny_folder_sync(folder, TRUE, &(priv->error));
1814 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1816 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1819 g_object_unref (G_OBJECT (folder));
1821 /* Notify about operation end */
1822 modest_mail_operation_notify_end (self);
1826 transfer_msgs_status_cb (GObject *obj,
1830 XFerMsgAsyncHelper *helper = NULL;
1831 ModestMailOperation *self;
1832 ModestMailOperationPrivate *priv;
1833 ModestMailOperationState *state;
1836 g_return_if_fail (status != NULL);
1837 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1839 helper = (XFerMsgAsyncHelper *) user_data;
1840 g_return_if_fail (helper != NULL);
1842 self = helper->mail_op;
1843 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1845 if ((status->position == 1) && (status->of_total == 100))
1848 priv->done = status->position;
1849 priv->total = status->of_total;
1851 state = modest_mail_operation_clone_state (self);
1852 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1853 g_slice_free (ModestMailOperationState, state);
1858 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1860 XFerMsgAsyncHelper *helper;
1861 ModestMailOperation *self;
1862 ModestMailOperationPrivate *priv;
1864 helper = (XFerMsgAsyncHelper *) user_data;
1865 self = helper->mail_op;
1867 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1870 priv->error = g_error_copy (*err);
1872 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1873 } else if (cancelled) {
1874 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1875 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1876 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1877 _("Error trying to refresh the contents of %s"),
1878 tny_folder_get_name (folder));
1881 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1884 /* If user defined callback function was defined, call it */
1885 if (helper->user_callback) {
1886 helper->user_callback (priv->source, helper->user_data);
1890 g_object_unref (helper->headers);
1891 g_object_unref (helper->dest_folder);
1892 g_object_unref (helper->mail_op);
1893 g_slice_free (XFerMsgAsyncHelper, helper);
1894 g_object_unref (folder);
1896 /* Notify about operation end */
1897 modest_mail_operation_notify_end (self);
1901 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1904 gboolean delete_original,
1905 XferMsgsAsynUserCallback user_callback,
1908 ModestMailOperationPrivate *priv;
1910 TnyFolder *src_folder;
1911 XFerMsgAsyncHelper *helper;
1913 ModestTnyFolderRules rules;
1915 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1916 g_return_if_fail (TNY_IS_LIST (headers));
1917 g_return_if_fail (TNY_IS_FOLDER (folder));
1919 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1922 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1924 /* Apply folder rules */
1925 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1927 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1928 /* Set status failed and set an error */
1929 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1930 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1931 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1932 _("FIXME: folder does not accept msgs"));
1933 /* Notify the queue */
1934 modest_mail_operation_notify_end (self);
1938 /* Create the helper */
1939 helper = g_slice_new0 (XFerMsgAsyncHelper);
1940 helper->mail_op = g_object_ref(self);
1941 helper->dest_folder = g_object_ref(folder);
1942 helper->headers = g_object_ref(headers);
1943 helper->user_callback = user_callback;
1944 helper->user_data = user_data;
1946 /* Get source folder */
1947 iter = tny_list_create_iterator (headers);
1948 header = TNY_HEADER (tny_iterator_get_current (iter));
1949 src_folder = tny_header_get_folder (header);
1950 g_object_unref (header);
1951 g_object_unref (iter);
1953 /* Get account and set it into mail_operation */
1954 priv->account = modest_tny_folder_get_account (src_folder);
1956 /* Transfer messages */
1957 tny_folder_transfer_msgs_async (src_folder,
1962 transfer_msgs_status_cb,
1968 on_refresh_folder (TnyFolder *folder,
1973 ModestMailOperation *self;
1974 ModestMailOperationPrivate *priv;
1976 self = MODEST_MAIL_OPERATION (user_data);
1977 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1980 priv->error = g_error_copy (*error);
1981 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1986 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1987 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1988 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1989 _("Error trying to refresh the contents of %s"),
1990 tny_folder_get_name (folder));
1994 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1998 g_object_unref (folder);
2000 /* Notify about operation end */
2001 modest_mail_operation_notify_end (self);
2005 on_refresh_folder_status_update (GObject *obj,
2009 ModestMailOperation *self;
2010 ModestMailOperationPrivate *priv;
2011 ModestMailOperationState *state;
2013 g_return_if_fail (status != NULL);
2014 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2016 self = MODEST_MAIL_OPERATION (user_data);
2017 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2019 priv->done = status->position;
2020 priv->total = status->of_total;
2022 state = modest_mail_operation_clone_state (self);
2023 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2024 g_slice_free (ModestMailOperationState, state);
2028 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2031 ModestMailOperationPrivate *priv;
2033 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2035 /* Pick a reference */
2036 g_object_ref (folder);
2038 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2040 /* Get account and set it into mail_operation */
2041 priv->account = modest_tny_folder_get_account (folder);
2043 /* Refresh the folder. TODO: tinymail could issue a status
2044 updates before the callback call then this could happen. We
2045 must review the design */
2046 tny_folder_refresh_async (folder,
2048 on_refresh_folder_status_update,
2054 * It's used by the mail operation queue to notify the observers
2055 * attached to that signal that the operation finished. We need to use
2056 * that because tinymail does not give us the progress of a given
2057 * operation when it finishes (it directly calls the operation
2061 modest_mail_operation_notify_end (ModestMailOperation *self)
2063 ModestMailOperationState *state;
2065 /* Notify the observers about the mail opertation end */
2066 state = modest_mail_operation_clone_state (self);
2067 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2068 g_slice_free (ModestMailOperationState, state);