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,
1367 ModestMailOperation *self = NULL;
1368 ModestMailOperationPrivate *priv = NULL;
1370 self = MODEST_MAIL_OPERATION (user_data);
1372 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1375 priv->error = g_error_copy (*err);
1377 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1378 } else if (cancelled) {
1379 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1380 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1381 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1382 _("Transference of %s was cancelled."),
1383 tny_folder_get_name (folder));
1386 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1390 g_object_unref (folder);
1391 g_object_unref (into);
1392 if (new_folder != NULL)
1393 g_object_unref (new_folder);
1395 /* Notify about operation end */
1396 modest_mail_operation_notify_end (self);
1400 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1402 TnyFolderStore *parent,
1403 gboolean delete_original)
1405 ModestMailOperationPrivate *priv = NULL;
1406 ModestTnyFolderRules parent_rules, rules;
1408 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1409 g_return_if_fail (TNY_IS_FOLDER (folder));
1410 g_return_if_fail (TNY_IS_FOLDER (parent));
1412 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1414 /* Get account and set it into mail_operation */
1415 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1416 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1418 /* Get folder rules */
1419 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1420 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1422 if (!TNY_IS_FOLDER_STORE (parent)) {
1426 /* The moveable restriction is applied also to copy operation */
1427 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1428 /* Set status failed and set an error */
1429 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1430 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1431 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1432 _("mail_in_ui_folder_move_target_error"));
1434 /* Notify the queue */
1435 modest_mail_operation_notify_end (self);
1436 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1437 /* Set status failed and set an error */
1438 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1439 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1440 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1441 _("FIXME: parent folder does not accept new folders"));
1443 /* Notify the queue */
1444 modest_mail_operation_notify_end (self);
1446 /* Pick references for async calls */
1447 g_object_ref (folder);
1448 g_object_ref (parent);
1450 /* Move/Copy folder */
1451 tny_folder_copy_async (folder,
1453 tny_folder_get_name (folder),
1456 transfer_folder_status_cb,
1462 modest_mail_operation_rename_folder (ModestMailOperation *self,
1466 ModestMailOperationPrivate *priv;
1467 ModestTnyFolderRules rules;
1469 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1470 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1471 g_return_if_fail (name);
1473 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1475 /* Get account and set it into mail_operation */
1476 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1478 /* Check folder rules */
1479 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1480 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1481 /* Set status failed and set an error */
1482 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1483 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1484 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1485 _("FIXME: unable to rename"));
1487 /* Notify about operation end */
1488 modest_mail_operation_notify_end (self);
1490 /* Rename. Camel handles folder subscription/unsubscription */
1491 TnyFolderStore *into;
1493 into = tny_folder_get_folder_store (folder);
1494 tny_folder_copy_async (folder, into, name, TRUE,
1496 transfer_folder_status_cb,
1499 g_object_unref (into);
1504 /* ******************************************************************* */
1505 /* ************************** MSG ACTIONS ************************* */
1506 /* ******************************************************************* */
1508 void modest_mail_operation_get_msg (ModestMailOperation *self,
1510 GetMsgAsyncUserCallback user_callback,
1513 GetMsgAsyncHelper *helper = NULL;
1515 ModestMailOperationPrivate *priv;
1517 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1518 g_return_if_fail (TNY_IS_HEADER (header));
1520 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1521 folder = tny_header_get_folder (header);
1523 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1525 /* Get message from folder */
1527 /* Get account and set it into mail_operation */
1528 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1530 helper = g_slice_new0 (GetMsgAsyncHelper);
1531 helper->mail_op = self;
1532 helper->user_callback = user_callback;
1533 helper->user_data = user_data;
1534 helper->header = g_object_ref (header);
1536 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1538 g_object_unref (G_OBJECT (folder));
1540 /* Set status failed and set an error */
1541 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1542 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1543 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1544 _("Error trying to get a message. No folder found for header"));
1546 /* Notify the queue */
1547 modest_mail_operation_notify_end (self);
1552 get_msg_cb (TnyFolder *folder,
1558 GetMsgAsyncHelper *helper = NULL;
1559 ModestMailOperation *self = NULL;
1560 ModestMailOperationPrivate *priv = NULL;
1562 helper = (GetMsgAsyncHelper *) user_data;
1563 g_return_if_fail (helper != NULL);
1564 self = helper->mail_op;
1565 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1566 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1568 /* Check errors and cancel */
1570 priv->error = g_error_copy (*error);
1571 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1575 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1576 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1577 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1578 _("Error trying to refresh the contents of %s"),
1579 tny_folder_get_name (folder));
1583 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1585 /* If user defined callback function was defined, call it */
1586 if (helper->user_callback) {
1587 /* This callback is called into an iddle by tinymail,
1588 and idles are not in the main lock */
1589 gdk_threads_enter ();
1590 helper->user_callback (self, helper->header, msg, helper->user_data);
1591 gdk_threads_leave ();
1596 g_object_unref (helper->header);
1597 g_slice_free (GetMsgAsyncHelper, helper);
1599 /* Notify about operation end */
1600 modest_mail_operation_notify_end (self);
1604 get_msg_status_cb (GObject *obj,
1608 GetMsgAsyncHelper *helper = NULL;
1609 ModestMailOperation *self;
1610 ModestMailOperationPrivate *priv;
1611 ModestMailOperationState *state;
1613 g_return_if_fail (status != NULL);
1614 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1616 helper = (GetMsgAsyncHelper *) user_data;
1617 g_return_if_fail (helper != NULL);
1619 self = helper->mail_op;
1620 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1622 if ((status->position == 1) && (status->of_total == 100))
1628 state = modest_mail_operation_clone_state (self);
1629 state->bytes_done = status->position;
1630 state->bytes_total = status->of_total;
1631 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1632 g_slice_free (ModestMailOperationState, state);
1635 /****************************************************/
1637 ModestMailOperation *mail_op;
1639 GetMsgAsyncUserCallback user_callback;
1641 GDestroyNotify notify;
1645 GetMsgAsyncUserCallback user_callback;
1649 ModestMailOperation *mail_op;
1650 } NotifyGetMsgsInfo;
1654 * Used by get_msgs_full_thread to call the user_callback for each
1655 * message that has been read
1658 notify_get_msgs_full (gpointer data)
1660 gdk_threads_enter ();
1662 NotifyGetMsgsInfo *info;
1664 info = (NotifyGetMsgsInfo *) data;
1666 /* Call the user callback. Idles are not in the main lock, so
1668 gdk_threads_enter ();
1669 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1670 gdk_threads_leave ();
1672 g_slice_free (NotifyGetMsgsInfo, info);
1674 gdk_threads_leave ();
1680 * Used by get_msgs_full_thread to free al the thread resources and to
1681 * call the destroy function for the passed user_data
1684 get_msgs_full_destroyer (gpointer data)
1686 gdk_threads_enter ();
1688 GetFullMsgsInfo *info;
1690 info = (GetFullMsgsInfo *) data;
1693 info->notify (info->user_data);
1696 g_object_unref (info->headers);
1697 g_slice_free (GetFullMsgsInfo, info);
1699 gdk_threads_leave ();
1705 get_msgs_full_thread (gpointer thr_user_data)
1707 GetFullMsgsInfo *info;
1708 ModestMailOperationPrivate *priv = NULL;
1709 TnyIterator *iter = NULL;
1711 info = (GetFullMsgsInfo *) thr_user_data;
1712 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1714 iter = tny_list_create_iterator (info->headers);
1715 while (!tny_iterator_is_done (iter)) {
1719 header = TNY_HEADER (tny_iterator_get_current (iter));
1720 folder = tny_header_get_folder (header);
1722 /* Get message from folder */
1725 /* The callback will call it per each header */
1726 msg = tny_folder_get_msg (folder, header, &(priv->error));
1729 ModestMailOperationState *state;
1734 /* notify progress */
1735 state = modest_mail_operation_clone_state (info->mail_op);
1736 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1737 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1738 pair, (GDestroyNotify) modest_pair_free);
1740 /* The callback is the responsible for
1741 freeing the message */
1742 if (info->user_callback) {
1743 NotifyGetMsgsInfo *info_notify;
1744 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1745 info_notify->user_callback = info->user_callback;
1746 info_notify->mail_op = info->mail_op;
1747 info_notify->header = g_object_ref (header);
1748 info_notify->msg = g_object_ref (msg);
1749 info_notify->user_data = info->user_data;
1750 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1751 notify_get_msgs_full,
1754 g_object_unref (msg);
1757 /* Set status failed and set an error */
1758 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1759 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1760 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1761 "Error trying to get a message. No folder found for header");
1763 g_object_unref (header);
1764 tny_iterator_next (iter);
1767 /* Set operation status */
1768 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1769 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1771 /* Notify about operation end */
1772 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1774 /* Free thread resources. Will be called after all previous idles */
1775 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1781 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1782 TnyList *header_list,
1783 GetMsgAsyncUserCallback user_callback,
1785 GDestroyNotify notify)
1787 TnyHeader *header = NULL;
1788 TnyFolder *folder = NULL;
1790 ModestMailOperationPrivate *priv = NULL;
1791 GetFullMsgsInfo *info = NULL;
1792 gboolean size_ok = TRUE;
1794 TnyIterator *iter = NULL;
1796 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1798 /* Init mail operation */
1799 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1800 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1802 priv->total = tny_list_get_length(header_list);
1804 /* Get account and set it into mail_operation */
1805 if (tny_list_get_length (header_list) >= 1) {
1806 iter = tny_list_create_iterator (header_list);
1807 header = TNY_HEADER (tny_iterator_get_current (iter));
1808 folder = tny_header_get_folder (header);
1809 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1810 g_object_unref (header);
1811 g_object_unref (folder);
1813 if (tny_list_get_length (header_list) == 1) {
1814 g_object_unref (iter);
1819 /* Get msg size limit */
1820 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1821 MODEST_CONF_MSG_SIZE_LIMIT,
1824 g_clear_error (&(priv->error));
1825 max_size = G_MAXINT;
1827 max_size = max_size * KB;
1830 /* Check message size limits. If there is only one message
1831 always retrieve it */
1833 while (!tny_iterator_is_done (iter) && size_ok) {
1834 header = TNY_HEADER (tny_iterator_get_current (iter));
1835 if (tny_header_get_message_size (header) >= max_size)
1837 g_object_unref (header);
1838 tny_iterator_next (iter);
1840 g_object_unref (iter);
1844 /* Create the info */
1845 info = g_slice_new0 (GetFullMsgsInfo);
1846 info->mail_op = self;
1847 info->user_callback = user_callback;
1848 info->user_data = user_data;
1849 info->headers = g_object_ref (header_list);
1850 info->notify = notify;
1852 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1854 /* Set status failed and set an error */
1855 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1856 /* FIXME: the error msg is different for pop */
1857 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1858 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1859 _("emev_ni_ui_imap_msg_size_exceed_error"));
1860 /* Remove from queue and free resources */
1861 modest_mail_operation_notify_end (self);
1869 modest_mail_operation_remove_msg (ModestMailOperation *self,
1871 gboolean remove_to_trash)
1874 ModestMailOperationPrivate *priv;
1876 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1877 g_return_if_fail (TNY_IS_HEADER (header));
1879 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1880 folder = tny_header_get_folder (header);
1882 /* Get account and set it into mail_operation */
1883 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1885 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1887 /* Delete or move to trash */
1888 if (remove_to_trash) {
1889 TnyFolder *trash_folder;
1890 TnyStoreAccount *store_account;
1892 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1893 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1894 TNY_FOLDER_TYPE_TRASH);
1899 headers = tny_simple_list_new ();
1900 tny_list_append (headers, G_OBJECT (header));
1901 g_object_unref (header);
1904 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1905 g_object_unref (headers);
1906 /* g_object_unref (trash_folder); */
1908 ModestMailOperationPrivate *priv;
1910 /* Set status failed and set an error */
1911 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1912 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1913 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1914 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1915 _("Error trying to delete a message. Trash folder not found"));
1918 g_object_unref (G_OBJECT (store_account));
1920 tny_folder_remove_msg (folder, header, &(priv->error));
1922 tny_folder_sync(folder, TRUE, &(priv->error));
1927 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1929 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1932 g_object_unref (G_OBJECT (folder));
1934 /* Notify about operation end */
1935 modest_mail_operation_notify_end (self);
1939 transfer_msgs_status_cb (GObject *obj,
1943 XFerMsgAsyncHelper *helper = NULL;
1944 ModestMailOperation *self;
1945 ModestMailOperationPrivate *priv;
1946 ModestMailOperationState *state;
1949 g_return_if_fail (status != NULL);
1950 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1952 helper = (XFerMsgAsyncHelper *) user_data;
1953 g_return_if_fail (helper != NULL);
1955 self = helper->mail_op;
1956 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1958 if ((status->position == 1) && (status->of_total == 100))
1961 priv->done = status->position;
1962 priv->total = status->of_total;
1964 state = modest_mail_operation_clone_state (self);
1965 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1966 g_slice_free (ModestMailOperationState, state);
1971 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1973 XFerMsgAsyncHelper *helper;
1974 ModestMailOperation *self;
1975 ModestMailOperationPrivate *priv;
1977 helper = (XFerMsgAsyncHelper *) user_data;
1978 self = helper->mail_op;
1980 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1983 priv->error = g_error_copy (*err);
1985 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1986 } else if (cancelled) {
1987 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1988 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1989 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1990 _("Error trying to refresh the contents of %s"),
1991 tny_folder_get_name (folder));
1994 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1997 /* If user defined callback function was defined, call it */
1998 if (helper->user_callback) {
1999 gdk_threads_enter ();
2000 helper->user_callback (priv->source, helper->user_data);
2001 gdk_threads_leave ();
2005 g_object_unref (helper->headers);
2006 g_object_unref (helper->dest_folder);
2007 g_object_unref (helper->mail_op);
2008 g_slice_free (XFerMsgAsyncHelper, helper);
2009 g_object_unref (folder);
2011 /* Notify about operation end */
2012 modest_mail_operation_notify_end (self);
2016 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2019 gboolean delete_original,
2020 XferMsgsAsynUserCallback user_callback,
2023 ModestMailOperationPrivate *priv;
2025 TnyFolder *src_folder;
2026 XFerMsgAsyncHelper *helper;
2028 ModestTnyFolderRules rules;
2030 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2031 g_return_if_fail (TNY_IS_LIST (headers));
2032 g_return_if_fail (TNY_IS_FOLDER (folder));
2034 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2037 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2039 /* Apply folder rules */
2040 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2041 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2042 /* Set status failed and set an error */
2043 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2044 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2045 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2046 _("FIXME: folder does not accept msgs"));
2047 /* Notify the queue */
2048 modest_mail_operation_notify_end (self);
2052 /* Create the helper */
2053 helper = g_slice_new0 (XFerMsgAsyncHelper);
2054 helper->mail_op = g_object_ref(self);
2055 helper->dest_folder = g_object_ref(folder);
2056 helper->headers = g_object_ref(headers);
2057 helper->user_callback = user_callback;
2058 helper->user_data = user_data;
2060 /* Get source folder */
2061 iter = tny_list_create_iterator (headers);
2062 header = TNY_HEADER (tny_iterator_get_current (iter));
2063 src_folder = tny_header_get_folder (header);
2064 g_object_unref (header);
2065 g_object_unref (iter);
2067 /* Get account and set it into mail_operation */
2068 priv->account = modest_tny_folder_get_account (src_folder);
2070 /* Transfer messages */
2071 tny_folder_transfer_msgs_async (src_folder,
2076 transfer_msgs_status_cb,
2082 on_refresh_folder (TnyFolder *folder,
2087 RefreshAsyncHelper *helper = NULL;
2088 ModestMailOperation *self = NULL;
2089 ModestMailOperationPrivate *priv = NULL;
2091 helper = (RefreshAsyncHelper *) user_data;
2092 self = helper->mail_op;
2093 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2096 priv->error = g_error_copy (*error);
2097 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2102 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2103 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2104 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2105 _("Error trying to refresh the contents of %s"),
2106 tny_folder_get_name (folder));
2110 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2113 /* Call user defined callback, if it exists */
2114 if (helper->user_callback) {
2115 /* gdk_threads_enter (); */
2116 helper->user_callback (priv->source, folder, helper->user_data);
2117 /* gdk_threads_leave (); */
2121 g_object_unref (helper->mail_op);
2122 g_slice_free (RefreshAsyncHelper, helper);
2123 g_object_unref (folder);
2125 /* Notify about operation end */
2126 modest_mail_operation_notify_end (self);
2130 on_refresh_folder_status_update (GObject *obj,
2134 RefreshAsyncHelper *helper = NULL;
2135 ModestMailOperation *self = NULL;
2136 ModestMailOperationPrivate *priv = NULL;
2137 ModestMailOperationState *state;
2139 g_return_if_fail (user_data != NULL);
2140 g_return_if_fail (status != NULL);
2141 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2143 helper = (RefreshAsyncHelper *) user_data;
2144 self = helper->mail_op;
2145 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2147 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2149 priv->done = status->position;
2150 priv->total = status->of_total;
2152 state = modest_mail_operation_clone_state (self);
2153 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2154 g_slice_free (ModestMailOperationState, state);
2158 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2160 RefreshAsyncUserCallback user_callback,
2163 ModestMailOperationPrivate *priv = NULL;
2164 RefreshAsyncHelper *helper = NULL;
2166 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2168 /* Pick a reference */
2169 g_object_ref (folder);
2171 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2173 /* Get account and set it into mail_operation */
2174 priv->account = modest_tny_folder_get_account (folder);
2176 /* Create the helper */
2177 helper = g_slice_new0 (RefreshAsyncHelper);
2178 helper->mail_op = g_object_ref(self);
2179 helper->user_callback = user_callback;
2180 helper->user_data = user_data;
2182 /* Refresh the folder. TODO: tinymail could issue a status
2183 updates before the callback call then this could happen. We
2184 must review the design */
2185 tny_folder_refresh_async (folder,
2187 on_refresh_folder_status_update,
2193 * It's used by the mail operation queue to notify the observers
2194 * attached to that signal that the operation finished. We need to use
2195 * that because tinymail does not give us the progress of a given
2196 * operation when it finishes (it directly calls the operation
2200 modest_mail_operation_notify_end (ModestMailOperation *self)
2202 ModestMailOperationState *state;
2203 ModestMailOperationPrivate *priv = NULL;
2205 g_return_if_fail (self);
2207 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2210 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2214 /* Set the account back to not busy */
2215 if (priv->account_name) {
2216 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2217 priv->account_name, FALSE);
2218 g_free(priv->account_name);
2219 priv->account_name = NULL;
2222 /* Notify the observers about the mail opertation end */
2223 state = modest_mail_operation_clone_state (self);
2224 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2225 g_slice_free (ModestMailOperationState, state);