[Spice-devel,02/11] Move MainChannelClient to separate file

Submitted by Jonathon Jongsma on May 12, 2016, 8:45 p.m.

Details

Message ID 1463085918-20567-3-git-send-email-jjongsma@redhat.com
State New
Headers show
Series "Rebased patches from refactory branch (May 12)" ( rev: 1 ) in Spice

Not browsing as part of any series.

Commit Message

Jonathon Jongsma May 12, 2016, 8:45 p.m.
Preparation for converting to GObject
---
 server/Makefile.am           |   2 +
 server/inputs-channel.c      |   2 +-
 server/main-channel-client.c | 549 ++++++++++++++++++++++++++++++++++++++
 server/main-channel-client.h | 153 +++++++++++
 server/main-channel.c        | 616 +++----------------------------------------
 server/main-channel.h        |  27 +-
 6 files changed, 747 insertions(+), 602 deletions(-)
 create mode 100644 server/main-channel-client.c
 create mode 100644 server/main-channel-client.h

Patch hide | download patch | download mbox

diff --git a/server/Makefile.am b/server/Makefile.am
index fbf4638..5a5c7e8 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -86,6 +86,8 @@  libserver_la_SOURCES =				\
 	lz4-encoder.h				\
 	main-channel.c				\
 	main-channel.h				\
+	main-channel-client.c			\
+	main-channel-client.h			\
 	mjpeg-encoder.c				\
 	red-channel.c				\
 	red-channel.h				\
diff --git a/server/inputs-channel.c b/server/inputs-channel.c
index 0ce12de..584204f 100644
--- a/server/inputs-channel.c
+++ b/server/inputs-channel.c
@@ -39,7 +39,7 @@ 
 #include "reds.h"
 #include "reds-stream.h"
 #include "red-channel.h"
-#include "main-channel.h"
+#include "main-channel-client.h"
 #include "inputs-channel.h"
 #include "migration-protocol.h"
 #include "utils.h"
diff --git a/server/main-channel-client.c b/server/main-channel-client.c
new file mode 100644
index 0000000..bac5316
--- /dev/null
+++ b/server/main-channel-client.c
@@ -0,0 +1,549 @@ 
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library 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.1 of the License, or (at your option) any later version.
+
+   This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+#include "main-channel-client.h"
+#include "main-channel-client.h"
+#include "main-channel.h"
+#include "reds.h"
+
+#define NET_TEST_WARMUP_BYTES 0
+#define NET_TEST_BYTES (1024 * 250)
+
+enum NetTestStage {
+    NET_TEST_STAGE_INVALID,
+    NET_TEST_STAGE_WARMUP,
+    NET_TEST_STAGE_LATENCY,
+    NET_TEST_STAGE_RATE,
+    NET_TEST_STAGE_COMPLETE,
+};
+
+#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
+#define PING_INTERVAL (MSEC_PER_SEC * 10)
+
+struct MainChannelClient {
+    RedChannelClient base;
+    uint32_t connection_id;
+    uint32_t ping_id;
+    uint32_t net_test_id;
+    int net_test_stage;
+    uint64_t latency;
+    uint64_t bitrate_per_sec;
+#ifdef RED_STATISTICS
+    SpiceTimer *ping_timer;
+    int ping_interval;
+#endif
+    int mig_wait_connect;
+    int mig_connect_ok;
+    int mig_wait_prev_complete;
+    int mig_wait_prev_try_seamless;
+    int init_sent;
+    int seamless_mig_dst;
+};
+
+static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
+
+static RedPipeItem *main_notify_item_new(void *data, int num)
+{
+    RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
+    const char *msg = data;
+
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
+    item->msg = spice_strdup(msg);
+    return &item->base;
+}
+
+void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
+{
+    if (!mcc || mcc->net_test_id) {
+        return;
+    }
+    if (test_rate) {
+        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
+            && main_channel_client_push_ping(mcc, 0)
+            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
+            mcc->net_test_id = mcc->ping_id - 2;
+            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
+        }
+    } else {
+        red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
+    }
+}
+
+static RedPipeItem *red_ping_item_new(int size)
+{
+    RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
+
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
+    item->size = size;
+    return &item->base;
+}
+
+static int main_channel_client_push_ping(MainChannelClient *mcc, int size)
+{
+    RedPipeItem *item;
+
+    if (mcc == NULL) {
+        return FALSE;
+    }
+    item = red_ping_item_new(size);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+    return TRUE;
+}
+
+static RedPipeItem *main_agent_tokens_item_new(uint32_t num_tokens)
+{
+    RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
+
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
+    item->tokens = num_tokens;
+    return &item->base;
+}
+
+
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
+{
+    RedPipeItem *item = main_agent_tokens_item_new(num_tokens);
+
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static RedPipeItem *main_agent_data_item_new(uint8_t* data, size_t len,
+                                             spice_marshaller_item_free_func free_data,
+                                             void *opaque)
+{
+    RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
+
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
+    item->data = data;
+    item->len = len;
+    item->free_data = free_data;
+    item->opaque = opaque;
+    return &item->base;
+}
+
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
+           spice_marshaller_item_free_func free_data, void *opaque)
+{
+    RedPipeItem *item;
+
+    item = main_agent_data_item_new(data, len, free_data, opaque);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static RedPipeItem *main_init_item_new(int connection_id,
+                                       int display_channels_hint,
+                                       int current_mouse_mode,
+                                       int is_client_mouse_allowed,
+                                       int multi_media_time,
+                                       int ram_hint)
+{
+    RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
+
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT);
+    item->connection_id = connection_id;
+    item->display_channels_hint = display_channels_hint;
+    item->current_mouse_mode = current_mouse_mode;
+    item->is_client_mouse_allowed = is_client_mouse_allowed;
+    item->multi_media_time = multi_media_time;
+    item->ram_hint = ram_hint;
+    return &item->base;
+}
+
+void main_channel_client_push_init(MainChannelClient *mcc,
+                                   int display_channels_hint,
+                                   int current_mouse_mode,
+                                   int is_client_mouse_allowed,
+                                   int multi_media_time,
+                                   int ram_hint)
+{
+    RedPipeItem *item;
+
+    item = main_init_item_new(mcc->connection_id, display_channels_hint,
+                              current_mouse_mode, is_client_mouse_allowed,
+                              multi_media_time, ram_hint);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static RedPipeItem *main_name_item_new(const char *name)
+{
+    RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) + strlen(name) + 1);
+
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
+    item->msg.name_len = strlen(name) + 1;
+    memcpy(&item->msg.name, name, item->msg.name_len);
+
+    return &item->base;
+}
+
+void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
+{
+    RedPipeItem *item;
+
+    if (!red_channel_client_test_remote_cap(&mcc->base,
+                                            SPICE_MAIN_CAP_NAME_AND_UUID))
+        return;
+
+    item = main_name_item_new(name);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static RedPipeItem *main_uuid_item_new(const uint8_t uuid[16])
+{
+    RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
+
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
+    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
+
+    return &item->base;
+}
+
+void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
+{
+    RedPipeItem *item;
+
+    if (!red_channel_client_test_remote_cap(&mcc->base,
+                                            SPICE_MAIN_CAP_NAME_AND_UUID))
+        return;
+
+    item = main_uuid_item_new(uuid);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
+{
+    RedPipeItem *item = main_notify_item_new((void *)msg, 1);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+                                                  int success,
+                                                  int seamless)
+{
+    spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
+    if (mcc->mig_wait_connect) {
+        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
+
+        mcc->mig_wait_connect = FALSE;
+        mcc->mig_connect_ok = success;
+        spice_assert(main_channel->num_clients_mig_wait);
+        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
+        if (!--main_channel->num_clients_mig_wait) {
+            reds_on_main_migrate_connected(mcc->base.channel->reds, seamless && success);
+        }
+    } else {
+        if (success) {
+            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
+            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+        }
+    }
+}
+
+void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
+                                                        uint32_t src_version)
+{
+    if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc, src_version)) {
+        mcc->seamless_mig_dst = TRUE;
+        red_channel_client_pipe_add_empty_msg(&mcc->base,
+                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
+    } else {
+        red_channel_client_pipe_add_empty_msg(&mcc->base,
+                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
+    }
+}
+void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, uint32_t size)
+{
+    uint64_t roundtrip;
+    RedChannelClient* rcc = (RedChannelClient*)mcc;
+
+    roundtrip = g_get_monotonic_time() - ping->timestamp;
+
+    if (ping->id == mcc->net_test_id) {
+        switch (mcc->net_test_stage) {
+            case NET_TEST_STAGE_WARMUP:
+                mcc->net_test_id++;
+                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
+                mcc->latency = roundtrip;
+                break;
+            case NET_TEST_STAGE_LATENCY:
+                mcc->net_test_id++;
+                mcc->net_test_stage = NET_TEST_STAGE_RATE;
+                mcc->latency = MIN(mcc->latency, roundtrip);
+                break;
+            case NET_TEST_STAGE_RATE:
+                mcc->net_test_id = 0;
+                if (roundtrip <= mcc->latency) {
+                    // probably high load on client or server result with incorrect values
+                    spice_printerr("net test: invalid values, latency %" PRIu64
+                                   " roundtrip %" PRIu64 ". assuming high"
+                                   "bandwidth", mcc->latency, roundtrip);
+                    mcc->latency = 0;
+                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
+                    red_channel_client_start_connectivity_monitoring(&mcc->base,
+                                                                     CLIENT_CONNECTIVITY_TIMEOUT);
+                    break;
+                }
+                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
+                    / (roundtrip - mcc->latency);
+                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
+                spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
+                               (double)mcc->latency / 1000,
+                               mcc->bitrate_per_sec,
+                               (double)mcc->bitrate_per_sec / 1024 / 1024,
+                               main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
+                red_channel_client_start_connectivity_monitoring(&mcc->base,
+                                                                 CLIENT_CONNECTIVITY_TIMEOUT);
+                break;
+            default:
+                spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
+                               ping->id,
+                               mcc->net_test_id,
+                               mcc->net_test_stage);
+                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
+        }
+        return;
+    } else {
+        /*
+         * channel client monitors the connectivity using ping-pong messages
+         */
+        red_channel_client_handle_message(rcc, size, SPICE_MSGC_PONG, ping);
+    }
+#ifdef RED_STATISTICS
+    stat_update_value(rcc->channel->reds, roundtrip);
+#endif
+}
+
+gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc)
+{
+    return mcc->seamless_mig_dst;
+}
+
+void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
+{
+    if (!red_client_during_migrate_at_target(mcc->base.client)) {
+        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
+        return;
+    }
+    if (!red_channel_client_test_remote_cap(&mcc->base,
+                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
+                   "client does not support semi-seamless migration");
+            return;
+    }
+    red_client_semi_seamless_migrate_complete(mcc->base.client);
+}
+
+void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc)
+{
+    if (mcc->mig_wait_connect) {
+        spice_printerr("client %p cancel wait connect", mcc->base.client);
+        mcc->mig_wait_connect = FALSE;
+        mcc->mig_connect_ok = FALSE;
+    }
+    mcc->mig_wait_prev_complete = FALSE;
+}
+
+void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
+{
+    if (mcc->mig_wait_prev_complete) {
+        if (mcc->mig_wait_prev_try_seamless) {
+            spice_assert(mcc->base.channel->clients_num == 1);
+            red_channel_client_pipe_add_type(&mcc->base,
+                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
+        } else {
+            red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
+        }
+        mcc->mig_wait_connect = TRUE;
+        mcc->mig_wait_prev_complete = FALSE;
+    }
+}
+
+gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
+                                                  gboolean success)
+{
+    gboolean ret = FALSE;
+    int semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
+                                                                   SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
+    if (semi_seamless_support && mcc->mig_connect_ok) {
+        if (success) {
+            spice_printerr("client %p MIGRATE_END", mcc->base.client);
+            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
+            ret = TRUE;
+        } else {
+            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
+            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+        }
+    } else {
+        if (success) {
+            spice_printerr("client %p SWITCH_HOST", mcc->base.client);
+            red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
+        }
+    }
+    mcc->mig_connect_ok = FALSE;
+    mcc->mig_wait_connect = FALSE;
+
+    return ret;
+}
+
+#ifdef RED_STATISTICS
+static void do_ping_client(MainChannelClient *mcc,
+    const char *opt, int has_interval, int interval)
+{
+    spice_printerr("");
+    if (!opt) {
+        main_channel_client_push_ping(mcc, 0);
+    } else if (!strcmp(opt, "on")) {
+        if (has_interval && interval > 0) {
+            mcc->ping_interval = interval * MSEC_PER_SEC;
+        }
+        reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
+    } else if (!strcmp(opt, "off")) {
+        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
+    } else {
+        return;
+    }
+}
+
+static void ping_timer_cb(void *opaque)
+{
+    MainChannelClient *mcc = opaque;
+
+    if (!red_channel_client_is_connected(&mcc->base)) {
+        spice_printerr("not connected to peer, ping off");
+        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
+        return;
+    }
+    do_ping_client(mcc, NULL, 0, 0);
+    reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
+}
+#endif /* RED_STATISTICS */
+
+MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
+                                              RedsStream *stream, uint32_t connection_id,
+                                              int num_common_caps, uint32_t *common_caps,
+                                              int num_caps, uint32_t *caps)
+{
+    MainChannelClient *mcc = (MainChannelClient*)
+                             red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
+                                                       client, stream, FALSE, num_common_caps,
+                                                       common_caps, num_caps, caps);
+    spice_assert(mcc != NULL);
+    mcc->connection_id = connection_id;
+    mcc->bitrate_per_sec = ~0;
+#ifdef RED_STATISTICS
+    if (!(mcc->ping_timer = reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb, mcc))) {
+        spice_error("ping timer create failed");
+    }
+    mcc->ping_interval = PING_INTERVAL;
+#endif
+    return mcc;
+}
+
+int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
+{
+    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
+}
+
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
+{
+    // TODO: configurable?
+    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
+}
+
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
+{
+    return mcc->bitrate_per_sec;
+}
+
+uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
+{
+    return mcc->latency / 1000;
+}
+
+void main_channel_client_migrate(RedChannelClient *rcc)
+{
+    reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc, MainChannelClient, base));
+    red_channel_client_default_migrate(rcc);
+}
+
+gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc)
+{
+    RedChannelClient *rcc = main_channel_client_get_base(mcc);
+    MainChannel* main_channel = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    if (red_channel_client_test_remote_cap(rcc,
+                                           SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+        RedClient *client = red_channel_client_get_client(rcc);
+        if (red_client_during_migrate_at_target(client)) {
+            spice_printerr("client %p: wait till previous migration completes", client);
+            mcc->mig_wait_prev_complete = TRUE;
+            mcc->mig_wait_prev_try_seamless = FALSE;
+        } else {
+            red_channel_client_pipe_add_type(rcc,
+                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
+            mcc->mig_wait_connect = TRUE;
+        }
+        mcc->mig_connect_ok = FALSE;
+        main_channel->num_clients_mig_wait++;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+void main_channel_client_connect_seamless(MainChannelClient *mcc)
+{
+    spice_assert(red_channel_client_test_remote_cap(&mcc->base,
+                                                    SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
+    if (red_client_during_migrate_at_target(mcc->base.client)) {
+        spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
+        mcc->mig_wait_prev_complete = TRUE;
+        mcc->mig_wait_prev_try_seamless = TRUE;
+    } else {
+        red_channel_client_pipe_add_type(&mcc->base,
+                                         RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
+        mcc->mig_wait_connect = TRUE;
+    }
+    mcc->mig_connect_ok = FALSE;
+}
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
+{
+    spice_assert(mcc);
+    return &mcc->base;
+}
+
+uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc)
+{
+    return mcc->connection_id;
+}
+
+uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc)
+{
+    return ++mcc->ping_id;
+}
+
+void main_channel_client_on_send_init(MainChannelClient *mcc)
+{
+    mcc->init_sent = TRUE;
+}
+
+gboolean main_channel_client_get_init_sent(MainChannelClient *mcc)
+{
+    return mcc->init_sent;
+}
diff --git a/server/main-channel-client.h b/server/main-channel-client.h
new file mode 100644
index 0000000..7e4daf9
--- /dev/null
+++ b/server/main-channel-client.h
@@ -0,0 +1,153 @@ 
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library 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.1 of the License, or (at your option) any later version.
+
+   This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __MAIN_CHANNEL_CLIENT_H__
+#define __MAIN_CHANNEL_CLIENT_H__
+
+#include "red-channel.h"
+
+typedef struct MainChannel MainChannel;
+typedef struct MainChannelClient MainChannelClient;
+
+MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
+                                              RedsStream *stream, uint32_t connection_id,
+                                              int num_common_caps, uint32_t *common_caps,
+                                              int num_caps, uint32_t *caps);
+
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
+                                         spice_marshaller_item_free_func free_data, void *opaque);
+void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
+// TODO: huge. Consider making a reds_* interface for these functions
+// and calling from main.
+void main_channel_client_push_init(MainChannelClient *mcc,
+                                   int display_channels_hint,
+                                   int current_mouse_mode,
+                                   int is_client_mouse_allowed,
+                                   int multi_media_time,
+                                   int ram_hint);
+void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
+void main_channel_client_migrate(RedChannelClient *rcc);
+gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc);
+void main_channel_client_connect_seamless(MainChannelClient *mcc);
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+                                                  int success,
+                                                  int seamless);
+void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
+                                                        uint32_t src_version);
+void main_channel_client_handle_migrate_end(MainChannelClient *mcc);
+void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc);
+void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
+gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
+                                                  gboolean success);
+
+void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, uint32_t size);
+
+/*
+ * return TRUE if network test had been completed successfully.
+ * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
+ */
+int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
+uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
+
+void main_channel_client_push_name(MainChannelClient *mcc, const char *name);
+void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
+
+uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc);
+uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc);
+
+gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc);
+void main_channel_client_on_send_init(MainChannelClient *mcc);
+gboolean main_channel_client_get_init_sent(MainChannelClient *mcc);
+
+enum {
+    RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
+    RED_PIPE_ITEM_TYPE_MAIN_PING,
+    RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
+    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
+    RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
+    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
+    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
+    RED_PIPE_ITEM_TYPE_MAIN_INIT,
+    RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
+    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
+    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
+    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
+    RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
+    RED_PIPE_ITEM_TYPE_MAIN_NAME,
+    RED_PIPE_ITEM_TYPE_MAIN_UUID,
+    RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
+};
+
+typedef struct RedPingPipeItem {
+    RedPipeItem base;
+    int size;
+} RedPingPipeItem;
+
+typedef struct RedMouseModePipeItem {
+    RedPipeItem base;
+    int current_mode;
+    int is_client_mouse_allowed;
+} RedMouseModePipeItem;
+
+typedef struct RedTokensPipeItem {
+    RedPipeItem base;
+    int tokens;
+} RedTokensPipeItem;
+
+typedef struct RedAgentDataPipeItem {
+    RedPipeItem base;
+    uint8_t* data;
+    size_t len;
+    spice_marshaller_item_free_func free_data;
+    void *opaque;
+} RedAgentDataPipeItem;
+
+typedef struct RedInitPipeItem {
+    RedPipeItem base;
+    int connection_id;
+    int display_channels_hint;
+    int current_mouse_mode;
+    int is_client_mouse_allowed;
+    int multi_media_time;
+    int ram_hint;
+} RedInitPipeItem;
+
+typedef struct RedNamePipeItem {
+    RedPipeItem base;
+    SpiceMsgMainName msg;
+} RedNamePipeItem;
+
+typedef struct RedUuidPipeItem {
+    RedPipeItem base;
+    SpiceMsgMainUuid msg;
+} RedUuidPipeItem;
+
+typedef struct RedNotifyPipeItem {
+    RedPipeItem base;
+    char *msg;
+} RedNotifyPipeItem;
+
+typedef struct RedMultiMediaTimePipeItem {
+    RedPipeItem base;
+    int time;
+} RedMultiMediaTimePipeItem;
+
+#endif /* __MAIN_CHANNEL_CLIENT_H__ */
diff --git a/server/main-channel.c b/server/main-channel.c
index 98ce660..7ec8b55 100644
--- a/server/main-channel.c
+++ b/server/main-channel.c
@@ -41,6 +41,7 @@ 
 
 #include "demarshallers.h"
 #include "main-channel.h"
+#include "main-channel-client.h"
 #include "red-channel.h"
 #include "red-common.h"
 #include "reds.h"
@@ -50,121 +51,8 @@ 
 
 #define ZERO_BUF_SIZE 4096
 
-#define NET_TEST_WARMUP_BYTES 0
-#define NET_TEST_BYTES (1024 * 250)
-
-#define PING_INTERVAL (MSEC_PER_SEC * 10)
-
-#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
-
 static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
 
-enum {
-    RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
-    RED_PIPE_ITEM_TYPE_MAIN_PING,
-    RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
-    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
-    RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
-    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
-    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
-    RED_PIPE_ITEM_TYPE_MAIN_INIT,
-    RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
-    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
-    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
-    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
-    RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
-    RED_PIPE_ITEM_TYPE_MAIN_NAME,
-    RED_PIPE_ITEM_TYPE_MAIN_UUID,
-    RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
-};
-
-typedef struct RedRefsPipeItem {
-    RedPipeItem base;
-    int *refs;
-} RedRefsPipeItem;
-
-typedef struct RedPingPipeItem {
-    RedPipeItem base;
-    int size;
-} RedPingPipeItem;
-
-typedef struct RedMouseModePipeItem {
-    RedPipeItem base;
-    int current_mode;
-    int is_client_mouse_allowed;
-} RedMouseModePipeItem;
-
-typedef struct RedTokensPipeItem {
-    RedPipeItem base;
-    int tokens;
-} RedTokensPipeItem;
-
-typedef struct RedAgentDataPipeItem {
-    RedPipeItem base;
-    uint8_t* data;
-    size_t len;
-    spice_marshaller_item_free_func free_data;
-    void *opaque;
-} RedAgentDataPipeItem;
-
-typedef struct RedInitPipeItem {
-    RedPipeItem base;
-    int connection_id;
-    int display_channels_hint;
-    int current_mouse_mode;
-    int is_client_mouse_allowed;
-    int multi_media_time;
-    int ram_hint;
-} RedInitPipeItem;
-
-typedef struct RedNamePipeItem {
-    RedPipeItem base;
-    SpiceMsgMainName msg;
-} RedNamePipeItem;
-
-typedef struct RedUuidPipeItem {
-    RedPipeItem base;
-    SpiceMsgMainUuid msg;
-} RedUuidPipeItem;
-
-typedef struct RedNotifyPipeItem {
-    RedPipeItem base;
-    char *msg;
-} RedNotifyPipeItem;
-
-typedef struct RedMultiMediaTimePipeItem {
-    RedPipeItem base;
-    int time;
-} RedMultiMediaTimePipeItem;
-
-struct MainChannelClient {
-    RedChannelClient base;
-    uint32_t connection_id;
-    uint32_t ping_id;
-    uint32_t net_test_id;
-    int net_test_stage;
-    uint64_t latency;
-    uint64_t bitrate_per_sec;
-#ifdef RED_STATISTICS
-    SpiceTimer *ping_timer;
-    int ping_interval;
-#endif
-    int mig_wait_connect;
-    int mig_connect_ok;
-    int mig_wait_prev_complete;
-    int mig_wait_prev_try_seamless;
-    int init_sent;
-    int seamless_mig_dst;
-};
-
-enum NetTestStage {
-    NET_TEST_STAGE_INVALID,
-    NET_TEST_STAGE_WARMUP,
-    NET_TEST_STAGE_LATENCY,
-    NET_TEST_STAGE_RATE,
-    NET_TEST_STAGE_COMPLETE,
-};
-
 static void main_channel_release_pipe_item(RedChannelClient *rcc,
                                            RedPipeItem *base, int item_pushed);
 
@@ -187,35 +75,18 @@  RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t c
 {
     RingItem *link;
     MainChannelClient *mcc;
+    RedChannelClient *rcc;
 
     RING_FOREACH(link, &main_chan->base.clients) {
-        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
-        if (mcc->connection_id == connection_id) {
-            return mcc->base.client;
+        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
+        mcc = (MainChannelClient*) rcc;
+        if (main_channel_client_get_connection_id(mcc) == connection_id) {
+            return rcc->client;
         }
     }
     return NULL;
 }
 
-static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
-
-void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
-{
-    if (!mcc || mcc->net_test_id) {
-        return;
-    }
-    if (test_rate) {
-        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
-            && main_channel_client_push_ping(mcc, 0)
-            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
-            mcc->net_test_id = mcc->ping_id - 2;
-            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
-        }
-    } else {
-        red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
-    }
-}
-
 typedef struct MainMouseModeItemInfo {
     int current_mode;
     int is_client_mouse_allowed;
@@ -232,88 +103,8 @@  static RedPipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data,
     return &item->base;
 }
 
-static RedPipeItem *red_ping_item_new(MainChannelClient *mcc, int size)
-{
-    RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
-
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
-    item->size = size;
-    return &item->base;
-}
-
-static RedPipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens)
-{
-    RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
-
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
-    item->tokens = num_tokens;
-    return &item->base;
-}
-
-static RedPipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len,
-                                             spice_marshaller_item_free_func free_data,
-                                             void *opaque)
-{
-    RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
-
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
-    item->data = data;
-    item->len = len;
-    item->free_data = free_data;
-    item->opaque = opaque;
-    return &item->base;
-}
-
-static RedPipeItem *main_init_item_new(MainChannelClient *mcc,
-    int connection_id, int display_channels_hint, int current_mouse_mode,
-    int is_client_mouse_allowed, int multi_media_time,
-    int ram_hint)
-{
-    RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
-
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT);
-    item->connection_id = connection_id;
-    item->display_channels_hint = display_channels_hint;
-    item->current_mouse_mode = current_mouse_mode;
-    item->is_client_mouse_allowed = is_client_mouse_allowed;
-    item->multi_media_time = multi_media_time;
-    item->ram_hint = ram_hint;
-    return &item->base;
-}
-
-static RedPipeItem *main_name_item_new(MainChannelClient *mcc, const char *name)
-{
-    RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) + strlen(name) + 1);
-
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
-    item->msg.name_len = strlen(name) + 1;
-    memcpy(&item->msg.name, name, item->msg.name_len);
-
-    return &item->base;
-}
-
-static RedPipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t uuid[16])
-{
-    RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
-
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
-    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
-
-    return &item->base;
-}
-
-static RedPipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num)
-{
-    RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
-    const char *msg = data;
-
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
-    item->msg = spice_strdup(msg);
-    return &item->base;
-}
-
-static RedPipeItem *main_multi_media_time_item_new(
-    RedChannelClient *rcc, void *data, int num)
+static RedPipeItem *main_multi_media_time_item_new(RedChannelClient *rcc,
+                                                   void *data, int num)
 {
     RedMultiMediaTimePipeItem *item, *info = data;
 
@@ -325,12 +116,12 @@  static RedPipeItem *main_multi_media_time_item_new(
 
 static void main_channel_push_channels(MainChannelClient *mcc)
 {
-    if (red_client_during_migrate_at_target(mcc->base.client)) {
+    if (red_client_during_migrate_at_target((main_channel_client_get_base(mcc))->client)) {
         spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
                    "during migration");
         return;
     }
-    red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
+    red_channel_client_pipe_add_type(main_channel_client_get_base(mcc), RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
 }
 
 static void main_channel_marshall_channels(RedChannelClient *rcc,
@@ -345,28 +136,16 @@  static void main_channel_marshall_channels(RedChannelClient *rcc,
     free(channels_info);
 }
 
-int main_channel_client_push_ping(MainChannelClient *mcc, int size)
-{
-    RedPipeItem *item;
-
-    if (mcc == NULL) {
-        return FALSE;
-    }
-    item = red_ping_item_new(mcc, size);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-    return TRUE;
-}
-
 static void main_channel_marshall_ping(RedChannelClient *rcc,
                                        SpiceMarshaller *m,
                                        RedPingPipeItem *item)
 {
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    MainChannelClient *mcc = (MainChannelClient*)rcc;
     SpiceMsgPing ping;
     int size_left = item->size;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
-    ping.id = ++(mcc->ping_id);
+    ping.id = main_channel_client_next_ping_id(mcc);
     ping.timestamp = g_get_monotonic_time();
     spice_marshall_msg_ping(m, &ping);
 
@@ -440,13 +219,6 @@  static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
     spice_marshall_msg_main_agent_disconnected(m, &disconnect);
 }
 
-void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
-{
-    RedPipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
-
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
 static void main_channel_marshall_tokens(RedChannelClient *rcc,
                                          SpiceMarshaller *m, RedTokensPipeItem *item)
 {
@@ -457,15 +229,6 @@  static void main_channel_marshall_tokens(RedChannelClient *rcc,
     spice_marshall_msg_main_agent_token(m, &tokens);
 }
 
-void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
-           spice_marshaller_item_free_func free_data, void *opaque)
-{
-    RedPipeItem *item;
-
-    item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
 static void main_channel_marshall_agent_data(RedChannelClient *rcc,
                                              SpiceMarshaller *m,
                                              RedAgentDataPipeItem *item)
@@ -490,7 +253,7 @@  static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
 static int main_channel_handle_migrate_data(RedChannelClient *rcc,
     uint32_t size, void *message)
 {
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    MainChannelClient *mcc = (MainChannelClient*)rcc;
     SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
 
     /* not supported with multi-clients */
@@ -509,21 +272,6 @@  static int main_channel_handle_migrate_data(RedChannelClient *rcc,
     return reds_handle_migrate_data(rcc->channel->reds, mcc, (SpiceMigrateDataMain *)(header + 1), size);
 }
 
-void main_channel_client_push_init(MainChannelClient *mcc,
-                                   int display_channels_hint,
-                                   int current_mouse_mode,
-                                   int is_client_mouse_allowed,
-                                   int multi_media_time,
-                                   int ram_hint)
-{
-    RedPipeItem *item;
-
-    item = main_init_item_new(mcc,
-             mcc->connection_id, display_channels_hint, current_mouse_mode,
-             is_client_mouse_allowed, multi_media_time, ram_hint);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
 static void main_channel_marshall_init(RedChannelClient *rcc,
                                        SpiceMarshaller *m,
                                        RedInitPipeItem *item)
@@ -546,36 +294,6 @@  static void main_channel_marshall_init(RedChannelClient *rcc,
     spice_marshall_msg_main_init(m, &init);
 }
 
-void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
-{
-    RedPipeItem *item;
-
-    if (!red_channel_client_test_remote_cap(&mcc->base,
-                                            SPICE_MAIN_CAP_NAME_AND_UUID))
-        return;
-
-    item = main_name_item_new(mcc, name);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
-{
-    RedPipeItem *item;
-
-    if (!red_channel_client_test_remote_cap(&mcc->base,
-                                            SPICE_MAIN_CAP_NAME_AND_UUID))
-        return;
-
-    item = main_uuid_item_new(mcc, uuid);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
-{
-    RedPipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
 static void main_channel_marshall_notify(RedChannelClient *rcc,
                                          SpiceMarshaller *m, RedNotifyPipeItem *item)
 {
@@ -701,14 +419,16 @@  static void main_channel_marshall_multi_media_time(RedChannelClient *rcc,
 
 static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
 {
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    MainChannelClient *mcc = (MainChannelClient*)rcc;
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
     /* In semi-seamless migration (dest side), the connection is started from scratch, and
      * we ignore any pipe item that arrives before the INIT msg is sent.
      * For seamless we don't send INIT, and the connection continues from the same place
      * it stopped on the src side. */
-    if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) {
+    if (!main_channel_client_get_init_sent(mcc) &&
+        !main_channel_client_get_seamless_migration(mcc) &&
+        base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) {
         spice_printerr("Init msg for client %p was not sent yet "
                        "(client is probably during semi-seamless migration). Ignoring msg type %d",
                    rcc->client, base->type);
@@ -745,7 +465,7 @@  static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
             main_channel_marshall_migrate_data_item(rcc, m, base);
             break;
         case RED_PIPE_ITEM_TYPE_MAIN_INIT:
-            mcc->init_sent = TRUE;
+            main_channel_client_on_send_init(mcc);
             main_channel_marshall_init(rcc, m,
                 SPICE_CONTAINEROF(base, RedInitPipeItem, base));
             break;
@@ -804,77 +524,11 @@  static void main_channel_release_pipe_item(RedChannelClient *rcc,
     free(base);
 }
 
-static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
-                                                         int success,
-                                                         int seamless)
-{
-    spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
-    if (mcc->mig_wait_connect) {
-        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
-
-        mcc->mig_wait_connect = FALSE;
-        mcc->mig_connect_ok = success;
-        spice_assert(main_channel->num_clients_mig_wait);
-        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
-        if (!--main_channel->num_clients_mig_wait) {
-            reds_on_main_migrate_connected(mcc->base.channel->reds, seamless && success);
-        }
-    } else {
-        if (success) {
-            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
-            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
-        }
-    }
-}
-
-void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
-                                                        uint32_t src_version)
-{
-    if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc, src_version)) {
-        mcc->seamless_mig_dst = TRUE;
-        red_channel_client_pipe_add_empty_msg(&mcc->base,
-                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
-    } else {
-        red_channel_client_pipe_add_empty_msg(&mcc->base,
-                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
-    }
-}
-
-void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
-{
-    if (!red_client_during_migrate_at_target(mcc->base.client)) {
-        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
-        return;
-    }
-    if (!red_channel_client_test_remote_cap(&mcc->base,
-                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
-        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
-                   "client does not support semi-seamless migration");
-            return;
-    }
-    red_client_semi_seamless_migrate_complete(mcc->base.client);
-}
-
-void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
-{
-    if (mcc->mig_wait_prev_complete) {
-        if (mcc->mig_wait_prev_try_seamless) {
-            spice_assert(mcc->base.channel->clients_num == 1);
-            red_channel_client_pipe_add_type(&mcc->base,
-                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
-        } else {
-            red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
-        }
-        mcc->mig_wait_connect = TRUE;
-        mcc->mig_wait_prev_complete = FALSE;
-    }
-}
-
 static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
                                       void *message)
 {
     MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    MainChannelClient *mcc = (MainChannelClient*)rcc;
 
     switch (type) {
     case SPICE_MSGC_MAIN_AGENT_START: {
@@ -923,64 +577,7 @@  static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
         reds_on_main_mouse_mode_request(rcc->channel->reds, message, size);
         break;
     case SPICE_MSGC_PONG: {
-        SpiceMsgPing *ping = (SpiceMsgPing *)message;
-        uint64_t roundtrip;
-
-        roundtrip = g_get_monotonic_time() - ping->timestamp;
-
-        if (ping->id == mcc->net_test_id) {
-            switch (mcc->net_test_stage) {
-            case NET_TEST_STAGE_WARMUP:
-                mcc->net_test_id++;
-                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
-                mcc->latency = roundtrip;
-                break;
-            case NET_TEST_STAGE_LATENCY:
-                mcc->net_test_id++;
-                mcc->net_test_stage = NET_TEST_STAGE_RATE;
-                mcc->latency = MIN(mcc->latency, roundtrip);
-                break;
-            case NET_TEST_STAGE_RATE:
-                mcc->net_test_id = 0;
-                if (roundtrip <= mcc->latency) {
-                    // probably high load on client or server result with incorrect values
-                    spice_printerr("net test: invalid values, latency %" PRIu64
-                                   " roundtrip %" PRIu64 ". assuming high"
-                                   " bandwidth", mcc->latency, roundtrip);
-                    mcc->latency = 0;
-                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
-                    red_channel_client_start_connectivity_monitoring(&mcc->base,
-                                                                     CLIENT_CONNECTIVITY_TIMEOUT);
-                    break;
-                }
-                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
-                                        / (roundtrip - mcc->latency);
-                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
-                spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
-                           (double)mcc->latency / 1000,
-                           mcc->bitrate_per_sec,
-                           (double)mcc->bitrate_per_sec / 1024 / 1024,
-                           main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
-                red_channel_client_start_connectivity_monitoring(&mcc->base,
-                                                                 CLIENT_CONNECTIVITY_TIMEOUT);
-                break;
-            default:
-                spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
-                           ping->id,
-                           mcc->net_test_id,
-                           mcc->net_test_stage);
-                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
-            }
-            break;
-        } else {
-            /*
-             * channel client monitors the connectivity using ping-pong messages
-             */
-            red_channel_client_handle_message(rcc, size, type, message);
-        }
-#ifdef RED_STATISTICS
-        stat_update_value(rcc->channel->reds, roundtrip);
-#endif
+        main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size);
         break;
     }
     case SPICE_MSGC_DISCONNECTING:
@@ -999,7 +596,7 @@  static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
                                                uint32_t size)
 {
     MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    MainChannelClient *mcc = (MainChannelClient*)rcc;
 
     if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
         return reds_get_agent_data_buffer(rcc->channel->reds, mcc, size);
@@ -1035,60 +632,6 @@  static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
     return TRUE;
 }
 
-#ifdef RED_STATISTICS
-static void do_ping_client(MainChannelClient *mcc,
-    const char *opt, int has_interval, int interval)
-{
-    spice_printerr("");
-    if (!opt) {
-        main_channel_client_push_ping(mcc, 0);
-    } else if (!strcmp(opt, "on")) {
-        if (has_interval && interval > 0) {
-            mcc->ping_interval = interval * MSEC_PER_SEC;
-        }
-        reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
-    } else if (!strcmp(opt, "off")) {
-        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
-    } else {
-        return;
-    }
-}
-
-static void ping_timer_cb(void *opaque)
-{
-    MainChannelClient *mcc = opaque;
-
-    if (!red_channel_client_is_connected(&mcc->base)) {
-        spice_printerr("not connected to peer, ping off");
-        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
-        return;
-    }
-    do_ping_client(mcc, NULL, 0, 0);
-    reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval);
-}
-#endif /* RED_STATISTICS */
-
-static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
-                                                     RedsStream *stream, uint32_t connection_id,
-                                                     int num_common_caps, uint32_t *common_caps,
-                                                     int num_caps, uint32_t *caps)
-{
-    MainChannelClient *mcc = (MainChannelClient*)
-                             red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
-                                                       client, stream, FALSE, num_common_caps,
-                                                       common_caps, num_caps, caps);
-    spice_assert(mcc != NULL);
-    mcc->connection_id = connection_id;
-    mcc->bitrate_per_sec = ~0;
-#ifdef RED_STATISTICS
-    if (!(mcc->ping_timer = reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb, mcc))) {
-        spice_error("ping timer create failed");
-    }
-    mcc->ping_interval = PING_INTERVAL;
-#endif
-    return mcc;
-}
-
 MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
                                      RedsStream *stream, uint32_t connection_id, int migration,
                                      int num_common_caps, uint32_t *common_caps, int num_caps,
@@ -1128,33 +671,6 @@  void main_channel_close(MainChannel *main_chan)
     }
 }
 
-int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
-{
-    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
-}
-
-int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
-{
-    // TODO: configurable?
-    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
-}
-
-uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
-{
-    return mcc->bitrate_per_sec;
-}
-
-uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
-{
-    return mcc->latency / 1000;
-}
-
-static void main_channel_client_migrate(RedChannelClient *rcc)
-{
-    reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc, MainChannelClient, base));
-    red_channel_client_default_migrate(rcc);
-}
-
 MainChannel* main_channel_new(RedsState *reds)
 {
     RedChannel *channel;
@@ -1190,33 +706,16 @@  MainChannel* main_channel_new(RedsState *reds)
     return (MainChannel *)channel;
 }
 
-RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
-{
-    spice_assert(mcc);
-    return &mcc->base;
-}
-
 static int main_channel_connect_semi_seamless(MainChannel *main_channel)
 {
     RingItem *client_link;
 
     RING_FOREACH(client_link, &main_channel->base.clients) {
-        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
-                                                    base.channel_link);
-        if (red_channel_client_test_remote_cap(&mcc->base,
-                                               SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
-            if (red_client_during_migrate_at_target(mcc->base.client)) {
-                spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
-                mcc->mig_wait_prev_complete = TRUE;
-                mcc->mig_wait_prev_try_seamless = FALSE;
-            } else {
-                red_channel_client_pipe_add_type(&mcc->base,
-                                                 RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
-                mcc->mig_wait_connect = TRUE;
-            }
-            mcc->mig_connect_ok = FALSE;
+        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+                                                    channel_link);
+        MainChannelClient *mcc = (MainChannelClient*)rcc;
+        if (main_channel_client_connect_semi_seamless(mcc))
             main_channel->num_clients_mig_wait++;
-        }
     }
     return main_channel->num_clients_mig_wait;
 }
@@ -1228,20 +727,10 @@  static int main_channel_connect_seamless(MainChannel *main_channel)
     spice_assert(main_channel->base.clients_num == 1);
 
     RING_FOREACH(client_link, &main_channel->base.clients) {
-        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
-                                                    base.channel_link);
-        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
-                                                        SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
-        if (red_client_during_migrate_at_target(mcc->base.client)) {
-           spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
-           mcc->mig_wait_prev_complete = TRUE;
-           mcc->mig_wait_prev_try_seamless = TRUE;
-        } else {
-            red_channel_client_pipe_add_type(&mcc->base,
-                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
-            mcc->mig_wait_connect = TRUE;
-        }
-        mcc->mig_connect_ok = FALSE;
+        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+                                                    channel_link);
+        MainChannelClient *mcc = (MainChannelClient*)rcc;
+        main_channel_client_connect_seamless(mcc);
         main_channel->num_clients_mig_wait++;
     }
     return main_channel->num_clients_mig_wait;
@@ -1261,12 +750,12 @@  int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
         return main_channel_connect_semi_seamless(main_channel);
     } else {
         RingItem *client_item;
-        MainChannelClient *mcc;
+        RedChannelClient *rcc;
 
         client_item = ring_get_head(&main_channel->base.clients);
-        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link);
+        rcc = SPICE_CONTAINEROF(client_item, RedChannelClient, channel_link);
 
-        if (!red_channel_client_test_remote_cap(&mcc->base,
+        if (!red_channel_client_test_remote_cap(rcc,
                                                 SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
             return main_channel_connect_semi_seamless(main_channel);
         } else {
@@ -1281,15 +770,10 @@  void main_channel_migrate_cancel_wait(MainChannel *main_chan)
     RingItem *client_link;
 
     RING_FOREACH(client_link, &main_chan->base.clients) {
-        MainChannelClient *mcc;
-
-        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
-        if (mcc->mig_wait_connect) {
-            spice_printerr("client %p cancel wait connect", mcc->base.client);
-            mcc->mig_wait_connect = FALSE;
-            mcc->mig_connect_ok = FALSE;
-        }
-        mcc->mig_wait_prev_complete = FALSE;
+        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+                                                    channel_link);
+        MainChannelClient *mcc = (MainChannelClient*)rcc;
+        main_channel_client_migrate_cancel_wait(mcc);
     }
     main_chan->num_clients_mig_wait = 0;
 }
@@ -1307,29 +791,11 @@  int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
     }
 
     RING_FOREACH(client_link, &main_chan->base.clients) {
-        MainChannelClient *mcc;
-        int semi_seamless_support;
-
-        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
-        semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
-                                                   SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
-        if (semi_seamless_support && mcc->mig_connect_ok) {
-            if (success) {
-                spice_printerr("client %p MIGRATE_END", mcc->base.client);
-                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
-                semi_seamless_count++;
-            } else {
-                spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
-                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
-            }
-        } else {
-            if (success) {
-                spice_printerr("client %p SWITCH_HOST", mcc->base.client);
-                red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
-            }
-        }
-        mcc->mig_connect_ok = FALSE;
-        mcc->mig_wait_connect = FALSE;
+        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient,
+                                                    channel_link);
+        MainChannelClient *mcc = (MainChannelClient*)rcc;
+        if (main_channel_client_migrate_src_complete(mcc, success))
+            semi_seamless_count++;
    }
    return semi_seamless_count;
 }
diff --git a/server/main-channel.h b/server/main-channel.h
index 4eb9a9c..868a14a 100644
--- a/server/main-channel.h
+++ b/server/main-channel.h
@@ -23,6 +23,7 @@ 
 #include <common/marshaller.h>
 
 #include "red-channel.h"
+#include "main-channel-client.h"
 
 // TODO: Defines used to calculate receive buffer size, and also by reds.c
 // other options: is to make a reds_main_consts.h, to duplicate defines.
@@ -59,34 +60,11 @@  void main_channel_close(MainChannel *main_chan); // not destroy, just socket clo
 void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
 void main_channel_push_agent_connected(MainChannel *main_chan);
 void main_channel_push_agent_disconnected(MainChannel *main_chan);
-void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
-void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
-                                         spice_marshaller_item_free_func free_data, void *opaque);
-void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
-// TODO: huge. Consider making a reds_* interface for these functions
-// and calling from main.
-void main_channel_client_push_init(MainChannelClient *mcc,
-                                   int display_channels_hint,
-                                   int current_mouse_mode,
-                                   int is_client_mouse_allowed,
-                                   int multi_media_time,
-                                   int ram_hint);
-void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
 void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
 int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
 int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
 
-/*
- * return TRUE if network test had been completed successfully.
- * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
- */
-int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
-int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
-uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
-uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
-
 int main_channel_is_connected(MainChannel *main_chan);
-RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
 
 /* switch host migration */
 void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
@@ -100,8 +78,5 @@  int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
 void main_channel_migrate_cancel_wait(MainChannel *main_chan);
 /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
 int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
-void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
-void main_channel_client_push_name(MainChannelClient *mcc, const char *name);
-void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
 
 #endif

Comments

> 
> Preparation for converting to GObject
> ---
>  server/Makefile.am           |   2 +
>  server/inputs-channel.c      |   2 +-
>  server/main-channel-client.c | 549 ++++++++++++++++++++++++++++++++++++++
>  server/main-channel-client.h | 153 +++++++++++
>  server/main-channel.c        | 616
>  +++----------------------------------------
>  server/main-channel.h        |  27 +-
>  6 files changed, 747 insertions(+), 602 deletions(-)
>  create mode 100644 server/main-channel-client.c
>  create mode 100644 server/main-channel-client.h
> 
> diff --git a/server/Makefile.am b/server/Makefile.am
> index fbf4638..5a5c7e8 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -86,6 +86,8 @@ libserver_la_SOURCES =				\
>  	lz4-encoder.h				\
>  	main-channel.c				\
>  	main-channel.h				\
> +	main-channel-client.c			\
> +	main-channel-client.h			\
>  	mjpeg-encoder.c				\
>  	red-channel.c				\
>  	red-channel.h				\
> diff --git a/server/inputs-channel.c b/server/inputs-channel.c
> index 0ce12de..584204f 100644
> --- a/server/inputs-channel.c
> +++ b/server/inputs-channel.c
> @@ -39,7 +39,7 @@
>  #include "reds.h"
>  #include "reds-stream.h"
>  #include "red-channel.h"
> -#include "main-channel.h"
> +#include "main-channel-client.h"
>  #include "inputs-channel.h"
>  #include "migration-protocol.h"
>  #include "utils.h"
> diff --git a/server/main-channel-client.c b/server/main-channel-client.c
> new file mode 100644
> index 0000000..bac5316
> --- /dev/null
> +++ b/server/main-channel-client.c
> @@ -0,0 +1,549 @@
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +

Wouldn't be good to update to 2016?

> +   This library 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.1 of the License, or (at your option) any later version.
> +
> +   This library 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 this library; if not, see
> <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <inttypes.h>
> +#include "main-channel-client.h"

replace above line with an empty one, its duplicated

> +#include "main-channel-client.h"
> +#include "main-channel.h"
> +#include "reds.h"
> +
> +#define NET_TEST_WARMUP_BYTES 0
> +#define NET_TEST_BYTES (1024 * 250)
> +
> +enum NetTestStage {
> +    NET_TEST_STAGE_INVALID,
> +    NET_TEST_STAGE_WARMUP,
> +    NET_TEST_STAGE_LATENCY,
> +    NET_TEST_STAGE_RATE,
> +    NET_TEST_STAGE_COMPLETE,
> +};
> +
> +#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
> +#define PING_INTERVAL (MSEC_PER_SEC * 10)
> +
> +struct MainChannelClient {
> +    RedChannelClient base;
> +    uint32_t connection_id;
> +    uint32_t ping_id;
> +    uint32_t net_test_id;
> +    int net_test_stage;
> +    uint64_t latency;
> +    uint64_t bitrate_per_sec;
> +#ifdef RED_STATISTICS
> +    SpiceTimer *ping_timer;
> +    int ping_interval;
> +#endif
> +    int mig_wait_connect;
> +    int mig_connect_ok;
> +    int mig_wait_prev_complete;
> +    int mig_wait_prev_try_seamless;
> +    int init_sent;
> +    int seamless_mig_dst;
> +};
> +
> +static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
> +
> +static RedPipeItem *main_notify_item_new(void *data, int num)
> +{
> +    RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
> +    const char *msg = data;
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
> +    item->msg = spice_strdup(msg);
> +    return &item->base;
> +}
> +
> +void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate)
> +{
> +    if (!mcc || mcc->net_test_id) {
> +        return;
> +    }
> +    if (test_rate) {
> +        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
> +            && main_channel_client_push_ping(mcc, 0)
> +            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
> +            mcc->net_test_id = mcc->ping_id - 2;
> +            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
> +        }
> +    } else {
> +        red_channel_client_start_connectivity_monitoring(&mcc->base,
> CLIENT_CONNECTIVITY_TIMEOUT);
> +    }
> +}
> +
> +static RedPipeItem *red_ping_item_new(int size)
> +{
> +    RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
> +    item->size = size;
> +    return &item->base;
> +}
> +
> +static int main_channel_client_push_ping(MainChannelClient *mcc, int size)
> +{
> +    RedPipeItem *item;
> +
> +    if (mcc == NULL) {
> +        return FALSE;
> +    }
> +    item = red_ping_item_new(size);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +    return TRUE;
> +}
> +
> +static RedPipeItem *main_agent_tokens_item_new(uint32_t num_tokens)
> +{
> +    RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
> +    item->tokens = num_tokens;
> +    return &item->base;
> +}
> +
> +
> +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens)
> +{
> +    RedPipeItem *item = main_agent_tokens_item_new(num_tokens);
> +
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_agent_data_item_new(uint8_t* data, size_t len,
> +                                             spice_marshaller_item_free_func
> free_data,
> +                                             void *opaque)
> +{
> +    RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
> +    item->data = data;
> +    item->len = len;
> +    item->free_data = free_data;
> +    item->opaque = opaque;
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> +           spice_marshaller_item_free_func free_data, void *opaque)
> +{
> +    RedPipeItem *item;
> +
> +    item = main_agent_data_item_new(data, len, free_data, opaque);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_init_item_new(int connection_id,
> +                                       int display_channels_hint,
> +                                       int current_mouse_mode,
> +                                       int is_client_mouse_allowed,
> +                                       int multi_media_time,
> +                                       int ram_hint)
> +{
> +    RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT);
> +    item->connection_id = connection_id;
> +    item->display_channels_hint = display_channels_hint;
> +    item->current_mouse_mode = current_mouse_mode;
> +    item->is_client_mouse_allowed = is_client_mouse_allowed;
> +    item->multi_media_time = multi_media_time;
> +    item->ram_hint = ram_hint;
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_init(MainChannelClient *mcc,
> +                                   int display_channels_hint,
> +                                   int current_mouse_mode,
> +                                   int is_client_mouse_allowed,
> +                                   int multi_media_time,
> +                                   int ram_hint)
> +{
> +    RedPipeItem *item;
> +
> +    item = main_init_item_new(mcc->connection_id, display_channels_hint,
> +                              current_mouse_mode, is_client_mouse_allowed,
> +                              multi_media_time, ram_hint);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_name_item_new(const char *name)
> +{
> +    RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) +
> strlen(name) + 1);
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
> +    item->msg.name_len = strlen(name) + 1;
> +    memcpy(&item->msg.name, name, item->msg.name_len);
> +
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
> +{
> +    RedPipeItem *item;
> +
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> +        return;
> +
> +    item = main_name_item_new(name);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_uuid_item_new(const uint8_t uuid[16])
> +{
> +    RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
> +    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
> +
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16])
> +{
> +    RedPipeItem *item;
> +
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> +        return;
> +
> +    item = main_uuid_item_new(uuid);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +void main_channel_client_push_notify(MainChannelClient *mcc, const char
> *msg)
> +{
> +    RedPipeItem *item = main_notify_item_new((void *)msg, 1);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
> +                                                  int success,
> +                                                  int seamless)
> +{
> +    spice_printerr("client %p connected: %d seamless %d", mcc->base.client,
> success, seamless);
> +    if (mcc->mig_wait_connect) {
> +        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel,
> MainChannel, base);
> +
> +        mcc->mig_wait_connect = FALSE;
> +        mcc->mig_connect_ok = success;
> +        spice_assert(main_channel->num_clients_mig_wait);
> +        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
> +        if (!--main_channel->num_clients_mig_wait) {
> +            reds_on_main_migrate_connected(mcc->base.channel->reds, seamless
> && success);
> +        }
> +    } else {
> +        if (success) {
> +            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> +            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> +        }
> +    }
> +}
> +
> +void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient
> *mcc,
> +                                                        uint32_t
> src_version)
> +{
> +    if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc,
> src_version)) {
> +        mcc->seamless_mig_dst = TRUE;
> +        red_channel_client_pipe_add_empty_msg(&mcc->base,
> +
> SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
> +    } else {
> +        red_channel_client_pipe_add_empty_msg(&mcc->base,
> +
> SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
> +    }
> +}
> +void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing
> *ping, uint32_t size)
> +{
> +    uint64_t roundtrip;
> +    RedChannelClient* rcc = (RedChannelClient*)mcc;
> +
> +    roundtrip = g_get_monotonic_time() - ping->timestamp;
> +
> +    if (ping->id == mcc->net_test_id) {
> +        switch (mcc->net_test_stage) {
> +            case NET_TEST_STAGE_WARMUP:
> +                mcc->net_test_id++;
> +                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
> +                mcc->latency = roundtrip;
> +                break;
> +            case NET_TEST_STAGE_LATENCY:
> +                mcc->net_test_id++;
> +                mcc->net_test_stage = NET_TEST_STAGE_RATE;
> +                mcc->latency = MIN(mcc->latency, roundtrip);
> +                break;
> +            case NET_TEST_STAGE_RATE:
> +                mcc->net_test_id = 0;
> +                if (roundtrip <= mcc->latency) {
> +                    // probably high load on client or server result with
> incorrect values
> +                    spice_printerr("net test: invalid values, latency %"
> PRIu64
> +                                   " roundtrip %" PRIu64 ". assuming high"
> +                                   "bandwidth", mcc->latency, roundtrip);
> +                    mcc->latency = 0;
> +                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> +
> red_channel_client_start_connectivity_monitoring(&mcc->base,
> +
> CLIENT_CONNECTIVITY_TIMEOUT);
> +                    break;
> +                }
> +                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) *
> 1000000
> +                    / (roundtrip - mcc->latency);
> +                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
> +                spice_printerr("net test: latency %f ms, bitrate %"PRIu64"
> bps (%f Mbps)%s",
> +                               (double)mcc->latency / 1000,
> +                               mcc->bitrate_per_sec,
> +                               (double)mcc->bitrate_per_sec / 1024 / 1024,
> +                               main_channel_client_is_low_bandwidth(mcc) ? "
> LOW BANDWIDTH" : "");
> +                red_channel_client_start_connectivity_monitoring(&mcc->base,
> +
> CLIENT_CONNECTIVITY_TIMEOUT);
> +                break;
> +            default:
> +                spice_printerr("invalid net test stage, ping id %d test id
> %d stage %d",
> +                               ping->id,
> +                               mcc->net_test_id,
> +                               mcc->net_test_stage);
> +                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> +        }
> +        return;
> +    } else {
> +        /*
> +         * channel client monitors the connectivity using ping-pong messages
> +         */
> +        red_channel_client_handle_message(rcc, size, SPICE_MSGC_PONG, ping);
> +    }
> +#ifdef RED_STATISTICS
> +    stat_update_value(rcc->channel->reds, roundtrip);
> +#endif
> +}
> +
> +gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc)
> +{
> +    return mcc->seamless_mig_dst;
> +}
> +
> +void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
> +{
> +    if (!red_client_during_migrate_at_target(mcc->base.client)) {
> +        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
> +        return;
> +    }
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +
> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE))
> {
> +        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
> +                   "client does not support semi-seamless migration");
> +            return;
> +    }
> +    red_client_semi_seamless_migrate_complete(mcc->base.client);
> +}
> +
> +void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc)
> +{
> +    if (mcc->mig_wait_connect) {
> +        spice_printerr("client %p cancel wait connect", mcc->base.client);
> +        mcc->mig_wait_connect = FALSE;
> +        mcc->mig_connect_ok = FALSE;
> +    }
> +    mcc->mig_wait_prev_complete = FALSE;
> +}
> +
> +void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
> +{
> +    if (mcc->mig_wait_prev_complete) {
> +        if (mcc->mig_wait_prev_try_seamless) {
> +            spice_assert(mcc->base.channel->clients_num == 1);
> +            red_channel_client_pipe_add_type(&mcc->base,
> +
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> +        } else {
> +            red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> +        }
> +        mcc->mig_wait_connect = TRUE;
> +        mcc->mig_wait_prev_complete = FALSE;
> +    }
> +}
> +
> +gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
> +                                                  gboolean success)
> +{
> +    gboolean ret = FALSE;
> +    int semi_seamless_support =
> red_channel_client_test_remote_cap(&mcc->base,
> +
> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> +    if (semi_seamless_support && mcc->mig_connect_ok) {
> +        if (success) {
> +            spice_printerr("client %p MIGRATE_END", mcc->base.client);
> +            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_END);
> +            ret = TRUE;
> +        } else {
> +            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> +            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> +        }
> +    } else {
> +        if (success) {
> +            spice_printerr("client %p SWITCH_HOST", mcc->base.client);
> +            red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> +        }
> +    }
> +    mcc->mig_connect_ok = FALSE;
> +    mcc->mig_wait_connect = FALSE;
> +
> +    return ret;
> +}
> +
> +#ifdef RED_STATISTICS
> +static void do_ping_client(MainChannelClient *mcc,
> +    const char *opt, int has_interval, int interval)
> +{
> +    spice_printerr("");
> +    if (!opt) {
> +        main_channel_client_push_ping(mcc, 0);
> +    } else if (!strcmp(opt, "on")) {
> +        if (has_interval && interval > 0) {
> +            mcc->ping_interval = interval * MSEC_PER_SEC;
> +        }
> +        reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer,
> mcc->ping_interval);
> +    } else if (!strcmp(opt, "off")) {
> +        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> +    } else {
> +        return;
> +    }
> +}
> +
> +static void ping_timer_cb(void *opaque)
> +{
> +    MainChannelClient *mcc = opaque;
> +
> +    if (!red_channel_client_is_connected(&mcc->base)) {
> +        spice_printerr("not connected to peer, ping off");
> +        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> +        return;
> +    }
> +    do_ping_client(mcc, NULL, 0, 0);
> +    reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer,
> mcc->ping_interval);
> +}
> +#endif /* RED_STATISTICS */
> +
> +MainChannelClient *main_channel_client_create(MainChannel *main_chan,
> RedClient *client,
> +                                              RedsStream *stream, uint32_t
> connection_id,
> +                                              int num_common_caps, uint32_t
> *common_caps,
> +                                              int num_caps, uint32_t *caps)
> +{
> +    MainChannelClient *mcc = (MainChannelClient*)
> +
> red_channel_client_create(sizeof(MainChannelClient),
> &main_chan->base,
> +                                                       client, stream,
> FALSE, num_common_caps,
> +                                                       common_caps,
> num_caps, caps);
> +    spice_assert(mcc != NULL);
> +    mcc->connection_id = connection_id;
> +    mcc->bitrate_per_sec = ~0;
> +#ifdef RED_STATISTICS
> +    if (!(mcc->ping_timer =
> reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb,
> mcc))) {
> +        spice_error("ping timer create failed");
> +    }
> +    mcc->ping_interval = PING_INTERVAL;
> +#endif
> +    return mcc;
> +}
> +
> +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
> +{
> +    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
> +}
> +
> +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
> +{
> +    // TODO: configurable?
> +    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
> +}
> +
> +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
> +{
> +    return mcc->bitrate_per_sec;
> +}
> +
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
> +{
> +    return mcc->latency / 1000;
> +}
> +
> +void main_channel_client_migrate(RedChannelClient *rcc)
> +{
> +    reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc,
> MainChannelClient, base));
> +    red_channel_client_default_migrate(rcc);
> +}
> +
> +gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc)
> +{
> +    RedChannelClient *rcc = main_channel_client_get_base(mcc);
> +    MainChannel* main_channel = SPICE_CONTAINEROF(rcc->channel, MainChannel,
> base);
> +    if (red_channel_client_test_remote_cap(rcc,
> +
> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE))
> {
> +        RedClient *client = red_channel_client_get_client(rcc);
> +        if (red_client_during_migrate_at_target(client)) {
> +            spice_printerr("client %p: wait till previous migration
> completes", client);
> +            mcc->mig_wait_prev_complete = TRUE;
> +            mcc->mig_wait_prev_try_seamless = FALSE;
> +        } else {
> +            red_channel_client_pipe_add_type(rcc,
> +
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> +            mcc->mig_wait_connect = TRUE;
> +        }
> +        mcc->mig_connect_ok = FALSE;
> +        main_channel->num_clients_mig_wait++;
> +        return TRUE;
> +    }
> +    return FALSE;
> +}
> +
> +void main_channel_client_connect_seamless(MainChannelClient *mcc)
> +{
> +    spice_assert(red_channel_client_test_remote_cap(&mcc->base,
> +
> SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
> +    if (red_client_during_migrate_at_target(mcc->base.client)) {
> +        spice_printerr("client %p: wait till previous migration completes",
> mcc->base.client);
> +        mcc->mig_wait_prev_complete = TRUE;
> +        mcc->mig_wait_prev_try_seamless = TRUE;
> +    } else {
> +        red_channel_client_pipe_add_type(&mcc->base,
> +
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> +        mcc->mig_wait_connect = TRUE;
> +    }
> +    mcc->mig_connect_ok = FALSE;
> +}
> +
> +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
> +{
> +    spice_assert(mcc);
> +    return &mcc->base;
> +}
> +
> +uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc)
> +{
> +    return mcc->connection_id;
> +}
> +
> +uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc)
> +{
> +    return ++mcc->ping_id;
> +}
> +
> +void main_channel_client_on_send_init(MainChannelClient *mcc)
> +{
> +    mcc->init_sent = TRUE;
> +}
> +
> +gboolean main_channel_client_get_init_sent(MainChannelClient *mcc)
> +{
> +    return mcc->init_sent;
> +}
> diff --git a/server/main-channel-client.h b/server/main-channel-client.h
> new file mode 100644
> index 0000000..7e4daf9
> --- /dev/null
> +++ b/server/main-channel-client.h
> @@ -0,0 +1,153 @@
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library 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.1 of the License, or (at your option) any later version.
> +
> +   This library 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 this library; if not, see
> <http://www.gnu.org/licenses/>.
> +*/
> +#ifndef __MAIN_CHANNEL_CLIENT_H__
> +#define __MAIN_CHANNEL_CLIENT_H__
> +
> +#include "red-channel.h"
> +
> +typedef struct MainChannel MainChannel;
> +typedef struct MainChannelClient MainChannelClient;
> +
> +MainChannelClient *main_channel_client_create(MainChannel *main_chan,
> RedClient *client,
> +                                              RedsStream *stream, uint32_t
> connection_id,
> +                                              int num_common_caps, uint32_t
> *common_caps,
> +                                              int num_caps, uint32_t *caps);
> +
> +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens);
> +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> +                                         spice_marshaller_item_free_func
> free_data, void *opaque);
> +void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate);
> +// TODO: huge. Consider making a reds_* interface for these functions
> +// and calling from main.
> +void main_channel_client_push_init(MainChannelClient *mcc,
> +                                   int display_channels_hint,
> +                                   int current_mouse_mode,
> +                                   int is_client_mouse_allowed,
> +                                   int multi_media_time,
> +                                   int ram_hint);
> +void main_channel_client_push_notify(MainChannelClient *mcc, const char
> *msg);
> +void main_channel_client_migrate(RedChannelClient *rcc);
> +gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc);
> +void main_channel_client_connect_seamless(MainChannelClient *mcc);
> +void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
> +                                                  int success,
> +                                                  int seamless);
> +void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient
> *mcc,
> +                                                        uint32_t
> src_version);
> +void main_channel_client_handle_migrate_end(MainChannelClient *mcc);
> +void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc);
> +void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
> +gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
> +                                                  gboolean success);
> +
> +void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing
> *ping, uint32_t size);
> +
> +/*
> + * return TRUE if network test had been completed successfully.
> + * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set
> to 0
> + */
> +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
> +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
> +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
> +
> +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
> +
> +void main_channel_client_push_name(MainChannelClient *mcc, const char
> *name);
> +void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16]);
> +
> +uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc);
> +uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc);
> +
> +gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc);
> +void main_channel_client_on_send_init(MainChannelClient *mcc);
> +gboolean main_channel_client_get_init_sent(MainChannelClient *mcc);
> +
> +enum {
> +    RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
> +    RED_PIPE_ITEM_TYPE_MAIN_PING,
> +    RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
> +    RED_PIPE_ITEM_TYPE_MAIN_INIT,
> +    RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
> +    RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
> +    RED_PIPE_ITEM_TYPE_MAIN_NAME,
> +    RED_PIPE_ITEM_TYPE_MAIN_UUID,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
> +};
> +
> +typedef struct RedPingPipeItem {
> +    RedPipeItem base;
> +    int size;
> +} RedPingPipeItem;
> +
> +typedef struct RedMouseModePipeItem {
> +    RedPipeItem base;
> +    int current_mode;
> +    int is_client_mouse_allowed;
> +} RedMouseModePipeItem;
> +
> +typedef struct RedTokensPipeItem {
> +    RedPipeItem base;
> +    int tokens;
> +} RedTokensPipeItem;
> +
> +typedef struct RedAgentDataPipeItem {
> +    RedPipeItem base;
> +    uint8_t* data;
> +    size_t len;
> +    spice_marshaller_item_free_func free_data;
> +    void *opaque;
> +} RedAgentDataPipeItem;
> +
> +typedef struct RedInitPipeItem {
> +    RedPipeItem base;
> +    int connection_id;
> +    int display_channels_hint;
> +    int current_mouse_mode;
> +    int is_client_mouse_allowed;
> +    int multi_media_time;
> +    int ram_hint;
> +} RedInitPipeItem;
> +
> +typedef struct RedNamePipeItem {
> +    RedPipeItem base;
> +    SpiceMsgMainName msg;
> +} RedNamePipeItem;
> +
> +typedef struct RedUuidPipeItem {
> +    RedPipeItem base;
> +    SpiceMsgMainUuid msg;
> +} RedUuidPipeItem;
> +
> +typedef struct RedNotifyPipeItem {
> +    RedPipeItem base;
> +    char *msg;
> +} RedNotifyPipeItem;
> +
> +typedef struct RedMultiMediaTimePipeItem {
> +    RedPipeItem base;
> +    int time;
> +} RedMultiMediaTimePipeItem;
> +
> +#endif /* __MAIN_CHANNEL_CLIENT_H__ */
> diff --git a/server/main-channel.c b/server/main-channel.c
> index 98ce660..7ec8b55 100644
> --- a/server/main-channel.c
> +++ b/server/main-channel.c
> @@ -41,6 +41,7 @@
>  
>  #include "demarshallers.h"
>  #include "main-channel.h"
> +#include "main-channel-client.h"
>  #include "red-channel.h"
>  #include "red-common.h"
>  #include "reds.h"
> @@ -50,121 +51,8 @@
>  
>  #define ZERO_BUF_SIZE 4096
>  
> -#define NET_TEST_WARMUP_BYTES 0
> -#define NET_TEST_BYTES (1024 * 250)
> -
> -#define PING_INTERVAL (MSEC_PER_SEC * 10)
> -
> -#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
> -
>  static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
>  
> -enum {
> -    RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
> -    RED_PIPE_ITEM_TYPE_MAIN_PING,
> -    RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
> -    RED_PIPE_ITEM_TYPE_MAIN_INIT,
> -    RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
> -    RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
> -    RED_PIPE_ITEM_TYPE_MAIN_NAME,
> -    RED_PIPE_ITEM_TYPE_MAIN_UUID,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
> -};
> -
> -typedef struct RedRefsPipeItem {
> -    RedPipeItem base;
> -    int *refs;
> -} RedRefsPipeItem;
> -
> -typedef struct RedPingPipeItem {
> -    RedPipeItem base;
> -    int size;
> -} RedPingPipeItem;
> -
> -typedef struct RedMouseModePipeItem {
> -    RedPipeItem base;
> -    int current_mode;
> -    int is_client_mouse_allowed;
> -} RedMouseModePipeItem;
> -
> -typedef struct RedTokensPipeItem {
> -    RedPipeItem base;
> -    int tokens;
> -} RedTokensPipeItem;
> -
> -typedef struct RedAgentDataPipeItem {
> -    RedPipeItem base;
> -    uint8_t* data;
> -    size_t len;
> -    spice_marshaller_item_free_func free_data;
> -    void *opaque;
> -} RedAgentDataPipeItem;
> -
> -typedef struct RedInitPipeItem {
> -    RedPipeItem base;
> -    int connection_id;
> -    int display_channels_hint;
> -    int current_mouse_mode;
> -    int is_client_mouse_allowed;
> -    int multi_media_time;
> -    int ram_hint;
> -} RedInitPipeItem;
> -
> -typedef struct RedNamePipeItem {
> -    RedPipeItem base;
> -    SpiceMsgMainName msg;
> -} RedNamePipeItem;
> -
> -typedef struct RedUuidPipeItem {
> -    RedPipeItem base;
> -    SpiceMsgMainUuid msg;
> -} RedUuidPipeItem;
> -
> -typedef struct RedNotifyPipeItem {
> -    RedPipeItem base;
> -    char *msg;
> -} RedNotifyPipeItem;
> -
> -typedef struct RedMultiMediaTimePipeItem {
> -    RedPipeItem base;
> -    int time;
> -} RedMultiMediaTimePipeItem;
> -
> -struct MainChannelClient {
> -    RedChannelClient base;
> -    uint32_t connection_id;
> -    uint32_t ping_id;
> -    uint32_t net_test_id;
> -    int net_test_stage;
> -    uint64_t latency;
> -    uint64_t bitrate_per_sec;
> -#ifdef RED_STATISTICS
> -    SpiceTimer *ping_timer;
> -    int ping_interval;
> -#endif
> -    int mig_wait_connect;
> -    int mig_connect_ok;
> -    int mig_wait_prev_complete;
> -    int mig_wait_prev_try_seamless;
> -    int init_sent;
> -    int seamless_mig_dst;
> -};
> -
> -enum NetTestStage {
> -    NET_TEST_STAGE_INVALID,
> -    NET_TEST_STAGE_WARMUP,
> -    NET_TEST_STAGE_LATENCY,
> -    NET_TEST_STAGE_RATE,
> -    NET_TEST_STAGE_COMPLETE,
> -};
> -
>  static void main_channel_release_pipe_item(RedChannelClient *rcc,
>                                             RedPipeItem *base, int
>                                             item_pushed);
>  
> @@ -187,35 +75,18 @@ RedClient
> *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t c
>  {
>      RingItem *link;
>      MainChannelClient *mcc;
> +    RedChannelClient *rcc;
>  
>      RING_FOREACH(link, &main_chan->base.clients) {
> -        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
> -        if (mcc->connection_id == connection_id) {
> -            return mcc->base.client;
> +        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
> +        mcc = (MainChannelClient*) rcc;
> +        if (main_channel_client_get_connection_id(mcc) == connection_id) {
> +            return rcc->client;
>          }
>      }
>      return NULL;
>  }
>  
> -static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
> -
> -void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate)
> -{
> -    if (!mcc || mcc->net_test_id) {
> -        return;
> -    }
> -    if (test_rate) {
> -        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
> -            && main_channel_client_push_ping(mcc, 0)
> -            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
> -            mcc->net_test_id = mcc->ping_id - 2;
> -            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
> -        }
> -    } else {
> -        red_channel_client_start_connectivity_monitoring(&mcc->base,
> CLIENT_CONNECTIVITY_TIMEOUT);
> -    }
> -}
> -
>  typedef struct MainMouseModeItemInfo {
>      int current_mode;
>      int is_client_mouse_allowed;
> @@ -232,88 +103,8 @@ static RedPipeItem
> *main_mouse_mode_item_new(RedChannelClient *rcc, void *data,
>      return &item->base;
>  }
>  
> -static RedPipeItem *red_ping_item_new(MainChannelClient *mcc, int size)
> -{
> -    RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
> -    item->size = size;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_agent_tokens_item_new(RedChannelClient *rcc,
> uint32_t num_tokens)
> -{
> -    RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
> -    item->tokens = num_tokens;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t*
> data, size_t len,
> -                                             spice_marshaller_item_free_func
> free_data,
> -                                             void *opaque)
> -{
> -    RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
> -    item->data = data;
> -    item->len = len;
> -    item->free_data = free_data;
> -    item->opaque = opaque;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_init_item_new(MainChannelClient *mcc,
> -    int connection_id, int display_channels_hint, int current_mouse_mode,
> -    int is_client_mouse_allowed, int multi_media_time,
> -    int ram_hint)
> -{
> -    RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT);
> -    item->connection_id = connection_id;
> -    item->display_channels_hint = display_channels_hint;
> -    item->current_mouse_mode = current_mouse_mode;
> -    item->is_client_mouse_allowed = is_client_mouse_allowed;
> -    item->multi_media_time = multi_media_time;
> -    item->ram_hint = ram_hint;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_name_item_new(MainChannelClient *mcc, const char
> *name)
> -{
> -    RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) +
> strlen(name) + 1);
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
> -    item->msg.name_len = strlen(name) + 1;
> -    memcpy(&item->msg.name, name, item->msg.name_len);
> -
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t
> uuid[16])
> -{
> -    RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
> -    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
> -
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_notify_item_new(RedChannelClient *rcc, void *data,
> int num)
> -{
> -    RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
> -    const char *msg = data;
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
> -    item->msg = spice_strdup(msg);
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_multi_media_time_item_new(
> -    RedChannelClient *rcc, void *data, int num)
> +static RedPipeItem *main_multi_media_time_item_new(RedChannelClient *rcc,
> +                                                   void *data, int num)
>  {
>      RedMultiMediaTimePipeItem *item, *info = data;
>  
> @@ -325,12 +116,12 @@ static RedPipeItem *main_multi_media_time_item_new(
>  
>  static void main_channel_push_channels(MainChannelClient *mcc)
>  {
> -    if (red_client_during_migrate_at_target(mcc->base.client)) {
> +    if
> (red_client_during_migrate_at_target((main_channel_client_get_base(mcc))->client))
> {
>          spice_printerr("warning: ignoring unexpected
>          SPICE_MSGC_MAIN_ATTACH_CHANNELS"
>                     "during migration");
>          return;
>      }
> -    red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
> +    red_channel_client_pipe_add_type(main_channel_client_get_base(mcc),
> RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
>  }
>  
>  static void main_channel_marshall_channels(RedChannelClient *rcc,
> @@ -345,28 +136,16 @@ static void
> main_channel_marshall_channels(RedChannelClient *rcc,
>      free(channels_info);
>  }
>  
> -int main_channel_client_push_ping(MainChannelClient *mcc, int size)
> -{
> -    RedPipeItem *item;
> -
> -    if (mcc == NULL) {
> -        return FALSE;
> -    }
> -    item = red_ping_item_new(mcc, size);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -    return TRUE;
> -}
> -
>  static void main_channel_marshall_ping(RedChannelClient *rcc,
>                                         SpiceMarshaller *m,
>                                         RedPingPipeItem *item)
>  {
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient,
> base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>      SpiceMsgPing ping;
>      int size_left = item->size;
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
> -    ping.id = ++(mcc->ping_id);
> +    ping.id = main_channel_client_next_ping_id(mcc);
>      ping.timestamp = g_get_monotonic_time();
>      spice_marshall_msg_ping(m, &ping);
>  
> @@ -440,13 +219,6 @@ static void
> main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
>      spice_marshall_msg_main_agent_disconnected(m, &disconnect);
>  }
>  
> -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens)
> -{
> -    RedPipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
> -
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_tokens(RedChannelClient *rcc,
>                                           SpiceMarshaller *m,
>                                           RedTokensPipeItem *item)
>  {
> @@ -457,15 +229,6 @@ static void
> main_channel_marshall_tokens(RedChannelClient *rcc,
>      spice_marshall_msg_main_agent_token(m, &tokens);
>  }
>  
> -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> -           spice_marshaller_item_free_func free_data, void *opaque)
> -{
> -    RedPipeItem *item;
> -
> -    item = main_agent_data_item_new(&mcc->base, data, len, free_data,
> opaque);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_agent_data(RedChannelClient *rcc,
>                                               SpiceMarshaller *m,
>                                               RedAgentDataPipeItem *item)
> @@ -490,7 +253,7 @@ static void
> main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
>  static int main_channel_handle_migrate_data(RedChannelClient *rcc,
>      uint32_t size, void *message)
>  {
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient,
> base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>      SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
>  
>      /* not supported with multi-clients */
> @@ -509,21 +272,6 @@ static int
> main_channel_handle_migrate_data(RedChannelClient *rcc,
>      return reds_handle_migrate_data(rcc->channel->reds, mcc,
>      (SpiceMigrateDataMain *)(header + 1), size);
>  }
>  
> -void main_channel_client_push_init(MainChannelClient *mcc,
> -                                   int display_channels_hint,
> -                                   int current_mouse_mode,
> -                                   int is_client_mouse_allowed,
> -                                   int multi_media_time,
> -                                   int ram_hint)
> -{
> -    RedPipeItem *item;
> -
> -    item = main_init_item_new(mcc,
> -             mcc->connection_id, display_channels_hint, current_mouse_mode,
> -             is_client_mouse_allowed, multi_media_time, ram_hint);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_init(RedChannelClient *rcc,
>                                         SpiceMarshaller *m,
>                                         RedInitPipeItem *item)
> @@ -546,36 +294,6 @@ static void main_channel_marshall_init(RedChannelClient
> *rcc,
>      spice_marshall_msg_main_init(m, &init);
>  }
>  
> -void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
> -{
> -    RedPipeItem *item;
> -
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> -        return;
> -
> -    item = main_name_item_new(mcc, name);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16])
> -{
> -    RedPipeItem *item;
> -
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> -        return;
> -
> -    item = main_uuid_item_new(mcc, uuid);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -void main_channel_client_push_notify(MainChannelClient *mcc, const char
> *msg)
> -{
> -    RedPipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_notify(RedChannelClient *rcc,
>                                           SpiceMarshaller *m,
>                                           RedNotifyPipeItem *item)
>  {
> @@ -701,14 +419,16 @@ static void
> main_channel_marshall_multi_media_time(RedChannelClient *rcc,
>  
>  static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
>  {
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient,
> base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>      SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>  
>      /* In semi-seamless migration (dest side), the connection is started
>      from scratch, and
>       * we ignore any pipe item that arrives before the INIT msg is sent.
>       * For seamless we don't send INIT, and the connection continues from
>       the same place
>       * it stopped on the src side. */
> -    if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type !=
> RED_PIPE_ITEM_TYPE_MAIN_INIT) {
> +    if (!main_channel_client_get_init_sent(mcc) &&
> +        !main_channel_client_get_seamless_migration(mcc) &&
> +        base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) {
>          spice_printerr("Init msg for client %p was not sent yet "
>                         "(client is probably during semi-seamless migration).
>                         Ignoring msg type %d",
>                     rcc->client, base->type);
> @@ -745,7 +465,7 @@ static void main_channel_send_item(RedChannelClient *rcc,
> RedPipeItem *base)
>              main_channel_marshall_migrate_data_item(rcc, m, base);
>              break;
>          case RED_PIPE_ITEM_TYPE_MAIN_INIT:
> -            mcc->init_sent = TRUE;
> +            main_channel_client_on_send_init(mcc);
>              main_channel_marshall_init(rcc, m,
>                  SPICE_CONTAINEROF(base, RedInitPipeItem, base));
>              break;
> @@ -804,77 +524,11 @@ static void
> main_channel_release_pipe_item(RedChannelClient *rcc,
>      free(base);
>  }
>  
> -static void main_channel_client_handle_migrate_connected(MainChannelClient
> *mcc,
> -                                                         int success,
> -                                                         int seamless)
> -{
> -    spice_printerr("client %p connected: %d seamless %d", mcc->base.client,
> success, seamless);
> -    if (mcc->mig_wait_connect) {
> -        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel,
> MainChannel, base);
> -
> -        mcc->mig_wait_connect = FALSE;
> -        mcc->mig_connect_ok = success;
> -        spice_assert(main_channel->num_clients_mig_wait);
> -        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
> -        if (!--main_channel->num_clients_mig_wait) {
> -            reds_on_main_migrate_connected(mcc->base.channel->reds, seamless
> && success);
> -        }
> -    } else {
> -        if (success) {
> -            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> -            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> -        }
> -    }
> -}
> -
> -void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient
> *mcc,
> -                                                        uint32_t
> src_version)
> -{
> -    if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc,
> src_version)) {
> -        mcc->seamless_mig_dst = TRUE;
> -        red_channel_client_pipe_add_empty_msg(&mcc->base,
> -
> SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
> -    } else {
> -        red_channel_client_pipe_add_empty_msg(&mcc->base,
> -
> SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
> -    }
> -}
> -
> -void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
> -{
> -    if (!red_client_during_migrate_at_target(mcc->base.client)) {
> -        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
> -        return;
> -    }
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -
> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE))
> {
> -        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
> -                   "client does not support semi-seamless migration");
> -            return;
> -    }
> -    red_client_semi_seamless_migrate_complete(mcc->base.client);
> -}
> -
> -void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
> -{
> -    if (mcc->mig_wait_prev_complete) {
> -        if (mcc->mig_wait_prev_try_seamless) {
> -            spice_assert(mcc->base.channel->clients_num == 1);
> -            red_channel_client_pipe_add_type(&mcc->base,
> -
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> -        } else {
> -            red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> -        }
> -        mcc->mig_wait_connect = TRUE;
> -        mcc->mig_wait_prev_complete = FALSE;
> -    }
> -}
> -
>  static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size,
>  uint16_t type,
>                                        void *message)
>  {
>      MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel,
>      base);
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient,
> base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>  
>      switch (type) {
>      case SPICE_MSGC_MAIN_AGENT_START: {
> @@ -923,64 +577,7 @@ static int main_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, uint
>          reds_on_main_mouse_mode_request(rcc->channel->reds, message, size);
>          break;
>      case SPICE_MSGC_PONG: {
> -        SpiceMsgPing *ping = (SpiceMsgPing *)message;
> -        uint64_t roundtrip;
> -
> -        roundtrip = g_get_monotonic_time() - ping->timestamp;
> -
> -        if (ping->id == mcc->net_test_id) {
> -            switch (mcc->net_test_stage) {
> -            case NET_TEST_STAGE_WARMUP:
> -                mcc->net_test_id++;
> -                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
> -                mcc->latency = roundtrip;
> -                break;
> -            case NET_TEST_STAGE_LATENCY:
> -                mcc->net_test_id++;
> -                mcc->net_test_stage = NET_TEST_STAGE_RATE;
> -                mcc->latency = MIN(mcc->latency, roundtrip);
> -                break;
> -            case NET_TEST_STAGE_RATE:
> -                mcc->net_test_id = 0;
> -                if (roundtrip <= mcc->latency) {
> -                    // probably high load on client or server result with
> incorrect values
> -                    spice_printerr("net test: invalid values, latency %"
> PRIu64
> -                                   " roundtrip %" PRIu64 ". assuming high"
> -                                   " bandwidth", mcc->latency, roundtrip);
> -                    mcc->latency = 0;
> -                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> -
> red_channel_client_start_connectivity_monitoring(&mcc->base,
> -
> CLIENT_CONNECTIVITY_TIMEOUT);
> -                    break;
> -                }
> -                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) *
> 1000000
> -                                        / (roundtrip - mcc->latency);
> -                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
> -                spice_printerr("net test: latency %f ms, bitrate %"PRIu64"
> bps (%f Mbps)%s",
> -                           (double)mcc->latency / 1000,
> -                           mcc->bitrate_per_sec,
> -                           (double)mcc->bitrate_per_sec / 1024 / 1024,
> -                           main_channel_client_is_low_bandwidth(mcc) ? " LOW
> BANDWIDTH" : "");
> -                red_channel_client_start_connectivity_monitoring(&mcc->base,
> -
> CLIENT_CONNECTIVITY_TIMEOUT);
> -                break;
> -            default:
> -                spice_printerr("invalid net test stage, ping id %d test id
> %d stage %d",
> -                           ping->id,
> -                           mcc->net_test_id,
> -                           mcc->net_test_stage);
> -                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> -            }
> -            break;
> -        } else {
> -            /*
> -             * channel client monitors the connectivity using ping-pong
> messages
> -             */
> -            red_channel_client_handle_message(rcc, size, type, message);
> -        }
> -#ifdef RED_STATISTICS
> -        stat_update_value(rcc->channel->reds, roundtrip);
> -#endif
> +        main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size);
>          break;
>      }
>      case SPICE_MSGC_DISCONNECTING:
> @@ -999,7 +596,7 @@ static uint8_t
> *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
>                                                 uint32_t size)
>  {
>      MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel,
>      base);
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient,
> base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>  
>      if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
>          return reds_get_agent_data_buffer(rcc->channel->reds, mcc, size);
> @@ -1035,60 +632,6 @@ static int
> main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
>      return TRUE;
>  }
>  
> -#ifdef RED_STATISTICS
> -static void do_ping_client(MainChannelClient *mcc,
> -    const char *opt, int has_interval, int interval)
> -{
> -    spice_printerr("");
> -    if (!opt) {
> -        main_channel_client_push_ping(mcc, 0);
> -    } else if (!strcmp(opt, "on")) {
> -        if (has_interval && interval > 0) {
> -            mcc->ping_interval = interval * MSEC_PER_SEC;
> -        }
> -        reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer,
> mcc->ping_interval);
> -    } else if (!strcmp(opt, "off")) {
> -        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> -    } else {
> -        return;
> -    }
> -}
> -
> -static void ping_timer_cb(void *opaque)
> -{
> -    MainChannelClient *mcc = opaque;
> -
> -    if (!red_channel_client_is_connected(&mcc->base)) {
> -        spice_printerr("not connected to peer, ping off");
> -        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> -        return;
> -    }
> -    do_ping_client(mcc, NULL, 0, 0);
> -    reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer,
> mcc->ping_interval);
> -}
> -#endif /* RED_STATISTICS */
> -
> -static MainChannelClient *main_channel_client_create(MainChannel *main_chan,
> RedClient *client,
> -                                                     RedsStream *stream,
> uint32_t connection_id,
> -                                                     int num_common_caps,
> uint32_t *common_caps,
> -                                                     int num_caps, uint32_t
> *caps)
> -{
> -    MainChannelClient *mcc = (MainChannelClient*)
> -
> red_channel_client_create(sizeof(MainChannelClient),
> &main_chan->base,
> -                                                       client, stream,
> FALSE, num_common_caps,
> -                                                       common_caps,
> num_caps, caps);
> -    spice_assert(mcc != NULL);
> -    mcc->connection_id = connection_id;
> -    mcc->bitrate_per_sec = ~0;
> -#ifdef RED_STATISTICS
> -    if (!(mcc->ping_timer =
> reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb,
> mcc))) {
> -        spice_error("ping timer create failed");
> -    }
> -    mcc->ping_interval = PING_INTERVAL;
> -#endif
> -    return mcc;
> -}
> -
>  MainChannelClient *main_channel_link(MainChannel *channel, RedClient
>  *client,
>                                       RedsStream *stream, uint32_t
>                                       connection_id, int migration,
>                                       int num_common_caps, uint32_t
>                                       *common_caps, int num_caps,
> @@ -1128,33 +671,6 @@ void main_channel_close(MainChannel *main_chan)
>      }
>  }
>  
> -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
> -{
> -    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
> -}
> -
> -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
> -{
> -    // TODO: configurable?
> -    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
> -}
> -
> -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
> -{
> -    return mcc->bitrate_per_sec;
> -}
> -
> -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
> -{
> -    return mcc->latency / 1000;
> -}
> -
> -static void main_channel_client_migrate(RedChannelClient *rcc)
> -{
> -    reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc,
> MainChannelClient, base));
> -    red_channel_client_default_migrate(rcc);
> -}
> -
>  MainChannel* main_channel_new(RedsState *reds)
>  {
>      RedChannel *channel;
> @@ -1190,33 +706,16 @@ MainChannel* main_channel_new(RedsState *reds)
>      return (MainChannel *)channel;
>  }
>  
> -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
> -{
> -    spice_assert(mcc);
> -    return &mcc->base;
> -}
> -
>  static int main_channel_connect_semi_seamless(MainChannel *main_channel)
>  {
>      RingItem *client_link;
>  
>      RING_FOREACH(client_link, &main_channel->base.clients) {
> -        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link,
> MainChannelClient,
> -                                                    base.channel_link);
> -        if (red_channel_client_test_remote_cap(&mcc->base,
> -
> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE))
> {
> -            if (red_client_during_migrate_at_target(mcc->base.client)) {
> -                spice_printerr("client %p: wait till previous migration
> completes", mcc->base.client);
> -                mcc->mig_wait_prev_complete = TRUE;
> -                mcc->mig_wait_prev_try_seamless = FALSE;
> -            } else {
> -                red_channel_client_pipe_add_type(&mcc->base,
> -
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> -                mcc->mig_wait_connect = TRUE;
> -            }
> -            mcc->mig_connect_ok = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        if (main_channel_client_connect_semi_seamless(mcc))
>              main_channel->num_clients_mig_wait++;
> -        }
>      }
>      return main_channel->num_clients_mig_wait;
>  }
> @@ -1228,20 +727,10 @@ static int main_channel_connect_seamless(MainChannel
> *main_channel)
>      spice_assert(main_channel->base.clients_num == 1);
>  
>      RING_FOREACH(client_link, &main_channel->base.clients) {
> -        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link,
> MainChannelClient,
> -                                                    base.channel_link);
> -        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
> -
> SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
> -        if (red_client_during_migrate_at_target(mcc->base.client)) {
> -           spice_printerr("client %p: wait till previous migration
> completes", mcc->base.client);
> -           mcc->mig_wait_prev_complete = TRUE;
> -           mcc->mig_wait_prev_try_seamless = TRUE;
> -        } else {
> -            red_channel_client_pipe_add_type(&mcc->base,
> -
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> -            mcc->mig_wait_connect = TRUE;
> -        }
> -        mcc->mig_connect_ok = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        main_channel_client_connect_seamless(mcc);
>          main_channel->num_clients_mig_wait++;
>      }
>      return main_channel->num_clients_mig_wait;
> @@ -1261,12 +750,12 @@ int main_channel_migrate_connect(MainChannel
> *main_channel, RedsMigSpice *mig_ta
>          return main_channel_connect_semi_seamless(main_channel);
>      } else {
>          RingItem *client_item;
> -        MainChannelClient *mcc;
> +        RedChannelClient *rcc;
>  
>          client_item = ring_get_head(&main_channel->base.clients);
> -        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient,
> base.channel_link);
> +        rcc = SPICE_CONTAINEROF(client_item, RedChannelClient,
> channel_link);
>  
> -        if (!red_channel_client_test_remote_cap(&mcc->base,
> +        if (!red_channel_client_test_remote_cap(rcc,
>                                                  SPICE_MAIN_CAP_SEAMLESS_MIGRATE))
>                                                  {
>              return main_channel_connect_semi_seamless(main_channel);
>          } else {
> @@ -1281,15 +770,10 @@ void main_channel_migrate_cancel_wait(MainChannel
> *main_chan)
>      RingItem *client_link;
>  
>      RING_FOREACH(client_link, &main_chan->base.clients) {
> -        MainChannelClient *mcc;
> -
> -        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> base.channel_link);
> -        if (mcc->mig_wait_connect) {
> -            spice_printerr("client %p cancel wait connect",
> mcc->base.client);
> -            mcc->mig_wait_connect = FALSE;
> -            mcc->mig_connect_ok = FALSE;
> -        }
> -        mcc->mig_wait_prev_complete = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        main_channel_client_migrate_cancel_wait(mcc);
>      }
>      main_chan->num_clients_mig_wait = 0;
>  }
> @@ -1307,29 +791,11 @@ int main_channel_migrate_src_complete(MainChannel
> *main_chan, int success)
>      }
>  
>      RING_FOREACH(client_link, &main_chan->base.clients) {
> -        MainChannelClient *mcc;
> -        int semi_seamless_support;
> -
> -        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> base.channel_link);
> -        semi_seamless_support =
> red_channel_client_test_remote_cap(&mcc->base,
> -
> SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> -        if (semi_seamless_support && mcc->mig_connect_ok) {
> -            if (success) {
> -                spice_printerr("client %p MIGRATE_END", mcc->base.client);
> -                red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_END);
> -                semi_seamless_count++;
> -            } else {
> -                spice_printerr("client %p MIGRATE_CANCEL",
> mcc->base.client);
> -                red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> -            }
> -        } else {
> -            if (success) {
> -                spice_printerr("client %p SWITCH_HOST", mcc->base.client);
> -                red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> -            }
> -        }
> -        mcc->mig_connect_ok = FALSE;
> -        mcc->mig_wait_connect = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        if (main_channel_client_migrate_src_complete(mcc, success))
> +            semi_seamless_count++;
>     }
>     return semi_seamless_count;
>  }
> diff --git a/server/main-channel.h b/server/main-channel.h
> index 4eb9a9c..868a14a 100644
> --- a/server/main-channel.h
> +++ b/server/main-channel.h
> @@ -23,6 +23,7 @@
>  #include <common/marshaller.h>
>  
>  #include "red-channel.h"
> +#include "main-channel-client.h"
>  
>  // TODO: Defines used to calculate receive buffer size, and also by reds.c
>  // other options: is to make a reds_main_consts.h, to duplicate defines.
> @@ -59,34 +60,11 @@ void main_channel_close(MainChannel *main_chan); // not
> destroy, just socket clo
>  void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
>  int is_client_mouse_allowed);
>  void main_channel_push_agent_connected(MainChannel *main_chan);
>  void main_channel_push_agent_disconnected(MainChannel *main_chan);
> -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens);
> -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> -                                         spice_marshaller_item_free_func
> free_data, void *opaque);
> -void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate);
> -// TODO: huge. Consider making a reds_* interface for these functions
> -// and calling from main.
> -void main_channel_client_push_init(MainChannelClient *mcc,
> -                                   int display_channels_hint,
> -                                   int current_mouse_mode,
> -                                   int is_client_mouse_allowed,
> -                                   int multi_media_time,
> -                                   int ram_hint);
> -void main_channel_client_push_notify(MainChannelClient *mcc, const char
> *msg);
>  void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
>  int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa,
>  socklen_t *salen);
>  int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa,
>  socklen_t *salen);
>  
> -/*
> - * return TRUE if network test had been completed successfully.
> - * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set
> to 0
> - */
> -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
> -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
> -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
> -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
> -
>  int main_channel_is_connected(MainChannel *main_chan);
> -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
>  
>  /* switch host migration */
>  void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice
>  *mig_target);
> @@ -100,8 +78,5 @@ int main_channel_migrate_connect(MainChannel
> *main_channel, RedsMigSpice *mig_ta
>  void main_channel_migrate_cancel_wait(MainChannel *main_chan);
>  /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was
>  sent*/
>  int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
> -void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
> -void main_channel_client_push_name(MainChannelClient *mcc, const char
> *name);
> -void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16]);
>  
>  #endif

Beside these small changes patch looks good

Acked.

Frediano
On Thu, 2016-05-12 at 15:45 -0500, Jonathon Jongsma wrote:
> Preparation for converting to GObject
> ---
>  server/Makefile.am           |   2 +
>  server/inputs-channel.c      |   2 +-
>  server/main-channel-client.c | 549 ++++++++++++++++++++++++++++++++++++++
>  server/main-channel-client.h | 153 +++++++++++
>  server/main-channel.c        | 616 +++---------------------------------------
> -
>  server/main-channel.h        |  27 +-
>  6 files changed, 747 insertions(+), 602 deletions(-)
>  create mode 100644 server/main-channel-client.c
>  create mode 100644 server/main-channel-client.h
> 
> diff --git a/server/Makefile.am b/server/Makefile.am
> index fbf4638..5a5c7e8 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -86,6 +86,8 @@ libserver_la_SOURCES =				\
>  	lz4-encoder.h				\
>  	main-channel.c				\
>  	main-channel.h				\
> +	main-channel-client.c			\
> +	main-channel-client.h			\
>  	mjpeg-encoder.c				\
>  	red-channel.c				\
>  	red-channel.h				\
> diff --git a/server/inputs-channel.c b/server/inputs-channel.c
> index 0ce12de..584204f 100644
> --- a/server/inputs-channel.c
> +++ b/server/inputs-channel.c
> @@ -39,7 +39,7 @@
>  #include "reds.h"
>  #include "reds-stream.h"
>  #include "red-channel.h"
> -#include "main-channel.h"
> +#include "main-channel-client.h"
>  #include "inputs-channel.h"
>  #include "migration-protocol.h"
>  #include "utils.h"
> diff --git a/server/main-channel-client.c b/server/main-channel-client.c
> new file mode 100644
> index 0000000..bac5316
> --- /dev/null
> +++ b/server/main-channel-client.c
> @@ -0,0 +1,549 @@
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library 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.1 of the License, or (at your option) any later version.
> +
> +   This library 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 this library; if not, see <http://www.gnu.org/licenses/
> >.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <inttypes.h>
> +#include "main-channel-client.h"
> +#include "main-channel-client.h"

Doubled ^

> +#include "main-channel.h"
> +#include "reds.h"
> +
> +#define NET_TEST_WARMUP_BYTES 0
> +#define NET_TEST_BYTES (1024 * 250)
> +
> +enum NetTestStage {
> +    NET_TEST_STAGE_INVALID,
> +    NET_TEST_STAGE_WARMUP,
> +    NET_TEST_STAGE_LATENCY,
> +    NET_TEST_STAGE_RATE,
> +    NET_TEST_STAGE_COMPLETE,
> +};
> +
> +#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
> +#define PING_INTERVAL (MSEC_PER_SEC * 10)
> +
> +struct MainChannelClient {
> +    RedChannelClient base;
> +    uint32_t connection_id;
> +    uint32_t ping_id;
> +    uint32_t net_test_id;
> +    int net_test_stage;
> +    uint64_t latency;
> +    uint64_t bitrate_per_sec;
> +#ifdef RED_STATISTICS
> +    SpiceTimer *ping_timer;
> +    int ping_interval;
> +#endif
> +    int mig_wait_connect;
> +    int mig_connect_ok;
> +    int mig_wait_prev_complete;
> +    int mig_wait_prev_try_seamless;
> +    int init_sent;
> +    int seamless_mig_dst;
> +};
> +
> +static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
> +
> +static RedPipeItem *main_notify_item_new(void *data, int num)
> +{
> +    RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
> +    const char *msg = data;
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
> +    item->msg = spice_strdup(msg);
> +    return &item->base;
> +}
> +
> +void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate)
> +{
> +    if (!mcc || mcc->net_test_id) {
> +        return;
> +    }
> +    if (test_rate) {
> +        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
> +            && main_channel_client_push_ping(mcc, 0)
> +            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
> +            mcc->net_test_id = mcc->ping_id - 2;
> +            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
> +        }
> +    } else {
> +        red_channel_client_start_connectivity_monitoring(&mcc->base,
> CLIENT_CONNECTIVITY_TIMEOUT);
> +    }
> +}
> +
> +static RedPipeItem *red_ping_item_new(int size)
> +{
> +    RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
> +    item->size = size;
> +    return &item->base;
> +}
> +
> +static int main_channel_client_push_ping(MainChannelClient *mcc, int size)
> +{
> +    RedPipeItem *item;
> +
> +    if (mcc == NULL) {
> +        return FALSE;
> +    }
> +    item = red_ping_item_new(size);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +    return TRUE;
> +}
> +
> +static RedPipeItem *main_agent_tokens_item_new(uint32_t num_tokens)
> +{
> +    RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
> +    item->tokens = num_tokens;
> +    return &item->base;
> +}
> +
> +
> +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens)
> +{
> +    RedPipeItem *item = main_agent_tokens_item_new(num_tokens);
> +
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_agent_data_item_new(uint8_t* data, size_t len,
> +                                             spice_marshaller_item_free_func
> free_data,
> +                                             void *opaque)
> +{
> +    RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
> +    item->data = data;
> +    item->len = len;
> +    item->free_data = free_data;
> +    item->opaque = opaque;
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> +           spice_marshaller_item_free_func free_data, void *opaque)
> +{
> +    RedPipeItem *item;
> +
> +    item = main_agent_data_item_new(data, len, free_data, opaque);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_init_item_new(int connection_id,
> +                                       int display_channels_hint,
> +                                       int current_mouse_mode,
> +                                       int is_client_mouse_allowed,
> +                                       int multi_media_time,
> +                                       int ram_hint)
> +{
> +    RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT);
> +    item->connection_id = connection_id;
> +    item->display_channels_hint = display_channels_hint;
> +    item->current_mouse_mode = current_mouse_mode;
> +    item->is_client_mouse_allowed = is_client_mouse_allowed;
> +    item->multi_media_time = multi_media_time;
> +    item->ram_hint = ram_hint;
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_init(MainChannelClient *mcc,
> +                                   int display_channels_hint,
> +                                   int current_mouse_mode,
> +                                   int is_client_mouse_allowed,
> +                                   int multi_media_time,
> +                                   int ram_hint)
> +{
> +    RedPipeItem *item;
> +
> +    item = main_init_item_new(mcc->connection_id, display_channels_hint,
> +                              current_mouse_mode, is_client_mouse_allowed,
> +                              multi_media_time, ram_hint);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_name_item_new(const char *name)
> +{
> +    RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) +
> strlen(name) + 1);
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
> +    item->msg.name_len = strlen(name) + 1;
> +    memcpy(&item->msg.name, name, item->msg.name_len);
> +
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
> +{
> +    RedPipeItem *item;
> +
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> +        return;
> +
> +    item = main_name_item_new(name);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static RedPipeItem *main_uuid_item_new(const uint8_t uuid[16])
> +{
> +    RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
> +
> +    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
> +    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
> +
> +    return &item->base;
> +}
> +
> +void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16])
> +{
> +    RedPipeItem *item;
> +
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> +        return;
> +
> +    item = main_uuid_item_new(uuid);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
> +{
> +    RedPipeItem *item = main_notify_item_new((void *)msg, 1);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
> +                                                  int success,
> +                                                  int seamless)
> +{
> +    spice_printerr("client %p connected: %d seamless %d", mcc->base.client,
> success, seamless);
> +    if (mcc->mig_wait_connect) {
> +        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel,
> MainChannel, base);
> +
> +        mcc->mig_wait_connect = FALSE;
> +        mcc->mig_connect_ok = success;
> +        spice_assert(main_channel->num_clients_mig_wait);
> +        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
> +        if (!--main_channel->num_clients_mig_wait) {
> +            reds_on_main_migrate_connected(mcc->base.channel->reds, seamless
> && success);
> +        }
> +    } else {
> +        if (success) {
> +            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> +            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> +        }
> +    }
> +}
> +
> +void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient
> *mcc,
> +                                                        uint32_t src_version)
> +{
> +    if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc,
> src_version)) {
> +        mcc->seamless_mig_dst = TRUE;
> +        red_channel_client_pipe_add_empty_msg(&mcc->base,
> +                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAML
> ESS_ACK);
> +    } else {
> +        red_channel_client_pipe_add_empty_msg(&mcc->base,
> +                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAM
> LESS_NACK);
> +    }
> +}
> +void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing
> *ping, uint32_t size)
> +{
> +    uint64_t roundtrip;
> +    RedChannelClient* rcc = (RedChannelClient*)mcc;
> +
> +    roundtrip = g_get_monotonic_time() - ping->timestamp;
> +
> +    if (ping->id == mcc->net_test_id) {
> +        switch (mcc->net_test_stage) {
> +            case NET_TEST_STAGE_WARMUP:
> +                mcc->net_test_id++;
> +                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
> +                mcc->latency = roundtrip;
> +                break;
> +            case NET_TEST_STAGE_LATENCY:
> +                mcc->net_test_id++;
> +                mcc->net_test_stage = NET_TEST_STAGE_RATE;
> +                mcc->latency = MIN(mcc->latency, roundtrip);
> +                break;
> +            case NET_TEST_STAGE_RATE:
> +                mcc->net_test_id = 0;
> +                if (roundtrip <= mcc->latency) {
> +                    // probably high load on client or server result with
> incorrect values
> +                    spice_printerr("net test: invalid values, latency %"
> PRIu64
> +                                   " roundtrip %" PRIu64 ". assuming high"
> +                                   "bandwidth", mcc->latency, roundtrip);
> +                    mcc->latency = 0;
> +                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> +                    red_channel_client_start_connectivity_monitoring(&mcc-
> >base,
> +                                                                     CLIENT_C
> ONNECTIVITY_TIMEOUT);
> +                    break;
> +                }
> +                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) *
> 1000000
> +                    / (roundtrip - mcc->latency);
> +                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
> +                spice_printerr("net test: latency %f ms, bitrate %"PRIu64"
> bps (%f Mbps)%s",
> +                               (double)mcc->latency / 1000,
> +                               mcc->bitrate_per_sec,
> +                               (double)mcc->bitrate_per_sec / 1024 / 1024,
> +                               main_channel_client_is_low_bandwidth(mcc) ? "
> LOW BANDWIDTH" : "");
> +                red_channel_client_start_connectivity_monitoring(&mcc->base,
> +                                                                 CLIENT_CONNE
> CTIVITY_TIMEOUT);
> +                break;
> +            default:
> +                spice_printerr("invalid net test stage, ping id %d test id %d
> stage %d",
> +                               ping->id,
> +                               mcc->net_test_id,
> +                               mcc->net_test_stage);
> +                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> +        }
> +        return;
> +    } else {
> +        /*
> +         * channel client monitors the connectivity using ping-pong messages
> +         */
> +        red_channel_client_handle_message(rcc, size, SPICE_MSGC_PONG, ping);
> +    }
> +#ifdef RED_STATISTICS
> +    stat_update_value(rcc->channel->reds, roundtrip);
> +#endif
> +}
> +
> +gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc)
> +{
> +    return mcc->seamless_mig_dst;
> +}
> +
> +void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
> +{
> +    if (!red_client_during_migrate_at_target(mcc->base.client)) {
> +        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
> +        return;
> +    }
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGR
> ATE)) {
> +        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
> +                   "client does not support semi-seamless migration");
> +            return;
> +    }
> +    red_client_semi_seamless_migrate_complete(mcc->base.client);
> +}
> +
> +void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc)
> +{
> +    if (mcc->mig_wait_connect) {
> +        spice_printerr("client %p cancel wait connect", mcc->base.client);
> +        mcc->mig_wait_connect = FALSE;
> +        mcc->mig_connect_ok = FALSE;
> +    }
> +    mcc->mig_wait_prev_complete = FALSE;
> +}
> +
> +void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
> +{
> +    if (mcc->mig_wait_prev_complete) {
> +        if (mcc->mig_wait_prev_try_seamless) {
> +            spice_assert(mcc->base.channel->clients_num == 1);
> +            red_channel_client_pipe_add_type(&mcc->base,
> +                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_
> BEGIN_SEAMLESS);
> +        } else {
> +            red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> +        }
> +        mcc->mig_wait_connect = TRUE;
> +        mcc->mig_wait_prev_complete = FALSE;
> +    }
> +}
> +
> +gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
> +                                                  gboolean success)
> +{
> +    gboolean ret = FALSE;
> +    int semi_seamless_support = red_channel_client_test_remote_cap(&mcc-
> >base,
> +                                                                   SPICE_MAIN
> _CAP_SEMI_SEAMLESS_MIGRATE);
> +    if (semi_seamless_support && mcc->mig_connect_ok) {
> +        if (success) {
> +            spice_printerr("client %p MIGRATE_END", mcc->base.client);
> +            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_END);
> +            ret = TRUE;
> +        } else {
> +            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> +            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> +        }
> +    } else {
> +        if (success) {
> +            spice_printerr("client %p SWITCH_HOST", mcc->base.client);
> +            red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> +        }
> +    }
> +    mcc->mig_connect_ok = FALSE;
> +    mcc->mig_wait_connect = FALSE;
> +
> +    return ret;
> +}
> +
> +#ifdef RED_STATISTICS
> +static void do_ping_client(MainChannelClient *mcc,
> +    const char *opt, int has_interval, int interval)
> +{
> +    spice_printerr("");
> +    if (!opt) {
> +        main_channel_client_push_ping(mcc, 0);
> +    } else if (!strcmp(opt, "on")) {
> +        if (has_interval && interval > 0) {
> +            mcc->ping_interval = interval * MSEC_PER_SEC;
> +        }
> +        reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc-
> >ping_interval);
> +    } else if (!strcmp(opt, "off")) {
> +        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> +    } else {
> +        return;
> +    }
> +}
> +
> +static void ping_timer_cb(void *opaque)
> +{
> +    MainChannelClient *mcc = opaque;
> +
> +    if (!red_channel_client_is_connected(&mcc->base)) {
> +        spice_printerr("not connected to peer, ping off");
> +        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> +        return;
> +    }
> +    do_ping_client(mcc, NULL, 0, 0);
> +    reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc-
> >ping_interval);
> +}
> +#endif /* RED_STATISTICS */
> +
> +MainChannelClient *main_channel_client_create(MainChannel *main_chan,
> RedClient *client,
> +                                              RedsStream *stream, uint32_t
> connection_id,
> +                                              int num_common_caps, uint32_t
> *common_caps,
> +                                              int num_caps, uint32_t *caps)
> +{
> +    MainChannelClient *mcc = (MainChannelClient*)
> +                             red_channel_client_create(sizeof(MainChannelClie
> nt), &main_chan->base,
> +                                                       client, stream, FALSE,
> num_common_caps,
> +                                                       common_caps, num_caps,
> caps);
> +    spice_assert(mcc != NULL);
> +    mcc->connection_id = connection_id;
> +    mcc->bitrate_per_sec = ~0;
> +#ifdef RED_STATISTICS
> +    if (!(mcc->ping_timer =
> reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb,
> mcc))) {
> +        spice_error("ping timer create failed");
> +    }
> +    mcc->ping_interval = PING_INTERVAL;
> +#endif
> +    return mcc;
> +}
> +
> +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
> +{
> +    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
> +}
> +
> +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
> +{
> +    // TODO: configurable?
> +    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
> +}
> +
> +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
> +{
> +    return mcc->bitrate_per_sec;
> +}
> +
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
> +{
> +    return mcc->latency / 1000;
> +}
> +
> +void main_channel_client_migrate(RedChannelClient *rcc)
> +{
> +    reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc,
> MainChannelClient, base));
> +    red_channel_client_default_migrate(rcc);
> +}
> +
> +gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc)
> +{
> +    RedChannelClient *rcc = main_channel_client_get_base(mcc);
> +    MainChannel* main_channel = SPICE_CONTAINEROF(rcc->channel, MainChannel,
> base);
> +    if (red_channel_client_test_remote_cap(rcc,
> +                                           SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRA
> TE)) {
> +        RedClient *client = red_channel_client_get_client(rcc);
> +        if (red_client_during_migrate_at_target(client)) {
> +            spice_printerr("client %p: wait till previous migration
> completes", client);
> +            mcc->mig_wait_prev_complete = TRUE;
> +            mcc->mig_wait_prev_try_seamless = FALSE;
> +        } else {
> +            red_channel_client_pipe_add_type(rcc,
> +                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_
> BEGIN);
> +            mcc->mig_wait_connect = TRUE;
> +        }
> +        mcc->mig_connect_ok = FALSE;
> +        main_channel->num_clients_mig_wait++;
> +        return TRUE;
> +    }
> +    return FALSE;
> +}
> +
> +void main_channel_client_connect_seamless(MainChannelClient *mcc)
> +{
> +    spice_assert(red_channel_client_test_remote_cap(&mcc->base,
> +                                                    SPICE_MAIN_CAP_SEAMLESS_M
> IGRATE));
> +    if (red_client_during_migrate_at_target(mcc->base.client)) {
> +        spice_printerr("client %p: wait till previous migration completes",
> mcc->base.client);
> +        mcc->mig_wait_prev_complete = TRUE;
> +        mcc->mig_wait_prev_try_seamless = TRUE;
> +    } else {
> +        red_channel_client_pipe_add_type(&mcc->base,
> +                                         RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGI
> N_SEAMLESS);
> +        mcc->mig_wait_connect = TRUE;
> +    }
> +    mcc->mig_connect_ok = FALSE;
> +}
> +
> +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
> +{
> +    spice_assert(mcc);
> +    return &mcc->base;
> +}
> +
> +uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc)
> +{
> +    return mcc->connection_id;
> +}
> +
> +uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc)
> +{
> +    return ++mcc->ping_id;
> +}
> +
> +void main_channel_client_on_send_init(MainChannelClient *mcc)
> +{
> +    mcc->init_sent = TRUE;
> +}
> +
> +gboolean main_channel_client_get_init_sent(MainChannelClient *mcc)
> +{
> +    return mcc->init_sent;
> +}
> diff --git a/server/main-channel-client.h b/server/main-channel-client.h
> new file mode 100644
> index 0000000..7e4daf9
> --- /dev/null
> +++ b/server/main-channel-client.h
> @@ -0,0 +1,153 @@
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library 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.1 of the License, or (at your option) any later version.
> +
> +   This library 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 this library; if not, see <http://www.gnu.org/licenses/
> >.
> +*/
> +#ifndef __MAIN_CHANNEL_CLIENT_H__
> +#define __MAIN_CHANNEL_CLIENT_H__
> +
> +#include "red-channel.h"
> +
> +typedef struct MainChannel MainChannel;
> +typedef struct MainChannelClient MainChannelClient;

typedefs ^, discussed in another patch. Consider adding a FIXME. 

Overall looks good,

Ack from me

> +
> +MainChannelClient *main_channel_client_create(MainChannel *main_chan,
> RedClient *client,
> +                                              RedsStream *stream, uint32_t
> connection_id,
> +                                              int num_common_caps, uint32_t
> *common_caps,
> +                                              int num_caps, uint32_t *caps);
> +
> +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens);
> +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> +                                         spice_marshaller_item_free_func
> free_data, void *opaque);
> +void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate);
> +// TODO: huge. Consider making a reds_* interface for these functions
> +// and calling from main.
> +void main_channel_client_push_init(MainChannelClient *mcc,
> +                                   int display_channels_hint,
> +                                   int current_mouse_mode,
> +                                   int is_client_mouse_allowed,
> +                                   int multi_media_time,
> +                                   int ram_hint);
> +void main_channel_client_push_notify(MainChannelClient *mcc, const char
> *msg);
> +void main_channel_client_migrate(RedChannelClient *rcc);
> +gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc);
> +void main_channel_client_connect_seamless(MainChannelClient *mcc);
> +void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
> +                                                  int success,
> +                                                  int seamless);
> +void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient
> *mcc,
> +                                                        uint32_t
> src_version);
> +void main_channel_client_handle_migrate_end(MainChannelClient *mcc);
> +void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc);
> +void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
> +gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc,
> +                                                  gboolean success);
> +
> +void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing
> *ping, uint32_t size);
> +
> +/*
> + * return TRUE if network test had been completed successfully.
> + * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to
> 0
> + */
> +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
> +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
> +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
> +
> +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
> +
> +void main_channel_client_push_name(MainChannelClient *mcc, const char *name);
> +void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16]);
> +
> +uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc);
> +uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc);
> +
> +gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc);
> +void main_channel_client_on_send_init(MainChannelClient *mcc);
> +gboolean main_channel_client_get_init_sent(MainChannelClient *mcc);
> +
> +enum {
> +    RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
> +    RED_PIPE_ITEM_TYPE_MAIN_PING,
> +    RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
> +    RED_PIPE_ITEM_TYPE_MAIN_INIT,
> +    RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
> +    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
> +    RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
> +    RED_PIPE_ITEM_TYPE_MAIN_NAME,
> +    RED_PIPE_ITEM_TYPE_MAIN_UUID,
> +    RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
> +};
> +
> +typedef struct RedPingPipeItem {
> +    RedPipeItem base;
> +    int size;
> +} RedPingPipeItem;
> +
> +typedef struct RedMouseModePipeItem {
> +    RedPipeItem base;
> +    int current_mode;
> +    int is_client_mouse_allowed;
> +} RedMouseModePipeItem;
> +
> +typedef struct RedTokensPipeItem {
> +    RedPipeItem base;
> +    int tokens;
> +} RedTokensPipeItem;
> +
> +typedef struct RedAgentDataPipeItem {
> +    RedPipeItem base;
> +    uint8_t* data;
> +    size_t len;
> +    spice_marshaller_item_free_func free_data;
> +    void *opaque;
> +} RedAgentDataPipeItem;
> +
> +typedef struct RedInitPipeItem {
> +    RedPipeItem base;
> +    int connection_id;
> +    int display_channels_hint;
> +    int current_mouse_mode;
> +    int is_client_mouse_allowed;
> +    int multi_media_time;
> +    int ram_hint;
> +} RedInitPipeItem;
> +
> +typedef struct RedNamePipeItem {
> +    RedPipeItem base;
> +    SpiceMsgMainName msg;
> +} RedNamePipeItem;
> +
> +typedef struct RedUuidPipeItem {
> +    RedPipeItem base;
> +    SpiceMsgMainUuid msg;
> +} RedUuidPipeItem;
> +
> +typedef struct RedNotifyPipeItem {
> +    RedPipeItem base;
> +    char *msg;
> +} RedNotifyPipeItem;
> +
> +typedef struct RedMultiMediaTimePipeItem {
> +    RedPipeItem base;
> +    int time;
> +} RedMultiMediaTimePipeItem;
> +
> +#endif /* __MAIN_CHANNEL_CLIENT_H__ */
> diff --git a/server/main-channel.c b/server/main-channel.c
> index 98ce660..7ec8b55 100644
> --- a/server/main-channel.c
> +++ b/server/main-channel.c
> @@ -41,6 +41,7 @@
>  
>  #include "demarshallers.h"
>  #include "main-channel.h"
> +#include "main-channel-client.h"
>  #include "red-channel.h"
>  #include "red-common.h"
>  #include "reds.h"
> @@ -50,121 +51,8 @@
>  
>  #define ZERO_BUF_SIZE 4096
>  
> -#define NET_TEST_WARMUP_BYTES 0
> -#define NET_TEST_BYTES (1024 * 250)
> -
> -#define PING_INTERVAL (MSEC_PER_SEC * 10)
> -
> -#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30)
> -
>  static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
>  
> -enum {
> -    RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
> -    RED_PIPE_ITEM_TYPE_MAIN_PING,
> -    RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
> -    RED_PIPE_ITEM_TYPE_MAIN_INIT,
> -    RED_PIPE_ITEM_TYPE_MAIN_NOTIFY,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
> -    RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
> -    RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
> -    RED_PIPE_ITEM_TYPE_MAIN_NAME,
> -    RED_PIPE_ITEM_TYPE_MAIN_UUID,
> -    RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
> -};
> -
> -typedef struct RedRefsPipeItem {
> -    RedPipeItem base;
> -    int *refs;
> -} RedRefsPipeItem;
> -
> -typedef struct RedPingPipeItem {
> -    RedPipeItem base;
> -    int size;
> -} RedPingPipeItem;
> -
> -typedef struct RedMouseModePipeItem {
> -    RedPipeItem base;
> -    int current_mode;
> -    int is_client_mouse_allowed;
> -} RedMouseModePipeItem;
> -
> -typedef struct RedTokensPipeItem {
> -    RedPipeItem base;
> -    int tokens;
> -} RedTokensPipeItem;
> -
> -typedef struct RedAgentDataPipeItem {
> -    RedPipeItem base;
> -    uint8_t* data;
> -    size_t len;
> -    spice_marshaller_item_free_func free_data;
> -    void *opaque;
> -} RedAgentDataPipeItem;
> -
> -typedef struct RedInitPipeItem {
> -    RedPipeItem base;
> -    int connection_id;
> -    int display_channels_hint;
> -    int current_mouse_mode;
> -    int is_client_mouse_allowed;
> -    int multi_media_time;
> -    int ram_hint;
> -} RedInitPipeItem;
> -
> -typedef struct RedNamePipeItem {
> -    RedPipeItem base;
> -    SpiceMsgMainName msg;
> -} RedNamePipeItem;
> -
> -typedef struct RedUuidPipeItem {
> -    RedPipeItem base;
> -    SpiceMsgMainUuid msg;
> -} RedUuidPipeItem;
> -
> -typedef struct RedNotifyPipeItem {
> -    RedPipeItem base;
> -    char *msg;
> -} RedNotifyPipeItem;
> -
> -typedef struct RedMultiMediaTimePipeItem {
> -    RedPipeItem base;
> -    int time;
> -} RedMultiMediaTimePipeItem;
> -
> -struct MainChannelClient {
> -    RedChannelClient base;
> -    uint32_t connection_id;
> -    uint32_t ping_id;
> -    uint32_t net_test_id;
> -    int net_test_stage;
> -    uint64_t latency;
> -    uint64_t bitrate_per_sec;
> -#ifdef RED_STATISTICS
> -    SpiceTimer *ping_timer;
> -    int ping_interval;
> -#endif
> -    int mig_wait_connect;
> -    int mig_connect_ok;
> -    int mig_wait_prev_complete;
> -    int mig_wait_prev_try_seamless;
> -    int init_sent;
> -    int seamless_mig_dst;
> -};
> -
> -enum NetTestStage {
> -    NET_TEST_STAGE_INVALID,
> -    NET_TEST_STAGE_WARMUP,
> -    NET_TEST_STAGE_LATENCY,
> -    NET_TEST_STAGE_RATE,
> -    NET_TEST_STAGE_COMPLETE,
> -};
> -
>  static void main_channel_release_pipe_item(RedChannelClient *rcc,
>                                             RedPipeItem *base, int
> item_pushed);
>  
> @@ -187,35 +75,18 @@ RedClient *main_channel_get_client_by_link_id(MainChannel
> *main_chan, uint32_t c
>  {
>      RingItem *link;
>      MainChannelClient *mcc;
> +    RedChannelClient *rcc;
>  
>      RING_FOREACH(link, &main_chan->base.clients) {
> -        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
> -        if (mcc->connection_id == connection_id) {
> -            return mcc->base.client;
> +        rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link);
> +        mcc = (MainChannelClient*) rcc;
> +        if (main_channel_client_get_connection_id(mcc) == connection_id) {
> +            return rcc->client;
>          }
>      }
>      return NULL;
>  }
>  
> -static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
> -
> -void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate)
> -{
> -    if (!mcc || mcc->net_test_id) {
> -        return;
> -    }
> -    if (test_rate) {
> -        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
> -            && main_channel_client_push_ping(mcc, 0)
> -            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
> -            mcc->net_test_id = mcc->ping_id - 2;
> -            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
> -        }
> -    } else {
> -        red_channel_client_start_connectivity_monitoring(&mcc->base,
> CLIENT_CONNECTIVITY_TIMEOUT);
> -    }
> -}
> -
>  typedef struct MainMouseModeItemInfo {
>      int current_mode;
>      int is_client_mouse_allowed;
> @@ -232,88 +103,8 @@ static RedPipeItem
> *main_mouse_mode_item_new(RedChannelClient *rcc, void *data,
>      return &item->base;
>  }
>  
> -static RedPipeItem *red_ping_item_new(MainChannelClient *mcc, int size)
> -{
> -    RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING);
> -    item->size = size;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_agent_tokens_item_new(RedChannelClient *rcc,
> uint32_t num_tokens)
> -{
> -    RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
> -    item->tokens = num_tokens;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t*
> data, size_t len,
> -                                             spice_marshaller_item_free_func
> free_data,
> -                                             void *opaque)
> -{
> -    RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
> -    item->data = data;
> -    item->len = len;
> -    item->free_data = free_data;
> -    item->opaque = opaque;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_init_item_new(MainChannelClient *mcc,
> -    int connection_id, int display_channels_hint, int current_mouse_mode,
> -    int is_client_mouse_allowed, int multi_media_time,
> -    int ram_hint)
> -{
> -    RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT);
> -    item->connection_id = connection_id;
> -    item->display_channels_hint = display_channels_hint;
> -    item->current_mouse_mode = current_mouse_mode;
> -    item->is_client_mouse_allowed = is_client_mouse_allowed;
> -    item->multi_media_time = multi_media_time;
> -    item->ram_hint = ram_hint;
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_name_item_new(MainChannelClient *mcc, const char
> *name)
> -{
> -    RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) +
> strlen(name) + 1);
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME);
> -    item->msg.name_len = strlen(name) + 1;
> -    memcpy(&item->msg.name, name, item->msg.name_len);
> -
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t
> uuid[16])
> -{
> -    RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem));
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID);
> -    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
> -
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_notify_item_new(RedChannelClient *rcc, void *data,
> int num)
> -{
> -    RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem));
> -    const char *msg = data;
> -
> -    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY);
> -    item->msg = spice_strdup(msg);
> -    return &item->base;
> -}
> -
> -static RedPipeItem *main_multi_media_time_item_new(
> -    RedChannelClient *rcc, void *data, int num)
> +static RedPipeItem *main_multi_media_time_item_new(RedChannelClient *rcc,
> +                                                   void *data, int num)
>  {
>      RedMultiMediaTimePipeItem *item, *info = data;
>  
> @@ -325,12 +116,12 @@ static RedPipeItem *main_multi_media_time_item_new(
>  
>  static void main_channel_push_channels(MainChannelClient *mcc)
>  {
> -    if (red_client_during_migrate_at_target(mcc->base.client)) {
> +    if
> (red_client_during_migrate_at_target((main_channel_client_get_base(mcc))-
> >client)) {
>          spice_printerr("warning: ignoring unexpected
> SPICE_MSGC_MAIN_ATTACH_CHANNELS"
>                     "during migration");
>          return;
>      }
> -    red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
> +    red_channel_client_pipe_add_type(main_channel_client_get_base(mcc),
> RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
>  }
>  
>  static void main_channel_marshall_channels(RedChannelClient *rcc,
> @@ -345,28 +136,16 @@ static void
> main_channel_marshall_channels(RedChannelClient *rcc,
>      free(channels_info);
>  }
>  
> -int main_channel_client_push_ping(MainChannelClient *mcc, int size)
> -{
> -    RedPipeItem *item;
> -
> -    if (mcc == NULL) {
> -        return FALSE;
> -    }
> -    item = red_ping_item_new(mcc, size);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -    return TRUE;
> -}
> -
>  static void main_channel_marshall_ping(RedChannelClient *rcc,
>                                         SpiceMarshaller *m,
>                                         RedPingPipeItem *item)
>  {
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>      SpiceMsgPing ping;
>      int size_left = item->size;
>  
>      red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
> -    ping.id = ++(mcc->ping_id);
> +    ping.id = main_channel_client_next_ping_id(mcc);
>      ping.timestamp = g_get_monotonic_time();
>      spice_marshall_msg_ping(m, &ping);
>  
> @@ -440,13 +219,6 @@ static void
> main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
>      spice_marshall_msg_main_agent_disconnected(m, &disconnect);
>  }
>  
> -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens)
> -{
> -    RedPipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
> -
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_tokens(RedChannelClient *rcc,
>                                           SpiceMarshaller *m,
> RedTokensPipeItem *item)
>  {
> @@ -457,15 +229,6 @@ static void main_channel_marshall_tokens(RedChannelClient
> *rcc,
>      spice_marshall_msg_main_agent_token(m, &tokens);
>  }
>  
> -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> -           spice_marshaller_item_free_func free_data, void *opaque)
> -{
> -    RedPipeItem *item;
> -
> -    item = main_agent_data_item_new(&mcc->base, data, len, free_data,
> opaque);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_agent_data(RedChannelClient *rcc,
>                                               SpiceMarshaller *m,
>                                               RedAgentDataPipeItem *item)
> @@ -490,7 +253,7 @@ static void
> main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
>  static int main_channel_handle_migrate_data(RedChannelClient *rcc,
>      uint32_t size, void *message)
>  {
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>      SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
>  
>      /* not supported with multi-clients */
> @@ -509,21 +272,6 @@ static int
> main_channel_handle_migrate_data(RedChannelClient *rcc,
>      return reds_handle_migrate_data(rcc->channel->reds, mcc,
> (SpiceMigrateDataMain *)(header + 1), size);
>  }
>  
> -void main_channel_client_push_init(MainChannelClient *mcc,
> -                                   int display_channels_hint,
> -                                   int current_mouse_mode,
> -                                   int is_client_mouse_allowed,
> -                                   int multi_media_time,
> -                                   int ram_hint)
> -{
> -    RedPipeItem *item;
> -
> -    item = main_init_item_new(mcc,
> -             mcc->connection_id, display_channels_hint, current_mouse_mode,
> -             is_client_mouse_allowed, multi_media_time, ram_hint);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_init(RedChannelClient *rcc,
>                                         SpiceMarshaller *m,
>                                         RedInitPipeItem *item)
> @@ -546,36 +294,6 @@ static void main_channel_marshall_init(RedChannelClient
> *rcc,
>      spice_marshall_msg_main_init(m, &init);
>  }
>  
> -void main_channel_client_push_name(MainChannelClient *mcc, const char *name)
> -{
> -    RedPipeItem *item;
> -
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> -        return;
> -
> -    item = main_name_item_new(mcc, name);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16])
> -{
> -    RedPipeItem *item;
> -
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> -        return;
> -
> -    item = main_uuid_item_new(mcc, uuid);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
> -{
> -    RedPipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
>  static void main_channel_marshall_notify(RedChannelClient *rcc,
>                                           SpiceMarshaller *m,
> RedNotifyPipeItem *item)
>  {
> @@ -701,14 +419,16 @@ static void
> main_channel_marshall_multi_media_time(RedChannelClient *rcc,
>  
>  static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
>  {
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>      SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>  
>      /* In semi-seamless migration (dest side), the connection is started from
> scratch, and
>       * we ignore any pipe item that arrives before the INIT msg is sent.
>       * For seamless we don't send INIT, and the connection continues from the
> same place
>       * it stopped on the src side. */
> -    if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type !=
> RED_PIPE_ITEM_TYPE_MAIN_INIT) {
> +    if (!main_channel_client_get_init_sent(mcc) &&
> +        !main_channel_client_get_seamless_migration(mcc) &&
> +        base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) {
>          spice_printerr("Init msg for client %p was not sent yet "
>                         "(client is probably during semi-seamless migration).
> Ignoring msg type %d",
>                     rcc->client, base->type);
> @@ -745,7 +465,7 @@ static void main_channel_send_item(RedChannelClient *rcc,
> RedPipeItem *base)
>              main_channel_marshall_migrate_data_item(rcc, m, base);
>              break;
>          case RED_PIPE_ITEM_TYPE_MAIN_INIT:
> -            mcc->init_sent = TRUE;
> +            main_channel_client_on_send_init(mcc);
>              main_channel_marshall_init(rcc, m,
>                  SPICE_CONTAINEROF(base, RedInitPipeItem, base));
>              break;
> @@ -804,77 +524,11 @@ static void
> main_channel_release_pipe_item(RedChannelClient *rcc,
>      free(base);
>  }
>  
> -static void main_channel_client_handle_migrate_connected(MainChannelClient
> *mcc,
> -                                                         int success,
> -                                                         int seamless)
> -{
> -    spice_printerr("client %p connected: %d seamless %d", mcc->base.client,
> success, seamless);
> -    if (mcc->mig_wait_connect) {
> -        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel,
> MainChannel, base);
> -
> -        mcc->mig_wait_connect = FALSE;
> -        mcc->mig_connect_ok = success;
> -        spice_assert(main_channel->num_clients_mig_wait);
> -        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
> -        if (!--main_channel->num_clients_mig_wait) {
> -            reds_on_main_migrate_connected(mcc->base.channel->reds, seamless
> && success);
> -        }
> -    } else {
> -        if (success) {
> -            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> -            red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> -        }
> -    }
> -}
> -
> -void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient
> *mcc,
> -                                                        uint32_t src_version)
> -{
> -    if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc,
> src_version)) {
> -        mcc->seamless_mig_dst = TRUE;
> -        red_channel_client_pipe_add_empty_msg(&mcc->base,
> -                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAML
> ESS_ACK);
> -    } else {
> -        red_channel_client_pipe_add_empty_msg(&mcc->base,
> -                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAM
> LESS_NACK);
> -    }
> -}
> -
> -void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
> -{
> -    if (!red_client_during_migrate_at_target(mcc->base.client)) {
> -        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
> -        return;
> -    }
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGR
> ATE)) {
> -        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
> -                   "client does not support semi-seamless migration");
> -            return;
> -    }
> -    red_client_semi_seamless_migrate_complete(mcc->base.client);
> -}
> -
> -void main_channel_client_migrate_dst_complete(MainChannelClient *mcc)
> -{
> -    if (mcc->mig_wait_prev_complete) {
> -        if (mcc->mig_wait_prev_try_seamless) {
> -            spice_assert(mcc->base.channel->clients_num == 1);
> -            red_channel_client_pipe_add_type(&mcc->base,
> -                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_
> BEGIN_SEAMLESS);
> -        } else {
> -            red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> -        }
> -        mcc->mig_wait_connect = TRUE;
> -        mcc->mig_wait_prev_complete = FALSE;
> -    }
> -}
> -
>  static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size,
> uint16_t type,
>                                        void *message)
>  {
>      MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel,
> base);
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>  
>      switch (type) {
>      case SPICE_MSGC_MAIN_AGENT_START: {
> @@ -923,64 +577,7 @@ static int main_channel_handle_parsed(RedChannelClient
> *rcc, uint32_t size, uint
>          reds_on_main_mouse_mode_request(rcc->channel->reds, message, size);
>          break;
>      case SPICE_MSGC_PONG: {
> -        SpiceMsgPing *ping = (SpiceMsgPing *)message;
> -        uint64_t roundtrip;
> -
> -        roundtrip = g_get_monotonic_time() - ping->timestamp;
> -
> -        if (ping->id == mcc->net_test_id) {
> -            switch (mcc->net_test_stage) {
> -            case NET_TEST_STAGE_WARMUP:
> -                mcc->net_test_id++;
> -                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
> -                mcc->latency = roundtrip;
> -                break;
> -            case NET_TEST_STAGE_LATENCY:
> -                mcc->net_test_id++;
> -                mcc->net_test_stage = NET_TEST_STAGE_RATE;
> -                mcc->latency = MIN(mcc->latency, roundtrip);
> -                break;
> -            case NET_TEST_STAGE_RATE:
> -                mcc->net_test_id = 0;
> -                if (roundtrip <= mcc->latency) {
> -                    // probably high load on client or server result with
> incorrect values
> -                    spice_printerr("net test: invalid values, latency %"
> PRIu64
> -                                   " roundtrip %" PRIu64 ". assuming high"
> -                                   " bandwidth", mcc->latency, roundtrip);
> -                    mcc->latency = 0;
> -                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> -                    red_channel_client_start_connectivity_monitoring(&mcc-
> >base,
> -                                                                     CLIENT_C
> ONNECTIVITY_TIMEOUT);
> -                    break;
> -                }
> -                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) *
> 1000000
> -                                        / (roundtrip - mcc->latency);
> -                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
> -                spice_printerr("net test: latency %f ms, bitrate %"PRIu64"
> bps (%f Mbps)%s",
> -                           (double)mcc->latency / 1000,
> -                           mcc->bitrate_per_sec,
> -                           (double)mcc->bitrate_per_sec / 1024 / 1024,
> -                           main_channel_client_is_low_bandwidth(mcc) ? " LOW
> BANDWIDTH" : "");
> -                red_channel_client_start_connectivity_monitoring(&mcc->base,
> -                                                                 CLIENT_CONNE
> CTIVITY_TIMEOUT);
> -                break;
> -            default:
> -                spice_printerr("invalid net test stage, ping id %d test id %d
> stage %d",
> -                           ping->id,
> -                           mcc->net_test_id,
> -                           mcc->net_test_stage);
> -                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> -            }
> -            break;
> -        } else {
> -            /*
> -             * channel client monitors the connectivity using ping-pong
> messages
> -             */
> -            red_channel_client_handle_message(rcc, size, type, message);
> -        }
> -#ifdef RED_STATISTICS
> -        stat_update_value(rcc->channel->reds, roundtrip);
> -#endif
> +        main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size);
>          break;
>      }
>      case SPICE_MSGC_DISCONNECTING:
> @@ -999,7 +596,7 @@ static uint8_t
> *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
>                                                 uint32_t size)
>  {
>      MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel,
> base);
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    MainChannelClient *mcc = (MainChannelClient*)rcc;
>  
>      if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
>          return reds_get_agent_data_buffer(rcc->channel->reds, mcc, size);
> @@ -1035,60 +632,6 @@ static int
> main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
>      return TRUE;
>  }
>  
> -#ifdef RED_STATISTICS
> -static void do_ping_client(MainChannelClient *mcc,
> -    const char *opt, int has_interval, int interval)
> -{
> -    spice_printerr("");
> -    if (!opt) {
> -        main_channel_client_push_ping(mcc, 0);
> -    } else if (!strcmp(opt, "on")) {
> -        if (has_interval && interval > 0) {
> -            mcc->ping_interval = interval * MSEC_PER_SEC;
> -        }
> -        reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc-
> >ping_interval);
> -    } else if (!strcmp(opt, "off")) {
> -        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> -    } else {
> -        return;
> -    }
> -}
> -
> -static void ping_timer_cb(void *opaque)
> -{
> -    MainChannelClient *mcc = opaque;
> -
> -    if (!red_channel_client_is_connected(&mcc->base)) {
> -        spice_printerr("not connected to peer, ping off");
> -        reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer);
> -        return;
> -    }
> -    do_ping_client(mcc, NULL, 0, 0);
> -    reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc-
> >ping_interval);
> -}
> -#endif /* RED_STATISTICS */
> -
> -static MainChannelClient *main_channel_client_create(MainChannel *main_chan,
> RedClient *client,
> -                                                     RedsStream *stream,
> uint32_t connection_id,
> -                                                     int num_common_caps,
> uint32_t *common_caps,
> -                                                     int num_caps, uint32_t
> *caps)
> -{
> -    MainChannelClient *mcc = (MainChannelClient*)
> -                             red_channel_client_create(sizeof(MainChannelClie
> nt), &main_chan->base,
> -                                                       client, stream, FALSE,
> num_common_caps,
> -                                                       common_caps, num_caps,
> caps);
> -    spice_assert(mcc != NULL);
> -    mcc->connection_id = connection_id;
> -    mcc->bitrate_per_sec = ~0;
> -#ifdef RED_STATISTICS
> -    if (!(mcc->ping_timer =
> reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb,
> mcc))) {
> -        spice_error("ping timer create failed");
> -    }
> -    mcc->ping_interval = PING_INTERVAL;
> -#endif
> -    return mcc;
> -}
> -
>  MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
>                                       RedsStream *stream, uint32_t
> connection_id, int migration,
>                                       int num_common_caps, uint32_t
> *common_caps, int num_caps,
> @@ -1128,33 +671,6 @@ void main_channel_close(MainChannel *main_chan)
>      }
>  }
>  
> -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
> -{
> -    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
> -}
> -
> -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
> -{
> -    // TODO: configurable?
> -    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
> -}
> -
> -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
> -{
> -    return mcc->bitrate_per_sec;
> -}
> -
> -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
> -{
> -    return mcc->latency / 1000;
> -}
> -
> -static void main_channel_client_migrate(RedChannelClient *rcc)
> -{
> -    reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc,
> MainChannelClient, base));
> -    red_channel_client_default_migrate(rcc);
> -}
> -
>  MainChannel* main_channel_new(RedsState *reds)
>  {
>      RedChannel *channel;
> @@ -1190,33 +706,16 @@ MainChannel* main_channel_new(RedsState *reds)
>      return (MainChannel *)channel;
>  }
>  
> -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
> -{
> -    spice_assert(mcc);
> -    return &mcc->base;
> -}
> -
>  static int main_channel_connect_semi_seamless(MainChannel *main_channel)
>  {
>      RingItem *client_link;
>  
>      RING_FOREACH(client_link, &main_channel->base.clients) {
> -        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link,
> MainChannelClient,
> -                                                    base.channel_link);
> -        if (red_channel_client_test_remote_cap(&mcc->base,
> -                                               SPICE_MAIN_CAP_SEMI_SEAMLESS_M
> IGRATE)) {
> -            if (red_client_during_migrate_at_target(mcc->base.client)) {
> -                spice_printerr("client %p: wait till previous migration
> completes", mcc->base.client);
> -                mcc->mig_wait_prev_complete = TRUE;
> -                mcc->mig_wait_prev_try_seamless = FALSE;
> -            } else {
> -                red_channel_client_pipe_add_type(&mcc->base,
> -                                                 RED_PIPE_ITEM_TYPE_MAIN_MIGR
> ATE_BEGIN);
> -                mcc->mig_wait_connect = TRUE;
> -            }
> -            mcc->mig_connect_ok = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        if (main_channel_client_connect_semi_seamless(mcc))
>              main_channel->num_clients_mig_wait++;
> -        }
>      }
>      return main_channel->num_clients_mig_wait;
>  }
> @@ -1228,20 +727,10 @@ static int main_channel_connect_seamless(MainChannel
> *main_channel)
>      spice_assert(main_channel->base.clients_num == 1);
>  
>      RING_FOREACH(client_link, &main_channel->base.clients) {
> -        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link,
> MainChannelClient,
> -                                                    base.channel_link);
> -        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
> -                                                        SPICE_MAIN_CAP_SEAMLE
> SS_MIGRATE));
> -        if (red_client_during_migrate_at_target(mcc->base.client)) {
> -           spice_printerr("client %p: wait till previous migration
> completes", mcc->base.client);
> -           mcc->mig_wait_prev_complete = TRUE;
> -           mcc->mig_wait_prev_try_seamless = TRUE;
> -        } else {
> -            red_channel_client_pipe_add_type(&mcc->base,
> -                                             RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_
> BEGIN_SEAMLESS);
> -            mcc->mig_wait_connect = TRUE;
> -        }
> -        mcc->mig_connect_ok = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        main_channel_client_connect_seamless(mcc);
>          main_channel->num_clients_mig_wait++;
>      }
>      return main_channel->num_clients_mig_wait;
> @@ -1261,12 +750,12 @@ int main_channel_migrate_connect(MainChannel
> *main_channel, RedsMigSpice *mig_ta
>          return main_channel_connect_semi_seamless(main_channel);
>      } else {
>          RingItem *client_item;
> -        MainChannelClient *mcc;
> +        RedChannelClient *rcc;
>  
>          client_item = ring_get_head(&main_channel->base.clients);
> -        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient,
> base.channel_link);
> +        rcc = SPICE_CONTAINEROF(client_item, RedChannelClient, channel_link);
>  
> -        if (!red_channel_client_test_remote_cap(&mcc->base,
> +        if (!red_channel_client_test_remote_cap(rcc,
>                                                  SPICE_MAIN_CAP_SEAMLESS_MIGRA
> TE)) {
>              return main_channel_connect_semi_seamless(main_channel);
>          } else {
> @@ -1281,15 +770,10 @@ void main_channel_migrate_cancel_wait(MainChannel
> *main_chan)
>      RingItem *client_link;
>  
>      RING_FOREACH(client_link, &main_chan->base.clients) {
> -        MainChannelClient *mcc;
> -
> -        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> base.channel_link);
> -        if (mcc->mig_wait_connect) {
> -            spice_printerr("client %p cancel wait connect", mcc-
> >base.client);
> -            mcc->mig_wait_connect = FALSE;
> -            mcc->mig_connect_ok = FALSE;
> -        }
> -        mcc->mig_wait_prev_complete = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        main_channel_client_migrate_cancel_wait(mcc);
>      }
>      main_chan->num_clients_mig_wait = 0;
>  }
> @@ -1307,29 +791,11 @@ int main_channel_migrate_src_complete(MainChannel
> *main_chan, int success)
>      }
>  
>      RING_FOREACH(client_link, &main_chan->base.clients) {
> -        MainChannelClient *mcc;
> -        int semi_seamless_support;
> -
> -        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> base.channel_link);
> -        semi_seamless_support = red_channel_client_test_remote_cap(&mcc-
> >base,
> -                                                   SPICE_MAIN_CAP_SEMI_SEAMLE
> SS_MIGRATE);
> -        if (semi_seamless_support && mcc->mig_connect_ok) {
> -            if (success) {
> -                spice_printerr("client %p MIGRATE_END", mcc->base.client);
> -                red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_END);
> -                semi_seamless_count++;
> -            } else {
> -                spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> -                red_channel_client_pipe_add_empty_msg(&mcc->base,
> SPICE_MSG_MAIN_MIGRATE_CANCEL);
> -            }
> -        } else {
> -            if (success) {
> -                spice_printerr("client %p SWITCH_HOST", mcc->base.client);
> -                red_channel_client_pipe_add_type(&mcc->base,
> RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> -            }
> -        }
> -        mcc->mig_connect_ok = FALSE;
> -        mcc->mig_wait_connect = FALSE;
> +        RedChannelClient *rcc = SPICE_CONTAINEROF(client_link,
> RedChannelClient,
> +                                                    channel_link);
> +        MainChannelClient *mcc = (MainChannelClient*)rcc;
> +        if (main_channel_client_migrate_src_complete(mcc, success))
> +            semi_seamless_count++;
>     }
>     return semi_seamless_count;
>  }
> diff --git a/server/main-channel.h b/server/main-channel.h
> index 4eb9a9c..868a14a 100644
> --- a/server/main-channel.h
> +++ b/server/main-channel.h
> @@ -23,6 +23,7 @@
>  #include <common/marshaller.h>
>  
>  #include "red-channel.h"
> +#include "main-channel-client.h"
>  
>  // TODO: Defines used to calculate receive buffer size, and also by reds.c
>  // other options: is to make a reds_main_consts.h, to duplicate defines.
> @@ -59,34 +60,11 @@ void main_channel_close(MainChannel *main_chan); // not
> destroy, just socket clo
>  void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
> int is_client_mouse_allowed);
>  void main_channel_push_agent_connected(MainChannel *main_chan);
>  void main_channel_push_agent_disconnected(MainChannel *main_chan);
> -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t
> num_tokens);
> -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t*
> data, size_t len,
> -                                         spice_marshaller_item_free_func
> free_data, void *opaque);
> -void main_channel_client_start_net_test(MainChannelClient *mcc, int
> test_rate);
> -// TODO: huge. Consider making a reds_* interface for these functions
> -// and calling from main.
> -void main_channel_client_push_init(MainChannelClient *mcc,
> -                                   int display_channels_hint,
> -                                   int current_mouse_mode,
> -                                   int is_client_mouse_allowed,
> -                                   int multi_media_time,
> -                                   int ram_hint);
> -void main_channel_client_push_notify(MainChannelClient *mcc, const char
> *msg);
>  void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
>  int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa,
> socklen_t *salen);
>  int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa,
> socklen_t *salen);
>  
> -/*
> - * return TRUE if network test had been completed successfully.
> - * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to
> 0
> - */
> -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
> -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
> -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
> -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
> -
>  int main_channel_is_connected(MainChannel *main_chan);
> -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
>  
>  /* switch host migration */
>  void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice
> *mig_target);
> @@ -100,8 +78,5 @@ int main_channel_migrate_connect(MainChannel *main_channel,
> RedsMigSpice *mig_ta
>  void main_channel_migrate_cancel_wait(MainChannel *main_chan);
>  /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was
> sent*/
>  int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
> -void main_channel_client_migrate_dst_complete(MainChannelClient *mcc);
> -void main_channel_client_push_name(MainChannelClient *mcc, const char *name);
> -void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t
> uuid[16]);
>  
>  #endif