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-simple-list.h>
39 #include <tny-send-queue.h>
40 #include <tny-status.h>
41 #include <tny-folder-observer.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include "modest-platform.h"
45 #include <modest-tny-account.h>
46 #include <modest-tny-send-queue.h>
47 #include <modest-runtime.h>
48 #include "modest-text-utils.h"
49 #include "modest-tny-msg.h"
50 #include "modest-tny-folder.h"
51 #include "modest-tny-platform-factory.h"
52 #include "modest-marshal.h"
53 #include "modest-error.h"
54 #include "modest-mail-operation.h"
58 /* 'private'/'protected' functions */
59 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
60 static void modest_mail_operation_init (ModestMailOperation *obj);
61 static void modest_mail_operation_finalize (GObject *obj);
63 static void get_msg_cb (TnyFolder *folder,
69 static void get_msg_status_cb (GObject *obj,
73 static void modest_mail_operation_notify_end (ModestMailOperation *self);
75 static gboolean did_a_cancel = FALSE;
77 enum _ModestMailOperationSignals
79 PROGRESS_CHANGED_SIGNAL,
84 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
85 struct _ModestMailOperationPrivate {
92 ErrorCheckingUserCallback error_checking;
93 gpointer error_checking_user_data;
94 ModestMailOperationStatus status;
95 ModestMailOperationTypeOperation op_type;
98 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
99 MODEST_TYPE_MAIL_OPERATION, \
100 ModestMailOperationPrivate))
102 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
103 priv->status = new_status;\
106 typedef struct _GetMsgAsyncHelper {
107 ModestMailOperation *mail_op;
109 GetMsgAsyncUserCallback user_callback;
113 typedef struct _RefreshAsyncHelper {
114 ModestMailOperation *mail_op;
115 RefreshAsyncUserCallback user_callback;
117 } RefreshAsyncHelper;
119 typedef struct _XFerMsgAsyncHelper
121 ModestMailOperation *mail_op;
123 TnyFolder *dest_folder;
124 XferMsgsAsynUserCallback user_callback;
126 } XFerMsgAsyncHelper;
129 static GObjectClass *parent_class = NULL;
131 static guint signals[NUM_SIGNALS] = {0};
134 modest_mail_operation_get_type (void)
136 static GType my_type = 0;
138 static const GTypeInfo my_info = {
139 sizeof(ModestMailOperationClass),
140 NULL, /* base init */
141 NULL, /* base finalize */
142 (GClassInitFunc) modest_mail_operation_class_init,
143 NULL, /* class finalize */
144 NULL, /* class data */
145 sizeof(ModestMailOperation),
147 (GInstanceInitFunc) modest_mail_operation_init,
150 my_type = g_type_register_static (G_TYPE_OBJECT,
151 "ModestMailOperation",
158 modest_mail_operation_class_init (ModestMailOperationClass *klass)
160 GObjectClass *gobject_class;
161 gobject_class = (GObjectClass*) klass;
163 parent_class = g_type_class_peek_parent (klass);
164 gobject_class->finalize = modest_mail_operation_finalize;
166 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
169 * ModestMailOperation::progress-changed
170 * @self: the #MailOperation that emits the signal
171 * @user_data: user data set when the signal handler was connected
173 * Emitted when the progress of a mail operation changes
175 signals[PROGRESS_CHANGED_SIGNAL] =
176 g_signal_new ("progress-changed",
177 G_TYPE_FROM_CLASS (gobject_class),
179 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
181 g_cclosure_marshal_VOID__POINTER,
182 G_TYPE_NONE, 1, G_TYPE_POINTER);
187 modest_mail_operation_init (ModestMailOperation *obj)
189 ModestMailOperationPrivate *priv;
191 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
193 priv->account = NULL;
194 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
195 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
200 priv->error_checking = NULL;
201 priv->error_checking_user_data = NULL;
205 modest_mail_operation_finalize (GObject *obj)
207 ModestMailOperationPrivate *priv;
209 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
214 g_error_free (priv->error);
218 g_object_unref (priv->source);
222 g_object_unref (priv->account);
223 priv->account = NULL;
227 G_OBJECT_CLASS(parent_class)->finalize (obj);
231 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
234 ModestMailOperation *obj;
235 ModestMailOperationPrivate *priv;
237 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
238 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
240 priv->op_type = op_type;
242 priv->source = g_object_ref(source);
248 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
250 ErrorCheckingUserCallback error_handler,
253 ModestMailOperation *obj;
254 ModestMailOperationPrivate *priv;
256 obj = modest_mail_operation_new (op_type, source);
257 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
259 g_return_val_if_fail (error_handler != NULL, obj);
260 priv->error_checking = error_handler;
266 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
268 ModestMailOperationPrivate *priv;
270 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
271 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
273 if (priv->error_checking != NULL)
274 priv->error_checking (self, priv->error_checking_user_data);
278 ModestMailOperationTypeOperation
279 modest_mail_operation_get_type_operation (ModestMailOperation *self)
281 ModestMailOperationPrivate *priv;
283 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
285 return priv->op_type;
289 modest_mail_operation_is_mine (ModestMailOperation *self,
292 ModestMailOperationPrivate *priv;
294 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
295 if (priv->source == NULL) return FALSE;
297 return priv->source == me;
301 modest_mail_operation_get_source (ModestMailOperation *self)
303 ModestMailOperationPrivate *priv;
305 g_return_val_if_fail (self, NULL);
307 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
309 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
313 return g_object_ref (priv->source);
316 ModestMailOperationStatus
317 modest_mail_operation_get_status (ModestMailOperation *self)
319 ModestMailOperationPrivate *priv;
321 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
322 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
323 MODEST_MAIL_OPERATION_STATUS_INVALID);
325 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
327 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
328 return MODEST_MAIL_OPERATION_STATUS_INVALID;
335 modest_mail_operation_get_error (ModestMailOperation *self)
337 ModestMailOperationPrivate *priv;
339 g_return_val_if_fail (self, NULL);
340 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
342 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
345 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
353 modest_mail_operation_cancel (ModestMailOperation *self)
355 ModestMailOperationPrivate *priv;
357 if (!MODEST_IS_MAIL_OPERATION (self)) {
358 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
362 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
364 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
371 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
373 /* This emits progress-changed on which the mail operation queue is
374 * listening, so the mail operation is correctly removed from the
375 * queue without further explicit calls. */
376 modest_mail_operation_notify_end (self);
382 modest_mail_operation_get_task_done (ModestMailOperation *self)
384 ModestMailOperationPrivate *priv;
386 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
388 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
393 modest_mail_operation_get_task_total (ModestMailOperation *self)
395 ModestMailOperationPrivate *priv;
397 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
399 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
404 modest_mail_operation_is_finished (ModestMailOperation *self)
406 ModestMailOperationPrivate *priv;
407 gboolean retval = FALSE;
409 if (!MODEST_IS_MAIL_OPERATION (self)) {
410 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
414 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
416 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
417 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
418 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
419 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
429 modest_mail_operation_get_id (ModestMailOperation *self)
431 ModestMailOperationPrivate *priv;
433 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
435 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
440 modest_mail_operation_set_id (ModestMailOperation *self,
443 ModestMailOperationPrivate *priv;
445 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
447 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
452 * Creates an image of the current state of a mail operation, the
453 * caller must free it
455 static ModestMailOperationState *
456 modest_mail_operation_clone_state (ModestMailOperation *self)
458 ModestMailOperationState *state;
459 ModestMailOperationPrivate *priv;
461 /* FIXME: this should be fixed properly
463 * in some cases, priv was NULL, so checking here to
466 g_return_val_if_fail (self, NULL);
467 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
468 g_return_val_if_fail (priv, NULL);
473 state = g_slice_new (ModestMailOperationState);
475 state->status = priv->status;
476 state->op_type = priv->op_type;
477 state->done = priv->done;
478 state->total = priv->total;
479 state->finished = modest_mail_operation_is_finished (self);
480 state->bytes_done = 0;
481 state->bytes_total = 0;
486 /* ******************************************************************* */
487 /* ************************** SEND ACTIONS ************************* */
488 /* ******************************************************************* */
491 modest_mail_operation_send_mail (ModestMailOperation *self,
492 TnyTransportAccount *transport_account,
495 TnySendQueue *send_queue = NULL;
496 ModestMailOperationPrivate *priv;
498 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
499 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
500 g_return_if_fail (TNY_IS_MSG (msg));
502 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
504 /* Get account and set it into mail_operation */
505 priv->account = g_object_ref (transport_account);
509 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
510 if (!TNY_IS_SEND_QUEUE(send_queue)) {
511 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
512 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
513 "modest: could not find send queue for account\n");
515 /* TODO: connect to the msg-sent in order to know when
516 the mail operation is finished */
517 tny_send_queue_add (send_queue, msg, &(priv->error));
518 /* TODO: we're setting always success, do the check in
520 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
523 /* TODO: do this in the handler of the "msg-sent"
524 signal.Notify about operation end */
525 modest_mail_operation_notify_end (self);
529 modest_mail_operation_send_new_mail (ModestMailOperation *self,
530 TnyTransportAccount *transport_account,
532 const gchar *from, const gchar *to,
533 const gchar *cc, const gchar *bcc,
534 const gchar *subject, const gchar *plain_body,
535 const gchar *html_body,
536 const GList *attachments_list,
537 TnyHeaderFlags priority_flags)
539 TnyMsg *new_msg = NULL;
540 TnyFolder *folder = NULL;
541 TnyHeader *header = NULL;
542 ModestMailOperationPrivate *priv = NULL;
544 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
545 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
547 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
549 /* Check parametters */
551 /* Set status failed and set an error */
552 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
553 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
554 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
555 _("Error trying to send a mail. You need to set at least one recipient"));
559 if (html_body == NULL) {
560 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
562 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
565 g_printerr ("modest: failed to create a new msg\n");
569 /* Set priority flags in message */
570 header = tny_msg_get_header (new_msg);
571 if (priority_flags != 0)
572 tny_header_set_flags (header, priority_flags);
574 /* Call mail operation */
575 modest_mail_operation_send_mail (self, transport_account, new_msg);
577 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
579 if (draft_msg != NULL) {
580 header = tny_msg_get_header (draft_msg);
581 /* Note: This can fail (with a warning) if the message is not really already in a folder,
582 * because this function requires it to have a UID. */
583 tny_folder_remove_msg (folder, header, NULL);
584 g_object_unref (header);
589 g_object_unref (G_OBJECT (new_msg));
593 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
594 TnyTransportAccount *transport_account,
596 const gchar *from, const gchar *to,
597 const gchar *cc, const gchar *bcc,
598 const gchar *subject, const gchar *plain_body,
599 const gchar *html_body,
600 const GList *attachments_list,
601 TnyHeaderFlags priority_flags)
604 TnyFolder *folder = NULL;
605 TnyHeader *header = NULL;
606 ModestMailOperationPrivate *priv = NULL;
608 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
609 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
611 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
613 /* Get account and set it into mail_operation */
614 priv->account = g_object_ref (transport_account);
616 if (html_body == NULL) {
617 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
619 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
622 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
623 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
624 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
625 "modest: failed to create a new msg\n");
629 /* add priority flags */
630 header = tny_msg_get_header (msg);
631 tny_header_set_flags (header, priority_flags);
633 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
635 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
636 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
637 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
638 "modest: failed to create a new msg\n");
642 if (draft_msg != NULL) {
643 header = tny_msg_get_header (draft_msg);
644 /* Remove the old draft expunging it */
645 tny_folder_remove_msg (folder, header, NULL);
646 tny_folder_sync (folder, TRUE, NULL);
647 g_object_unref (header);
650 tny_folder_add_msg (folder, msg, &(priv->error));
652 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
656 g_object_unref (G_OBJECT(msg));
658 g_object_unref (G_OBJECT(folder));
660 modest_mail_operation_notify_end (self);
665 ModestMailOperation *mail_op;
666 TnyStoreAccount *account;
667 TnyTransportAccount *transport_account;
670 gchar *retrieve_type;
672 UpdateAccountCallback callback;
676 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
677 /* We use this folder observer to track the headers that have been
678 * added to a folder */
681 TnyList *new_headers;
682 } InternalFolderObserver;
686 } InternalFolderObserverClass;
688 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
690 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
691 internal_folder_observer,
693 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
697 foreach_add_item (gpointer header, gpointer user_data)
699 /* printf("DEBUG: %s: header subject=%s\n",
700 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
702 tny_list_prepend (TNY_LIST (user_data),
703 g_object_ref (G_OBJECT (header)));
706 /* This is the method that looks for new messages in a folder */
708 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
710 InternalFolderObserver *derived = (InternalFolderObserver *)self;
712 TnyFolderChangeChanged changed;
714 changed = tny_folder_change_get_changed (change);
716 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
719 /* Get added headers */
720 list = tny_simple_list_new ();
721 tny_folder_change_get_added_headers (change, list);
723 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
724 * __FUNCTION__, tny_list_get_length(list));
727 /* Add them to the folder observer */
728 tny_list_foreach (list, foreach_add_item,
729 derived->new_headers);
731 g_object_unref (G_OBJECT (list));
736 internal_folder_observer_init (InternalFolderObserver *self)
738 self->new_headers = tny_simple_list_new ();
741 internal_folder_observer_finalize (GObject *object)
743 InternalFolderObserver *self;
745 self = (InternalFolderObserver *) object;
746 g_object_unref (self->new_headers);
748 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
751 tny_folder_observer_init (TnyFolderObserverIface *iface)
753 iface->update_func = internal_folder_observer_update;
756 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
758 GObjectClass *object_class;
760 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
761 object_class = (GObjectClass*) klass;
762 object_class->finalize = internal_folder_observer_finalize;
768 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
771 TnyList *folders = tny_simple_list_new ();
773 tny_folder_store_get_folders (store, folders, query, NULL);
774 iter = tny_list_create_iterator (folders);
776 while (!tny_iterator_is_done (iter)) {
778 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
780 tny_list_prepend (all_folders, G_OBJECT (folder));
781 recurse_folders (folder, query, all_folders);
782 g_object_unref (G_OBJECT (folder));
784 tny_iterator_next (iter);
786 g_object_unref (G_OBJECT (iter));
787 g_object_unref (G_OBJECT (folders));
791 * Issues the "progress-changed" signal. The timer won't be removed,
792 * so you must call g_source_remove to stop the signal emission
795 idle_notify_progress (gpointer data)
797 gdk_threads_enter ();
799 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
800 ModestMailOperationState *state;
802 state = modest_mail_operation_clone_state (mail_op);
803 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
804 g_slice_free (ModestMailOperationState, state);
806 gdk_threads_leave ();
812 * Issues the "progress-changed" signal and removes the timer. It uses
813 * a lock to ensure that the progress information of the mail
814 * operation is not modified while there are notifications pending
817 idle_notify_progress_once (gpointer data)
819 gdk_threads_enter ();
823 pair = (ModestPair *) data;
825 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
827 /* Free the state and the reference to the mail operation */
828 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
829 g_object_unref (pair->first);
831 gdk_threads_leave ();
837 * Used by update_account_thread to notify the queue from the main
838 * loop. We call it inside an idle call to achieve that
841 idle_notify_update_account_queue (gpointer data)
843 gdk_threads_enter ();
845 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
846 ModestMailOperationPrivate *priv = NULL;
848 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
850 modest_mail_operation_notify_end (mail_op);
851 g_object_unref (mail_op);
853 gdk_threads_leave ();
859 compare_headers_by_date (gconstpointer a,
862 TnyHeader **header1, **header2;
865 header1 = (TnyHeader **) a;
866 header2 = (TnyHeader **) b;
868 sent1 = tny_header_get_date_sent (*header1);
869 sent2 = tny_header_get_date_sent (*header2);
871 /* We want the most recent ones (greater time_t) at the
880 set_last_updated_idle (gpointer data)
882 gdk_threads_enter ();
884 /* It does not matter if the time is not exactly the same than
885 the time when this idle was called, it's just an
886 approximation and it won't be very different */
887 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
889 MODEST_ACCOUNT_LAST_UPDATED,
893 gdk_threads_leave ();
899 update_account_thread (gpointer thr_user_data)
901 static gboolean first_time = TRUE;
902 UpdateAccountInfo *info;
903 TnyList *all_folders = NULL;
904 GPtrArray *new_headers = NULL;
905 TnyIterator *iter = NULL;
906 TnyFolderStoreQuery *query = NULL;
907 ModestMailOperationPrivate *priv = NULL;
908 ModestTnySendQueue *send_queue = NULL;
910 info = (UpdateAccountInfo *) thr_user_data;
911 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
913 /* Get account and set it into mail_operation */
914 priv->account = g_object_ref (info->account);
917 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
918 * show any updates unless we do that
920 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
921 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
923 /* Get all the folders. We can do it synchronously because
924 we're already running in a different thread than the UI */
925 all_folders = tny_simple_list_new ();
926 query = tny_folder_store_query_new ();
927 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
928 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
933 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
937 iter = tny_list_create_iterator (all_folders);
938 while (!tny_iterator_is_done (iter)) {
939 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
941 recurse_folders (folder, query, all_folders);
942 tny_iterator_next (iter);
944 g_object_unref (G_OBJECT (iter));
946 /* Update status and notify. We need to call the notification
947 with a source function in order to call it from the main
948 loop. We need that in order not to get into trouble with
949 Gtk+. We use a timeout in order to provide more status
950 information, because the sync tinymail call does not
951 provide it for the moment */
952 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
954 /* Refresh folders */
955 new_headers = g_ptr_array_new ();
956 iter = tny_list_create_iterator (all_folders);
958 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
960 InternalFolderObserver *observer;
961 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
963 /* Refresh the folder */
964 /* Our observer receives notification of new emails during folder refreshes,
965 * so we can use observer->new_headers.
967 observer = g_object_new (internal_folder_observer_get_type (), NULL);
968 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
970 /* This gets the status information (headers) from the server.
971 * We use the blocking version, because we are already in a separate
975 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
976 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
979 /* If the retrieve type is full messages, refresh and get the messages */
980 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
982 iter = tny_list_create_iterator (observer->new_headers);
983 while (!tny_iterator_is_done (iter)) {
984 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
986 /* Apply per-message size limits */
987 if (tny_header_get_message_size (header) < info->max_size)
988 g_ptr_array_add (new_headers, g_object_ref (header));
990 g_object_unref (header);
991 tny_iterator_next (iter);
993 g_object_unref (iter);
995 /* We do not need to do it the first time
996 because it's automatically done by the tree
998 if (G_UNLIKELY (!first_time))
999 tny_folder_poke_status (TNY_FOLDER (folder));
1001 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1002 g_object_unref (observer);
1005 g_object_unref (G_OBJECT (folder));
1008 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1012 tny_iterator_next (iter);
1015 did_a_cancel = FALSE;
1017 g_object_unref (G_OBJECT (iter));
1018 g_source_remove (timeout);
1020 if (new_headers->len > 0) {
1024 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1026 /* Apply message count limit */
1027 /* If the number of messages exceeds the maximum, ask the
1028 * user to download them all,
1029 * as per the UI spec "Retrieval Limits" section in 4.4:
1031 if (new_headers->len > info->retrieve_limit) {
1032 /* TODO: Ask the user, instead of just
1034 * mail_nc_msg_count_limit_exceeded, with 'Get
1035 * all' and 'Newest only' buttons. */
1036 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1037 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1038 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1039 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1040 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1045 priv->total = MIN (new_headers->len, info->retrieve_limit);
1046 while (msg_num < priv->total) {
1048 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1049 TnyFolder *folder = tny_header_get_folder (header);
1050 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1051 ModestMailOperationState *state;
1055 /* We can not just use the mail operation because the
1056 values of done and total could change before the
1058 state = modest_mail_operation_clone_state (info->mail_op);
1059 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1060 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1061 pair, (GDestroyNotify) modest_pair_free);
1063 g_object_unref (msg);
1064 g_object_unref (folder);
1068 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1069 g_ptr_array_free (new_headers, FALSE);
1073 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1076 if (priv->account != NULL)
1077 g_object_unref (priv->account);
1078 priv->account = g_object_ref (info->transport_account);
1080 send_queue = modest_runtime_get_send_queue (info->transport_account);
1082 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1083 modest_tny_send_queue_try_to_send (send_queue);
1084 g_source_remove (timeout);
1086 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1087 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1088 "cannot create a send queue for %s\n",
1089 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1090 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1093 /* Check if the operation was a success */
1095 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1097 /* Update the last updated key */
1098 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1099 set_last_updated_idle,
1100 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1101 (GDestroyNotify) g_free);
1105 /* Notify about operation end. Note that the info could be
1106 freed before this idle happens, but the mail operation will
1108 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1110 if (info->callback) {
1111 /* This thread is not in the main lock */
1112 gdk_threads_enter ();
1113 info->callback (info->mail_op,
1114 (new_headers) ? new_headers->len : 0,
1116 gdk_threads_leave ();
1120 g_object_unref (query);
1121 g_object_unref (all_folders);
1122 g_object_unref (info->account);
1123 g_object_unref (info->transport_account);
1124 g_free (info->retrieve_type);
1125 g_slice_free (UpdateAccountInfo, info);
1133 modest_mail_operation_update_account (ModestMailOperation *self,
1134 const gchar *account_name,
1135 UpdateAccountCallback callback,
1139 UpdateAccountInfo *info;
1140 ModestMailOperationPrivate *priv;
1141 ModestAccountMgr *mgr;
1142 TnyStoreAccount *modest_account;
1143 TnyTransportAccount *transport_account;
1145 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1146 g_return_val_if_fail (account_name, FALSE);
1148 /* Init mail operation. Set total and done to 0, and do not
1149 update them, this way the progress objects will know that
1150 we have no clue about the number of the objects */
1151 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1154 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1156 /* Make sure that we have a connection, and request one
1158 * TODO: Is there some way to trigger this for every attempt to
1159 * use the network? */
1160 if (!modest_platform_connect_and_wait(NULL))
1163 /* Get the Modest account */
1164 modest_account = (TnyStoreAccount *)
1165 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1167 TNY_ACCOUNT_TYPE_STORE);
1169 if (!modest_account) {
1170 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1171 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1172 "cannot get tny store account for %s\n", account_name);
1177 /* Get the transport account, we can not do it in the thread
1178 due to some problems with dbus */
1179 transport_account = (TnyTransportAccount *)
1180 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1182 if (!transport_account) {
1183 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1184 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1185 "cannot get tny transport account for %s\n", account_name);
1189 /* Create the helper object */
1190 info = g_slice_new (UpdateAccountInfo);
1191 info->mail_op = self;
1192 info->account = modest_account;
1193 info->transport_account = transport_account;
1194 info->callback = callback;
1195 info->user_data = user_data;
1197 /* Get the message size limit */
1198 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1199 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1200 if (info->max_size == 0)
1201 info->max_size = G_MAXINT;
1203 info->max_size = info->max_size * KB;
1205 /* Get per-account retrieval type */
1206 mgr = modest_runtime_get_account_mgr ();
1207 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1208 MODEST_ACCOUNT_RETRIEVE, FALSE);
1210 /* Get per-account message amount retrieval limit */
1211 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1212 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1213 if (info->retrieve_limit == 0)
1214 info->retrieve_limit = G_MAXINT;
1216 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1218 /* Set account busy */
1219 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1220 priv->account_name = g_strdup(account_name);
1222 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1227 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1229 callback (self, 0, user_data);
1230 modest_mail_operation_notify_end (self);
1234 /* ******************************************************************* */
1235 /* ************************** STORE ACTIONS ************************* */
1236 /* ******************************************************************* */
1240 modest_mail_operation_create_folder (ModestMailOperation *self,
1241 TnyFolderStore *parent,
1244 ModestMailOperationPrivate *priv;
1245 TnyFolder *new_folder = NULL;
1247 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1248 g_return_val_if_fail (name, NULL);
1250 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1253 if (TNY_IS_FOLDER (parent)) {
1254 /* Check folder rules */
1255 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1256 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1257 /* Set status failed and set an error */
1258 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1259 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1260 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1261 _("mail_in_ui_folder_create_error"));
1266 /* Create the folder */
1267 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1268 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1270 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1273 /* Notify about operation end */
1274 modest_mail_operation_notify_end (self);
1280 modest_mail_operation_remove_folder (ModestMailOperation *self,
1282 gboolean remove_to_trash)
1284 TnyAccount *account;
1285 ModestMailOperationPrivate *priv;
1286 ModestTnyFolderRules rules;
1288 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1289 g_return_if_fail (TNY_IS_FOLDER (folder));
1291 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1293 /* Check folder rules */
1294 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1295 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1296 /* Set status failed and set an error */
1297 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1298 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1299 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1300 _("mail_in_ui_folder_delete_error"));
1304 /* Get the account */
1305 account = modest_tny_folder_get_account (folder);
1306 priv->account = g_object_ref(account);
1308 /* Delete folder or move to trash */
1309 if (remove_to_trash) {
1310 TnyFolder *trash_folder = NULL;
1311 trash_folder = modest_tny_account_get_special_folder (account,
1312 TNY_FOLDER_TYPE_TRASH);
1313 /* TODO: error_handling */
1314 modest_mail_operation_xfer_folder (self, folder,
1315 TNY_FOLDER_STORE (trash_folder), TRUE);
1317 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1319 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1320 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1323 g_object_unref (G_OBJECT (parent));
1325 g_object_unref (G_OBJECT (account));
1328 /* Notify about operation end */
1329 modest_mail_operation_notify_end (self);
1333 transfer_folder_status_cb (GObject *obj,
1337 ModestMailOperation *self;
1338 ModestMailOperationPrivate *priv;
1339 ModestMailOperationState *state;
1341 g_return_if_fail (status != NULL);
1342 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1344 self = MODEST_MAIL_OPERATION (user_data);
1345 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1347 if ((status->position == 1) && (status->of_total == 100))
1350 priv->done = status->position;
1351 priv->total = status->of_total;
1353 state = modest_mail_operation_clone_state (self);
1354 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1355 g_slice_free (ModestMailOperationState, state);
1360 transfer_folder_cb (TnyFolder *folder,
1361 TnyFolderStore *into,
1363 TnyFolder *new_folder, GError **err,
1366 ModestMailOperation *self = NULL;
1367 ModestMailOperationPrivate *priv = NULL;
1369 self = MODEST_MAIL_OPERATION (user_data);
1371 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1374 priv->error = g_error_copy (*err);
1376 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1377 } else if (cancelled) {
1378 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1379 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1380 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1381 _("Transference of %s was cancelled."),
1382 tny_folder_get_name (folder));
1385 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1389 g_object_unref (folder);
1390 g_object_unref (into);
1391 if (new_folder != NULL)
1392 g_object_unref (new_folder);
1394 /* Notify about operation end */
1395 modest_mail_operation_notify_end (self);
1399 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1401 TnyFolderStore *parent,
1402 gboolean delete_original)
1404 ModestMailOperationPrivate *priv = NULL;
1405 ModestTnyFolderRules parent_rules, rules;
1407 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1408 g_return_if_fail (TNY_IS_FOLDER (folder));
1409 g_return_if_fail (TNY_IS_FOLDER (parent));
1411 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1413 /* Get account and set it into mail_operation */
1414 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1415 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1417 /* Get folder rules */
1418 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1419 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1421 if (!TNY_IS_FOLDER_STORE (parent)) {
1425 /* The moveable restriction is applied also to copy operation */
1426 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1427 /* Set status failed and set an error */
1428 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1429 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1430 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1431 _("mail_in_ui_folder_move_target_error"));
1433 /* Notify the queue */
1434 modest_mail_operation_notify_end (self);
1435 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1436 /* Set status failed and set an error */
1437 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1438 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1439 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1440 _("FIXME: parent folder does not accept new folders"));
1442 /* Notify the queue */
1443 modest_mail_operation_notify_end (self);
1445 /* Pick references for async calls */
1446 g_object_ref (folder);
1447 g_object_ref (parent);
1449 /* Move/Copy folder */
1450 tny_folder_copy_async (folder,
1452 tny_folder_get_name (folder),
1455 transfer_folder_status_cb,
1461 modest_mail_operation_rename_folder (ModestMailOperation *self,
1465 ModestMailOperationPrivate *priv;
1466 ModestTnyFolderRules rules;
1468 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1469 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1470 g_return_if_fail (name);
1472 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1474 /* Get account and set it into mail_operation */
1475 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1477 /* Check folder rules */
1478 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1479 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1480 /* Set status failed and set an error */
1481 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1482 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1483 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1484 _("FIXME: unable to rename"));
1486 /* Notify about operation end */
1487 modest_mail_operation_notify_end (self);
1489 /* Rename. Camel handles folder subscription/unsubscription */
1490 TnyFolderStore *into;
1492 into = tny_folder_get_folder_store (folder);
1493 tny_folder_copy_async (folder, into, name, TRUE,
1495 transfer_folder_status_cb,
1498 g_object_unref (into);
1503 /* ******************************************************************* */
1504 /* ************************** MSG ACTIONS ************************* */
1505 /* ******************************************************************* */
1507 void modest_mail_operation_get_msg (ModestMailOperation *self,
1509 GetMsgAsyncUserCallback user_callback,
1512 GetMsgAsyncHelper *helper = NULL;
1514 ModestMailOperationPrivate *priv;
1516 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1517 g_return_if_fail (TNY_IS_HEADER (header));
1519 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1520 folder = tny_header_get_folder (header);
1522 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1524 /* Get message from folder */
1526 /* Get account and set it into mail_operation */
1527 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1529 helper = g_slice_new0 (GetMsgAsyncHelper);
1530 helper->mail_op = self;
1531 helper->user_callback = user_callback;
1532 helper->user_data = user_data;
1533 helper->header = g_object_ref (header);
1535 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1537 g_object_unref (G_OBJECT (folder));
1539 /* Set status failed and set an error */
1540 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1541 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1542 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1543 _("Error trying to get a message. No folder found for header"));
1545 /* Notify the queue */
1546 modest_mail_operation_notify_end (self);
1551 get_msg_cb (TnyFolder *folder,
1557 GetMsgAsyncHelper *helper = NULL;
1558 ModestMailOperation *self = NULL;
1559 ModestMailOperationPrivate *priv = NULL;
1561 helper = (GetMsgAsyncHelper *) user_data;
1562 g_return_if_fail (helper != NULL);
1563 self = helper->mail_op;
1564 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1565 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1567 /* Check errors and cancel */
1569 priv->error = g_error_copy (*error);
1570 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1574 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1575 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1576 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1577 _("Error trying to refresh the contents of %s"),
1578 tny_folder_get_name (folder));
1582 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1584 /* If user defined callback function was defined, call it */
1585 if (helper->user_callback) {
1586 /* This callback is called into an iddle by tinymail,
1587 and idles are not in the main lock */
1588 gdk_threads_enter ();
1589 helper->user_callback (self, helper->header, msg, helper->user_data);
1590 gdk_threads_leave ();
1595 g_object_unref (helper->header);
1596 g_slice_free (GetMsgAsyncHelper, helper);
1598 /* Notify about operation end */
1599 modest_mail_operation_notify_end (self);
1603 get_msg_status_cb (GObject *obj,
1607 GetMsgAsyncHelper *helper = NULL;
1608 ModestMailOperation *self;
1609 ModestMailOperationPrivate *priv;
1610 ModestMailOperationState *state;
1612 g_return_if_fail (status != NULL);
1613 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1615 helper = (GetMsgAsyncHelper *) user_data;
1616 g_return_if_fail (helper != NULL);
1618 self = helper->mail_op;
1619 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1621 if ((status->position == 1) && (status->of_total == 100))
1627 state = modest_mail_operation_clone_state (self);
1628 state->bytes_done = status->position;
1629 state->bytes_total = status->of_total;
1630 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1631 g_slice_free (ModestMailOperationState, state);
1634 /****************************************************/
1636 ModestMailOperation *mail_op;
1638 GetMsgAsyncUserCallback user_callback;
1640 GDestroyNotify notify;
1644 GetMsgAsyncUserCallback user_callback;
1648 ModestMailOperation *mail_op;
1649 } NotifyGetMsgsInfo;
1653 * Used by get_msgs_full_thread to call the user_callback for each
1654 * message that has been read
1657 notify_get_msgs_full (gpointer data)
1659 gdk_threads_enter ();
1661 NotifyGetMsgsInfo *info;
1663 info = (NotifyGetMsgsInfo *) data;
1665 /* Call the user callback. Idles are not in the main lock, so
1667 gdk_threads_enter ();
1668 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1669 gdk_threads_leave ();
1671 g_slice_free (NotifyGetMsgsInfo, info);
1673 gdk_threads_leave ();
1679 * Used by get_msgs_full_thread to free al the thread resources and to
1680 * call the destroy function for the passed user_data
1683 get_msgs_full_destroyer (gpointer data)
1685 gdk_threads_enter ();
1687 GetFullMsgsInfo *info;
1689 info = (GetFullMsgsInfo *) data;
1692 info->notify (info->user_data);
1695 g_object_unref (info->headers);
1696 g_slice_free (GetFullMsgsInfo, info);
1698 gdk_threads_leave ();
1704 get_msgs_full_thread (gpointer thr_user_data)
1706 GetFullMsgsInfo *info;
1707 ModestMailOperationPrivate *priv = NULL;
1708 TnyIterator *iter = NULL;
1710 info = (GetFullMsgsInfo *) thr_user_data;
1711 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1713 iter = tny_list_create_iterator (info->headers);
1714 while (!tny_iterator_is_done (iter)) {
1718 header = TNY_HEADER (tny_iterator_get_current (iter));
1719 folder = tny_header_get_folder (header);
1721 /* Get message from folder */
1724 /* The callback will call it per each header */
1725 msg = tny_folder_get_msg (folder, header, &(priv->error));
1728 ModestMailOperationState *state;
1733 /* notify progress */
1734 state = modest_mail_operation_clone_state (info->mail_op);
1735 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1736 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1737 pair, (GDestroyNotify) modest_pair_free);
1739 /* The callback is the responsible for
1740 freeing the message */
1741 if (info->user_callback) {
1742 NotifyGetMsgsInfo *info_notify;
1743 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1744 info_notify->user_callback = info->user_callback;
1745 info_notify->mail_op = info->mail_op;
1746 info_notify->header = g_object_ref (header);
1747 info_notify->msg = g_object_ref (msg);
1748 info_notify->user_data = info->user_data;
1749 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1750 notify_get_msgs_full,
1753 g_object_unref (msg);
1756 /* Set status failed and set an error */
1757 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1758 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1759 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1760 "Error trying to get a message. No folder found for header");
1762 g_object_unref (header);
1763 tny_iterator_next (iter);
1766 /* Set operation status */
1767 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1768 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1770 /* Notify about operation end */
1771 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1773 /* Free thread resources. Will be called after all previous idles */
1774 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1780 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1781 TnyList *header_list,
1782 GetMsgAsyncUserCallback user_callback,
1784 GDestroyNotify notify)
1786 TnyHeader *header = NULL;
1787 TnyFolder *folder = NULL;
1789 ModestMailOperationPrivate *priv = NULL;
1790 GetFullMsgsInfo *info = NULL;
1791 gboolean size_ok = TRUE;
1793 TnyIterator *iter = NULL;
1795 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1797 /* Init mail operation */
1798 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1799 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1801 priv->total = tny_list_get_length(header_list);
1803 /* Get account and set it into mail_operation */
1804 if (tny_list_get_length (header_list) >= 1) {
1805 iter = tny_list_create_iterator (header_list);
1806 header = TNY_HEADER (tny_iterator_get_current (iter));
1807 folder = tny_header_get_folder (header);
1808 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1809 g_object_unref (header);
1810 g_object_unref (folder);
1812 if (tny_list_get_length (header_list) == 1) {
1813 g_object_unref (iter);
1818 /* Get msg size limit */
1819 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1820 MODEST_CONF_MSG_SIZE_LIMIT,
1823 g_clear_error (&(priv->error));
1824 max_size = G_MAXINT;
1826 max_size = max_size * KB;
1829 /* Check message size limits. If there is only one message
1830 always retrieve it */
1832 while (!tny_iterator_is_done (iter) && size_ok) {
1833 header = TNY_HEADER (tny_iterator_get_current (iter));
1834 if (tny_header_get_message_size (header) >= max_size)
1836 g_object_unref (header);
1837 tny_iterator_next (iter);
1839 g_object_unref (iter);
1843 /* Create the info */
1844 info = g_slice_new0 (GetFullMsgsInfo);
1845 info->mail_op = self;
1846 info->user_callback = user_callback;
1847 info->user_data = user_data;
1848 info->headers = g_object_ref (header_list);
1849 info->notify = notify;
1851 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1853 /* Set status failed and set an error */
1854 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1855 /* FIXME: the error msg is different for pop */
1856 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1857 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1858 _("emev_ni_ui_imap_msg_size_exceed_error"));
1859 /* Remove from queue and free resources */
1860 modest_mail_operation_notify_end (self);
1868 modest_mail_operation_remove_msg (ModestMailOperation *self,
1870 gboolean remove_to_trash)
1873 ModestMailOperationPrivate *priv;
1875 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1876 g_return_if_fail (TNY_IS_HEADER (header));
1878 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1879 folder = tny_header_get_folder (header);
1881 /* Get account and set it into mail_operation */
1882 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1884 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1886 /* Delete or move to trash */
1887 if (remove_to_trash) {
1888 TnyFolder *trash_folder;
1889 TnyStoreAccount *store_account;
1891 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1892 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1893 TNY_FOLDER_TYPE_TRASH);
1898 headers = tny_simple_list_new ();
1899 tny_list_append (headers, G_OBJECT (header));
1900 g_object_unref (header);
1903 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1904 g_object_unref (headers);
1905 /* g_object_unref (trash_folder); */
1907 ModestMailOperationPrivate *priv;
1909 /* Set status failed and set an error */
1910 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1911 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1912 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1913 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1914 _("Error trying to delete a message. Trash folder not found"));
1917 g_object_unref (G_OBJECT (store_account));
1919 tny_folder_remove_msg (folder, header, &(priv->error));
1921 tny_folder_sync(folder, TRUE, &(priv->error));
1926 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1928 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1931 g_object_unref (G_OBJECT (folder));
1933 /* Notify about operation end */
1934 modest_mail_operation_notify_end (self);
1938 transfer_msgs_status_cb (GObject *obj,
1942 XFerMsgAsyncHelper *helper = NULL;
1943 ModestMailOperation *self;
1944 ModestMailOperationPrivate *priv;
1945 ModestMailOperationState *state;
1948 g_return_if_fail (status != NULL);
1949 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1951 helper = (XFerMsgAsyncHelper *) user_data;
1952 g_return_if_fail (helper != NULL);
1954 self = helper->mail_op;
1955 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1957 if ((status->position == 1) && (status->of_total == 100))
1960 priv->done = status->position;
1961 priv->total = status->of_total;
1963 state = modest_mail_operation_clone_state (self);
1964 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1965 g_slice_free (ModestMailOperationState, state);
1970 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1972 XFerMsgAsyncHelper *helper;
1973 ModestMailOperation *self;
1974 ModestMailOperationPrivate *priv;
1976 helper = (XFerMsgAsyncHelper *) user_data;
1977 self = helper->mail_op;
1979 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1982 priv->error = g_error_copy (*err);
1984 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1985 } else if (cancelled) {
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));
1993 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1996 /* If user defined callback function was defined, call it */
1997 if (helper->user_callback) {
1998 gdk_threads_enter ();
1999 helper->user_callback (priv->source, helper->user_data);
2000 gdk_threads_leave ();
2004 g_object_unref (helper->headers);
2005 g_object_unref (helper->dest_folder);
2006 g_object_unref (helper->mail_op);
2007 g_slice_free (XFerMsgAsyncHelper, helper);
2008 g_object_unref (folder);
2010 /* Notify about operation end */
2011 modest_mail_operation_notify_end (self);
2015 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2018 gboolean delete_original,
2019 XferMsgsAsynUserCallback user_callback,
2022 ModestMailOperationPrivate *priv;
2024 TnyFolder *src_folder;
2025 XFerMsgAsyncHelper *helper;
2027 ModestTnyFolderRules rules;
2029 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2030 g_return_if_fail (TNY_IS_LIST (headers));
2031 g_return_if_fail (TNY_IS_FOLDER (folder));
2033 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2036 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2038 /* Apply folder rules */
2039 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2040 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2041 /* Set status failed and set an error */
2042 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2043 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2044 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2045 _("FIXME: folder does not accept msgs"));
2046 /* Notify the queue */
2047 modest_mail_operation_notify_end (self);
2051 /* Create the helper */
2052 helper = g_slice_new0 (XFerMsgAsyncHelper);
2053 helper->mail_op = g_object_ref(self);
2054 helper->dest_folder = g_object_ref(folder);
2055 helper->headers = g_object_ref(headers);
2056 helper->user_callback = user_callback;
2057 helper->user_data = user_data;
2059 /* Get source folder */
2060 iter = tny_list_create_iterator (headers);
2061 header = TNY_HEADER (tny_iterator_get_current (iter));
2062 src_folder = tny_header_get_folder (header);
2063 g_object_unref (header);
2064 g_object_unref (iter);
2066 /* Get account and set it into mail_operation */
2067 priv->account = modest_tny_folder_get_account (src_folder);
2069 /* Transfer messages */
2070 tny_folder_transfer_msgs_async (src_folder,
2075 transfer_msgs_status_cb,
2081 on_refresh_folder (TnyFolder *folder,
2086 RefreshAsyncHelper *helper = NULL;
2087 ModestMailOperation *self = NULL;
2088 ModestMailOperationPrivate *priv = NULL;
2090 helper = (RefreshAsyncHelper *) user_data;
2091 self = helper->mail_op;
2092 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2095 priv->error = g_error_copy (*error);
2096 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2101 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2102 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2103 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2104 _("Error trying to refresh the contents of %s"),
2105 tny_folder_get_name (folder));
2109 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2112 /* Call user defined callback, if it exists */
2113 if (helper->user_callback) {
2114 /* gdk_threads_enter (); */
2115 helper->user_callback (priv->source, folder, helper->user_data);
2116 /* gdk_threads_leave (); */
2120 g_object_unref (helper->mail_op);
2121 g_slice_free (RefreshAsyncHelper, helper);
2122 g_object_unref (folder);
2124 /* Notify about operation end */
2125 modest_mail_operation_notify_end (self);
2129 on_refresh_folder_status_update (GObject *obj,
2133 RefreshAsyncHelper *helper = NULL;
2134 ModestMailOperation *self = NULL;
2135 ModestMailOperationPrivate *priv = NULL;
2136 ModestMailOperationState *state;
2138 g_return_if_fail (user_data != NULL);
2139 g_return_if_fail (status != NULL);
2140 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2142 helper = (RefreshAsyncHelper *) user_data;
2143 self = helper->mail_op;
2144 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2146 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2148 priv->done = status->position;
2149 priv->total = status->of_total;
2151 state = modest_mail_operation_clone_state (self);
2152 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2153 g_slice_free (ModestMailOperationState, state);
2157 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2159 RefreshAsyncUserCallback user_callback,
2162 ModestMailOperationPrivate *priv = NULL;
2163 RefreshAsyncHelper *helper = NULL;
2165 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2167 /* Pick a reference */
2168 g_object_ref (folder);
2170 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2172 /* Get account and set it into mail_operation */
2173 priv->account = modest_tny_folder_get_account (folder);
2175 /* Create the helper */
2176 helper = g_slice_new0 (RefreshAsyncHelper);
2177 helper->mail_op = g_object_ref(self);
2178 helper->user_callback = user_callback;
2179 helper->user_data = user_data;
2181 /* Refresh the folder. TODO: tinymail could issue a status
2182 updates before the callback call then this could happen. We
2183 must review the design */
2184 tny_folder_refresh_async (folder,
2186 on_refresh_folder_status_update,
2192 * It's used by the mail operation queue to notify the observers
2193 * attached to that signal that the operation finished. We need to use
2194 * that because tinymail does not give us the progress of a given
2195 * operation when it finishes (it directly calls the operation
2199 modest_mail_operation_notify_end (ModestMailOperation *self)
2201 ModestMailOperationState *state;
2202 ModestMailOperationPrivate *priv = NULL;
2204 g_return_if_fail (self);
2206 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2209 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2213 /* Set the account back to not busy */
2214 if (priv->account_name) {
2215 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2217 g_free(priv->account_name);
2218 priv->account_name = NULL;
2221 /* Notify the observers about the mail opertation end */
2222 state = modest_mail_operation_clone_state (self);
2223 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2224 g_slice_free (ModestMailOperationState, state);