aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2010-10-23 05:26:08 +0800
committerMatthew Barnes <mbarnes@redhat.com>2010-10-23 05:26:08 +0800
commit2636da7dac1683c7bac98b7a6d1ee084ea2efbca (patch)
tree6263ef3079c3fc885339f46a48cfdea9c35a04ef
parenta910a31a06fa3eadb1a44698f0502436bc5db6ce (diff)
downloadgsoc2013-evolution-2636da7dac1683c7bac98b7a6d1ee084ea2efbca.tar
gsoc2013-evolution-2636da7dac1683c7bac98b7a6d1ee084ea2efbca.tar.gz
gsoc2013-evolution-2636da7dac1683c7bac98b7a6d1ee084ea2efbca.tar.bz2
gsoc2013-evolution-2636da7dac1683c7bac98b7a6d1ee084ea2efbca.tar.lz
gsoc2013-evolution-2636da7dac1683c7bac98b7a6d1ee084ea2efbca.tar.xz
gsoc2013-evolution-2636da7dac1683c7bac98b7a6d1ee084ea2efbca.tar.zst
gsoc2013-evolution-2636da7dac1683c7bac98b7a6d1ee084ea2efbca.zip
Kill mail_store_set_offline().
Replace it with new async functions: e_mail_store_go_offline() e_mail_store_go_offline_finish() e_mail_store_go_online() e_mail_store_go_online_finish()
-rw-r--r--mail/Makefile.am2
-rw-r--r--mail/e-mail-backend.c40
-rw-r--r--mail/e-mail-store-utils.c190
-rw-r--r--mail/e-mail-store-utils.h47
-rw-r--r--mail/e-mail.h1
-rw-r--r--mail/em-folder-tree-model.c2
-rw-r--r--mail/mail-folder-cache.c23
-rw-r--r--mail/mail-ops.c179
-rw-r--r--mail/mail-ops.h8
9 files changed, 277 insertions, 215 deletions
diff --git a/mail/Makefile.am b/mail/Makefile.am
index eb7ef84ecb..a88a7c889a 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -69,6 +69,7 @@ mailinclude_HEADERS = \
e-mail-session-utils.h \
e-mail-sidebar.h \
e-mail-store.h \
+ e-mail-store-utils.h \
e-mail-tag-editor.h \
em-account-editor.h \
em-composer-utils.h \
@@ -141,6 +142,7 @@ libevolution_mail_la_SOURCES = \
e-mail-session-utils.c \
e-mail-sidebar.c \
e-mail-store.c \
+ e-mail-store-utils.c \
e-mail-tag-editor.c \
em-account-editor.c \
em-composer-utils.c \
diff --git a/mail/e-mail-backend.c b/mail/e-mail-backend.c
index ce227f8c27..36b244d8d4 100644
--- a/mail/e-mail-backend.c
+++ b/mail/e-mail-backend.c
@@ -38,6 +38,7 @@
#include "mail/e-mail-migrate.h"
#include "mail/e-mail-session.h"
#include "mail/e-mail-store.h"
+#include "mail/e-mail-store-utils.h"
#include "mail/em-event.h"
#include "mail/em-folder-tree-model.h"
#include "mail/em-utils.h"
@@ -86,9 +87,14 @@ mail_shell_backend_get_config_dir (EShellBackend *backend)
* the EActivity's reference count is used as a counting semaphore. */
static void
mail_backend_store_operation_done_cb (CamelStore *store,
- gpointer user_data)
+ GAsyncResult *result,
+ EActivity *activity)
{
- g_object_unref (E_ACTIVITY (user_data));
+ /* FIXME Not checking result for error. To fix this, we need
+ * separate callbacks to call different finish functions
+ * and then submit an EAlert on error. */
+
+ g_object_unref (activity);
}
/* Helper for mail_backend_prepare_for_offline_cb() */
@@ -97,11 +103,11 @@ mail_store_prepare_for_offline_cb (CamelService *service,
gpointer unused,
EActivity *activity)
{
- if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service))
- mail_store_set_offline (
- CAMEL_STORE (service), TRUE,
- mail_backend_store_operation_done_cb,
- g_object_ref (activity));
+ /* FIXME Not passing a GCancellable. */
+ e_mail_store_go_offline (
+ CAMEL_STORE (service), G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) mail_backend_store_operation_done_cb,
+ g_object_ref (activity));
}
static void
@@ -136,11 +142,11 @@ mail_store_prepare_for_online_cb (CamelService *service,
gpointer unused,
EActivity *activity)
{
- if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service))
- mail_store_set_offline (
- CAMEL_STORE (service), FALSE,
- mail_backend_store_operation_done_cb,
- g_object_ref (activity));
+ /* FIXME Not passing a GCancellable. */
+ e_mail_store_go_online (
+ CAMEL_STORE (service), G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) mail_backend_store_operation_done_cb,
+ g_object_ref (activity));
}
static void
@@ -200,11 +206,11 @@ mail_backend_final_sync (CamelStore *store,
gboolean empty_trash;
} *sync_data = user_data;
- /* Reffing the activity delays quitting; the reference count
- * acts like a counting semaphore. */
- mail_sync_store (
- store, sync_data->empty_trash,
- mail_backend_store_operation_done_cb,
+ /* FIXME Not passing a GCancellable. */
+ /* FIXME This operation should be queued. */
+ camel_store_synchronize (
+ store, sync_data->empty_trash, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) mail_backend_store_operation_done_cb,
g_object_ref (sync_data->activity));
}
diff --git a/mail/e-mail-store-utils.c b/mail/e-mail-store-utils.c
new file mode 100644
index 0000000000..57304f926d
--- /dev/null
+++ b/mail/e-mail-store-utils.c
@@ -0,0 +1,190 @@
+/*
+ * e-mail-store-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-store-utils.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+typedef struct _AsyncContext AsyncContext;
+
+static void
+mail_store_go_offline_thread (GSimpleAsyncResult *simple,
+ CamelStore *store,
+ GCancellable *cancellable)
+{
+ CamelService *service;
+ gchar *service_name;
+ GError *error = NULL;
+
+ service = CAMEL_SERVICE (store);
+
+ service_name = camel_service_get_name (service, TRUE);
+ camel_operation_push_message (
+ cancellable, _("Disconnecting from '%s'"), service_name);
+ g_free (service_name);
+
+ if (CAMEL_IS_DISCO_STORE (store)) {
+ CamelDiscoStore *disco_store;
+
+ disco_store = CAMEL_DISCO_STORE (store);
+
+ if (camel_disco_store_can_work_offline (disco_store))
+ camel_disco_store_set_status (
+ disco_store, CAMEL_DISCO_STORE_OFFLINE,
+ cancellable, &error);
+ else
+ camel_service_disconnect_sync (service, TRUE, &error);
+
+ } else if (CAMEL_IS_OFFLINE_STORE (store)) {
+ CamelOfflineStore *offline_store;
+
+ offline_store = CAMEL_OFFLINE_STORE (store);
+
+ camel_offline_store_set_online_sync (
+ offline_store, FALSE, cancellable, &error);
+
+ } else
+ camel_service_disconnect_sync (service, TRUE, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_go_offline (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ /* Cancel any pending connect first so the set_offline_op
+ * thread won't get queued behind a hung connect op. */
+ camel_service_cancel_connect (CAMEL_SERVICE (store));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (store), callback,
+ user_data, e_mail_store_go_offline);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_store_go_offline_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_go_offline_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (store), e_mail_store_go_offline), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_store_go_online_thread (GSimpleAsyncResult *simple,
+ CamelStore *store,
+ GCancellable *cancellable)
+{
+ CamelService *service;
+ gchar *service_name;
+ GError *error = NULL;
+
+ service = CAMEL_SERVICE (store);
+
+ service_name = camel_service_get_name (service, TRUE);
+ camel_operation_push_message (
+ cancellable, _("Reconnecting to '%s'"), service_name);
+ g_free (service_name);
+
+ if (CAMEL_IS_DISCO_STORE (store))
+ camel_disco_store_set_status (
+ CAMEL_DISCO_STORE (store),
+ CAMEL_DISCO_STORE_ONLINE,
+ cancellable, &error);
+
+ else if (CAMEL_IS_OFFLINE_STORE (store))
+ camel_offline_store_set_online_sync (
+ CAMEL_OFFLINE_STORE (store),
+ TRUE, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ camel_operation_pop_message (cancellable);
+}
+
+void
+e_mail_store_go_online (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (store), callback,
+ user_data, e_mail_store_go_online);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_store_go_online_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_store_go_online_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (store), e_mail_store_go_online), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
diff --git a/mail/e-mail-store-utils.h b/mail/e-mail-store-utils.h
new file mode 100644
index 0000000000..daf7a98412
--- /dev/null
+++ b/mail/e-mail-store-utils.h
@@ -0,0 +1,47 @@
+/*
+ * e-mail-store-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_STORE_UTILS_H
+#define E_MAIL_STORE_UTILS_H
+
+/* CamelStore wrappers with Evolution-specific policies. */
+
+#include <camel/camel.h>
+
+G_BEGIN_DECLS
+
+void e_mail_store_go_offline (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_store_go_offline_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error);
+void e_mail_store_go_online (CamelStore *store,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_store_go_online_finish (CamelStore *store,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_STORE_UTILS_H */
diff --git a/mail/e-mail.h b/mail/e-mail.h
index 4e40ac370b..aa19737785 100644
--- a/mail/e-mail.h
+++ b/mail/e-mail.h
@@ -44,6 +44,7 @@
#include <mail/e-mail-session-utils.h>
#include <mail/e-mail-sidebar.h>
#include <mail/e-mail-store.h>
+#include <mail/e-mail-store-utils.h>
#include <mail/e-mail-tag-editor.h>
#include <mail/e-mail-view.h>
diff --git a/mail/em-folder-tree-model.c b/mail/em-folder-tree-model.c
index ac147c43fd..f1f69a7276 100644
--- a/mail/em-folder-tree-model.c
+++ b/mail/em-folder-tree-model.c
@@ -749,8 +749,6 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model,
g_object_unref (folder);
}
- /* TODO Maybe this should be handled by mail_get_folderinfo
- * (except em-folder-tree doesn't use it, duh) */
flags = fi->flags;
name = fi->name;
if (si->store == e_mail_local_get_store ()) {
diff --git a/mail/mail-folder-cache.c b/mail/mail-folder-cache.c
index 8b19da4879..8b3f3d2c24 100644
--- a/mail/mail-folder-cache.c
+++ b/mail/mail-folder-cache.c
@@ -52,6 +52,7 @@
#include "em-utils.h"
#include "e-mail-local.h"
#include "e-mail-session.h"
+#include "e-mail-store-utils.h"
#define w(x)
#define d(x)
@@ -856,9 +857,11 @@ ping_cb (MailFolderCache *self)
}
static void
-store_online_cb (CamelStore *store, gpointer data)
+store_go_online_cb (CamelStore *store,
+ GAsyncResult *result,
+ struct _update_data *ud)
{
- struct _update_data *ud = data;
+ /* FIXME Not checking result for error. */
g_mutex_lock (ud->cache->priv->stores_mutex);
@@ -1101,10 +1104,8 @@ mail_folder_cache_note_store (MailFolderCache *self,
si = g_malloc0 (sizeof (*si));
si->folders = g_hash_table_new (g_str_hash, g_str_equal);
si->folders_uri = g_hash_table_new (
- CAMEL_STORE_CLASS (
- CAMEL_OBJECT_GET_CLASS (store))->hash_folder_name,
- CAMEL_STORE_CLASS (
- CAMEL_OBJECT_GET_CLASS (store))->compare_folder_name);
+ CAMEL_STORE_GET_CLASS (store)->hash_folder_name,
+ CAMEL_STORE_GET_CLASS (store)->compare_folder_name);
si->store = g_object_ref (store);
g_hash_table_insert (self->priv->stores, store, si);
g_queue_init (&si->folderinfo_updates);
@@ -1124,8 +1125,9 @@ mail_folder_cache_note_store (MailFolderCache *self,
if (camel_session_get_online (session) &&
camel_disco_store_status (CAMEL_DISCO_STORE (store)) ==
CAMEL_DISCO_STORE_OFFLINE) {
- /* Note: we use the 'id' here, even though its not the right id, its still ok */
- ud->id = mail_store_set_offline (store, FALSE, store_online_cb, ud);
+ e_mail_store_go_online (
+ store, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) store_go_online_cb, ud);
} else {
goto normal_setup;
}
@@ -1133,8 +1135,9 @@ mail_folder_cache_note_store (MailFolderCache *self,
if (camel_session_get_online (session) &&
!camel_offline_store_get_online (
CAMEL_OFFLINE_STORE (store))) {
- /* Note: we use the 'id' here, even though its not the right id, its still ok */
- ud->id = mail_store_set_offline (store, FALSE, store_online_cb, ud);
+ e_mail_store_go_online (
+ store, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) store_go_online_cb, ud);
} else {
goto normal_setup;
}
diff --git a/mail/mail-ops.c b/mail/mail-ops.c
index ce219ed372..d10473606b 100644
--- a/mail/mail-ops.c
+++ b/mail/mail-ops.c
@@ -2351,91 +2351,7 @@ mail_save_messages (CamelFolder *folder, GPtrArray *uids, const gchar *path,
return id;
}
-/* ** PREPARE OFFLINE ***************************************************** */
-
-struct _prep_offline_msg {
- MailMsg base;
-
- EMailSession *session;
- GCancellable *cancel;
- gchar *uri;
- void (*done)(const gchar *uri, gpointer data);
- gpointer data;
-};
-
-static void
-prep_offline_exec (struct _prep_offline_msg *m,
- GCancellable *cancellable,
- GError **error)
-{
- CamelFolder *folder;
-
- folder = e_mail_session_uri_to_folder_sync (
- m->session, m->uri, 0,
- cancellable, error);
- if (folder) {
- if (CAMEL_IS_DISCO_FOLDER (folder)) {
- camel_disco_folder_prepare_for_offline (
- CAMEL_DISCO_FOLDER (folder),
- "(match-all)", m->cancel, error);
- } else if (CAMEL_IS_OFFLINE_FOLDER (folder)) {
- camel_offline_folder_downsync_sync (
- CAMEL_OFFLINE_FOLDER (folder),
- "(match-all)", m->cancel, error);
- }
- /* prepare_for_offline should do this? */
- /* of course it should all be atomic, but ... */
- /* FIXME Not passing a GCancellable here. */
- camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
- g_object_unref (folder);
- }
-}
-
-static void
-prep_offline_done (struct _prep_offline_msg *m)
-{
- if (m->done)
- m->done (m->uri, m->data);
-}
-
-static void
-prep_offline_free (struct _prep_offline_msg *m)
-{
- g_object_unref (m->session);
- if (m->cancel)
- g_object_unref (m->cancel);
- g_free (m->uri);
-}
-
-static MailMsgInfo prep_offline_info = {
- sizeof (struct _prep_offline_msg),
- (MailMsgDescFunc) NULL, /* DO NOT CHANGE THIS, IT MUST BE NULL FOR CANCELLATION TO WORK */
- (MailMsgExecFunc) prep_offline_exec,
- (MailMsgDoneFunc) prep_offline_done,
- (MailMsgFreeFunc) prep_offline_free
-};
-
-void
-mail_prep_offline (EMailSession *session,
- const gchar *uri,
- CamelOperation *cancel,
- void (*done)(const gchar *, gpointer data),
- gpointer data)
-{
- struct _prep_offline_msg *m;
-
- m = mail_msg_new (&prep_offline_info);
- m->session = g_object_ref (session);
- if (G_IS_CANCELLABLE (cancel))
- m->cancel = g_object_ref (cancel);
- m->uri = g_strdup (uri);
- m->data = data;
- m->done = done;
-
- mail_msg_slow_ordered_push (m);
-}
-
-/* ** GO OFFLINE ***************************************************** */
+/* ** Prepare OFFLINE ***************************************************** */
struct _set_offline_msg {
MailMsg base;
@@ -2447,99 +2363,6 @@ struct _set_offline_msg {
};
static gchar *
-set_offline_desc (struct _set_offline_msg *m)
-{
- gchar *service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
- gchar *msg;
-
- msg = g_strdup_printf (m->offline ? _("Disconnecting from '%s'") : _("Reconnecting to '%s'"),
- service_name);
- g_free (service_name);
- return msg;
-}
-
-static void
-set_offline_exec (struct _set_offline_msg *m,
- GCancellable *cancellable,
- GError **error)
-{
- if (CAMEL_IS_DISCO_STORE (m->store)) {
- if (!m->offline) {
- camel_disco_store_set_status (
- CAMEL_DISCO_STORE (m->store),
- CAMEL_DISCO_STORE_ONLINE,
- cancellable, error);
- return;
- } else if (camel_disco_store_can_work_offline (CAMEL_DISCO_STORE (m->store))) {
- camel_disco_store_set_status (
- CAMEL_DISCO_STORE (m->store),
- CAMEL_DISCO_STORE_OFFLINE,
- cancellable, error);
- return;
- }
- } else if (CAMEL_IS_OFFLINE_STORE (m->store)) {
- camel_offline_store_set_online_sync (
- CAMEL_OFFLINE_STORE (m->store),
- !m->offline, cancellable, error);
- return;
- }
-
- if (m->offline)
- camel_service_disconnect_sync (
- CAMEL_SERVICE (m->store), TRUE, error);
-}
-
-static void
-set_offline_done (struct _set_offline_msg *m)
-{
- if (m->done)
- m->done (m->store, m->data);
-}
-
-static void
-set_offline_free (struct _set_offline_msg *m)
-{
- g_object_unref (m->store);
-}
-
-static MailMsgInfo set_offline_info = {
- sizeof (struct _set_offline_msg),
- (MailMsgDescFunc) set_offline_desc,
- (MailMsgExecFunc) set_offline_exec,
- (MailMsgDoneFunc) set_offline_done,
- (MailMsgFreeFunc) set_offline_free
-};
-
-gint
-mail_store_set_offline (CamelStore *store, gboolean offline,
- void (*done)(CamelStore *, gpointer data),
- gpointer data)
-{
- struct _set_offline_msg *m;
- gint id;
-
- /* Cancel any pending connect first so the set_offline_op
- * thread won't get queued behind a hung connect op.
- */
- if (offline)
- camel_service_cancel_connect (CAMEL_SERVICE (store));
-
- m = mail_msg_new (&set_offline_info);
- m->store = store;
- g_object_ref (store);
- m->offline = offline;
- m->data = data;
- m->done = done;
-
- id = m->base.seq;
- mail_msg_unordered_push (m);
-
- return id;
-}
-
-/* ** Prepare OFFLINE ***************************************************** */
-
-static gchar *
prepare_offline_desc (struct _set_offline_msg *m)
{
gchar *service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
diff --git a/mail/mail-ops.h b/mail/mail-ops.h
index 751ac9de93..ea9e9f7d0b 100644
--- a/mail/mail-ops.h
+++ b/mail/mail-ops.h
@@ -162,14 +162,6 @@ void mail_filter_folder (EMailSession *session,
gboolean notify);
/* Work Offline */
-void mail_prep_offline (EMailSession *session,
- const gchar *uri,
- CamelOperation *cancel,
- void (*done)(const gchar *, gpointer data),
- gpointer data);
-gint mail_store_set_offline (CamelStore *store, gboolean offline,
- void (*done)(CamelStore *, gpointer data),
- gpointer data);
gint mail_store_prepare_offline (CamelStore *store);
/* filter driver execute shell command async callback */